<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>solrevdev tech radar</title>
 <link href="https://solrevdev.com/atom.xml" rel="self"/>
 <link href="https://solrevdev.com/"/>
 <updated>2026-05-27T22:23:29+00:00</updated>
 <id>https://solrevdev.com</id>
 <author>
   <name>John Smith</name>
   <email>john@solrevdev.com</email>
 </author>

 
 <entry>
   <title>How to Upgrade to .NET 10 LTS - Complete Guide for .NET Global Tools with Multi-Targeting</title>
   <link href="https://solrevdev.com/2025/11/14/upgrading-seedfolder-to-dotnet-10-lts.html"/>
   <updated>2025-11-14T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2025/11/14/upgrading-seedfolder-to-dotnet-10-lts</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt; ☀&lt;/p&gt;

&lt;p&gt;With .NET 10 now released as the latest Long-Term Support (LTS) version, it was time to upgrade &lt;a href=&quot;https://github.com/solrevdev/seedfolder&quot;&gt;SeedFolder&lt;/a&gt; to support the newest framework.&lt;/p&gt;

&lt;p&gt;This comprehensive .NET 10 upgrade guide walks you through migrating a .NET global tool from .NET 8 and 9 to .NET 10 while maintaining full backward compatibility.&lt;/p&gt;

&lt;p&gt;Whether you’re upgrading a .NET global tool, console application, or library, this migration tutorial covers everything you need: multi-target framework configuration, dependency management, CI/CD pipeline updates, and thorough testing strategies for a smooth .NET 10 migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Upgrade to .NET 10?&lt;/strong&gt; 🎯&lt;/p&gt;

&lt;p&gt;Migrating to .NET 10 LTS provides significant benefits for .NET developers. As the latest Long-Term Support release (supported until November 2028), upgrading to .NET 10 ensures your applications stay current with the latest framework improvements.&lt;/p&gt;

&lt;p&gt;Benefits of upgrading to .NET 10:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Latest LTS&lt;/strong&gt;: Long-term support until November 2028&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Performance improvements&lt;/strong&gt;: Built-in performance enhancements from .NET 10&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Forward compatibility&lt;/strong&gt;: Automatic use of the highest installed SDK&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Backward compatibility&lt;/strong&gt;: Continued support for .NET 8 (LTS) and .NET 9 (STS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The beauty of multi-targeting during your .NET 10 migration is that users with any of these SDK versions can install and run the tool.&lt;/p&gt;

&lt;p&gt;When multiple SDKs are installed, the .NET CLI automatically selects the highest compatible version, making your upgrade path seamless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Upgrade Process&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;The upgrade was surprisingly straightforward, thanks to .NET’s excellent multi-targeting support. Here’s the step-by-step process I followed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Research and Planning&lt;/strong&gt; 📚&lt;/p&gt;

&lt;p&gt;Before making any changes, I researched the .NET 10 requirements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Target Framework Moniker (TFM)&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net10.0&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SDK Version&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.100&lt;/code&gt; or later&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Breaking Changes&lt;/strong&gt;: Reviewed Microsoft docs for any breaking changes (none affecting SeedFolder)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also reviewed the git history to understand how previous SDK upgrades were handled:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git log &lt;span class=&quot;nt&quot;&gt;--all&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--grep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;.NET&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--oneline&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This showed that the last major upgrade (&lt;a href=&quot;https://github.com/solrevdev/seedfolder/commit/259c45284d67cb8ab6b1ff2f65d9fe2c5bfa8223&quot;&gt;PR #259c452&lt;/a&gt;) added .NET 8 and 9 support, following a similar pattern I could replicate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Update Project File&lt;/strong&gt; 🔧&lt;/p&gt;

&lt;p&gt;The key change was adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net10.0&lt;/code&gt; to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TargetFrameworks&lt;/code&gt; property in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.csproj&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
&lt;/span&gt;  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-   &amp;lt;TargetFrameworks&amp;gt;net8.0;net9.0&amp;lt;/TargetFrameworks&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;TargetFrameworks&amp;gt;net8.0;net9.0;net10.0&amp;lt;/TargetFrameworks&amp;gt;
&lt;/span&gt;    &amp;lt;LangVersion&amp;gt;latest&amp;lt;/LangVersion&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;PackAsTool&amp;gt;true&amp;lt;/PackAsTool&amp;gt;
    &amp;lt;ToolCommandName&amp;gt;seedfolder&amp;lt;/ToolCommandName&amp;gt;
    &amp;lt;PackageOutputPath&amp;gt;./nupkg&amp;lt;/PackageOutputPath&amp;gt;
    &amp;lt;NoDefaultExcludes&amp;gt;true&amp;lt;/NoDefaultExcludes&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-   &amp;lt;Version&amp;gt;1.3.3&amp;lt;/Version&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;Version&amp;gt;1.4.0&amp;lt;/Version&amp;gt;
&lt;/span&gt;    &amp;lt;!-- ... other properties ... --&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
&lt;span class=&quot;gd&quot;&gt;&amp;lt;/Project&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Important considerations:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Use semicolon-separated list for multiple targets&lt;/li&gt;
  &lt;li&gt;Bump the version number for NuGet release (1.3.3 → 1.4.0)&lt;/li&gt;
  &lt;li&gt;Maintain existing targets for backward compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Update CI/CD Pipeline&lt;/strong&gt; ⚙️&lt;/p&gt;

&lt;p&gt;The GitHub Actions workflow needed to install the .NET 10 SDK alongside existing versions:&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;- name: setup .net core sdk
&lt;/span&gt;  uses: actions/setup-dotnet@v4
  with:
      dotnet-version: |
        8.0.x
        9.0.x
&lt;span class=&quot;gi&quot;&gt;+       10.0.x
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This ensures the CI pipeline can build all three target frameworks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Dependency Management&lt;/strong&gt; 📦&lt;/p&gt;

&lt;p&gt;I ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet outdated&lt;/code&gt; to check for package updates:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet tool &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; dotnet-outdated-tool
dotnet outdated
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This revealed that Figgle (the ASCII art library) had a newer version (0.6.5). However, upon investigation, version 0.6.x introduced breaking changes by splitting fonts into a separate package. Since the upgrade goal was .NET 10 support, not dependency updates, I kept Figgle at 0.5.1 to avoid unnecessary complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Build and Test&lt;/strong&gt; ✅&lt;/p&gt;

&lt;p&gt;Building for multiple frameworks is straightforward with multi-targeting:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet build solrevdev.seedfolder.sln &lt;span class=&quot;nt&quot;&gt;--configuration&lt;/span&gt; Release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Output showed successful builds for all three targets:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;solrevdev.seedfolder -&amp;gt; src/bin/Release/net8.0/solrevdev.seedfolder.dll
solrevdev.seedfolder -&amp;gt; src/bin/Release/net9.0/solrevdev.seedfolder.dll
solrevdev.seedfolder -&amp;gt; src/bin/Release/net10.0/solrevdev.seedfolder.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Testing the Tool Locally&lt;/strong&gt; 🧪&lt;/p&gt;

&lt;p&gt;Before publishing, I packaged and tested the tool locally:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Package the tool&lt;/span&gt;
dotnet pack &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; /tmp/seedfolder-test

&lt;span class=&quot;c&quot;&gt;# Install from local package&lt;/span&gt;
dotnet tool uninstall &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; solrevdev.seedfolder
dotnet tool &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--add-source&lt;/span&gt; /tmp/seedfolder-test solrevdev.seedfolder

&lt;span class=&quot;c&quot;&gt;# Verify installation&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Output: seedfolder version 1.4.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then I tested various commands in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; to ensure functionality:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Test dry-run mode&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--dry-run&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; node test-node-app

&lt;span class=&quot;c&quot;&gt;# Create Python project&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; python test-python-app

&lt;span class=&quot;c&quot;&gt;# Create .NET project&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; dotnet test-dotnet-app

&lt;span class=&quot;c&quot;&gt;# Verify help and template listing&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--help&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--list-templates&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;All tests passed successfully! 🎉&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Update Documentation&lt;/strong&gt; 📝&lt;/p&gt;

&lt;p&gt;Documentation updates included:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;README.md&lt;/strong&gt; - Updated requirements section:&lt;/p&gt;
&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;##&lt;/span&gt; Requirements

- This tool requires **.NET 8.0 or .NET 9.0 SDK** to be installed
&lt;span class=&quot;gi&quot;&gt;+ This tool requires **.NET 8.0, .NET 9.0, or .NET 10.0 SDK** to be installed
&lt;/span&gt;
- **Runtime**: .NET 8.0 or later
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt; - Updated framework information:&lt;/p&gt;
&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;##&lt;/span&gt; Multi-Target Framework Support
&lt;span class=&quot;gd&quot;&gt;- The project targets .NET 8.0 (LTS) and 9.0 (STS)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ The project targets .NET 8.0 (LTS), 9.0 (STS), and 10.0 (LTS)
+ .NET 10 is the latest LTS release providing long-term support until November 2028.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Multi-Targeting Best Practices&lt;/strong&gt; 💡&lt;/p&gt;

&lt;p&gt;From this experience, here are some best practices for multi-targeting .NET global tools:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Use Multi-Targeting, Not Multiple Projects&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Good: Single project, multiple targets --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net8.0;net9.0;net10.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;&amp;lt;!-- Bad: Multiple projects for different versions --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. Maintain LTS Versions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keep the previous LTS version (net8.0) alongside the new one. This gives users flexibility and ensures broad compatibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Test All Targets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The NuGet package includes all target frameworks:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tools/net8.0/any/solrevdev.seedfolder.dll
tools/net9.0/any/solrevdev.seedfolder.dll
tools/net10.0/any/solrevdev.seedfolder.dll
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verify each target builds correctly and the tool runs on all supported SDKs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Handle Dependencies Carefully&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When upgrading, check if dependencies support all your target frameworks. If a dependency doesn’t support your newest target, you have options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Keep the dependency at a compatible version&lt;/li&gt;
  &lt;li&gt;Use conditional package references for different targets&lt;/li&gt;
  &lt;li&gt;Find an alternative package&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Update CI/CD First&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Install all SDK versions in your CI pipeline before merging. This catches incompatibilities early:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;setup .net core sdk&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-dotnet@v4&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dotnet-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;8.0.x&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;9.0.x&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;10.0.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The Results&lt;/strong&gt; 📊&lt;/p&gt;

&lt;p&gt;After merging &lt;a href=&quot;https://github.com/solrevdev/seedfolder/pull/17&quot;&gt;PR #17&lt;/a&gt;, the CI/CD pipeline automatically:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Built all three target frameworks&lt;/li&gt;
  &lt;li&gt;Ran integration tests&lt;/li&gt;
  &lt;li&gt;Packaged the NuGet package&lt;/li&gt;
  &lt;li&gt;Published version 1.4.0 to NuGet&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Users can now install the updated version:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet tool update &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; solrevdev.seedfolder
seedfolder &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Output: seedfolder version 1.4.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The tool automatically uses the highest installed SDK when run, so users with .NET 10 get the latest performance improvements while users on .NET 8 or 9 continue to work seamlessly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework Support Timeline&lt;/strong&gt; 📅&lt;/p&gt;

&lt;p&gt;Current support timeline for SeedFolder:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Framework&lt;/th&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;th&gt;Support Until&lt;/th&gt;
      &lt;th&gt;Status&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;.NET 8.0&lt;/td&gt;
      &lt;td&gt;LTS&lt;/td&gt;
      &lt;td&gt;November 2026&lt;/td&gt;
      &lt;td&gt;✅ Supported&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;.NET 9.0&lt;/td&gt;
      &lt;td&gt;STS&lt;/td&gt;
      &lt;td&gt;18 months&lt;/td&gt;
      &lt;td&gt;✅ Supported&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;.NET 10.0&lt;/td&gt;
      &lt;td&gt;LTS&lt;/td&gt;
      &lt;td&gt;November 2028&lt;/td&gt;
      &lt;td&gt;✅ Supported&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;strong&gt;Lessons Learned&lt;/strong&gt; 🎓&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Multi-targeting is powerful&lt;/strong&gt;: Adding .NET 10 support while maintaining .NET 8 and 9 compatibility was trivial thanks to proper multi-targeting setup.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Dependency management matters&lt;/strong&gt;: Always check dependencies when upgrading. Sometimes staying on older (but stable) dependency versions is the right choice.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Testing is essential&lt;/strong&gt;: Local testing before publishing caught issues that automated tests might miss.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;Documentation updates are important&lt;/strong&gt;: Users need to know what versions are supported and what’s changed.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;strong&gt;CI/CD automation pays off&lt;/strong&gt;: Once configured properly, the entire build-test-publish pipeline runs automatically on merge.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What’s Next?&lt;/strong&gt; 🔮&lt;/p&gt;

&lt;p&gt;With .NET 10 support in place, SeedFolder is well-positioned for the future. The next areas of focus include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Template marketplace functionality (&lt;a href=&quot;https://github.com/solrevdev/seedfolder/issues/15&quot;&gt;Issue #15&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;Additional project templates based on community feedback&lt;/li&gt;
  &lt;li&gt;Enhanced template customization options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full source code and history of this upgrade are available on &lt;a href=&quot;https://github.com/solrevdev/seedfolder/pull/17&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation&lt;/strong&gt; 📦&lt;/p&gt;

&lt;p&gt;Try the latest version with .NET 10 support:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install or update to the latest version&lt;/span&gt;
dotnet tool update &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; solrevdev.seedfolder

&lt;span class=&quot;c&quot;&gt;# Create a new project with your favorite template&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; python my-new-project

&lt;span class=&quot;c&quot;&gt;# Or use interactive mode&lt;/span&gt;
seedfolder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Success! 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Building ytx - A YouTube Transcript Extractor as a .NET Global Tool</title>
   <link href="https://solrevdev.com/2025/10/27/building-ytx-a-youtube-transcript-extractor-as-a-dotnet-global-tool.html"/>
   <updated>2025-10-27T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2025/10/27/building-ytx-a-youtube-transcript-extractor-as-a-dotnet-global-tool</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt; ☀&lt;/p&gt;

&lt;p&gt;Sometimes you need to extract structured data from YouTube videos for analysis, documentation, or automation. While there are various web-based solutions, having a command-line tool that outputs clean JSON makes integration with scripts and pipelines much easier.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;ytx&lt;/strong&gt; - a .NET Global Tool that extracts YouTube video metadata and transcripts as structured JSON. The tool takes a YouTube URL and returns the video title, description, and full transcript with timestamps in both raw text and markdown formats. This post walks you through building your own .NET global tool from scratch, covering architecture design, caption handling, JSON serialization, NuGet packaging, and setting up automated CI/CD with GitHub Actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt; 🎯&lt;/p&gt;

&lt;p&gt;I wanted a simple way to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Extract YouTube video transcripts for analysis&lt;/li&gt;
  &lt;li&gt;Get structured JSON output for easy parsing&lt;/li&gt;
  &lt;li&gt;Handle different caption languages and auto-generated captions&lt;/li&gt;
  &lt;li&gt;Create timestamped links back to specific moments in videos&lt;/li&gt;
  &lt;li&gt;Package everything as a portable command-line tool&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Project Architecture&lt;/strong&gt; 🏗️&lt;/p&gt;

&lt;p&gt;The tool is built as a single-file .NET console application with a simple but effective architecture:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Output&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transcriptRaw&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transcript&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The data flow is straightforward:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Input validation (command-line args or JSON via stdin)&lt;/li&gt;
  &lt;li&gt;YouTube video data extraction via YoutubeExplode&lt;/li&gt;
  &lt;li&gt;Caption track discovery and selection&lt;/li&gt;
  &lt;li&gt;Transcript formatting (raw + markdown with timestamps)&lt;/li&gt;
  &lt;li&gt;JSON serialization to stdout&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Getting Started&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;First, I created the project structure:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key to making this a global tool is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.csproj&lt;/code&gt; configuration:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFrameworks&amp;gt;&lt;/span&gt;net8.0;net9.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFrameworks&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ImplicitUsings&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ImplicitUsings&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Nullable&amp;gt;&lt;/span&gt;enable&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Nullable&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- dotnet tool packaging --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackAsTool&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackAsTool&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ToolCommandName&amp;gt;&lt;/span&gt;ytx&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ToolCommandName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageId&amp;gt;&lt;/span&gt;solrevdev.ytx&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageId&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- CI auto-bumps this --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;1.0.2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- NuGet metadata --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Authors&amp;gt;&lt;/span&gt;solrevdev&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Authors&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageDescription&amp;gt;&lt;/span&gt;Extract YouTube title, description, and transcript (raw + Markdown) as JSON.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageDescription&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageTags&amp;gt;&lt;/span&gt;YouTube;transcript;captions;cli;dotnet-tool;json&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageTags&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RepositoryUrl&amp;gt;&lt;/span&gt;https://github.com/solrevdev/solrevdev.ytx&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RepositoryUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageProjectUrl&amp;gt;&lt;/span&gt;https://github.com/solrevdev/solrevdev.ytx&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageProjectUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageLicenseExpression&amp;gt;&lt;/span&gt;MIT&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageLicenseExpression&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReadmeFile&amp;gt;&lt;/span&gt;README.md&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageReadmeFile&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- where pack puts .nupkg --&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageOutputPath&amp;gt;&lt;/span&gt;../../nupkg&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageOutputPath&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;YoutubeExplode&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;6.5.4&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;../../README.md&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Pack=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;PackagePath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;\&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The crucial elements are:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PackAsTool&amp;gt;true&lt;/code&gt; - Makes this a global tool&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToolCommandName&lt;/code&gt; - The command users will type&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TargetFrameworks&lt;/code&gt; - Multi-targeting for compatibility&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PackageOutputPath&lt;/code&gt; - Consistent build artifacts location&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Core Implementation&lt;/strong&gt; ⚙️&lt;/p&gt;

&lt;p&gt;The main challenge was handling YouTube’s various caption formats and languages. Here’s the complete Main method:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsInputRedirected&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;In&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ReadToEndAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stdin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializerOptions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PropertyNameCaseInsensitive&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Usage: ytx &amp;lt;YouTube URL&amp;gt;\n   or: echo &apos;{\&quot;url\&quot;:\&quot;https://...\&quot;}&apos; | ytx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;YoutubeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoId&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;VideoId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ArgumentException&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Invalid YouTube URL/ID.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;video&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Videos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;videoId&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transcriptRaw&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transcriptMd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifest&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Videos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClosedCaptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetManifestAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;track&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;manifest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tracks&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;OrderByDescending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Language&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;English&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringComparison&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ThenByDescending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsAutoGenerated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;FirstOrDefault&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;track&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;captions&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Videos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClosedCaptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;track&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawSb&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mdSb&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;StringBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

                &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;captions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Captions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NormalizeCaption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rawSb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawSb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;&apos; &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;rawSb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToHhMmSs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;https://www.youtube.com/watch?v=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;amp;t=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TotalSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;mdSb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AppendLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;- [&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;) &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;n&quot;&gt;transcriptRaw&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawSb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;transcriptMd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mdSb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TrimEnd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;transcriptRaw&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;transcriptMd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;_No transcript/captions available for this video._&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;transcriptRaw&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;transcriptMd&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;_No transcript/captions available or captions retrieval failed._&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Output&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;transcriptRaw&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transcriptRaw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;transcript&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transcriptMd&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializerOptions&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;WriteIndented&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Encoder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Encodings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;JavaScriptEncoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnsafeRelaxedJsonEscaping&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OutputEncoding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Encoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UTF8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Error: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Smart Caption Detection&lt;/strong&gt; 🧠&lt;/p&gt;

&lt;p&gt;One of the trickiest parts was handling YouTube’s various caption formats. The tool needs to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Prefer English captions when available&lt;/li&gt;
  &lt;li&gt;Fall back to any available language&lt;/li&gt;
  &lt;li&gt;Handle both manual and auto-generated captions&lt;/li&gt;
  &lt;li&gt;Gracefully handle videos without captions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The caption selection logic is embedded in the Main method above (lines 32-62), where it:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Gets the caption manifest for the video&lt;/li&gt;
  &lt;li&gt;Orders tracks by English language preference, then by auto-generated status&lt;/li&gt;
  &lt;li&gt;Downloads the selected caption track and formats both raw and markdown output&lt;/li&gt;
  &lt;li&gt;Handles error cases gracefully with appropriate fallback messages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Utility Functions&lt;/strong&gt; 🔧&lt;/p&gt;

&lt;p&gt;The tool includes helper functions for formatting and text normalization:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ToHhMmSs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TimeSpan&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TotalHours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Minutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Seconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;NormalizeCaption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;\s+&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&amp;amp;nbsp;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Local Development and Testing&lt;/strong&gt; 🧪&lt;/p&gt;

&lt;p&gt;During development, I used this workflow:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Restore dependencies&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;restore&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src/Ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Build the project&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src/Ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Release&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Test with a YouTube URL&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--project&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src/Ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--framework&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;net8.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.youtube.com/watch?v=dQw4w9WgXcQ&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Package for local installation testing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pack&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;src/Ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Release&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;solrevdev.ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--add-source&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;/nupkg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This allowed me to test the tool end-to-end before publishing to NuGet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output Format&lt;/strong&gt; 📄&lt;/p&gt;

&lt;p&gt;The tool produces clean, structured JSON:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.youtube.com/watch?v=dQw4w9WgXcQ&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Rick Astley - Never Gonna Give You Up (Official Music Video)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The official video for &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Never Gonna Give You Up&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; by Rick Astley...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;transcriptRaw&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;We&apos;re no strangers to love You know the rules and so do I...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;transcript&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;- [00:17](https://www.youtube.com/watch?v=dQw4w9WgXcQ&amp;amp;t=17s) We&apos;re no strangers to love&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;- [00:20](https://www.youtube.com/watch?v=dQw4w9WgXcQ&amp;amp;t=20s) You know the rules and so do I...&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The markdown transcript format makes it easy to create documentation with clickable timestamps that jump directly to specific moments in the video.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production-Ready CI/CD Pipeline with GitHub Actions&lt;/strong&gt; 🤖&lt;/p&gt;

&lt;p&gt;To streamline releases and reduce manual work, I set up GitHub Actions to automatically handle the entire release pipeline. Unlike simple workflows, this production pipeline:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Runs on every push to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; (only when source files change, avoiding redundant builds)&lt;/li&gt;
  &lt;li&gt;Allows manual triggers with version bump selection (patch, minor, or major)&lt;/li&gt;
  &lt;li&gt;Automatically increments semantic versions in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.csproj&lt;/code&gt; file&lt;/li&gt;
  &lt;li&gt;Commits version changes back to the repository with git tags&lt;/li&gt;
  &lt;li&gt;Builds and publishes to NuGet with proper error handling&lt;/li&gt;
  &lt;li&gt;Creates GitHub releases with auto-generated release notes&lt;/li&gt;
  &lt;li&gt;Supports multiple .NET versions (8.x and 9.x) for maximum compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complete workflow file (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows/publish.yml&lt;/code&gt;) handles all of this:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Publish NuGet (ytx)&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;bump&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Version&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;bump&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;(major|minor|patch)&apos;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;patch&apos;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;master&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;src/Ytx/**&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.github/workflows/publish.yml&apos;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;write&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;read&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;PROJECT_DIR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;src/Ytx&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;CSPROJ&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;src/Ytx/Ytx.csproj&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;NUPKG_DIR&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nupkg&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;NUGET_SOURCE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://api.nuget.org/v3/index.json&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build-pack-publish&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Checkout&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Setup .NET&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-dotnet@v4&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;dotnet-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;9.x&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;8.x&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Restore&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet restore $PROJECT_DIR&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Determine and bump version&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;bump&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;bash&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;set -euo pipefail&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;CURR=$(grep -oPm1 &apos;(?&amp;lt;=&amp;lt;Version&amp;gt;)[^&amp;lt;]+&apos; &quot;$CSPROJ&quot;)&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;echo &quot;Current version: $CURR&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;IFS=&apos;.&apos; read -r MAJ MIN PAT &amp;lt;&amp;lt;&amp;lt; &quot;$CURR&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;BUMP=&quot;${{ github.event.inputs.bump || &apos;patch&apos; }}&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;case &quot;$BUMP&quot; in&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;major) MAJ=$((MAJ+1)); MIN=0; PAT=0 ;;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;minor) MIN=$((MIN+1)); PAT=0 ;;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;patch|*) PAT=$((PAT+1)) ;;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;esac&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;NEW=&quot;$MAJ.$MIN.$PAT&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;echo &quot;New version: $NEW&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;sed -i &quot;s|&amp;lt;Version&amp;gt;$CURR&amp;lt;/Version&amp;gt;|&amp;lt;Version&amp;gt;$NEW&amp;lt;/Version&amp;gt;|&quot; &quot;$CSPROJ&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;echo &quot;version=$NEW&quot; &amp;gt;&amp;gt; &quot;$GITHUB_OUTPUT&quot;&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Commit version bump&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ github.ref == &apos;refs/heads/master&apos; }}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;git config user.name &quot;github-actions[bot]&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;git config user.email &quot;github-actions[bot]@users.noreply.github.com&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;git add ${{ env.CSPROJ }}&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;git commit -m &quot;chore: bump version to ${{ steps.bump.outputs.version }}&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;git tag &quot;v${{ steps.bump.outputs.version }}&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;git push --follow-tags&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet build $PROJECT_DIR -c Release --no-restore&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Pack&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet pack $PROJECT_DIR -c Release --no-build&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Publish to NuGet&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;NUGET_API_KEY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ secrets.NUGET_API_KEY }}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;dotnet nuget push $NUPKG_DIR/*.nupkg \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--api-key &quot;$NUGET_API_KEY&quot; \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--source &quot;$NUGET_SOURCE&quot; \&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;--skip-duplicate&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Create GitHub Release&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;softprops/action-gh-release@v2&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v${{ steps.bump.outputs.version }}&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ytx v${{ steps.bump.outputs.version }}&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;generate_release_notes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Understanding the Workflow Architecture&lt;/strong&gt; 🏗️&lt;/p&gt;

&lt;p&gt;This workflow implements several production best practices that help .NET developers distribute global tools effectively:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment Variables for DRY Principle:&lt;/strong&gt;
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;env:&lt;/code&gt; block defines reusable values (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROJECT_DIR&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CSPROJ&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NUPKG_DIR&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NUGET_SOURCE&lt;/code&gt;) referenced throughout the workflow. This approach keeps configuration centralized: change a directory path once, and it updates everywhere. This is crucial when managing complex multi-project solutions or adjusting package output locations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissions Block:&lt;/strong&gt;
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;permissions:&lt;/code&gt; section restricts the workflow to only what it needs:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;contents: write&lt;/code&gt;: Required to create commits, tags, and push back to the repository&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;packages: read&lt;/code&gt;: Required for accessing NuGet package data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This follows the principle of least privilege, improving security by preventing the workflow from performing unauthorized actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smart Trigger Configuration:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;master&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;src/Ytx/**&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.github/workflows/publish.yml&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;paths:&lt;/code&gt; filter prevents unnecessary builds when only documentation or other non-source files change. This saves CI/CD minutes and reduces feedback latency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic Version Bumping with bash:&lt;/strong&gt;
The version bump step demonstrates how to parse and manipulate semantic versions programmatically:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;IFS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;.&apos;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; MAJ MIN PAT &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$CURR&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# Parse 1.0.2 into components&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$BUMP&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in
  &lt;/span&gt;major&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;MAJ&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;MAJ+1&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;MIN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PAT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# 1.0.2 → 2.0.0&lt;/span&gt;
  minor&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;MIN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;MIN+1&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PAT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0 &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;         &lt;span class=&quot;c&quot;&gt;# 1.0.2 → 1.1.0&lt;/span&gt;
  patch|&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PAT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;PAT+1&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;;&lt;/span&gt;             &lt;span class=&quot;c&quot;&gt;# 1.0.2 → 1.0.3&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This approach ensures version consistency without manually editing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.csproj&lt;/code&gt; files. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo &quot;version=$NEW&quot; &amp;gt;&amp;gt; &quot;$GITHUB_OUTPUT&quot;&lt;/code&gt; sends the new version to subsequent steps, a key pattern in GitHub Actions workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git Automation for Reproducible Releases:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git config user.name &lt;span class=&quot;s2&quot;&gt;&quot;github-actions[bot]&quot;&lt;/span&gt;
git add &lt;span class=&quot;err&quot;&gt;$&lt;/span&gt;
git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;chore: bump version to $&quot;&lt;/span&gt;
git tag &lt;span class=&quot;s2&quot;&gt;&quot;v$&quot;&lt;/span&gt;
git push &lt;span class=&quot;nt&quot;&gt;--follow-tags&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This creates an immutable audit trail. Every NuGet release corresponds to:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;A specific git commit (with the bumped version)&lt;/li&gt;
  &lt;li&gt;A git tag (for easy checkout: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git checkout v1.0.3&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;A GitHub release (with release notes)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This traceability is essential for troubleshooting issues and understanding what code produced which package version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimized Build Pipeline:&lt;/strong&gt;
Notice the careful use of build flags:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet restore &lt;span class=&quot;nv&quot;&gt;$PROJECT_DIR&lt;/span&gt;              &lt;span class=&quot;c&quot;&gt;# Explicit restore&lt;/span&gt;
dotnet build &lt;span class=&quot;nv&quot;&gt;$PROJECT_DIR&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;--no-restore&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;# Skip redundant restore&lt;/span&gt;
dotnet pack &lt;span class=&quot;nv&quot;&gt;$PROJECT_DIR&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;--no-build&lt;/span&gt;       &lt;span class=&quot;c&quot;&gt;# Skip redundant build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-restore&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-build&lt;/code&gt; flags prevent repeating expensive operations. For .NET global tools especially, proper dependency isolation matters because you want to ensure your tool works across different .NET SDK versions, which is why this workflow tests against both 8.x and 9.x.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NuGet Publishing with Idempotency:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet nuget push &lt;span class=&quot;nv&quot;&gt;$NUPKG_DIR&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.nupkg &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--skip-duplicate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--skip-duplicate&lt;/code&gt; flag means you can safely re-run the workflow without errors if a version was already published. This is crucial for reliability, since sometimes you need to retry a build due to temporary network issues or API timeouts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated GitHub Releases:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Create GitHub Release&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;softprops/action-gh-release@v2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tag_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v$&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;generate_release_notes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This automatically creates a GitHub release with auto-generated release notes based on commit messages since the last release. Users see a clear changelog without manual effort, and the release is properly associated with the NuGet package version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installation and Usage&lt;/strong&gt; 📦&lt;/p&gt;

&lt;p&gt;Once published to NuGet, users can install the tool globally:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install the tool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;solrevdev.ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Basic usage&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.youtube.com/watch?v=dQw4w9WgXcQ&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Via JSON input for scripting&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;echo&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;{&quot;url&quot;:&quot;https://www.youtube.com/watch?v=dQw4w9WgXcQ&quot;}&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Save to file for further processing&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.youtube.com/watch?v=dQw4w9WgXcQ&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;video-data.json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Update to latest version&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;solrevdev.ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Error Handling and Edge Cases&lt;/strong&gt; ⚠️&lt;/p&gt;

&lt;p&gt;The tool handles various error scenarios gracefully:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Invalid YouTube URLs (exit code 2)&lt;/li&gt;
  &lt;li&gt;Private or region-restricted videos (exit code 1)&lt;/li&gt;
  &lt;li&gt;Videos without captions (returns explanatory message)&lt;/li&gt;
  &lt;li&gt;Network errors (exit code 1)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it suitable for use in scripts and automation pipelines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Learnings &amp;amp; Best Practices&lt;/strong&gt; 💡&lt;/p&gt;

&lt;p&gt;Building this .NET global tool taught me several valuable lessons applicable to any command-line tool project:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;YoutubeExplode Library Maturity&lt;/strong&gt;: Version 6.5.4 resolved transcript extraction issues that plagued earlier versions. Always verify library versions match your use case requirements.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;.NET Global Tool Packaging&lt;/strong&gt;: The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PackAsTool&lt;/code&gt; property, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ToolCommandName&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PackageReadmeFile&lt;/code&gt; are crucial for NuGet discoverability. Missing these makes your tool harder to find.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Multi-targeting Strategy&lt;/strong&gt;: Supporting both .NET 8 and 9 simultaneously ensures broader compatibility across development environments and CI/CD pipelines.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Flexible Input/Output Design&lt;/strong&gt;: Supporting both command-line arguments and stdin (JSON) makes your tool more versatile for automation, scripting, and pipeline integration.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Intelligent Caption Selection&lt;/strong&gt;: Smart ordering logic (English preference → auto-generated fallback) dramatically improves user experience compared to simple “first available” approaches.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Semantic Versioning in CI/CD&lt;/strong&gt;: Automating patch/minor/major version bumps reduces manual work and ensures consistency across releases.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Future Enhancements&lt;/strong&gt; 🔮&lt;/p&gt;

&lt;p&gt;Potential improvements for future versions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Support for downloading specific time ranges&lt;/li&gt;
  &lt;li&gt;Batch processing multiple URLs&lt;/li&gt;
  &lt;li&gt;Custom output formats (CSV, XML)&lt;/li&gt;
  &lt;li&gt;Integration with subtitle file formats (SRT, VTT)&lt;/li&gt;
  &lt;li&gt;Translation support for non-English captions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Development Velocity with Modern AI Tooling&lt;/strong&gt; ⚡&lt;/p&gt;

&lt;p&gt;What stands out about this project is the development speed enabled by modern AI assistance. From initial concept through architecture, implementation, testing, and NuGet publication took just a few hours - something that would have required days of work just five years ago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The AI-Assisted Development Workflow&lt;/strong&gt; 🤖&lt;/p&gt;

&lt;p&gt;This .NET global tool project showcased the power of combining multiple AI tools effectively:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;: Handled core architecture decisions, .NET-specific patterns, error handling strategies, and GitHub Actions CI/CD pipeline configuration. Particularly valuable for getting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.csproj&lt;/code&gt; packaging configuration correct on the first attempt.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt;: Excelled at generating repetitive code patterns, JSON serialization boilerplate, regex text normalization functions, and test scaffold code.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;MCPs (Model Context Protocol)&lt;/strong&gt;: Provided seamless integration between different AI tools and development contexts, making the workflow feel natural rather than fragmented.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Human-AI Partnership in Practice&lt;/strong&gt; 🤝&lt;/p&gt;

&lt;p&gt;The most interesting insight wasn’t that AI wrote the code, but how it transformed the development process itself:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Design-First Development&lt;/strong&gt;: Instead of iterating through implementation details, focus shifted to user experience and clean data flow architecture.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Documentation-Driven Development&lt;/strong&gt;: Writing this technical blog post in parallel with coding helped clarify requirements and catch edge cases early.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Risk-Free Exploration&lt;/strong&gt;: AI assistance made it easy to try different architectural approaches without the usual “sunk cost” hesitation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;What This Means for .NET Developers&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;This project represents a new normal in software development, where the bottleneck shifts from typing code to thinking through problems and user needs. The combination of AI coding assistants, intelligent build toolchains (GitHub Actions, NuGet), and human creativity is genuinely transformative.&lt;/p&gt;

&lt;p&gt;For developers hesitant about AI tools: they’re not replacing you; they’re amplifying your ability to solve meaningful problems quickly. The future belongs to developers who can effectively collaborate with AI to build better software faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get Started Building Your Own .NET Global Tool&lt;/strong&gt; 📦&lt;/p&gt;

&lt;p&gt;Ready to create your own command-line tool and publish it to NuGet? Install ytx to see a working example:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;dotnet&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tool&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;solrevdev.ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ytx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://www.youtube.com/watch?v=dQw4w9WgXcQ&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or &lt;a href=&quot;https://github.com/solrevdev/solrevdev.ytx&quot;&gt;explore the source code on GitHub&lt;/a&gt; to see the complete implementation.&lt;/p&gt;

&lt;p&gt;Success! 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Building Winget Search - A fast web interface for Windows Package Manager</title>
   <link href="https://solrevdev.com/2025/09/15/building-winget-search-a-fast-web-interface-for-winget-packages.html"/>
   <updated>2025-09-15T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2025/09/15/building-winget-search-a-fast-web-interface-for-winget-packages</id>
   <content type="html">&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;When setting up a new Windows machine, I used to rely on &lt;a href=&quot;https://scoop.sh/&quot;&gt;Scoop&lt;/a&gt; and &lt;a href=&quot;https://chocolatey.org/&quot;&gt;Chocolatey&lt;/a&gt; for package management. Both are excellent tools, but when Microsoft introduced &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/package-manager/&quot;&gt;Windows Package Manager (winget)&lt;/a&gt;, I decided to give it a try on my latest machine setup.&lt;/p&gt;

&lt;p&gt;The problem? Finding winget package IDs was tedious. While &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;winget search&lt;/code&gt; works, I wanted something faster - a web interface where I could quickly search, find packages, and copy installation commands. That’s how &lt;a href=&quot;https://github.com/solrevdev/winget-search&quot;&gt;winget-search&lt;/a&gt; was born, available at &lt;a href=&quot;https://solrevdev.com/winget-search/&quot;&gt;https://solrevdev.com/winget-search/&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Winget packages are stored in Microsoft’s &lt;a href=&quot;https://github.com/microsoft/winget-pkgs&quot;&gt;winget-pkgs repository&lt;/a&gt; with over 30,000 YAML manifest files. The repository structure follows a pattern: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manifests/publisher/package_name/version/&lt;/code&gt; with separate files for different locales and installers.&lt;/p&gt;

&lt;p&gt;I needed to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Extract package metadata from thousands of YAML files&lt;/li&gt;
  &lt;li&gt;Handle multiple versions and keep only the latest&lt;/li&gt;
  &lt;li&gt;Filter for English descriptions only&lt;/li&gt;
  &lt;li&gt;Build a fast, searchable web interface&lt;/li&gt;
  &lt;li&gt;Keep data fresh with automated updates&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h3 id=&quot;architecture-overview&quot;&gt;Architecture Overview&lt;/h3&gt;

&lt;p&gt;I built a three-part system:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Python extraction script&lt;/strong&gt; - Parses YAML manifests and generates JSON&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Static HTML search interface&lt;/strong&gt; - Client-side search with instant results&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GitHub Actions automation&lt;/strong&gt; - Daily updates and deployment&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;data-extraction&quot;&gt;Data Extraction&lt;/h3&gt;

&lt;p&gt;The Python script (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extract_packages.py&lt;/code&gt;) does the heavy lifting:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;extract_package_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifest_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Extract comprehensive package info from a manifest directory&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;package_info&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;publisher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;tags&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;homepage&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;license&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Process version manifest first
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;version_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml_files&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;.locale.&apos;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifest_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;safe_load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;package_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PackageIdentifier&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;package_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PackageVersion&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# Look for English locale file
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;locale_file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml_files&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;.locale.en-US.&apos;&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;locale_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;manifest_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;locale_file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;encoding&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;yaml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;safe_load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;package_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PackageName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;package_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;publisher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Publisher&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;package_info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The script handles version comparison using Python’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;packaging&lt;/code&gt; library to ensure we only keep the latest version of each package:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;parse_version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ver_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Parse version string for proper comparison&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ver_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Fallback for non-standard versions
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;web-interface&quot;&gt;Web Interface&lt;/h3&gt;

&lt;p&gt;The search interface is pure vanilla JavaScript - no frameworks needed. It loads a ~5MB JSON file containing all package data and performs client-side search for instant results:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;showResults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;packages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pkg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nameMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;descMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;publisherMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;publisher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tagMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;pkg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;idMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;nameMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;descMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;publisherMatch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;tagMatch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// Render results...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each search result includes a one-click copy button for the winget install command:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;copyCommand&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;clipboard&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;writeText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;originalHtml&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;`
            &amp;lt;svg width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot;&amp;gt;
                &amp;lt;polyline points=&quot;20 6 9 17 4 12&quot;/&amp;gt;
            &amp;lt;/svg&amp;gt;
            Copied!
        `&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;innerHTML&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;originalHtml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;automation-with-github-actions&quot;&gt;Automation with GitHub Actions&lt;/h3&gt;

&lt;p&gt;The magic happens in the GitHub Actions workflow that runs daily at 2 AM UTC:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Build and Deploy&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;schedule&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;cron&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&apos;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Daily at 2 AM UTC&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build-and-deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Checkout&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Setup Python&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-python@v5&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;python-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3.11&apos;&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Cache winget-pkgs&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/cache@v4&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;winget-pkgs&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;winget-pkgs-$&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;restore-keys&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;winget-pkgs-&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Clone winget-pkgs repository&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;if [ ! -d &quot;winget-pkgs&quot; ]; then&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;git clone --depth 1 https://github.com/microsoft/winget-pkgs.git&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;cd winget-pkgs &amp;amp;&amp;amp; git pull origin main&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;fi&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Extract packages&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;python extract_packages.py winget-pkgs/manifests/ packages.json&lt;/span&gt;

      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deploy to GitHub Pages&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;peaceiris/actions-gh-pages@v4&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;github_token&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;publish_dir&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;technical-highlights&quot;&gt;Technical Highlights&lt;/h2&gt;

&lt;h3 id=&quot;performance-optimizations&quot;&gt;Performance Optimizations&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Client-side search&lt;/strong&gt;: No server required, instant results&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Debounced input&lt;/strong&gt;: 300ms delay prevents excessive filtering&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Result limiting&lt;/strong&gt;: Shows max 100 results for smooth scrolling&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Caching&lt;/strong&gt;: GitHub Actions caches the large winget-pkgs repository&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;user-experience-features&quot;&gt;User Experience Features&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Keyboard shortcuts&lt;/strong&gt;: Press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; to focus search, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Esc&lt;/code&gt; to clear&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Dark mode&lt;/strong&gt;: Automatic theme based on system preferences&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Mobile-friendly&lt;/strong&gt;: Responsive design works on all devices&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Copy feedback&lt;/strong&gt;: Visual confirmation when commands are copied&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;data-quality&quot;&gt;Data Quality&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;English-only&lt;/strong&gt;: Filters for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.locale.en-US.yaml&lt;/code&gt; files&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Latest versions&lt;/strong&gt;: Semantic version comparison ensures freshness&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Type safety&lt;/strong&gt;: Handles edge cases like non-string tags&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Error resilience&lt;/strong&gt;: Continues processing even if individual manifests fail&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The entire deployment is automated through GitHub Pages. The workflow:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Clones the microsoft/winget-pkgs repository (1GB+, hence the caching)&lt;/li&gt;
  &lt;li&gt;Extracts package data using Python script&lt;/li&gt;
  &lt;li&gt;Generates a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;packages.json&lt;/code&gt; file with ~30,000 packages&lt;/li&gt;
  &lt;li&gt;Deploys everything to GitHub Pages using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;peaceiris/actions-gh-pages&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No manual intervention needed - just push code and GitHub Actions handles the rest!&lt;/p&gt;

&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;

&lt;p&gt;The end result is a fast, searchable interface hosted at GitHub Pages that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Loads 30,000+ packages in under 3 seconds&lt;/li&gt;
  &lt;li&gt;Provides instant search results&lt;/li&gt;
  &lt;li&gt;Generates copy-ready &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;winget install&lt;/code&gt; commands&lt;/li&gt;
  &lt;li&gt;Updates automatically every day&lt;/li&gt;
  &lt;li&gt;Costs nothing to host&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perfect for when you need to quickly find that package ID for your setup scripts! You can use it right now at &lt;a href=&quot;https://solrevdev.com/winget-search/&quot;&gt;https://solrevdev.com/winget-search/&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;future-improvements&quot;&gt;Future Improvements&lt;/h2&gt;

&lt;p&gt;Some ideas I’m considering:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Package categories&lt;/strong&gt; - Group by software type&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Fuzzy search&lt;/strong&gt; - Better matching for typos&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PowerShell commands&lt;/strong&gt; - Alternative to cmd syntax&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Package details modal&lt;/strong&gt; - Show more metadata&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Search history&lt;/strong&gt; - Remember recent searches&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;live-demo--resources&quot;&gt;Live Demo &amp;amp; Resources&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Try the winget search tool now:&lt;/strong&gt; &lt;a href=&quot;https://solrevdev.com/winget-search/&quot;&gt;https://solrevdev.com/winget-search/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source code is available on &lt;a href=&quot;https://github.com/solrevdev/winget-search&quot;&gt;GitHub&lt;/a&gt; under the MIT license.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Evolving SeedFolder with GitHub Copilot - From Personal Tool to Multi-Template System</title>
   <link href="https://solrevdev.com/2025/08/20/evolving-seedfolder-with-github-copilot.html"/>
   <updated>2025-08-20T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2025/08/20/evolving-seedfolder-with-github-copilot</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt; ☀&lt;/p&gt;

&lt;p&gt;It’s been over 4 years since I first published my &lt;a href=&quot;/2020/10/05/creating-a.net-core-global-tool.html&quot;&gt;.NET Core Global Tool&lt;/a&gt; blog post about creating SeedFolder.&lt;/p&gt;

&lt;p&gt;What started as a simple tool to copy my personal dotfiles has evolved into something much more powerful and hopefully eventually will be useful to the broader developer community.&lt;/p&gt;

&lt;p&gt;The original version was quite limited - it basically just copied my specific &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.editorconfig&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.gitignore&lt;/code&gt;, and other dotfiles to new project folders. While this was useful for me, it wasn’t particularly helpful to other developers who might have different preferences or work with different technology stacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Evolution Journey&lt;/strong&gt; 🌱&lt;/p&gt;

&lt;p&gt;Over the years, I’ve made several significant improvements to SeedFolder, particularly leveraging GitHub Copilot to help accelerate development and implement features I might not have found time to build otherwise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upgrading Through .NET LTS Versions&lt;/strong&gt; ⬆️&lt;/p&gt;

&lt;p&gt;One of the consistent maintenance tasks has been keeping the tool updated with each .NET LTS release. For example, &lt;a href=&quot;https://github.com/solrevdev/seedfolder/pull/4&quot;&gt;upgrading to more currwnt .NET versions&lt;/a&gt; involved updating the target framework and ensuring compatibility:&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;&amp;lt;Project Sdk=&quot;Microsoft.NET.Sdk&quot;&amp;gt;
&lt;/span&gt;  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-    &amp;lt;TargetFramework&amp;gt;net6.0&amp;lt;/TargetFramework&amp;gt;
&lt;/span&gt;    &amp;lt;!-- Multi-targeting for backward compatibility --&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+   &amp;lt;TargetFrameworks&amp;gt;net8.0;net9.0&amp;lt;/TargetFrameworks&amp;gt;
&lt;/span&gt;    &amp;lt;PackAsTool&amp;gt;true&amp;lt;/PackAsTool&amp;gt;
    &amp;lt;ToolCommandName&amp;gt;seedfolder&amp;lt;/ToolCommandName&amp;gt;
    &amp;lt;!-- ... other properties --&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;
&lt;span class=&quot;gd&quot;&gt;&amp;lt;/Project&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The tool now uses multi-targeting to support various .NET SDKs, ensuring users can install and use it regardless of which .NET version they have installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Copilot as a Development Partner&lt;/strong&gt; 🤖&lt;/p&gt;

&lt;p&gt;The real transformation happened when I started using GitHub Copilot - both from the web interface and my iOS app - to help implement more ambitious features. This was a game-changer for a side project that I rarely had dedicated time to improve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My AI Development Journey&lt;/strong&gt; 🛤️&lt;/p&gt;

&lt;p&gt;This project coincided with my own evolution in AI-assisted development. Over the past couple of years, I’ve progressed through several stages:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Context Sharing Era&lt;/strong&gt;: Using tools like Repomix to bundle codebase snippets for web-based ChatGPT, Claude, and Gemini conversations&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Desktop Integration&lt;/strong&gt;: Adopting dedicated ChatGPT and Claude desktop clients for more seamless workflows&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;IDE-Native AI&lt;/strong&gt;: Integrating GitHub Copilot directly into VSCode, progressing through OpenAI’s GPT 4.0, 4.1, and eventually to GPT-5 and Claude Sonnet 4&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Terminal-First Development&lt;/strong&gt;: Embracing agentic terminal clients like Warp Terminal, Claude Code, and Codex for more direct development interaction&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Today, my workflow splits between contexts: during work hours, I rely on VSCode with GitHub Copilot and terminal-based AI clients. But evenings and weekends - when I’m often watching TV with my phone in hand - GitHub’s web and iOS Copilot interfaces became the perfect tools for iterating on side projects through issue-driven development.&lt;/p&gt;

&lt;p&gt;This hybrid approach proved ideal for SeedFolder’s evolution: I could sketch out features and improvements during downtime, then implement them through focused GitHub issue conversations. The combination of accessibility and power made consistent progress possible on a project that might otherwise have stagnated between day job commitments.&lt;/p&gt;

&lt;p&gt;Here’s how the process typically worked:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Issue Creation&lt;/strong&gt;: I’d create a GitHub issue describing what I wanted to achieve&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Copilot Collaboration&lt;/strong&gt;: Using GitHub Copilot from the web or iOS app, I’d work through the implementation&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Iterative Development&lt;/strong&gt;: Copilot would suggest implementations, and we’d refine them together&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, &lt;a href=&quot;https://github.com/solrevdev/seedfolder/issues/9&quot;&gt;Issue #9&lt;/a&gt; outlined a comprehensive roadmap for turning SeedFolder into a proper template system. What would have taken me weeks to implement manually, Copilot helped me accomplish in focused sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;From Single Template to Multi-Template System&lt;/strong&gt; 📂&lt;/p&gt;

&lt;p&gt;The biggest transformation was moving from a single set of dotfiles to a comprehensive template system. &lt;a href=&quot;https://github.com/solrevdev/seedfolder/pull/10&quot;&gt;Pull Request #10&lt;/a&gt; introduced support for six different project types:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Interactive mode - prompts for template selection&lt;/span&gt;
seedfolder

&lt;span class=&quot;c&quot;&gt;# Specific template usage&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; dotnet MyNewProject
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; node MyNodeApp
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; python data-analysis
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; ruby rails-app
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; markdown blog-posts
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; universal generic-project

&lt;span class=&quot;c&quot;&gt;# Preview what would be created&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--dry-run&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; node MyApp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each template now includes carefully curated files appropriate for that project type:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;dotnet&lt;/strong&gt;: .editorconfig, .gitignore for C#, omnisharp.json&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;node&lt;/strong&gt;: package.json template, .nvmrc, npm-specific .gitignore&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;python&lt;/strong&gt;: requirements.txt, .python-version, Python .gitignore&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ruby&lt;/strong&gt;: Gemfile template, .ruby-version, Ruby .gitignore&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;markdown&lt;/strong&gt;: Basic structure for documentation projects&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;universal&lt;/strong&gt;: Generic files useful across all project types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Enhanced User Experience&lt;/strong&gt; ✨&lt;/p&gt;

&lt;p&gt;The tool is now much more user-friendly and customizable. Some key improvements include:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interactive Mode&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;seedfolder
? Select a project template: &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;Use arrow keys&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
❯ dotnet - .NET applications with C# configuration
  node - Node.js applications with npm setup
  python - Python projects with pip requirements
  ruby - Ruby applications with Bundler setup
  markdown - Documentation and static sites
  universal - Generic template &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;any project &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Better CLI Interface&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# All the standard options you&apos;d expect&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--help&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--version&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--list-templates&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; dotnet &lt;span class=&quot;nt&quot;&gt;--output&lt;/span&gt; ./projects MyApi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Cross-Platform Compatibility&lt;/strong&gt;: The tool now works seamlessly across Windows, macOS, and Linux, with platform-specific optimizations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improved CI/CD Pipeline&lt;/strong&gt; 🔄&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/solrevdev/seedfolder/pull/16&quot;&gt;Pull Request #16&lt;/a&gt; addressed CI workflow issues and simplified the build process:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;CI&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;release/*&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;paths-ignore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;**/*.md&apos;&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;**/*.gitignore&apos;&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;**/*.gitattributes&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;release/*&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;paths-ignore&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;**/*.md&apos;&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;**/*.gitignore&apos;&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;**/*.gitattributes&apos;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;github.event_name == &apos;push&apos; &amp;amp;&amp;amp; contains(toJson(github.event.commits), &apos;***NO_CI***&apos;) == &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &amp;amp;&amp;amp; contains(toJson(github.event.commits), &apos;[ci skip]&apos;) == &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &amp;amp;&amp;amp; contains(toJson(github.event.commits), &apos;[skip ci]&apos;) == &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ACTIONS_ALLOW_UNSECURE_COMMANDS&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DOTNET_CLI_TELEMETRY_OPTOUT&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DOTNET_SKIP_FIRST_TIME_EXPERIENCE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DOTNET_NOLOGO&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DOTNET_GENERATE_ASPNET_CERTIFICATE&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DOTNET_ADD_GLOBAL_TOOLS_TO_PATH&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;DOTNET_MULTILEVEL_LOOKUP&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;

        &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;checkout code&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v4&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;setup .net core sdk&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-dotnet@v4&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;dotnet-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;8.0.x&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;9.0.x&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet build&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet build solrevdev.seedfolder.sln --configuration Release&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;run integration tests&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./tests/integration-test.sh&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet pack&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet pack solrevdev.seedfolder.sln -c Release --no-build --include-source --include-symbols&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;setup nuget&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;github.event_name == &apos;push&apos; &amp;amp;&amp;amp; github.ref == &apos;refs/heads/master&apos;&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nuget/setup-nuget@v1&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;nuget-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;latest&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Publish NuGet&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;github.event_name == &apos;push&apos; &amp;amp;&amp;amp; github.ref == &apos;refs/heads/master&apos;&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rohith/publish-nuget@v2.1.1&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;PROJECT_FILE_PATH&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;src/solrevdev.seedfolder.csproj&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Relative to repository root&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;NUGET_KEY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# nuget.org API key&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;PACKAGE_NAME&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;solrevdev.seedfolder&lt;/span&gt;


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

&lt;p&gt;&lt;strong&gt;The Power of AI-Assisted Development&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;What I found particularly interesting about working with GitHub Copilot was how it helped me think through problems I might not have tackled otherwise. For instance:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;: Copilot suggested comprehensive error handling patterns I wouldn’t have thought to implement&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Cross-Platform Considerations&lt;/strong&gt;: It caught platform-specific issues early in development&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;User Experience&lt;/strong&gt;: Proposed CLI interface improvements that made the tool much more pleasant to use&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Testing Strategies&lt;/strong&gt;: Suggested test cases and scenarios I hadn’t considered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s an example of how Copilot helped implement the template system using enums and pattern matching:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Template metadata structure for future extensibility&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TemplateFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResourceName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Enum for supported project types&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Dotnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Python&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Ruby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Markdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Universal&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TryParseProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;projectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;projectType&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;dotnet&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;net&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;csharp&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dotnet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;node&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;nodejs&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;javascript&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;js&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;python&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;py&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Python&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;ruby&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;rb&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ruby&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;markdown&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;md&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;docs&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Markdown&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;universal&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;basic&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;minimal&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Universal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dotnet&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;dotnet&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;net&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;csharp&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;node&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;nodejs&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;javascript&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;js&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;python&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;py&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;ruby&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;rb&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;markdown&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;md&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;docs&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;universal&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;basic&quot;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;minimal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TemplateFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetTemplateFiles&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;projectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;projectType&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetNodeTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Python&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetPythonTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Ruby&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetRubyTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Markdown&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetMarkdownTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;ProjectType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Universal&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetUniversalTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetDotnetTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Next Steps: The Marketplace Vision&lt;/strong&gt; 🏪&lt;/p&gt;

&lt;p&gt;The next major evolution is planned around &lt;a href=&quot;https://github.com/solrevdev/seedfolder/issues/15&quot;&gt;Issue #15&lt;/a&gt; - creating a template marketplace. This will allow the community to share and install custom templates:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Future marketplace commands&lt;/span&gt;
seedfolder marketplace search angular
seedfolder marketplace &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;solrevdev/vue-typescript
seedfolder marketplace list &lt;span class=&quot;nt&quot;&gt;--installed&lt;/span&gt;
seedfolder marketplace update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The marketplace will be hosted as a separate GitHub repository at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;solrevdev/seedfolder-marketplace&lt;/code&gt; with this structure:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;solrevdev/seedfolder-marketplace/
├── templates/
│   ├── angular/
│   │   ├── template.json
│   │   └── files/
│   ├── vue/
│   └── rust/
├── registry.json
└── README.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will enable developers to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Share their own project templates&lt;/li&gt;
  &lt;li&gt;Override built-in templates with their preferences&lt;/li&gt;
  &lt;li&gt;Fork the marketplace for team-specific template collections&lt;/li&gt;
  &lt;li&gt;Contribute improvements back to the community&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Installation and Usage Today&lt;/strong&gt; 📦&lt;/p&gt;

&lt;p&gt;The current version is available on NuGet and much more capable than the original:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install the latest version&lt;/span&gt;
dotnet tool &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; solrevdev.seedfolder

&lt;span class=&quot;c&quot;&gt;# Create a new .NET project with proper scaffolding&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--template&lt;/span&gt; dotnet MyWebApi

&lt;span class=&quot;c&quot;&gt;# Interactive mode for template selection&lt;/span&gt;
seedfolder MyNewProject

&lt;span class=&quot;c&quot;&gt;# See all available options&lt;/span&gt;
seedfolder &lt;span class=&quot;nt&quot;&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Lessons Learned&lt;/strong&gt; 💡&lt;/p&gt;

&lt;p&gt;Working on SeedFolder’s evolution taught me several valuable lessons:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;AI as a Coding Partner&lt;/strong&gt;: GitHub Copilot isn’t just an autocomplete tool - it’s a thinking partner that can help you explore solutions you might not consider&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Incremental Improvement&lt;/strong&gt;: Small, consistent improvements over time can transform a simple tool into something genuinely useful&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Community Focus&lt;/strong&gt;: Building for your own needs first is fine, but thinking about broader use cases makes tools more valuable&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Template Systems&lt;/strong&gt;: Flexibility through templates is much more powerful than hardcoded configurations&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Context-Driven Development&lt;/strong&gt;: Different AI tools excel in different contexts - terminal clients for focused coding sessions, mobile interfaces for planning and ideation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The transformation from a personal dotfile copier to a comprehensive project scaffolding tool shows how AI assistance can help maintain and evolve side projects that might otherwise stagnate. The key insight was finding the right AI tool for each development context rather than trying to force a single solution across all scenarios.&lt;/p&gt;

&lt;p&gt;Watch out for the next steps as I work on the marketplace functionality - the goal is to make SeedFolder not just more useful, but a platform for the community to share their own project setup best practices.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you’re curious about the specific workflows and tools that made this multi-context AI development approach work, I’m planning a follow-up post diving deeper into the practical setup and decision-making process behind choosing the right AI tool for different development scenarios.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Success! 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unable to load shared library libgdiplus or one of its dependencies</title>
   <link href="https://solrevdev.com/2020/12/04/dllnotfoundexception-unable-to-load-shared-library-libgdiplus-or-one-of-its-dependencies.html"/>
   <updated>2020-12-04T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/12/04/dllnotfoundexception-unable-to-load-shared-library-libgdiplus-or-one-of-its-dependencies</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt; ☀&lt;/p&gt;

&lt;p&gt;While testing a feature locally on my macmini I was uploading an image when I got the following error:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unable to load shared library ‘libgdiplus’ or one of its dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/bctzWAR.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependencies&lt;/strong&gt; 🌱&lt;/p&gt;

&lt;p&gt;So, after a quick google the following was suggested to me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;mono-libgdiplus&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mono-libgdiplus
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I already had this installed but I re-installed just in case&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/tCZONkD.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That did not work so the next option available was to add a reference to a NuGet package that allows you to use System.Drawing on macOS&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;runtime.osx.10.10-x64.CoreCompat.System.Drawing&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;runtime.osx.10.10-x64.CoreCompat.System.Drawing&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Version=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;5.8.64&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And with that all was working again.&lt;/p&gt;

&lt;p&gt;Success 🥳&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Migrate .NET Core 3.1 to .NET Core 5.0</title>
   <link href="https://solrevdev.com/2020/11/13/how-to-migrate-from-dotnet-core-31-to-dotnet-core-50.html"/>
   <updated>2020-11-13T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/11/13/how-to-migrate-from-dotnet-core-31-to-dotnet-core-50</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt; ☀&lt;/p&gt;

&lt;p&gt;The very latest version of .NET Core was launched at &lt;a href=&quot;https://www.dotnetconf.net/&quot;&gt;.NET Conf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is the free, cross-platform and open-source developer platform from Microsoft which includes the latest versions of ASP.NET and C# among others.&lt;/p&gt;

&lt;p&gt;I decided to wait until the upgrade was available in all the various package managers such as &lt;a href=&quot;https://brew.sh/&quot;&gt;homebrew&lt;/a&gt; on macOS and &lt;a href=&quot;https://linux.die.net/man/8/apt-get&quot;&gt;apt-get&lt;/a&gt; on Ubuntu and &lt;a href=&quot;https://chocolatey.org/&quot;&gt;chocolatey&lt;/a&gt; on Windows before I upgraded my projects.&lt;/p&gt;

&lt;p&gt;This ensured that my operating systems were upgraded from .NET Core 3.1 to .NET Core 5.0 for me almost automatically.&lt;/p&gt;

&lt;p&gt;This post will hopefully document the steps needed to upgrade an ASP.NET Core 3.1 Razor Pages project from ASP.NET Core 3.1 to ASP.NET Core 5.0.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/migration/31-to-50?view=aspnetcore-5.0&amp;amp;tabs=visual-studio-code&quot;&gt;migrate from .NET Core 3.1 to 5.0&lt;/a&gt; document over at Microsoft should help you as it did me.&lt;/p&gt;

&lt;p&gt;But for those that want to know what I had change here goes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting Started&lt;/strong&gt; 🌱&lt;/p&gt;

&lt;p&gt;The main change will be to the Target Framework property in the website’s .csproj file however, in my case I had to change it in my Directory.Build.Props file which covers all of the projects in my solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Directory.Build.Props:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;- &amp;lt;TargetFramework&amp;gt;netcoreapp3.1&amp;lt;/TargetFramework&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ &amp;lt;TargetFramework&amp;gt;net5.0&amp;lt;/TargetFramework&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next up I had to make a change to prevent a new build error that cropped up in an extension method of mine, something I am sure worked fine under .NET Core 3.1:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HttpContextExtensions.cs:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;public static T GetHeaderValueAs&amp;lt;T&amp;gt;(this IHttpContextAccessor accessor, string headerName)
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
-   StringValues values;
&lt;span class=&quot;gi&quot;&gt;+   StringValues values = default;
&lt;/span&gt;
    if (accessor.HttpContext?.Request?.Headers?.TryGetValue(headerName, out values) ?? false)
    {
        var rawValues = values.ToString();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then I needed to make a change to ensure that Visual Studio Code (Insiders) would debug my project properly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.vscode/launch.json:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;{&lt;/span&gt;
    &quot;name&quot;: &quot;.NET Core Launch (console)&quot;,
    &quot;type&quot;: &quot;coreclr&quot;,
    &quot;request&quot;: &quot;launch&quot;,
    &quot;preLaunchTask&quot;: &quot;build&quot;,
&lt;span class=&quot;gd&quot;&gt;-   &quot;program&quot;: &quot;${workspaceRoot}/src/projectname/bin/Debug/netcoreapp3.1/projectname.dll&quot;,
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+   &quot;program&quot;: &quot;${workspaceRoot}/src/projectname/bin/Debug/net5.0/projectname.dll&quot;,
&lt;/span&gt;    &quot;args&quot;: [],
    &quot;cwd&quot;: &quot;${workspaceFolder}&quot;,
    &quot;stopAtEntry&quot;: false,
    &quot;console&quot;: &quot;externalTerminal&quot;
&lt;span class=&quot;err&quot;&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This particular project has the source code hosted at Bitbucket and my pipelines file needed the following change.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://support.atlassian.com/bitbucket-cloud/docs/get-started-with-bitbucket-pipelines&quot;&gt;Bitbucket Pipelines&lt;/a&gt; is basically Atlassian’s version of &lt;a href=&quot;https://github.com/features/actions&quot;&gt;Github Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;bitbucket-pipelines.yml:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;- image: mcr.microsoft.com/dotnet/core/sdk:3.1
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ image: mcr.microsoft.com/dotnet/sdk:5.0
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;pipelines:
&lt;/span&gt;    default:
        - step:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A related change was that I needed to make a change to my Dockerfile so that it uses the latest .NET 5 SDK and runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dockerfile:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;- FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
&lt;/span&gt;
- FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS runtime
&lt;span class=&quot;gi&quot;&gt;+ FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I then ran a tool called &lt;a href=&quot;https://github.com/dotnet-outdated/dotnet-outdated&quot;&gt;dotnet outdated&lt;/a&gt; which upgraded all my NuGet packages including the Microsoft Frameworks packages going from 3.1 to 5.0.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;dotnet outdated:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet tool &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; dotnet-outdated-tool
dotnet outdated &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt;

» web
  &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;.NETCoreApp,Version&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;v5.0]
  AWSSDK.S3                                          3.5.3.2       -&amp;gt; 3.5.4
  Microsoft.AspNetCore.Mvc.NewtonsoftJson            3.1.9         -&amp;gt; 5.0.0
  Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation  3.1.9         -&amp;gt; 5.0.0
  Microsoft.EntityFrameworkCore.Design               3.1.9         -&amp;gt; 5.0.0
  Microsoft.Extensions.Configuration.UserSecrets     3.1.9         -&amp;gt; 5.0.0
  Microsoft.VisualStudio.Web.CodeGeneration.Design   3.1.4         -&amp;gt; 5.0.0
  Microsoft.Web.LibraryManager.Build                 2.1.76        -&amp;gt; 2.1.113
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This changed my website’s csproj file to use the correct nuget packages for .NET 5.&lt;/p&gt;

&lt;p&gt;A much quicker way than doing it manually!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;web.csproj:&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;- &amp;lt;PackageReference Include=&quot;Microsoft.AspNetCore.Mvc.NewtonsoftJson&quot; Version=&quot;3.1.9&quot; /&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+ &amp;lt;PackageReference Include=&quot;Microsoft.AspNetCore.Mvc.NewtonsoftJson&quot; Version=&quot;5.0.0&quot; /&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Deployments&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;And finally, one thing I forgot once I tried to deploy was that in my project, I use &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/visual-studio-publish-profiles?view=aspnetcore-5.0&quot;&gt;Visual Studio Publish Profiles&lt;/a&gt; to automatically deploy the site via MsBuild and I needed to change the Target Framework and Publish Framework versions before it would deploy correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;/Properties/PublishProfiles/deploy.pubxml&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gd&quot;&gt;-    &amp;lt;PublishFramework&amp;gt;netcoreapp3.1&amp;lt;/PublishFramework&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    &amp;lt;PublishFramework&amp;gt;net5.0&amp;lt;/PublishFramework&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-    &amp;lt;TargetFramework&amp;gt;netcoreapp3.1&amp;lt;/TargetFramework&amp;gt;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    &amp;lt;TargetFramework&amp;gt;net5.0&amp;lt;/TargetFramework&amp;gt;
&lt;/span&gt;     &amp;lt;SelfContained&amp;gt;false&amp;lt;/SelfContained&amp;gt;
     &amp;lt;_IsPortable&amp;gt;true&amp;lt;/_IsPortable&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And with that I was done. A fairly large and complex application was ported over.&lt;/p&gt;

&lt;p&gt;By all accounts .NET 5 has performance and allocation improvements all across the board so I am looking forward to seeing the results of all that hard work.&lt;/p&gt;

&lt;p&gt;Success 🥳&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Creating a .NET Core Global Tool</title>
   <link href="https://solrevdev.com/2020/10/05/creating-a.net-core-global-tool.html"/>
   <updated>2020-10-05T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/10/05/creating-a.net-core-global-tool</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Evolution and Updates&lt;/strong&gt; 🔄&lt;/p&gt;

&lt;p&gt;Since publishing this post, SeedFolder has evolved significantly! The tool now supports multiple project templates, cross-platform compatibility, and has been enhanced with the help of GitHub Copilot.&lt;/p&gt;

&lt;p&gt;Read about the journey from a simple dotfile copier to a comprehensive project scaffolding tool in my follow-up post: &lt;a href=&quot;/2025/08/20/evolving-seedfolder-with-github-copilot.html&quot;&gt;Evolving SeedFolder with GitHub Copilot - From Personal Tool to Multi-Template System&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Overview&lt;/strong&gt; ☀&lt;/p&gt;

&lt;p&gt;I have now built my first .NET Core Global Tool!&lt;/p&gt;

&lt;p&gt;A .NET Core Global Tool is special NuGet package that contains a console application that is installed globally on your machine.&lt;/p&gt;

&lt;p&gt;It is installed in a default directory that is added to the PATH environment variable.&lt;/p&gt;

&lt;p&gt;This means you can invoke the tool from any directory on the machine without specifying its location.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Application&lt;/strong&gt; 🌱&lt;/p&gt;

&lt;p&gt;So, rather than the usual &lt;em&gt;Hello World&lt;/em&gt; example to install as a global tool I wanted a tool that would be useful to me.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-10-03_2020-10-03_16-02-52-seedfolder-app-running.png&quot; alt=&quot;solrevdev.seedfolder app&quot; title=&quot;solrevdev.seedfolder app&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I wanted to build a tool that will create a folder prefixed with either a bespoke reference (in my case a Trello card number) or the current date in a YYYY-MM-DD format followed by a normal folder name.&lt;/p&gt;

&lt;p&gt;The tool once it has created the folder will then also copy some dotfiles that I find useful in most projects over.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;818_create-dotnet-tool&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2020-09-29_create-dotnet-tool&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It will also copy the following dotfiles over:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;.dockerignore&lt;/li&gt;
  &lt;li&gt;.editorconfig&lt;/li&gt;
  &lt;li&gt;.gitattributes&lt;/li&gt;
  &lt;li&gt;.gitignore&lt;/li&gt;
  &lt;li&gt;.prettierignore&lt;/li&gt;
  &lt;li&gt;.prettierrc&lt;/li&gt;
  &lt;li&gt;omnisharp.json&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won’t explain how this code was written; you can view the &lt;a href=&quot;https://github.com/solrevdev/seedfolder&quot; title=&quot;source code&quot;&gt;source code&lt;/a&gt; over at GitHub to understand how this was done.&lt;/p&gt;

&lt;p&gt;The important thing to note is that the application is a standard .NET Core console application that you can create as follows:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet new console &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; solrevdev.seedfolder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Metadata&lt;/strong&gt; 📖&lt;/p&gt;

&lt;p&gt;What sets a standard .NET Core console application and a global tool apart is some important metadata in the `.csproj` file.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;OutputType&amp;gt;&lt;/span&gt;Exe&lt;span class=&quot;nt&quot;&gt;&amp;lt;/OutputType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp3.1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackAsTool&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackAsTool&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;ToolCommandName&amp;gt;&lt;/span&gt;seedfolder&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ToolCommandName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageOutputPath&amp;gt;&lt;/span&gt;./nupkg&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageOutputPath&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;NoDefaultExcludes&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/NoDefaultExcludes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Version&amp;gt;&lt;/span&gt;1.0.0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Version&amp;gt;&lt;/span&gt;

    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Title&amp;gt;&lt;/span&gt;solrevdev.seedfolder&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Title&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Description&amp;gt;&lt;/span&gt;A nice description of your tool&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Description&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageDescription&amp;gt;&lt;/span&gt;A nice description of your tool&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageDescription&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Authors&amp;gt;&lt;/span&gt;your github username&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Authors&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;Company&amp;gt;&lt;/span&gt;your github username&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Company&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RepositoryUrl&amp;gt;&lt;/span&gt;https://github.com/username/projectname&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RepositoryUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageProjectUrl&amp;gt;&lt;/span&gt;https://github.com/username/projectname&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageProjectUrl&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReleaseNotes&amp;gt;&lt;/span&gt;https://github.com/username/projectname&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageReleaseNotes&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageLicenseExpression&amp;gt;&lt;/span&gt;MIT&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageLicenseExpression&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;RepositoryType&amp;gt;&lt;/span&gt;git&lt;span class=&quot;nt&quot;&gt;&amp;lt;/RepositoryType&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageTags&amp;gt;&lt;/span&gt;dotnetcore;;dotnet;csharp;dotnet-global-tool;dotnet-global-tools;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/PackageTags&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The extra tags from  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PackAsTool&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Version&lt;/code&gt;  are required fields while the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Title&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PackageTags&lt;/code&gt; are useful to help describe the package in NuGet and help get it discovered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Packaging and Installation&lt;/strong&gt; ⚙&lt;/p&gt;

&lt;p&gt;Once I was happy that my console application was working the next step was to create a NuGet package by running the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-pack&quot; title=&quot;dotnet pack&quot;&gt;dotnet pack&lt;/a&gt; command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet pack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This produces a &lt;em&gt;nupkg&lt;/em&gt; package.  This nupkg NuGet package is what the .NET Core CLI uses to install the global tool.&lt;/p&gt;

&lt;p&gt;So, to package and install locally without publishing to NuGet which will be needed while you are still testing you need the following:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet pack
dotnet tool &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--add-source&lt;/span&gt; ./nupkg solrevdev.seedfolder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Your tool should now be in your path accessible from any folder.&lt;/p&gt;

&lt;p&gt;You call your tool whatever was in the ToolCommandName property in your .csproj file&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;ToolCommandName&amp;gt;&lt;/span&gt;seedfolder&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ToolCommandName&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You may find you need uninstall and install while you debug.&lt;/p&gt;

&lt;p&gt;To uninstall you need to do as follows:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet tool uninstall &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; solrevdev.seedfolder
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you are happy with your tool and you have installed in globally and tested it you can now publish this to NuGet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Publish to NuGet&lt;/strong&gt; 🚀&lt;/p&gt;

&lt;p&gt;Head over to NuGet and &lt;a href=&quot;https://www.nuget.org/account/apikeys&quot; title=&quot;create an API Key&quot;&gt;create an API Key&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-10-03_2020-10-03_14-27-10_nuget-api-keys.png&quot; alt=&quot;NuGet API Keys&quot; title=&quot;NuGet API Keys&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once you have this key go to your GitHub Project and under settings and secrets create a new secret named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NUGET_API_KEY&lt;/code&gt; with the value you just created over at NuGet.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-10-03_2020-10-03_14-35-47_github-secrets.png&quot; alt=&quot;Github Secrets&quot; title=&quot;Github Secrets&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Finally create a new workflow like the one below which will check out the code, build and package the .NET Core console application as a NuGet package then using the API key we just created we will automatically publish the tool to NuGet.&lt;/p&gt;

&lt;p&gt;Each time you commit do not forget to bump the version tag e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;Version&amp;gt;1.0.0&amp;lt;/Version&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;CI&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;release/*&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;release/*&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;windows-latest&lt;/span&gt;

        &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;checkout code&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v2&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;setup .net core sdk&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/setup-dotnet@v1&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;dotnet-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3.1.x&apos;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# SDK Version to use; x will use the latest version of the 3.1 channel&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet build&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet build solrevdev.seedfolder.sln --configuration Release&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet pack&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;dotnet pack solrevdev.seedfolder.sln -c Release --no-build --include-source --include-symbols&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;setup nuget&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;github.event_name == &apos;push&apos; &amp;amp;&amp;amp; github.ref == &apos;refs/heads/master&apos;&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NuGet/setup-nuget@v1.0.2&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;nuget-version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;latest&lt;/span&gt;

            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Publish NuGet&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rohith/publish-nuget@v2.1.1&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;PROJECT_FILE_PATH&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;src/solrevdev.seedfolder.csproj&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Relative to repository root&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;NUGET_KEY&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# nuget.org API key&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;PACKAGE_NAME&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;solrevdev.seedfolder&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Find More&lt;/strong&gt; 🔍&lt;/p&gt;

&lt;p&gt;Now that you have built and published a .NET Core Global Tool you may wish to find some others for inspiration.&lt;/p&gt;

&lt;p&gt;Search the &lt;a href=&quot;https://www.nuget.org/&quot; title=&quot;NuGet&quot;&gt;NuGet&lt;/a&gt; website by using the “.NET tool” package type filter or see the list of tools in the &lt;a href=&quot;https://github.com/natemcmaster/dotnet-tools&quot; title=&quot;natemcmaster/dotnet-tools&quot;&gt;natemcmaster/dotnet-tools&lt;/a&gt; GitHub repository.&lt;/p&gt;

&lt;p&gt;Success! 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Spotlight stops indexing Applications</title>
   <link href="https://solrevdev.com/2020/10/02/spotlight-stops-indexing-applications.html"/>
   <updated>2020-10-02T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/10/02/spotlight-stops-indexing-applications</id>
   <content type="html">&lt;p&gt;All of a sudden spotlight on my macOS Mojave macmini stopped working…&lt;/p&gt;

&lt;p&gt;There is a process called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mdutil&lt;/code&gt; which manages the metadata stores used by Spotlight and was the culprit for my issue.&lt;/p&gt;

&lt;p&gt;The fix after some Google Fu and some trial and error was to restart this process as follows:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;mdutil &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; off  
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;launchctl unload &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; /System/Library/LaunchDaemons/com.apple.metadata.mds.plist  
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;launchctl load &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; /System/Library/LaunchDaemons/com.apple.metadata.mds.plist  
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;mdutil &lt;span class=&quot;nt&quot;&gt;-a&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; on
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Hopefully this won’t happen too often but if it does at least I have a fix!&lt;/p&gt;

&lt;p&gt;Success? 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Access denied for user root'@'localhost</title>
   <link href="https://solrevdev.com/2020/09/30/access-denied-for-user-root-localhost.html"/>
   <updated>2020-09-30T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/09/30/access-denied-for-user-root-localhost</id>
   <content type="html">&lt;p&gt;Every time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get upgrade&lt;/code&gt; upgrades my local MySQL instance on my Ubuntu laptop I get the following error:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;1698, &lt;span class=&quot;s2&quot;&gt;&quot;Access denied for user &apos;root&apos;@&apos;localhost&apos;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The fix each time is the following, so here it is for me next time save me wasting time googling the error every time.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;mysql &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; root

use mysql&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

update user &lt;span class=&quot;nb&quot;&gt;set &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;mysql_native_password&apos;&lt;/span&gt; where &lt;span class=&quot;nv&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

flush privileges&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And with that all is well again!&lt;/p&gt;

&lt;p&gt;Success? 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Move an Ubuntu window to another workspace</title>
   <link href="https://solrevdev.com/2020/06/11/move-an-ubuntu-window-to-another-workspace.html"/>
   <updated>2020-06-11T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/06/11/move-an-ubuntu-window-to-another-workspace</id>
   <content type="html">&lt;p&gt;Last night I decided to pull the trigger and upgrade from &lt;a href=&quot;http://releases.ubuntu.com/19.10/&quot;&gt;Ubuntu 19.10 (Eoan Ermine)&lt;/a&gt; to &lt;a href=&quot;http://releases.ubuntu.com/focal/&quot;&gt;Ubuntu Focal Fossa 20.04&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A fairly smooth upgrade all in all.&lt;/p&gt;

&lt;p&gt;I did have to re-enable the .NET Core APT repository using the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-add-repository https://packages.microsoft.com/ubuntu/20.04/prod
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also discovered a neat shortcut to move programs from one workspace to another:&lt;/p&gt;

&lt;p&gt;&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;Shift&lt;/kbd&gt;+&lt;kbd&gt;Arrow key&lt;/kbd&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rAeIM3K.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I hope this will soon become muscle memory 💪 !&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Remove page or site from Google search results</title>
   <link href="https://solrevdev.com/2020/06/08/remove-page-or-site-from-google-search.html"/>
   <updated>2020-06-08T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/06/08/remove-page-or-site-from-google-search</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What do you do when you have a website that you do not want Google or other search engines to index and therefore NOT display in search results?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Robots!&lt;/strong&gt; 🤖&lt;/p&gt;

&lt;p&gt;In the past, you have simply been able to add a &lt;a href=&quot;https://www.robotstxt.org/robotstxt.html&quot;&gt;robots.txt&lt;/a&gt; file.&lt;/p&gt;

&lt;p&gt;This is a file that website owners could use to inform web crawlers and robots such as the &lt;a href=&quot;https://support.google.com/webmasters/answer/182072?hl=en&quot;&gt;Googlebot&lt;/a&gt; about whether you wanted your site indexed or not and if so which parts of your site.&lt;/p&gt;

&lt;p&gt;If you wanted to stop all robots from indexing your site you created a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt; in your site root with the following content&lt;/p&gt;

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

&lt;p&gt;While still used, it is no longer the recommended way to block or remove a URL or site from Google.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google’s Removal Tool&lt;/strong&gt; ✂&lt;/p&gt;

&lt;p&gt;Your first step should be to head over to the &lt;a href=&quot;https://search.google.com/search-console/removals?resource_id=sc-domain%3Awww.yourdomain.com&quot;&gt;Google Search Removal Tool&lt;/a&gt; and enter your page or site into the tool and submit.&lt;/p&gt;

&lt;p&gt;For more information you can &lt;a href=&quot;https://support.google.com/webmasters/answer/9689846?hl=en&quot;&gt;read about it here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Doing this will remove your page or site for up to 6 months.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/LsyvpRu.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meta Tags&lt;/strong&gt; 📓&lt;/p&gt;

&lt;p&gt;To permanently remove it you will need to tell Google not to index your page using the &lt;a href=&quot;https://developers.google.com/search/reference/robots_meta_tag&quot;&gt;robots meta tag&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You add this into any page that you do not want Google to index.&lt;/p&gt;
&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;robots&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;noindex&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;robots&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;noindex&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Robots&lt;span class=&quot;nt&quot;&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
You do not want Google to index this page
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Inspect&lt;/strong&gt; 🔎&lt;/p&gt;

&lt;p&gt;Once you have removed your page or site using the removal tool and used meta tags to stop it being indexed again in the future you will want to keep an eye on your domain and inspect the page(s) or site you removed.&lt;/p&gt;

&lt;p&gt;To view your pending removals login to the &lt;a href=&quot;https://search.google.com/search-console/about&quot;&gt;Google Search Console&lt;/a&gt; and choose the Removals tab.&lt;/p&gt;

&lt;p&gt;From here you can submit new pages for removal and generally inspect your website and how it is managed by Google’s index.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/lm5LXBD.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To recap, when you want to remove a page or site from Google search index you need to…&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Use the &lt;a href=&quot;https://search.google.com/search-console/removals?resource_id=sc-domain%3Awww.yourdomain.com&quot;&gt;Google Search Removal Tool&lt;/a&gt; to remove temporarily.&lt;/li&gt;
  &lt;li&gt;Permanently remove using &lt;a href=&quot;https://developers.google.com/search/reference/robots_meta_tag&quot;&gt;robots meta tag&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hope this helps others or me from the future!&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Archiving all bookmarks using the Pocket Developer API</title>
   <link href="https://solrevdev.com/2020/06/07/archive-all-bookmarks-using-the-pocket-developer-api.html"/>
   <updated>2020-06-07T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/06/07/archive-all-bookmarks-using-the-pocket-developer-api</id>
   <content type="html">&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;Today I wanted to clean up my &lt;a href=&quot;https://getpocket.com/&quot;&gt;Pocket&lt;/a&gt; account, I had thousands of unread articles in my inbox
and while their web interface allows you to bulk edit your bookmarks it would have taken days to archive all of them that
way.&lt;/p&gt;

&lt;p&gt;So, instead of spending days to do this, I used their &lt;a href=&quot;https://getpocket.com/developer/docs/overview&quot;&gt;API&lt;/a&gt; and ran a
quick and dirty script to archive bookmarks going back to 2016!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/YLysmmV.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;here-be-dragons&quot;&gt;Here be dragons!&lt;/h2&gt;

&lt;p&gt;Now, since I ran this script I found a handy dandy page that would have done the job for me although instead of archiving
all my bookmarks it would have deleted them so I am pleased I used my script instead.&lt;/p&gt;

&lt;p&gt;If you want to clear your Pocket account without deleting your account head over to this page:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://getpocket.com/privacy_clear&quot;&gt;https://getpocket.com/privacy_clear&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To be clear this will delete ALL your bookmarks and there is no going back&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, If like me you want to archive all your content carry on reading&lt;/p&gt;

&lt;h2 id=&quot;onwards&quot;&gt;Onwards!&lt;/h2&gt;

&lt;p&gt;To follow along you will need &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt; and a marketplace plugin called
&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=humao.rest-client&quot;&gt;Rest Client&lt;/a&gt; which allows you to interact with
API’s nicely.&lt;/p&gt;

&lt;p&gt;I will not be using it to its full potential as it supports variables and such like so I will leave that for an exercise
for the reader to refactor away.&lt;/p&gt;

&lt;p&gt;So, to get started create a working folder, 2 files to work with and then open Visual Studio Code&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;pocket-api
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;pocket-api
&lt;span class=&quot;nb&quot;&gt;touch &lt;/span&gt;api.http
&lt;span class=&quot;nb&quot;&gt;touch &lt;/span&gt;api.js
code &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;step-1-obtain-a-pocket-platform-consumer-key&quot;&gt;Step 1: Obtain a Pocket platform consumer key&lt;/h3&gt;

&lt;p&gt;Create a new application over at &lt;a href=&quot;https://getpocket.com/developer/apps/new&quot;&gt;https://getpocket.com/developer/apps/new&lt;/a&gt;
and make sure you select all of the Add/Modify/Retrieve permissions and choose Web as the platform.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/mRF2g4Z.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Make a note of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;consumer_key&lt;/code&gt; that is created.&lt;/p&gt;

&lt;p&gt;You can also find it over at &lt;a href=&quot;https://getpocket.com/developer/apps/&quot;&gt;https://getpocket.com/developer/apps/&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;step-2-obtain-a-request-token&quot;&gt;Step 2: Obtain a request token&lt;/h3&gt;

&lt;p&gt;To begin the Pocket authorization process, our script must obtain a request token from Pocket by making a POST request.&lt;/p&gt;

&lt;p&gt;So in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api.http&lt;/code&gt; enter the following&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;### Step 2: Obtain a request token
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;POST&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;https://getpocket.com/v3/oauth/request&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json; charset=UTF-8&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;X-Accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;consumer_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;11111-1111111111111111111111&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;redirect_uri&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://solrevdev.com&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This redirect_uri does not matter. You can enter anything here.&lt;/p&gt;

&lt;p&gt;Using the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=humao.rest-client&quot;&gt;Rest Client&lt;/a&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Send Request&lt;/code&gt; feature you can make the request and get the response in the right-hand pane.&lt;/p&gt;

&lt;p&gt;You will get a response that gives you a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;code&lt;/code&gt; that you need for the next step so make sure you make a note of it&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;code:&apos;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;111111-1111-1111-1111-111111&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;step-3-redirect-user-to-pocket-to-continue-authorization&quot;&gt;Step 3: Redirect user to Pocket to continue authorization&lt;/h3&gt;

&lt;p&gt;Take your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;code&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_url&lt;/code&gt; from Step 2 above and replace in the URL below and copy and paste the below URL in to a browser and follow the instructions.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://getpocket.com/auth/authorize?request_token=111111-1111-1111-1111-111111&amp;amp;redirect_uri=https://solrevdev.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;step-4-receive-the-callback-from-pocket&quot;&gt;Step 4: Receive the callback from Pocket&lt;/h3&gt;

&lt;p&gt;Pocket will redirect you to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_url&lt;/code&gt; you entered in step 3 above.&lt;/p&gt;

&lt;p&gt;This step authorizes the application giving it the add/modify/delete permissions we asked for in step 1.&lt;/p&gt;

&lt;h3 id=&quot;step-5-convert-a-request-token-into-a-pocket-access-token&quot;&gt;Step 5: Convert a request token into a Pocket access token&lt;/h3&gt;

&lt;p&gt;Now that you have given your application the permissions it needs you can now get an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; to make further requests.&lt;/p&gt;

&lt;p&gt;Enter the following into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api.http&lt;/code&gt; replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;consumer_key&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;code&lt;/code&gt; from Steps 1 and 2 above.&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;POST&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;https://getpocket.com/v3/oauth/authorize&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json; charset=UTF-8&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;X-Accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;consumer_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;11111-1111111111111111111111&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;111111-1111-1111-1111-111111&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Again, using the fantastic Rest Client send the request and make a note of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; in the response&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;access_token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;111111-1111-1111-1111-111111&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;solrevdev&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;make-some-requests&quot;&gt;Make some requests&lt;/h2&gt;

&lt;p&gt;Now we have an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; we  can make some requests against our account, take a look at the &lt;a href=&quot;https://getpocket.com/developer/docs/overview&quot;&gt;documentation&lt;/a&gt; for more information on what can be done with the API&lt;/p&gt;

&lt;p&gt;We can view all pockets:&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;### get all pockets
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;POST&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;https://getpocket.com/v3/get&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json; charset=UTF-8&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;X-Accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;consumer_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1111-1111111111111111111111111&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;access_token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;111111-1111-1111-1111-111111&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;100&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;detailType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;simple&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;state&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;unread&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can modify pockets:&lt;/p&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;### modify  pockets
&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;POST&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;https://getpocket.com/v3/send&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;HTTP&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1.1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json; charset=UTF-8&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;X-Accept&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application/json&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;consumer_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1111-1111111111111111111111111&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;access_token&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;111111-1111-1111-1111-111111&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;actions&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;action&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;archive&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;item_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;82500974&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;generate-code-snippet&quot;&gt;Generate Code Snippet&lt;/h2&gt;

&lt;p&gt;I used the Generate Code Snippet feature of the Rest Client Extension to get me some
boilerplate code which I extended to loop until I had no more bookmarks left archiving them in batches of 100.&lt;/p&gt;

&lt;p&gt;To do this once you’ve sent a request as above, use shortcut &lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;C&lt;/kbd&gt; or &lt;kbd&gt;Cmd&lt;/kbd&gt;+&lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;C&lt;/kbd&gt; for macOS, or right-click in the editor and then select Generate Code Snippet in the menu, or press &lt;kbd&gt;F1&lt;/kbd&gt; and then select/type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Rest Client: Generate Code Snippet&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It will show the available languages.&lt;/p&gt;

&lt;p&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JavaScript&lt;/code&gt; then select enter and your code will appear in a right-hand pane.&lt;/p&gt;

&lt;p&gt;Below is that code slightly modified to iterate all unread items then archive them until all complete.&lt;/p&gt;

&lt;p&gt;You will need to replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;consumer_key&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;access_token&lt;/code&gt; for the values you noted earlier.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;keepGoing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;keepGoing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://getpocket.com/v3/get&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;content-type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json; charset=UTF-8&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;x-accept&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;{&quot;consumer_key&quot;:&quot;1111-1111111111111111111111111&quot;,&quot;access_token&quot;:&quot;111111-1111-1111-1111-111111&quot;,&quot;count&quot;:&quot;100&quot;,&quot;detailType&quot;:&quot;simple&quot;,&quot;state&quot;: &quot;unread&quot;}&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//console.log(&apos;json&apos;, json);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//console.log(&apos;list&apos;, list);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;actions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;

        &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;action&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;archive&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;item_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;current&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;//console.log(&apos;actions&apos;, actions);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;{&quot;consumer_key&quot;:&quot;1111-1111111111111111111111111&quot;,&quot;access_token&quot;:&quot;111111-1111-1111-1111-111111&quot;,&quot;actions&quot; : &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;
        &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;//console.log(&apos;body&apos;, body);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;https://getpocket.com/v3/send&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;POST&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;content-type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json; charset=UTF-8&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;x-accept&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;http post json&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;keepGoing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;more items to process&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;run-in-chromes-console-window&quot;&gt;Run in Chrome’s console window&lt;/h2&gt;

&lt;p&gt;And so the quick and dirty solution for me was to copy the above JavaScript and in a Chrome console window paste and run.&lt;/p&gt;

&lt;p&gt;It took a while as I had content going back to 2016 but once it was finished I had a nice clean inbox again!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/YLysmmV.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Adding TypeScript to an existing aspnetcore project</title>
   <link href="https://solrevdev.com/2020/06/06/adding-typescript-to-existing-aspnetcore-projects.html"/>
   <updated>2020-06-06T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/06/06/adding-typescript-to-existing-aspnetcore-projects</id>
   <content type="html">&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;So, I have a small &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-3.1&quot;&gt;ASP.NET Core Razor Pages&lt;/a&gt; application that I recently enhanced by adding &lt;a href=&quot;https://vuejs.org/&quot;&gt;Vue&lt;/a&gt; in the same way that I once would add &lt;a href=&quot;https://jquery.com/&quot;&gt;jQuery&lt;/a&gt; to an existing application to add some interactivity to an existing page.&lt;/p&gt;

&lt;p&gt;Not all websites need to be &lt;a href=&quot;https://en.wikipedia.org/wiki/Single-page_application&quot;&gt;SPA’s&lt;/a&gt; with full-on JavaScript frameworks and build processes and just like with jQuery back in the day I was able to add Vue by simply adding a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag to my page.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;environment&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Development&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;script &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;~/lib/vue/vue.js&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/environment&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;environment&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;exclude=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Development&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;script
        &lt;/span&gt;&lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;asp-fallback-src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;~/lib/vue/vue.min.js&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;asp-fallback-test=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;window.Vue&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;integrity=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sha256-chlNFSVx3TdcQ2Xlw7SvnbLAavAQLO0Y/LBiWX04viY=&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;crossorigin=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;anonymous&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/environment&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The one issue I did have was that my accompanying code used the latest and greatest JavaScript features which ruled out the page working on some older browsers.&lt;/p&gt;

&lt;p&gt;This needed fixing!&lt;/p&gt;

&lt;h2 id=&quot;typescript-to-the-rescue&quot;&gt;TypeScript to the rescue&lt;/h2&gt;

&lt;p&gt;One of the reasons I prefer Vue over React and other JavaScript frameworks is that it’s so easy to simply add Vue to an existing project without going all in.&lt;/p&gt;

&lt;p&gt;You can add as little or as much as you want.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt; I believe is similar in that you can add it bit by bit to a project.&lt;/p&gt;

&lt;p&gt;And not only do you get type safety as a benefit but it can also transpile TypeScript to older versions of JavaScript.&lt;/p&gt;

&lt;p&gt;Exactly what I wanted!&lt;/p&gt;

&lt;p&gt;So for anyone else that wants to do the same and for future me wanting to know how to do this here we are!&lt;/p&gt;

&lt;h3 id=&quot;install-typescript-nuget-package&quot;&gt;Install TypeScript NuGet package&lt;/h3&gt;

&lt;p&gt;First you need to install the &lt;a href=&quot;https://www.nuget.org/packages/Microsoft.TypeScript.MSBuild/3.9.2?_src=template&quot;&gt;Microsoft.TypeScript.MSBuild &lt;/a&gt; nuget package into your ASP.NET Core website project.&lt;/p&gt;

&lt;p&gt;This will allow you to build and transpile from your IDE, the command line or even a build server.&lt;/p&gt;

&lt;h3 id=&quot;create-tsconfigjson&quot;&gt;Create tsconfig.json&lt;/h3&gt;

&lt;p&gt;Next up create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tsconfig.json&lt;/code&gt; file in the root of your website project. This tells the TypeScript compiler what to do and how to behave.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;lib&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;DOM&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ES2015&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;target&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;es5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;noEmitOnError&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;strict&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;module&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;es2015&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;moduleResolution&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;outDir&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;wwwroot/js&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;include&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Scripts/**/*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;compileOnSave&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;target&lt;/strong&gt; : The target is es5 which is the JavaScript version I want to support and transpile down to.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;noEmitOnError&lt;/strong&gt;: This will stop the script wiping any existing code if the TypeScript errors.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;outDir&lt;/strong&gt;: I want the source TypeScript to put the JavaScript in the same place I was putting my original code&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;include&lt;/strong&gt;: This says take all the TypeScript in this folder and transpile into .js files of the same name into &lt;strong&gt;outDir&lt;/strong&gt; above&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;compileOnSave&lt;/strong&gt;: This is a productivity booster!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;create-folders&quot;&gt;Create Folders&lt;/h3&gt;

&lt;p&gt;Now create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Scripts&lt;/code&gt; folder alongside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pages&lt;/code&gt; to store the TypeScript files.&lt;/p&gt;

&lt;h3 id=&quot;create-first-typescript-file&quot;&gt;Create first TypeScript file&lt;/h3&gt;

&lt;p&gt;Add the following to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Scripts/site.ts&lt;/code&gt; and then save the file to kick off the TypeScript compiler.&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;site.ts &amp;gt; site.js &amp;gt; site.js.min&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;save-and-build&quot;&gt;Save And Build!&lt;/h3&gt;

&lt;p&gt;If all has gone well there should be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;site.js&lt;/code&gt; file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wwwroot\js&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;Now whenever the project is built every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.ts&lt;/code&gt; file you add to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Scripts&lt;/code&gt; will be transpiled to a file with the same name but with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.js&lt;/code&gt; extension in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wwwroot\js&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;And best of all you should notice that it has taken the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;let&lt;/code&gt; keyword in the source TypeScript file and transpiled that to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var&lt;/code&gt; in the destination site.js JavaScript file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;site.ts &amp;gt; site.js &amp;gt; site.js.min&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;site.ts &amp;gt; site.js &amp;gt; site.js.min&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;typescript-with-vue-jquery-and-lodash&quot;&gt;TypeScript with Vue, jQuery and Lodash&lt;/h2&gt;

&lt;p&gt;However, while site.js is a nice simple example, my project as I mentioned above uses Vue (and jQuery and Lodash) and if you try and build that with TypeScript you may get errors related to those external libraries.&lt;/p&gt;

&lt;p&gt;One fix would be to import the types for those libraries however, I wanted to keep my project simple and do not want to try and import types for my external libraries.&lt;/p&gt;

&lt;p&gt;So, the following example shows how to tell TypeScript that your code is using Vue, jQuery and Lodash while keeping the codebase light and not having to import any types.&lt;/p&gt;

&lt;p&gt;You will not get full intellisense for these as TypeScript does not have the type definitions for them however you will not get any errors because of them.&lt;/p&gt;

&lt;p&gt;That for me was fine.&lt;/p&gt;

&lt;div class=&quot;language-typescript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kr&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Vue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kr&quot;&gt;declare&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kr&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Vue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;el&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;#app&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Hello Vue!&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;created&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;form&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;HTMLFormElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;email&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;HTMLInputElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;HTMLInputElement&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;.jqueryExample&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fadeTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;slideUp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another common error is that TypeScript may not know about HTML form elements.&lt;/p&gt;

&lt;p&gt;As in the example above you can fix this by declaring your form variables as the relevant types.&lt;/p&gt;

&lt;p&gt;In my case the common ones were &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTMLFormElement&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTMLInputElement&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And that is it basically!&lt;/p&gt;

&lt;h2 id=&quot;more-typescript&quot;&gt;More TypeScript?&lt;/h2&gt;

&lt;p&gt;So, for now, this is the right amount of TypeScript for my needs.&lt;/p&gt;

&lt;p&gt;I did not have to bring too much ceremony to my application but I still get some type checking and more importantly I can code using the latest language features but still have JavaScript that works in older browsers.&lt;/p&gt;

&lt;p&gt;If the project grows I will see how else I can improve it with TypeScript!&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Instagram Basic Display API</title>
   <link href="https://solrevdev.com/2020/05/28/instagram-basic-display-api.html"/>
   <updated>2020-05-28T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/05/28/instagram-basic-display-api</id>
   <content type="html">&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;

&lt;p&gt;A while ago I was working on a project that consumed the &lt;a href=&quot;https://www.instagram.com/developer/&quot;&gt;Instagram Legacy API Platform&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/PHCXZXK.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To make things easier there was a fantastic library called &lt;a href=&quot;http://instasharp.github.io/InstaSharp/&quot;&gt;InstaSharp&lt;/a&gt; which wrapped the HTTP calls to the Instagram Legacy API endpoints.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/YgLkx4K.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However, Instagram began disabling the &lt;a href=&quot;https://www.instagram.com/developer/&quot;&gt;Instagram Legacy API Platform&lt;/a&gt; and on June 29, 2020, any remaining endpoints will no longer be available.&lt;/p&gt;

&lt;p&gt;The replacements to the &lt;a href=&quot;https://www.instagram.com/developer/&quot;&gt;Instagram Legacy API Platform&lt;/a&gt; are the &lt;a href=&quot;https://developers.facebook.com/docs/instagram-graph-api&quot;&gt;Instagram Graph API&lt;/a&gt; and the &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api&quot;&gt;Instagram Basic Display API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, If my project was to continue to work I needed to migrate over to the &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api&quot;&gt;Instagram Basic Display API&lt;/a&gt; before the deadline.&lt;/p&gt;

&lt;p&gt;I decided to build and release an open-source library, A  wrapper around the &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api&quot;&gt;Instagram Basic Display API&lt;/a&gt; in the same way as &lt;a href=&quot;http://instasharp.github.io/InstaSharp/&quot;&gt;InstaSharp&lt;/a&gt; did for the original.&lt;/p&gt;

&lt;h2 id=&quot;solrevdevinstagrambasicdisplay&quot;&gt;Solrevdev.InstagramBasicDisplay&lt;/h2&gt;

&lt;p&gt;And so began &lt;a href=&quot;https://github.com/solrevdev/instagram-basic-display&quot;&gt;Solrevdev.InstagramBasicDisplay&lt;/a&gt;,  a &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/standard/net-standard&quot;&gt;netstandard2.0&lt;/a&gt; library that consumes the new &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api/&quot;&gt;Instagram Basic Display API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Te7DLdK.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It is also available on &lt;a href=&quot;https://www.nuget.org/packages/Solrevdev.InstagramBasicDisplay/&quot;&gt;nuget&lt;/a&gt; so you can add this functionality to your .NET projects.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/FWeRqgo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

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

&lt;p&gt;So, to consume the &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api/&quot;&gt;Instagram Basic Display API&lt;/a&gt; you will need to generate an Instagram  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_secret&lt;/code&gt; by creating a Facebook app and configuring it so that it knows your &lt;strong&gt;&lt;em&gt;https only&lt;/em&gt;&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_url&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;facebook-and-instagram-setup&quot;&gt;Facebook and Instagram Setup&lt;/h3&gt;

&lt;p&gt;Before you begin you will need to create an Instagram  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_secret&lt;/code&gt; by creating a Facebook app and configuring it so that it knows your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_url&lt;/code&gt;. There are full &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api/getting-started&quot;&gt;instructions here&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;step-1---create-a-facebook-app&quot;&gt;Step 1 - Create a Facebook App&lt;/h4&gt;

&lt;p&gt;Go to &lt;a href=&quot;https://developers.facebook.com&quot;&gt;developers.facebook.com&lt;/a&gt;, click &lt;strong&gt;My Apps&lt;/strong&gt;, and create a new app. Once you have created the app and are in the App Dashboard, navigate to &lt;strong&gt;Settings &amp;gt; Basic&lt;/strong&gt;, scroll the bottom of page, and click &lt;strong&gt;Add Platform&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/kgnGHkv.png&quot; alt=&quot;Step 1a - Create a Facebook App&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Choose &lt;strong&gt;Website&lt;/strong&gt;, add your website’s URL, and save your changes. You can change the platform later if you wish, but for this tutorial, use &lt;strong&gt;Website&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1BFpWpQ.png&quot; alt=&quot;Step 1b - Create a Facebook App&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;step-2---configure-instagram-basic-display&quot;&gt;Step 2 - Configure Instagram Basic Display&lt;/h4&gt;

&lt;p&gt;Click &lt;strong&gt;Products&lt;/strong&gt;, locate the &lt;strong&gt;Instagram&lt;/strong&gt; product, and click &lt;strong&gt;Set Up&lt;/strong&gt; to add it to your app.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/6sK3tkC.png&quot; alt=&quot;Step 2a - Configure Instagram Basic Display&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Basic Display&lt;/strong&gt;, scroll to the bottom of the page, then click &lt;strong&gt;Create New App&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Nq6V25f.png&quot; alt=&quot;Step 2b - Configure Instagram Basic Display&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the form that appears, complete each section using the guidelines below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Display Name&lt;/strong&gt;
Enter the name of the Facebook app you just created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Valid OAuth Redirect URIs&lt;/strong&gt;
Enter &lt;a href=&quot;https://localhost:5001/auth/oauth/&quot;&gt;https://localhost:5001/auth/oauth/&lt;/a&gt; for your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_url&lt;/code&gt; that will be used later. &lt;strong&gt;HTTPS must be used on all redirect URLs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deauthorize Callback URL&lt;/strong&gt;
Enter &lt;a href=&quot;https://localhost:5001/deauthorize&quot;&gt;https://localhost:5001/deauthorize&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Deletion Request Callback URL&lt;/strong&gt;
Enter &lt;a href=&quot;https://localhost:5001/datadeletion&quot;&gt;https://localhost:5001/datadeletion&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;App Review&lt;/strong&gt;
Skip this section for now since this is just a demo.&lt;/p&gt;

&lt;h4 id=&quot;step-3---add-an-instagram-test-user&quot;&gt;Step 3 - Add an Instagram Test User&lt;/h4&gt;

&lt;p&gt;Navigate to &lt;strong&gt;Roles &amp;gt; Roles&lt;/strong&gt; and scroll down to the &lt;strong&gt;Instagram Testers section&lt;/strong&gt;. Click &lt;strong&gt;Add Instagram Testers&lt;/strong&gt; and enter your Instagram account’s username and send the invitation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/UWPx2NK.png&quot; alt=&quot;Step 3a - Add an Instagram Test User&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Open a new web browser and go to &lt;a href=&quot;https://www.instagram.com/&quot;&gt;www.instagram.com&lt;/a&gt; and sign in to your Instagram account that you just invited. Navigate to &lt;strong&gt;(Profile Icon) &amp;gt; Edit Profile &amp;gt; Apps and Websites &amp;gt; Tester Invites&lt;/strong&gt; and accept the invitation.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Z25fBaq.png&quot; alt=&quot;Step 3b - Add an Instagram Test User&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can view these &lt;a href=&quot;https://www.instagram.com/accounts/manage_access/&quot;&gt;invitations and applications&lt;/a&gt; by navigating to &lt;strong&gt;(Profile Icon) &amp;gt; Edit Profile &amp;gt; Apps and Websites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/oFqXPEG.png&quot; alt=&quot;Step 3c - Add an Instagram Test User&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;facebook-and-instagram-credentials&quot;&gt;Facebook and Instagram Credentials&lt;/h3&gt;

&lt;p&gt;Navigate to &lt;strong&gt;My Apps &amp;gt; Your App Name &amp;gt; Basic Display&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/YoI2Wrm.png&quot; alt=&quot;Navigate to My App&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Make a note of the following Facebook and Instagram credentials:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instagram App ID&lt;/strong&gt;
This is going to be known as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_id&lt;/code&gt; later&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instagram App Secret&lt;/strong&gt;
This is going to be known as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_secret&lt;/code&gt; later&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Client OAuth Settings &amp;gt; Valid OAuth Redirect URIs&lt;/strong&gt;
This is going to be known as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redirect_url&lt;/code&gt; later&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/k5H2JSN.png&quot; alt=&quot;Facebook and Instagram Credentials&quot; /&gt; &lt;em&gt;&lt;a href=&quot;https://i.imgur.com/bSHOS5p.png&quot;&gt;go here for a full size screenshot&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;p&gt;Now that you have an Instagram &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;client_secret&lt;/code&gt; to use we can now create a new dotnet project and add the Solrevdev.InstagramBasicDisplay package to it.&lt;/p&gt;

&lt;p&gt;Create a .NET Core Razor Pages project.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet new webapp &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; web
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;web
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To install via &lt;a href=&quot;https://www.nuget.org/packages/Solrevdev.InstagramBasicDisplay/&quot;&gt;nuget&lt;/a&gt; using the dotnet cli&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet add package Solrevdev.InstagramBasicDisplay
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To install via &lt;a href=&quot;https://www.nuget.org/packages/Solrevdev.InstagramBasicDisplay/&quot;&gt;nuget&lt;/a&gt; using Visual Studio / Powershell&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Install-Package&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Solrevdev.InstagramBasicDisplay&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;app-configuration&quot;&gt;App Configuration&lt;/h2&gt;

&lt;p&gt;In your &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/about&quot;&gt;.NET Core&lt;/a&gt; library or application create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appsettings.json&lt;/code&gt; file if one does not already exist and fill out the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InstagramSettings&lt;/code&gt; section with your Instagram credentials such as client_id, client_secret and redirect_url as mentioned above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;appsettings.json&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Logging&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;LogLevel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Information&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Microsoft&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Warning&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Microsoft.Hosting.Lifetime&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Information&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;AllowedHosts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;InstagramCredentials&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;friendly name or your app name can go here - this is passed to Instagram as the user-agent&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ClientId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;client-id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ClientSecret&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;client-secret&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;RedirectUrl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://localhost:5001/auth/oauth&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;common-uses&quot;&gt;Common Uses&lt;/h2&gt;

&lt;p&gt;Now that you have a .NET Core Razor Pages website and the Solrevdev.InstagramBasicDisplay library has been added you can achieve some of the following common uses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get an Instagram User Access Token and permissions from an Instagram user&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, you send the user to Instagram to authenticate using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Authorize&lt;/code&gt; method, they will be redirected to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RedirectUrl&lt;/code&gt; set in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InstagramCredentials&lt;/code&gt; so ensure that is set-up correctly in the Instagram app settings page.&lt;/p&gt;

&lt;p&gt;Instagram will redirect the user on successful login to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RedirectUrl&lt;/code&gt; page you configured in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InstagramCredentials&lt;/code&gt; and this is where you can call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AuthenticateAsync&lt;/code&gt; which exchanges the &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api/overview#authorization-codes&quot;&gt;Authorization Code&lt;/a&gt; for a &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api/overview#instagram-user-access-tokens&quot;&gt;short-lived Instagram user access token&lt;/a&gt; or optionally a &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api/guides/long-lived-access-tokens#get-a-long-lived-token&quot;&gt;long-lived Instagram user access token&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You then have access to an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OAuthResponse&lt;/code&gt; which contains your &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api/reference/access_token&quot;&gt;access token&lt;/a&gt; and a &lt;a href=&quot;https://developers.facebook.com/docs/instagram-basic-display-api/reference/user&quot;&gt;user&lt;/a&gt; which can be used to make further API calls.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Authorize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;anything-passed-here-will-be-returned-as-state-variable&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Redirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RedirectUrl&lt;/code&gt; page&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// code is passed by Instagram, the state is whatever you passed in _api.Authorize sent back to you&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnGetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// this returns an access token that will last for 1 hour - short-lived access token&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AuthenticateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// this returns an access token that will last for 60 days - long-lived access token&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// var response = await _api.AuthenticateAsync(code, state, true).ConfigureAwait(false);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// store in session - see System.Text.Json code below for sample&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HttpContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Instagram.Response&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want to store the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OAuthResponse&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpContext.Session&lt;/code&gt; you can use the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Text.Json&lt;/code&gt; namespace like this&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System.Text.Json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Microsoft.AspNetCore.Http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SessionExtensions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ISession&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Serialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ISession&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;JsonSerializer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Deserialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Get an Instagram user’s profile&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// code is passed by Instagram, the state is whatever you passed in _api.Authorize sent back to you&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnGetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// this returns an access token that will last for 1 hour - short-lived access token&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AuthenticateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// this returns an access token that will last for 60 days - long-lived access token&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// var response = await _api.AuthenticateAsync(code, state, true).ConfigureAwait(false);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// store and log&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;UserId: {userid} Username: {username} Media Count: {count} Account Type: {type}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MediaCount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccountType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Access Token: {token}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Get an Instagram user’s images, videos, and albums&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// code is passed by Instagram, the state is whatever you passed in _api.Authorize sent back to you&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnGetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// this returns an access token that will last for 1 hour - short-lived access token&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AuthenticateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// this returns an access token that will last for 60 days - long-lived access token&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// var response = await _api.AuthenticateAsync(code, state, true).ConfigureAwait(false);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;// store and log&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMediaListAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Initial media response returned with [{count}] records &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;First caption: {caption}, First media url: {url}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Caption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MediaUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//  toggle the following boolean for a quick and dirty way of getting all a user&apos;s media.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;//&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Paging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Paging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Getting next page [{next}]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;media&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetMediaListAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;next media response returned with [{count}] records &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;c1&quot;&gt;// add to list&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;The user has a total of {count} items in their Instagram feed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Media&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Exchange a short-lived access token for a long-lived access token&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// code is passed by Instagram, the state is whatever you passed in _api.Authorize sent back to you&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnGetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// this returns an access token that will last for 1 hour - short-lived access token&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AuthenticateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;response access token {token}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;longLived&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLongLivedAccessTokenAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;longLived access token {token}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;longLived&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Refresh a long-lived access token for another long-lived access token&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InstagramApi&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ILogger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IndexModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// code is passed by Instagram, the state is whatever you passed in _api.Authorize sent back to you&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnGetAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// this returns an access token that will last for 1 hour - short-lived access token&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AuthenticateAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;response access token {token}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;longLived&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetLongLivedAccessTokenAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;longLived access token {token}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;longLived&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;another&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;RefreshLongLivedAccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAwait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;response access token {token}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;another&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AccessToken&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;sample-code&quot;&gt;Sample Code&lt;/h2&gt;

&lt;p&gt;For more documentation and a sample ASP.Net Core Razor Pages web application visit the &lt;a href=&quot;https://github.com/solrevdev/instagram-basic-display/blob/master/samples/Web/readme.md&quot;&gt;samples folder&lt;/a&gt; in the &lt;a href=&quot;https://github.com/solrevdev/instagram-basic-display&quot;&gt;GitHub repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Deploy ASP.NET Core Web API to Fly via Docker</title>
   <link href="https://solrevdev.com/2020/05/18/deploy-aspnet-core-web-api-to-fly-via-docker.html"/>
   <updated>2020-05-18T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/05/18/deploy-aspnet-core-web-api-to-fly-via-docker</id>
   <content type="html">&lt;p&gt;In my &lt;a href=&quot;https://solrevdev.com/2020/05/17/blazor-hosted-on-vercel-aka-zeit-now.html&quot;&gt;last post&lt;/a&gt; I deployed the standard &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/blazor/get-started?view=aspnetcore-3.1&quot;&gt;Blazor template&lt;/a&gt; over to &lt;a href=&quot;https://vercel.com/&quot;&gt;vercel static site hosting&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the standard template, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FetchData&lt;/code&gt; component gets its data from a local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sample-data/weather.json&lt;/code&gt; file via an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HttpClient&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;forecasts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFromJsonAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WeatherForecast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;sample-data/weather.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I wanted to upgrade this by replacing that call to the local json file with a call to an ASP.NET Core Web API backend.&lt;/p&gt;

&lt;p&gt;Unfortunately unlike in the version 1 days of zeit where you could deploy Docker based apps to them vercel now offer &lt;a href=&quot;https://vercel.com/docs/v2/serverless-functions/introduction&quot;&gt;serverless functions&lt;/a&gt; instead but &lt;a href=&quot;https://vercel.com/docs/v2/serverless-functions/supported-languages&quot;&gt;do not support .NET&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, as an alternative, I looked at &lt;a href=&quot;https://fly.io/docs/&quot;&gt;fly.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I first used them in 2017 before GitHub supported HTTPS/SSL for custom domains by &lt;a href=&quot;/2017/08/31/http-ssl-via-github-pages-with-flyio.html&quot;&gt;using them as middleware&lt;/a&gt; to provide this service.&lt;/p&gt;

&lt;p&gt;Since then they now support &lt;a href=&quot;https://fly.io/docs/hands-on/start/&quot;&gt;deploying Docker based app servers&lt;/a&gt; which works in pretty much the same way as zeit used to.&lt;/p&gt;

&lt;p&gt;Perfect!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, the plan was to create a backend to replace the weather.json file, deploy and host it via Docker on fly.io and point my vercel hosted blazor website to that!&lt;/p&gt;

&lt;p&gt;First up I created a backend web API using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet new&lt;/code&gt; template and added that to my solution.&lt;/p&gt;

&lt;p&gt;Fortunately, the .NET Core Web API template comes out of the box with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/weatherforecast&lt;/code&gt; endpoint that returns the same shape data as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sample_data/weather.json&lt;/code&gt; file in the frontend.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet new webapi &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; backend
dotnet sln add backend/backend.csproj
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, I needed to tell my web API backend that another domain (my vercel hosted blazor app) would be connecting to it. This would fix any &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-3.1&quot;&gt;CORS&lt;/a&gt; related error messages.&lt;/p&gt;

&lt;p&gt;So in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;backend/Program.cs&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_myAllowSpecificOrigins&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;_myAllowSpecificOrigins&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddCors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddPolicy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_myAllowSpecificOrigins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithOrigins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blazor.now.sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                                &lt;span class=&quot;s&quot;&gt;&quot;https://blazor.solrevdev.now.sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                                &lt;span class=&quot;s&quot;&gt;&quot;https://localhost:5001&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                                &lt;span class=&quot;s&quot;&gt;&quot;http://localhost:5000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApplicationBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWebHostEnvironment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDevelopment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseDeveloperExceptionPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseHttpsRedirection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseRouting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseCors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;policy&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithOrigins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blazor.now.sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;s&quot;&gt;&quot;https://blazor.solrevdev.now.sh&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;s&quot;&gt;&quot;https://localhost:5001&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                    &lt;span class=&quot;s&quot;&gt;&quot;http://localhost:5000&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AllowAnyMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WithHeaders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HeaderNames&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseAuthorization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseEndpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that the backend project is ready it was time to deploy it to &lt;a href=&quot;https://fly.io/&quot;&gt;fly.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From a previous project, I already had a handy dandy working Dockerfile I could re-use so making sure I replaced the name of dotnet dll and ensured I was pulling a recent version of .NET Core SDK&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mcr.microsoft.com/dotnet/core/sdk:3.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# update the debian based system&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get upgrade &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# install my dev dependacies inc sqllite and curl and unzip&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; sqlite3
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; libsqlite3-dev
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; curl
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; unzip
&lt;span class=&quot;c&quot;&gt;# not sure why im deleting these&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# add debugging in a docker tooling - install the dependencies for Visual Studio Remote Debugger&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--no-install-recommends&lt;/span&gt; unzip procps
&lt;span class=&quot;c&quot;&gt;# install Visual Studio Remote Debugger&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-sSL&lt;/span&gt; https://aka.ms/getvsdbgsh | bash /dev/stdin &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; latest &lt;span class=&quot;nt&quot;&gt;-l&lt;/span&gt; ~/vsdbg
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app/web&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# layer and build&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . .&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app/web&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet restore

&lt;span class=&quot;c&quot;&gt;# layer adding linker then publish after tree shaking&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;publish&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app/web&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet publish &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; out

&lt;span class=&quot;c&quot;&gt;# final layer using smallest runtime available&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mcr.microsoft.com/dotnet/core/aspnet:3.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;runtime&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app/web&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=publish app/web/out ./&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# expose port and execute aspnetcore app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;EXPOSE&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 5000&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ASPNETCORE_URLS=http://+:5000&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;dotnet&quot;, &quot;backend.dll&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The lines of code in that Dockerfile that were really important for fly.io to work were&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;EXPOSE&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 5000&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ASPNETCORE_URLS=http://+:5000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also created a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.dockerignorefile&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bin/
obj/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I had already installed and authenticated the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flyctl&lt;/code&gt; command-line tool, head over to &lt;a href=&quot;https://fly.io/docs/speedrun/&quot;&gt;https://fly.io/docs/speedrun/&lt;/a&gt; for a simple tutorial on how to get started.&lt;/p&gt;

&lt;p&gt;After some trial and error and some fantastic help from support, I worked out that I needed to override the port that fly.io used so that it matched my .NET Core Web API project.&lt;/p&gt;

&lt;p&gt;I created an app using port 5000 by first navigating into the backend project so that I was in the same location as the csproj file.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;backend
flyctl apps create &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 5000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should find a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fly.toml&lt;/code&gt; file has been added to your project folder&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;s&quot;&gt;app = &quot;blue-dust-2805&quot;&lt;/span&gt;

&lt;span class=&quot;pi&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]]&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;internal_port = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;5000&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;protocol = &quot;tcp&quot;&lt;/span&gt;

  &lt;span class=&quot;s&quot;&gt;[services.concurrency]&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;hard_limit = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;25&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;soft_limit = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;

  &lt;span class=&quot;pi&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;services.ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]]&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;handlers = [&quot;http&quot;]&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;port = &quot;80&quot;&lt;/span&gt;

  &lt;span class=&quot;s&quot;&gt;[[services.ports]]&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;handlers = [&quot;tls&quot;, &quot;http&quot;]&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;port = &quot;443&quot;&lt;/span&gt;

  &lt;span class=&quot;s&quot;&gt;[[services.tcp_checks]]&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;interval = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10000&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;timeout = &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make a mental note of the app name you will see it again in the final hostname, also note the port number that we overrode in the previous step.&lt;/p&gt;

&lt;p&gt;Now to deploy the app…&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flyctl deploy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And get the deployed endpoint URL back to use in the front end…&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flyctl info
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;flyctl info&lt;/code&gt; command will return a deployed endpoint along with a random hostname such as&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;flyctl info
App
  Name &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; blue-dust-2805
  Owner &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; your fly username
  Version &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 10
  Status &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; running
  Hostname &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; blue-dust-2805.fly.dev

Services
  PROTOCOL PORTS
  TCP 80 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; 5000 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;HTTP]
             443 &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; 5000 &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;TLS, HTTP]

IP Addresses
  TYPE ADDRESS CREATED AT
  v4 77.83.141.66 2020-05-17T20:49:30Z
  v6 2a09:8280:1:c3b:5352:d1d5:9afd:fb65 2020-05-17T20:49:31Z
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that the app is deployed you can view it by taking the hostname &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;blue-dust-2805.fly.dev&lt;/code&gt; and appending the weather forecast endpoint at the end.&lt;/p&gt;

&lt;p&gt;For example &lt;a href=&quot;https://blue-dust-2805.fly.dev/weatherforecast&quot;&gt;https://blue-dust-2805.fly.dev/weatherforecast&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If all has gone well you should see some random weather!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Hfyzi1J.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Login to you fly.io control panel to see some stats&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1MyvTwn.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next up it was just a case of replacing the frontend’s call to the local json file with the backend endpoint.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddTransient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BaseAddress&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://blue-dust-2805.fly.dev&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A small change to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FetchData.razor&lt;/code&gt; page.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnInitializedAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_forecasts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetFromJsonAsync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WeatherForecast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;]&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;weatherforecast&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Re-deploy that to vercel by navigating to the root of our solution and running the deploy.sh script or manually via&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ../../
dotnet publish &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release
now &lt;span class=&quot;nt&quot;&gt;--prod&lt;/span&gt; frontend/bin/Release/netstandard2.1/publish/wwwroot/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Test that everything has worked by navigating to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FetchData&lt;/code&gt; endpoint of our frontend. In my case &lt;a href=&quot;https://blazor.now.sh/fetchdata&quot;&gt;https://blazor.now.sh/fetchdata&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/OMSih22.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a final nice to have fly.io have GitHub action we can use to &lt;a href=&quot;https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/&quot;&gt;automatically build and deploy&lt;/a&gt; our Dockerfile based .NET Core Web API on each push or pull request to GitHub.&lt;/p&gt;

&lt;p&gt;Create an auth token in your project&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;backend
flyctl auth token
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Go to your repository on GitHub and select Setting then to Secrets and create a secret called FLY_API_TOKEN with the value of the token we just created.&lt;/p&gt;

&lt;p&gt;Next, create the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.github/workflows/fly.yml&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Fly Deploy&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;release/*&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;master&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;release/*&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;FLY_API_TOKEN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;FLY_PROJECT_PATH&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backend&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deploy app&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v2&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;superfly/flyctl-actions@1.0&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deploy&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that in that file we have told the GitHub action to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FLY_API_TOKEN&lt;/code&gt; we just setup.&lt;/p&gt;

&lt;p&gt;Also because my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fly.toml&lt;/code&gt; is not in the solution root but in the backend folder I can tell fly to look for it by setting the environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FLY_PROJECT_PATH&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;FLY_API_TOKEN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;$&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;FLY_PROJECT_PATH&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;backend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Also, make sure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fly.toml&lt;/code&gt; is not in your .gitignore file.&lt;/p&gt;

&lt;p&gt;And so with that, every time I accept a pull request or I push to master my backend will get deployed to fly.io!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/LqxEeQ2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The new code is up on GitHub at &lt;a href=&quot;https://github.com/solrevdev/blazor-on-vercel&quot;&gt;https://github.com/solrevdev/blazor-on-vercel&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Blazor hosted on vercel aka zeit now.sh</title>
   <link href="https://solrevdev.com/2020/05/17/blazor-hosted-on-vercel-aka-zeit-now.html"/>
   <updated>2020-05-17T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/05/17/blazor-hosted-on-vercel-aka-zeit-now</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; - I did &lt;a href=&quot;https://solrevdev.com/2020/05/18/deploy-aspnet-core-web-api-to-fly-via-docker.html&quot;&gt;deploy an ASP.NET Core Web API backend via Docker&lt;/a&gt; to &lt;a href=&quot;https://fly.io/docs/&quot;&gt;fly.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, I decided it was time to play with Blazor WebAssembly which is in preview for ASP.NET Core 3.1.&lt;/p&gt;

&lt;p&gt;I decided I wanted to publish the sample on Zeit’s now.sh platform which has now been &lt;a href=&quot;https://vercel.com/blog/zeit-is-now-vercel&quot;&gt;rebranded Vercel&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to follow along this was my &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/blazor/get-started?view=aspnetcore-3.1&amp;amp;tabs=netcore-cli&quot;&gt;starting point&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I use Visual Studio Code and for IDE support with vscode you will want to follow the &lt;a href=&quot;https://docs.microsoft.com/en-gb/aspnet/core/blazor/debug?tabs=visual-studio-code&amp;amp;view=aspnetcore-3.1#vscode&quot;&gt;instructions on this page&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Firstly make sure you have &lt;a href=&quot;https://dotnet.microsoft.com/download/dotnet-core/3.1&quot;&gt;.NET Core 3.1&lt;/a&gt; SDK installed.&lt;/p&gt;

&lt;p&gt;Optionally install the Blazor WebAssembly preview template by running the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet new &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; Microsoft.AspNetCore.Components.WebAssembly.Templates::3.2.0-rc1.20223.4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make any changes to the template that you like then when you are ready to publish enter the following command&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet publish &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will build and publish the assets you can deploy to the folder:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bin/Release/netstandard2.1/publish/wwwroot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Make sure you have the now.sh command line tool installed.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm i &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; vercel
now login
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Navigate to this folder and run the now command line tool for deploying.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;bin/Release/netstandard2.1/publish/wwwroot
now &lt;span class=&quot;nt&quot;&gt;--prod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now you have deployed you app to vercel/now.sh.&lt;/p&gt;

&lt;p&gt;This is my deployment &lt;a href=&quot;https://blazor.now.sh/&quot;&gt;https://blazor.now.sh/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may notice that if you navigate to a page like &lt;a href=&quot;https://blazor.now.sh/counter&quot;&gt;https://blazor.now.sh/counter&lt;/a&gt; then hit &lt;kbd&gt;F5&lt;/kbd&gt; to reload you get a 404 not found error.&lt;/p&gt;

&lt;p&gt;To fix this we need to create a configuration file to tell vercel to redirect 404’s to index.html.&lt;/p&gt;

&lt;p&gt;Create a file in your project named vercel.json that will match the publish path&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;publish/wwwroot/vercel.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/WR5zJKR.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Use the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vercel.json&lt;/code&gt; configuration to tell the now.sh platform to redirect 404’s to the index.html page and let Blazor handle the routing&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;routes&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;handle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;filesystem&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/.*&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;dest&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;/index.html&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next we need to tell .NET Core to publish that file so open your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.csproj&lt;/code&gt; file and add the following&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;publish/wwwroot/vercel.json&quot;&lt;/span&gt;  &lt;span class=&quot;na&quot;&gt;CopyToPublishDirectory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PreserveNewest&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

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

&lt;p&gt;Finally you can create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deploy.sh&lt;/code&gt; file that can publish and deploy all in one command.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;

dotnet publish &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release
now &lt;span class=&quot;nt&quot;&gt;--prod&lt;/span&gt; bin/Release/netstandard2.1/publish/wwwroot/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To run this make sure it has the correct permissions&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x deploy.sh
./deploy.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And with that I can deploy Blazor WebAssembly to vercel’s now.sh platform at &lt;a href=&quot;https://blazor.now.sh/&quot;&gt;https://blazor.now.sh/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rZ0wCta.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The code is now up on GitHub at &lt;a href=&quot;https://github.com/solrevdev/blazor-on-vercel&quot;&gt;https://github.com/solrevdev/blazor-on-vercel&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next up I am thinking of deploying a Web API backend for it to talk to.&lt;/p&gt;

&lt;p&gt;Maybe a docker based deployment over at &lt;a href=&quot;https://fly.io/docs/&quot;&gt;fly.io&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt; - I did &lt;a href=&quot;https://solrevdev.com/2020/05/18/deploy-aspnet-core-web-api-to-fly-via-docker.html&quot;&gt;deploy an ASP.NET Core Web API backend via Docker&lt;/a&gt; to &lt;a href=&quot;https://fly.io/docs/&quot;&gt;fly.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Install .NET Core on Ubuntu 20.04 LTS Focal Fossa</title>
   <link href="https://solrevdev.com/2020/04/25/install-dotnetcore-on-ubuntu-focal-fossa.html"/>
   <updated>2020-04-25T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/04/25/install-dotnetcore-on-ubuntu-focal-fossa</id>
   <content type="html">&lt;p&gt;A couple of days ago Canonical the custodians of the Ubuntu Linux distribution released the latest long term support version of their desktop Linux operating system.&lt;/p&gt;

&lt;p&gt;Codenamed &lt;em&gt;Focal Fossa&lt;/em&gt; the 20.04 LTS release is the latest and greatest version. For more information about its new features head over to their &lt;a href=&quot;https://ubuntu.com/blog/ubuntu-20-04-lts-arrives&quot;&gt;blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/862L8Gb.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For us .NET Core developers each new release of Ubuntu generally means that whenever we need to update the .NET Core version we need to alter our package manager location so that we get the correct version.&lt;/p&gt;

&lt;p&gt;Microsoft has now updated the dedicated page titled &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/install/linux-package-manager-ubuntu-2004&quot;&gt;“Ubuntu 20.04 Package Manager - Install .NET Core”&lt;/a&gt; which has instructions on how to use a package manager to install .NET Core on Ubuntu 20.04.&lt;/p&gt;

&lt;p&gt;For those looking for a TLDR; here is the info copied from that page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microsoft repository key and feed needed.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb &lt;span class=&quot;nt&quot;&gt;-O&lt;/span&gt; packages-microsoft-prod.deb
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dpkg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; packages-microsoft-prod.deb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Install the .NET Core SDK&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;apt-transport-https
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;dotnet-sdk-3.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Install the ASP.NET Core runtime&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;apt-transport-https
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;aspnetcore-runtime-3.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Install the .NET Core runtime&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;apt-transport-https
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;dotnet-runtime-3.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I have not pulled the trigger yet. I am waiting for things to settle down and for my 19.10 distribution to tell me its time to upgrade.&lt;/p&gt;

&lt;p&gt;However, for those who want to upgrade now and cannot wait you can force the issue by the following.&lt;/p&gt;

&lt;p&gt;Press &lt;kbd&gt;ALT&lt;/kbd&gt; + &lt;kbd&gt;F2&lt;/kbd&gt; followed by&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;update-manager &lt;span class=&quot;nt&quot;&gt;-cd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following dialog will then appear allowing you to then upgrade now.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GPOkbZb.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>aspnetcore 3.1.2 windows hosting bundle caused 503 services unavailable</title>
   <link href="https://solrevdev.com/2020/03/17/aspnetcore-312-windows-hosting-bundle-caused-503-services-unavailable.html"/>
   <updated>2020-03-17T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/03/17/aspnetcore-312-windows-hosting-bundle-caused-503-services-unavailable</id>
   <content type="html">&lt;p&gt;Today &lt;a href=&quot;https://github.com/dotnet/core/blob/master/release-notes/3.1/3.1.2/3.1.200-sdk.md&quot;&gt;NET Core 3.1.200 SDK - March 16, 2020&lt;/a&gt; was installed on my development and production boxes.&lt;/p&gt;

&lt;p&gt;With a new release, I tend to also install the Windows hosting bundle associated with each release, and in this case, it was &lt;a href=&quot;https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-aspnetcore-3.1.2-windows-hosting-bundle-installer&quot;&gt;ASP.NET Core Runtime 3.1.2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, on installing it, the next request to the website showed a 503 Service Unavailable error:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/le8MZAZ.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Debugging the w3 process in Visual Studio showed this error:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Unhandled exception at 0x53226EE9 (aspnetcorev2.dll) in w3wp.exe: 0xC000001D: Illegal Instruction.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Event Viewer had entries such as this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/KoDAVk2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I tried &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IISRESET&lt;/code&gt; and uninstalling the hosting bundle but that did not help.&lt;/p&gt;

&lt;p&gt;I noticed that the application pool was stopped for my website, Restarting it would result in the same unhandled exception as above.&lt;/p&gt;

&lt;p&gt;As a troubleshooting exercise, I created a new application pool and pointed my website to that one and deleted the old one.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/yqh8IS3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This seems to fixed things for now.&lt;/p&gt;

&lt;p&gt;Success? 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>localhost HTTPS subdomains with a Kestrel SSL certificate</title>
   <link href="https://solrevdev.com/2020/03/06/localhost-https-subdomains-with-a-kestrel-ssl-certificate.html"/>
   <updated>2020-03-06T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/03/06/localhost-https-subdomains-with-a-kestrel-ssl-certificate</id>
   <content type="html">&lt;p&gt;When you build ASP.NET Core websites locally, you can view your local site under HTTPS/SSL, go read &lt;a href=&quot;https://www.hanselman.com/blog/DevelopingLocallyWithASPNETCoreUnderHTTPSSSLAndSelfSignedCerts.aspx&quot;&gt;this article&lt;/a&gt; by Scott Hanselman for more information.&lt;/p&gt;

&lt;p&gt;For the most part, this works great out of the box.&lt;/p&gt;

&lt;p&gt;However, I am building a multi-tenant application as in I make use of subdomains such as &lt;a href=&quot;https://www.mywebsite.com&quot;&gt;https://www.mywebsite.com&lt;/a&gt; and &lt;a href=&quot;https://customer1.mywebsite.com&quot;&gt;https://customer1.mywebsite.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So naturally, when I develop locally I want to visit &lt;a href=&quot;https://www.localhost:5001/&quot;&gt;https://www.localhost:5001/&lt;/a&gt; and &lt;a href=&quot;https://customer1.localhost:5001/&quot;&gt;https://customer1.localhost:5001/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can do this out of the box you just need to add this to your hosts file.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#macos / linux&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; /etc/hosts

127.0.0.1 www.localhost
127.0.0.1 customer1.localhost
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#windows&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C:\Windows\System32\drivers\etc\hosts&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;www.localhost&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;127.0.0.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;customer1.localhost&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However when you visit either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www.&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;customer1.&lt;/code&gt; you will get an SSL cert warning from your browser as the SSL cert that kestrel and/or IISExpress uses only covers the apex localhost domain.&lt;/p&gt;

&lt;p&gt;Yesterday I posted on &lt;a href=&quot;https://twitter.com/solrevdev/status/1235641092256784385&quot;&gt;twitter asking for help&lt;/a&gt; and the replies I got pointed me in the right direction.&lt;/p&gt;

&lt;h3 id=&quot;mkcert-to-the-rescue&quot;&gt;mkcert to the rescue&lt;/h3&gt;

&lt;p&gt;The answer is to use some software called &lt;a href=&quot;https://github.com/FiloSottile/mkcert&quot;&gt;mkcert&lt;/a&gt; to generate a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pfx&lt;/code&gt; certificate and configure kestrel to use this certificate when in development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First install mkcert&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#macOS&lt;/span&gt;
brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mkcert
brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;nss &lt;span class=&quot;c&quot;&gt;# if you use Firefox&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#linux&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;libnss3-tools
    &lt;span class=&quot;nt&quot;&gt;-or-&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;yum &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;nss-tools
    &lt;span class=&quot;nt&quot;&gt;-or-&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;pacman &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; nss
    &lt;span class=&quot;nt&quot;&gt;-or-&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;zypper &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mozilla-nss-tools

brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mkcert
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#windows&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;choco&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mkcert&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scoop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;bucket&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;extras&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scoop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mkcert&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Then create a new local certificate authority.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkcert &lt;span class=&quot;nt&quot;&gt;-install&lt;/span&gt;
Using the &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;CA at &lt;span class=&quot;s2&quot;&gt;&quot;/Users/solrevdev/Library/Application Support/mkcert&quot;&lt;/span&gt; ✨
The &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;CA is already installed &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;the system trust store! 👍
The &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;CA is now installed &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;the Firefox trust store &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;requires browser restart&lt;span class=&quot;o&quot;&gt;)!&lt;/span&gt; 🦊
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;create-pfx-certificate&quot;&gt;Create .pfx certificate&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Now create your certificate covering the subdomains you want to use&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#navigate to your website root&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;src/web/
&lt;span class=&quot;c&quot;&gt;#remove any earlier failed attempts!&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;rm &lt;/span&gt;kestrel.pfx
&lt;span class=&quot;c&quot;&gt;#create the cert adding each subdomain you want to use&lt;/span&gt;
mkcert &lt;span class=&quot;nt&quot;&gt;-pkcs12&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p12-file&lt;/span&gt; kestrel.pfx www.localhost customer1.localhost localhost
&lt;span class=&quot;c&quot;&gt;#gives this output&lt;/span&gt;
Using the &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;CA at &lt;span class=&quot;s2&quot;&gt;&quot;/Users/solrevdev/Library/Application Support/mkcert&quot;&lt;/span&gt; ✨

Created a new certificate valid &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;the following names 📜
 - &lt;span class=&quot;s2&quot;&gt;&quot;www.localhost&quot;&lt;/span&gt;
 - &lt;span class=&quot;s2&quot;&gt;&quot;customer1.localhost&quot;&lt;/span&gt;
 - &lt;span class=&quot;s2&quot;&gt;&quot;localhost&quot;&lt;/span&gt;

The PKCS#12 bundle is at &lt;span class=&quot;s2&quot;&gt;&quot;kestrel.pfx&quot;&lt;/span&gt; ✅

The legacy PKCS#12 encryption password is the often hardcoded default &lt;span class=&quot;s2&quot;&gt;&quot;changeit&quot;&lt;/span&gt; ℹ️
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Now ensure you copy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.pfx&lt;/code&gt; file over when in development mode.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;web.csproj&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&apos;$(Configuration)&apos; == &apos;Debug&apos; &quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;None&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Update=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;kestrel.pfx&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;CopyToOutputDirectory=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;PreserveNewest&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Condition=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exists(&apos;kestrel.pfx&apos;)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Now configure kestrel to use this certificate when in development not production&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You have two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appsettings&lt;/code&gt; files, one for development and one for every other environment. Open up your development one and tell kestrel to use your newly created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pfx&lt;/code&gt; file when not in production.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;appsettings.Development.json&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Logging&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;LogLevel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Debug&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Microsoft&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Warning&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Microsoft.Hosting.Lifetime&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Warning&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Kestrel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Certificates&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Path&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;kestrel.pfx&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Password&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;changeit&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And with that, I was done. If you need to add more subdomains you will need to add them to your hosts file and recreate your pfx file by &lt;a href=&quot;#create-pfx-certificate&quot;&gt;redoing the instructions&lt;/a&gt; above.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>3008 A configuration error has occurred</title>
   <link href="https://solrevdev.com/2020/03/06/3008-a-configuration-error-has-occurred.html"/>
   <updated>2020-03-06T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/03/06/3008-a-configuration-error-has-occurred</id>
   <content type="html">&lt;p&gt;A static HTML website I look after is hosted on a Windows Server 2012R2 instance running IIS, it makes use of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.config&lt;/code&gt; file as it has some settings that allow this site to be served from behind an Amazon Web Services Elastic Load Balancer.&lt;/p&gt;

&lt;p&gt;Today it kept crashing with the thousands of these events in event viewer:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Event code: 3008
Event message: A configuration error has occurred.
Event time: 05/03/2020 09:15:49
Event time (UTC): 05/03/2020 09:15:49
Event ID: 83032f1dc8d9486e95dfc13f9f88a22d
Event sequence: 1
Event occurrence: 1
Event detail code: 0
Application information:
    Application domain: /LM/W3SVC/6/ROOT-1789-132278733487264657
    Trust level: Full
    Application Virtual Path: /
    Application Path: C:\Sites\your-website.com\static\
    Machine name: PRODUCTION-WEB-
Process information:
    Process ID: 2104
    Process name: w3wp.exe
    Account name: IIS APPPOOL\your-website.com
Exception information:
    Exception type: ConfigurationErrorsException
    Exception message: Unrecognized attribute &apos;targetFramework&apos;. Note that attribute names are case-sensitive. (C:\Sites\your-website.com\static\web.config line 4)
Request information:
    Request URL: http://www.your-website.com/default.aspx
    Request path: /default.aspx
    User host address: 172.31.38.122
    User:
    Is authenticated: False
    Authentication Type:
    Thread account name: IIS APPPOOL\your-website.com
Thread information:
    Thread ID: 5
    Thread account name: IIS APPPOOL\your-website.com
    Is impersonating: False
    Stack trace:
   at System.Web.HttpRuntime.HostingInit(HostingEnvironmentFlags hostingFlags)
Custom event details:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/tO2yXq5.png&quot; alt=&quot;EventViewer&quot; title=&quot;EventViewer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The fix was to change the websites application pool to use .NET CLR Version 4 rather than .NET CLR Version 2&lt;/p&gt;

&lt;p&gt;So, open &lt;strong&gt;IIS&lt;/strong&gt;, choose &lt;strong&gt;Application Pools&lt;/strong&gt; from the left-hand navigation, Choose your app pool and click &lt;strong&gt;basic settings&lt;/strong&gt; to open the dialog to change which .NET CLR Version to use.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/UuKgIM7.png&quot; alt=&quot;AppPool&quot; title=&quot;AppPool&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once this was done the errors stopped and the site stopped crashing&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Call UseSession after UseRouting and before UseEndpoints</title>
   <link href="https://solrevdev.com/2020/03/05/call-usesession-after-userouting-and-before-useendpoints.html"/>
   <updated>2020-03-05T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/03/05/call-usesession-after-userouting-and-before-useendpoints</id>
   <content type="html">&lt;p&gt;Today, I fixed a bug where session cookies were not being persisted in an ASP.Net Core Razor Pages application.&lt;/p&gt;

&lt;p&gt;The answer was in the &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-3.1#configure-session-state&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To quote that page:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The order of middleware is important. Call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseSession&lt;/code&gt; after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseRouting&lt;/code&gt; and before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseEndpoints&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So my code which did work in the past, but probably before &lt;a href=&quot;https://wildermuth.com/2019/09/09/Endpoint-Routing-in-ASP-NET-Core-3-0&quot;&gt;endpoint routing&lt;/a&gt; was introduced was this:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseRouting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseEndpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapRazorPages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the fix was to move &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseSession&lt;/code&gt; below &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UseRouting&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseRouting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseEndpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;endpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;MapRazorPages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Restart Omnisharp process within Visual Studio Code</title>
   <link href="https://solrevdev.com/2020/03/01/restart-omnisharp-process-within-visual-studio-code.html"/>
   <updated>2020-03-01T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/03/01/restart-omnisharp-process-within-visual-studio-code</id>
   <content type="html">&lt;p&gt;Another quick one for today, Every now and again my intellisense gets confused in Visual Studio Code displaying errors and warnings that should not exist.&lt;/p&gt;

&lt;p&gt;The fix for this is to restart the Omnisharp process.&lt;/p&gt;

&lt;p&gt;So first off get the commmand pallette up:&lt;/p&gt;

&lt;p&gt;&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;Shift&lt;/kbd&gt;+&lt;kbd&gt;P&lt;/kbd&gt;&lt;/p&gt;

&lt;p&gt;Then type:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;omnisharp:restart omnisharp&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Everything should then go back to normal.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Assembly with same name is already loaded</title>
   <link href="https://solrevdev.com/2020/02/21/assembly-with-same-name-is-already-loaded.html"/>
   <updated>2020-02-21T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/02/21/assembly-with-same-name-is-already-loaded</id>
   <content type="html">&lt;p&gt;I am in the process of building and publishing my first ever NuGet package and while I am not ready to go into that today I can post a quick tip about fixing an error I had with a library I am using to help with git versioning.&lt;/p&gt;

&lt;p&gt;The library is &lt;a href=&quot;https://github.com/AArnott/Nerdbank.GitVersioning&quot;&gt;Nerdbank.GitVersioning&lt;/a&gt; and the error I got was when I tried to upgrade from an older version to the current one.&lt;/p&gt;

&lt;p&gt;The error?&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The &lt;span class=&quot;s2&quot;&gt;&quot;Nerdbank.GitVersioning.Tasks.GetBuildVersion&quot;&lt;/span&gt; task could not be loaded from the assembly

Assembly with same name is already loaded Confirm that the &amp;lt;UsingTask&amp;gt; declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the fix was to run the following command, thanks to &lt;a href=&quot;https://github.com/AArnott/Nerdbank.GitVersioning/issues/374&quot;&gt;this issue&lt;/a&gt; over on GitHub&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet build-server shutdown
nbgv &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Show hidden files with a macOS keyboard shortcut</title>
   <link href="https://solrevdev.com/2020/02/12/show-hidden-files-mac-keyboard-shortcut.html"/>
   <updated>2020-02-12T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/02/12/show-hidden-files-mac-keyboard-shortcut</id>
   <content type="html">&lt;p&gt;A very very quick one today.&lt;/p&gt;

&lt;p&gt;Sometimes when developing on macOS I want to view hidden files in Finder but most of the time it is just extra noise so I like them hidden.&lt;/p&gt;

&lt;p&gt;There is a keyboard shortcut to toggle the visibility of these files.&lt;/p&gt;

&lt;p&gt;&lt;kbd&gt;cmd&lt;/kbd&gt; + &lt;kbd&gt;Shift&lt;/kbd&gt; + &lt;kbd&gt;.&lt;/kbd&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/PUXmIjP.jpg&quot; alt=&quot;cmd+shift+.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;(thanks to &lt;a href=&quot;http://osxdaily.com/2018/02/12/show-hidden-files-mac-keyboard-shortcut/&quot;&gt;osx daily&lt;/a&gt; for the tip and image.)&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;This keyboard shortcut will show hidden files or hide them if shown…&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Windows does not remember git password</title>
   <link href="https://solrevdev.com/2020/02/11/windows-does-not-remember-git-password.html"/>
   <updated>2020-02-11T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/02/11/windows-does-not-remember-git-password</id>
   <content type="html">&lt;p&gt;Today I was writing a Windows batch script that would at some stage run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When I ran the script it paused and displayed the message:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Enter passphrase for key: &apos;c/Users/Administrator/.ssh/id_rsa&apos;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-02-11_09_22_25.png&quot; alt=&quot;2020-02-11_09_22_25.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;No matter how many times I entered the passphrase Windows would not remember it and the prompt would appear again.&lt;/p&gt;

&lt;p&gt;So, after some time on Google and some trial and error, I was able to fix the issue and so for anyone else that has the same issue or indeed for me from the future here are those steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable the OpenSSH Authentication Agent service and make it start automatically.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-02-11_09_36_02.png&quot; alt=&quot;2020-02-11_09_36_02.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add your SSH key to the agent with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh-add&lt;/code&gt; at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C:\Users\Administrator\.ssh&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-02-11_09_37_39.png&quot; alt=&quot;2020-02-11_09_37_39.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test git integration by doing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt; from the command line and enter the passphrase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Enter your passphrase when asked during a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add an environment variable for GIT_SSH&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;setx&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;GIT_SSH&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;C:\Windows\System32\OpenSSH\ssh.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-02-11_09_41_45.png&quot; alt=&quot;2020-02-11_09_41_45.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once these steps were done all was fine and no prompt came up again.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to Fix: MySQL Server Has Gone Away Error on macOS</title>
   <link href="https://solrevdev.com/2020/02/05/error-2006-hy000-mysql-server-has-gone-away.html"/>
   <updated>2020-02-05T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/02/05/error-2006-hy000-mysql-server-has-gone-away</id>
   <content type="html">&lt;p&gt;In this post, I’ll address a common issue many developers face when working with MySQL on macOS: the “MySQL server has gone away” error. This error can be frustrating, but it’s usually straightforward to fix.&lt;/p&gt;

&lt;h2 id=&quot;understanding-the-error&quot;&gt;Understanding the Error&lt;/h2&gt;

&lt;p&gt;When connecting to MySQL via the terminal using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql -u root&lt;/code&gt;, you might encounter the following error messages:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ERROR 2006 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;HY000&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: MySQL server has gone away
No connection. Trying to reconnect...
ERROR 2013 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;HY000&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: Lost connection to MySQL server at &lt;span class=&quot;s1&quot;&gt;&apos;reading initial communication packet&apos;&lt;/span&gt;, system error: 102
ERROR:
Can&lt;span class=&quot;s1&quot;&gt;&apos;t connect to the server
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GZILrCL.png&quot; alt=&quot;screenshot&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;possible-causes&quot;&gt;Possible Causes&lt;/h3&gt;

&lt;p&gt;This error typically occurs due to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Server timeout settings&lt;/li&gt;
  &lt;li&gt;Network issues&lt;/li&gt;
  &lt;li&gt;Incorrect configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-by-step-solution&quot;&gt;Step-by-Step Solution&lt;/h2&gt;

&lt;h3 id=&quot;step-1-restart-mysql-service&quot;&gt;Step 1: Restart MySQL Service&lt;/h3&gt;

&lt;p&gt;One of the simplest troubleshooting steps is to restart the MySQL service. This can resolve many transient issues.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;killall mysqld
mysql.server start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;step-2-check-mysql-configuration&quot;&gt;Step 2: Check MySQL Configuration&lt;/h3&gt;

&lt;p&gt;Ensure your MySQL configuration (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my.cnf&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;my.ini&lt;/code&gt;) is set up correctly. Key settings to check include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_allowed_packet&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wait_timeout&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;interactive_timeout&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;step-3-monitor-logs&quot;&gt;Step 3: Monitor Logs&lt;/h3&gt;

&lt;p&gt;Check MySQL logs for any additional error messages that might give more context to the issue. Logs are typically located in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/var/mysql&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;step-4-verify-network-stability&quot;&gt;Step 4: Verify Network Stability&lt;/h3&gt;

&lt;p&gt;Ensure your network connection is stable, as intermittent connectivity can cause these types of errors.&lt;/p&gt;

&lt;h2 id=&quot;additional-tips&quot;&gt;Additional Tips&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Regular Maintenance&lt;/strong&gt;: Regularly check and maintain your MySQL server to prevent such issues.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Backup Data&lt;/strong&gt;: Always backup your data before making any significant changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these steps, you should be able to resolve the “MySQL server has gone away” error and continue your development smoothly.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Upgrade bootstrap and jquery in ASP.NET Core 3.1 with libman</title>
   <link href="https://solrevdev.com/2020/02/02/upgrade-bootstrap-and-jquery-in-aspnet-core-31-with-libman.html"/>
   <updated>2020-02-02T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/02/02/upgrade-bootstrap-and-jquery-in-aspnet-core-31-with-libman</id>
   <content type="html">&lt;p&gt;Building server-rendered HTML websites is a nice experience these days with ASP.NET Core.&lt;/p&gt;

&lt;p&gt;The new &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/razor-pages-start?view=aspnetcore-3.1&quot;&gt;Razor Pages&lt;/a&gt; paradigm is a wonderful addition and improvement over MVC in that it tends to keep all your feature logic grouped rather than having your logic split over many folders.&lt;/p&gt;

&lt;p&gt;The standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet new&lt;/code&gt; template does a good job of giving you what you need to get started.&lt;/p&gt;

&lt;p&gt;It bundles in bootstrap and jquery for you which is great but it’s not obvious how you manage to add new client-side dependencies or indeed how to upgrade existing ones such as bootstrap and jquery.&lt;/p&gt;

&lt;p&gt;In the dark old days, Bower used to be the recommended way but that has since been &lt;a href=&quot;https://devblogs.microsoft.com/aspnet/what-happened-to-bower/&quot;&gt;depreacted&lt;/a&gt; in favour of a new tool called &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/client-side/libman/?view=aspnetcore-3.1&quot;&gt;LibMan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/JoaMpl0.png&quot; alt=&quot;LibMan&quot; title=&quot;LibMan&quot; /&gt;&lt;/p&gt;

&lt;p&gt;LibMan is like most things from Microsoft these days &lt;a href=&quot;https://github.com/aspnet/LibraryManager&quot;&gt;open source&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Designed as a replacement for Bower and npm, LibMan helps to find and fetch client-side libraries from most external sources or any file system library catalogue.&lt;/p&gt;

&lt;p&gt;There are tutorials for how to &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/client-side/libman/libman-vs?view=aspnetcore-3.1&quot;&gt;use LibMan with ASP.NET Core in Visual Studio&lt;/a&gt; and to &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/client-side/libman/libman-cli?view=aspnetcore-3.1&quot;&gt;use the LibMan CLI with ASP.NET Core&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The magic is done via a file in your project root called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libman.json&lt;/code&gt; which describes what files, from where and to where they need to go basically.&lt;/p&gt;

&lt;p&gt;I needed to upgrade the version of jquery and bootstrap in a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet new&lt;/code&gt; project so here is the libman.json file that will replace bootstrap and jquery bundled with ASP.NET Core with the latest versions.&lt;/p&gt;

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

&lt;p&gt;I was using Visual Studio at the time and this will manage this for you but if like me who mostly codes in Visual Studio Code on macOS or Linux then you can achieve the same result by installing and using the &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/client-side/libman/libman-cli?view=aspnetcore-3.1&quot;&gt;LibMan Cli&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Event Viewer Logs with .NET Core Workers as Windows Services</title>
   <link href="https://solrevdev.com/2020/01/31/event-viewer-logs-with-net-core-workers-as-windows-services.html"/>
   <updated>2020-01-31T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/31/event-viewer-logs-with-net-core-workers-as-windows-services</id>
   <content type="html">&lt;p&gt;Back in the older classic windows only .NET Framework days, I would use a cool framework called &lt;a href=&quot;http://topshelf-project.com/&quot;&gt;TopShelf&lt;/a&gt; to help turn a console application during development into a running windows service in production.&lt;/p&gt;

&lt;p&gt;Today instead I was able to install and run a windows service by modifying a &lt;a href=&quot;https://devblogs.microsoft.com/aspnet/net-core-workers-as-windows-services/&quot;&gt;.NET Core Worker project&lt;/a&gt; by just using .NET Core natively.&lt;/p&gt;

&lt;p&gt;Also, I was able to add some logging to the Windows Event Viewer Application Log.&lt;/p&gt;

&lt;p&gt;First, I created a .NET Core Worker project:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;tempy &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;
dotnet new worker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then I added some references:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet add package Microsoft.Extensions.Hosting
dotnet add package Microsoft.Extensions.Hosting.WindowsServices
dotnet add package Microsoft.Extensions.Logging.EventLog
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next up I made changes to Program.cs, In my project I am adding a HttpClient to make external Web Requests to an API.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateHostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;services&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddHttpClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

        &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHostBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateHostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseWindowsService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureLogging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AddHostedService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Worker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The key line for adding Windows Services support is :&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseWindowsService&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Logging to Event Viewer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I also wanted to log to the Application Event Viewer log so notice the line:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureLogging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddEventLog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now for a little gotcha, this will only log events &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Warning&lt;/code&gt; and higher so the Worker template’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;logger.LogInformation()&lt;/code&gt; statements will display when debugging in the console but not when installed as a windows service.&lt;/p&gt;

&lt;p&gt;To fix this make this change to appsettings.json, note the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EventLog&lt;/code&gt; section where the levels have been dialled back down to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Information&lt;/code&gt; level.&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Logging&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;LogLevel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Information&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Microsoft&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Warning&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Microsoft.Hosting.Lifetime&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Information&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;EventLog&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;LogLevel&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Default&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Information&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;Microsoft.Hosting.Lifetime&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Information&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Publishing and managing the service&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So with this done, I then needed to first publish, then install, start and have means to stop and uninstall the service.&lt;/p&gt;

&lt;p&gt;I was able to manage all of this from the command line, using the &lt;a href=&quot;https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/cc754599(v=ws.11)&quot;&gt;SC tool&lt;/a&gt; (Sc.exe) that should already be installed on windows for you and be in your path already.&lt;/p&gt;

&lt;p&gt;Publish:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;C:&lt;span class=&quot;se&quot;&gt;\P&lt;/span&gt;athToSource&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
dotnet publish &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; win-x64 &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; C:&lt;span class=&quot;se&quot;&gt;\P&lt;/span&gt;athToDestination
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Install:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;your service name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;binPath&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;C:\PathToDestination\worker.exe&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Start:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;your service name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Stop:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;your service name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Uninstall:&lt;/p&gt;

&lt;div class=&quot;language-powershell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;sc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;your service name&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once I saw that all was well I was able to dial back the logging to the Event Viewer by making a change to appsettings.json, In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EventLog&lt;/code&gt; section I changed the levels back up to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Warning&lt;/code&gt; level.&lt;/p&gt;

&lt;p&gt;This means that anything important will indeed get logged to the Windows Event Viewer but most &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Information&lt;/code&gt; level noise will not.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Apply Cut or Copy to blank lines when there is no selection</title>
   <link href="https://solrevdev.com/2020/01/31/apply-cut-or-copy-to-blank-lines-when-there-is-no-selection.html"/>
   <updated>2020-01-31T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/31/apply-cut-or-copy-to-blank-lines-when-there-is-no-selection</id>
   <content type="html">&lt;p&gt;I mostly code in &lt;a href=&quot;https://code.visualstudio.com/insiders/&quot;&gt;Visual Studio Code Insiders&lt;/a&gt; on either macOS or Linux but on the occasion that I develop on windows, I do like to use the old faithful Visual Studio.&lt;/p&gt;

&lt;p&gt;And today I fixed a slight annoyance that I have with Visual Studio 2019.&lt;/p&gt;

&lt;p&gt;If you cut or copy on a blank line accidentally which does happen you will lose your clipboard contents.&lt;/p&gt;

&lt;p&gt;To fix this in the search bar at the top enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Apply Cut or Copy to blank lines when there is no selection&lt;/code&gt; and open its’s dialog.&lt;/p&gt;

&lt;p&gt;Uncheck that box for your language (or all languages as I did).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/s2Hn38e.png&quot; alt=&quot;vs dialog&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Timers in .NET Part 2</title>
   <link href="https://solrevdev.com/2020/01/28/timers-in-dotnet-part-2.html"/>
   <updated>2020-01-28T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/28/timers-in-dotnet-part-2</id>
   <content type="html">&lt;p&gt;I have started to cross-post to the &lt;a href=&quot;https://dev.to/&quot;&gt;Dev Community&lt;/a&gt; website as well as on my &lt;a href=&quot;https://solrevdev.com&quot;&gt;solrevdev blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A previous post about &lt;a href=&quot;https://dev.to/solrevdev/timers-in-net-omd&quot;&gt;Timers in .NET&lt;/a&gt; received an interesting reply from &lt;a href=&quot;https://dev.to/katnel20&quot;&gt;Katie Nelson&lt;/a&gt; who asked about what do do with &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken?view=netcore-3.1&quot;&gt;Cancellation Tokens&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TimerCallBack&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.timer?view=netcore-3.1&quot;&gt;System.Threading.Timer&lt;/a&gt; class has been in the original .NET Framework almost from the very beginning and the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.timercallback?view=netcore-3.1&quot;&gt;TimerCallback&lt;/a&gt; delegate has a method signature that does not handle CancellationTokens nativly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trial and error&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, I span up a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet new worker&lt;/code&gt; project which has StartAsync and StopAsync methods that take in a CancellationToken in their method signatures and seemed like a good place to start.&lt;/p&gt;

&lt;p&gt;After some tinkering with my original class and some research on StackOverflow, I came across &lt;a href=&quot;https://stackoverflow.com/a/56666084/2041&quot;&gt;this post&lt;/a&gt; which I used as the basis as a new improved Timer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improvements&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Firstly I was able to improve on my &lt;a href=&quot;https://gist.github.com/solrevdev/60f58cc72b3576617486162470c50280&quot;&gt;original TimerTest class&lt;/a&gt; by replacing the field level locking object combined with its’s calls to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Monitor.TryEnter(_locker)&lt;/code&gt; by using the Timer’s built-in  &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.timer.change?view=netcore-3.1&quot;&gt;Change&lt;/a&gt; method.&lt;/p&gt;

&lt;p&gt;Next up I modified the original TimerCallback DoWork method so that it called my new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DoWorkAsync(CancellationToken token)&lt;/code&gt; method that with a CancellationToken as a parameter does check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsCancellationRequested&lt;/code&gt; before doing my long-running work.&lt;/p&gt;

&lt;p&gt;The class is a little more complicated than the original but it does handle &lt;kbd&gt;ctrl&lt;/kbd&gt;&lt;kbd&gt;c&lt;/kbd&gt; gracefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, here is the new and improved Timer in a new dotnet core background worker class.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/f28ab81363e9875d25b3f3453339a556.js?file=Worker.cs&quot;&gt; &lt;/script&gt;

&lt;p&gt;And here is the full gist with the rest of the project files for future reference.&lt;/p&gt;

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

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Navigate into a newly created directory</title>
   <link href="https://solrevdev.com/2020/01/28/navigate-into-newly-created-directory.html"/>
   <updated>2020-01-28T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/28/navigate-into-newly-created-directory</id>
   <content type="html">&lt;p&gt;Today I came across a fantastic command line trick.&lt;/p&gt;

&lt;p&gt;Normally when I want to create a directory in the command line it takes multiple commands to start working in that directory.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;tempy
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;tempy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Well, that can be shortened to a one-liner!&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;tempy &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;🤯&lt;/p&gt;

&lt;p&gt;This is why I love software development.&lt;/p&gt;

&lt;p&gt;It does not matter how long you have been doing it you are always learning something new!&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Timers in .NET</title>
   <link href="https://solrevdev.com/2020/01/27/timers-in-dotnet.html"/>
   <updated>2020-01-27T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/27/timers-in-dotnet</id>
   <content type="html">&lt;p&gt;A current C# project of mine required a timer where every couple of seconds a method would fire and a potentially fairly long-running process would run.&lt;/p&gt;

&lt;p&gt;With .NET we have a few built-in options for timers:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Web.UI.Timer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Available in the &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.timer?redirectedfrom=MSDN&amp;amp;view=netframework-4.8&amp;amp;viewFallbackFrom=netcore-3.1&quot;&gt;.NET Framework 4.8&lt;/a&gt; which performs asynchronous or synchronous Web page postbacks at a defined interval and was used back in the older WebForms days.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Windows.Forms.Timer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This timer is optimized for use in &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.timer?view=netcore-3.1&quot;&gt;Windows Forms&lt;/a&gt; applications and must be used in a window.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Timers.Timer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Generates an event after a set interval, with an option to generate recurring events. This &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer?redirectedfrom=MSDN&amp;amp;view=netcore-3.1&quot;&gt;timer&lt;/a&gt; is almost what I need however this has quite a few &lt;a href=&quot;https://stackoverflow.com/questions/46176486/system-timers-timer-crashes-on-exception-thrown&quot;&gt;stackoverflow posts&lt;/a&gt; where exceptions get swallowed.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.Threading.Timer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Provides a mechanism for &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/api/system.threading.timer?redirectedfrom=MSDN&amp;amp;view=netcore-3.1&quot;&gt;executing a method on a thread pool thread&lt;/a&gt; at specified intervals and is the one I decided to go with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issues&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I came across a couple of minor issues the first being that even though I held a reference to my Timer object in my class and disposed of it in a Dispose method the timer would stop ticking after a while suggesting that the garbage collector was sweeping up and removing it.&lt;/p&gt;

&lt;p&gt;My Dispose method looks like the first method below and I suspect it is because I am using the &lt;a href=&quot;https://www.c-sharpcorner.com/code/275/conditional-access-in-C-Sharp-6-0.aspx&quot;&gt;conditional access shortcut&lt;/a&gt; feature from C# 6 rather than explicitly checking for null first.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// conditional access shortcut&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// null check&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_timer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Dispose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A workaround is to tell the garbage collector to not collect this reference by using this line of code in timer’s elapsed method.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;GC&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;KeepAlive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The next issue was that my TimerTick event would fire and before the method that was being called could finish another tick event would fire.&lt;/p&gt;

&lt;p&gt;This required a &lt;a href=&quot;https://stackoverflow.com/a/13267259/2041&quot;&gt;stackoverflow&lt;/a&gt; search where the following code fixed my issue.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// private field&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_locker&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// this in TimerTick event&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Monitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;TryEnter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_locker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// do long running work here&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;DoWork&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;finally&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Monitor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_locker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And so with these two fixes in place, my timer work was behaving as expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is a sample class with the above code all in context for future reference&lt;/p&gt;

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

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>My uses page with my setup, gear, software and config</title>
   <link href="https://solrevdev.com/2020/01/23/my-uses-page-with-my-setup-gear-software-and-config.html"/>
   <updated>2020-01-23T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/23/my-uses-page-with-my-setup-gear-software-and-config</id>
   <content type="html">&lt;p&gt;Another quick one today.&lt;/p&gt;

&lt;p&gt;I was recently listening to an &lt;a href=&quot;https://syntax.fm/show/215/hasty-treat-picking-the-stack-for-uses-tech-gatsby-react-context-styled-components&quot;&gt;episode of syntax.fm&lt;/a&gt; where wes bos was talking about a new site &lt;a href=&quot;https://uses.tech/&quot;&gt;uses.tech&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rBdR6B6.gif&quot; alt=&quot;uses.tech&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is a site that lists /uses pages detailing developer setups, gear, software and configs.&lt;/p&gt;

&lt;p&gt;This inspired me to create my own &lt;a href=&quot;https://solrevdev.com/uses/&quot;&gt;/uses&lt;/a&gt; page.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Links do not open in Google Chrome</title>
   <link href="https://solrevdev.com/2020/01/22/links-do-not-open-google-chrome.html"/>
   <updated>2020-01-22T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/22/links-do-not-open-google-chrome</id>
   <content type="html">&lt;p&gt;A quick one today.&lt;/p&gt;

&lt;p&gt;Sometimes I will click on a link in an external application such as Mail.app (I am careful where these links come from of course!) and nothing will happen.&lt;/p&gt;

&lt;p&gt;Well, Google Chrome will launch if it was closed when I clicked the link however the URL I clicked will not open.&lt;/p&gt;

&lt;p&gt;Nothing, no new tab. nothing.&lt;/p&gt;

&lt;p&gt;The fix before was to re-install Google Chrome but today I found this quick solution.&lt;/p&gt;

&lt;p&gt;In Chrome’s URL bar enter this…&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chrome://restart
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will restart all the Google Chrome processes and will generally fix this issue for a while.&lt;/p&gt;

&lt;p&gt;Success 🎉.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>MySqlException (0x80004005): The Command Timeout expired before the operation completed</title>
   <link href="https://solrevdev.com/2020/01/20/mysqlexception-0x80004005-the-command-timeout-expired-before-the-operation-completed.html"/>
   <updated>2020-01-20T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/20/ mysqlexception-0x80004005-the-command-timeout-expired-before-the-operation-completed</id>
   <content type="html">&lt;p&gt;Tonight has all been about trying to get rid of some ASP.Net MVC yellow screens of death (YSOD) caused by MySQL timing out.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-20_12_56_38_ysod.png&quot; alt=&quot;2020-01-20_12_56_38_ysod.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My application is a fairly old ASP.Net MVC 5 web application that used to talk to a local instance of MySQL and now has been ported the cloud (AWS) with the MySQL database migrated to use Amazon’s &lt;a href=&quot;https://aws.amazon.com/rds/aurora/serverless/&quot;&gt;Aurora Serverless MySQL database service&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have a few of these now. They suit certain workloads and my dev environments very well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Timeouts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A page in my application is quite query heavy which wasn’t a problem but would just take a little while to load. After migrating to the AWS Aurora Serverless MySQL database I started to get some intermittent timeouts.&lt;/p&gt;

&lt;p&gt;I would get either:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MySqlException (0x80004005): The Command Timeout expired before the operation completed&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[MySqlException (0x80004005): The Command Timeout expired before the operation completed.]
 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +102
 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +64
 MySqlConnector.Protocol.Serialization.d__2.MoveNext() +690
 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +102
 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +64
 MySqlConnector.Protocol.Serialization.ProtocolUtility.ReadPacketAsync(BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func`1 getNextSequenceNumber, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior) +191
 MySqlConnector.Protocol.Serialization.ProtocolUtility.DoReadPayloadAsync(BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func`1 getNextSequenceNumber, ArraySegmentHolder`1 previousPayloads, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior) +61
 MySqlConnector.Protocol.Serialization.StandardPayloadHandler.ReadPayloadAsync(ArraySegmentHolder`1 cache, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior) +54
 MySqlConnector.Core.ServerSession.ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) +80
 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +102
 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +64
 MySqlConnector.Core.d__78.MoveNext() +737
 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +102
 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +64
 System.Threading.Tasks.ValueTask`1.get_Result() +80
 MySqlConnector.Core.d__2.MoveNext() +346
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[SocketException (0x274c): A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond]
 System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) +94
 System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size) +130

[IOException: Unable to read data from the transport connection: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.]
 System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size) +290
 System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count) +32
 System.Net.Security._SslStream.StartFrameHeader(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest) +137
 System.Net.Security._SslStream.StartReading(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest) +171
 System.Net.Security._SslStream.ProcessRead(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest) +270
 System.Net.Security.SslStream.Read(Byte[] buffer, Int32 offset, Int32 count) +35
 MySqlConnector.Utilities.Utility.Read(Stream stream, Memory`1 buffer) +59
 MySqlConnector.Protocol.Serialization.StreamByteHandler.g__DoReadBytesSync|6_0(Memory`1 buffer_) +101

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

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After a quick google I was able to improve the situation for the &lt;em&gt;(0x80004005): The Command Timeout&lt;/em&gt; exception by telling the &lt;a href=&quot;https://mysqlconnector.net/&quot;&gt;MySQLConnector.NET&lt;/a&gt; driver to increase the timeout by appending this to the connection string where the timeout is in seconds:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default command timeout=120&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As I also use &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack.OrmLite&quot;&gt;ServiceStack.OrmLite&lt;/a&gt; to talk to my data layer I could instead have added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrmLiteConfig.CommandTimeout = 120;&lt;/code&gt; in my code but apending to the web.config seemed a neater solution.&lt;/p&gt;

&lt;p&gt;That left the rare but repeatable &lt;em&gt;A connection attempt failed because the connected party…&lt;/em&gt; timeout error, then when looking at some other code that talks to AWS Aurora MySQL Serverless databases I noticed the connection strings had this little gem:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SslMode=none&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So I decided to try that by appending that to my connection string and it seems to have worked!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success?!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Perhaps its an Amazon Aurora Serverless database oddity or perhaps the bug will still re-appear but for now adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SslMode=none&lt;/code&gt; to my connection string seems to have worked.&lt;/p&gt;

&lt;p&gt;So, this for others and for me in the future is the full connection string I ended up using when connecting to an &lt;a href=&quot;https://aws.amazon.com/rds/aurora/serverless/&quot;&gt;AWS Aurora Serverless MySQL database service&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MYSQL_CONNECTION_STRING_RDS&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Uid=userid;Password=pass;Server=auroa-mysql-rds.cluster-random.eu-west-1.rds.amazonaws.com;Port=3306;Database=dbname;default command timeout=120;SslMode=none&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ubuntu Arc Menu Upgrade Error</title>
   <link href="https://solrevdev.com/2020/01/19/ubuntu-arc-menu-upgrade-error.html"/>
   <updated>2020-01-19T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/19/ubuntu-arc-menu-upgrade-error</id>
   <content type="html">&lt;p&gt;Tonight a desktop notification popped up on my Ubuntu 19.10 desktop to remind me that my &lt;a href=&quot;https://extensions.gnome.org/extension/1228/arc-menu/&quot;&gt;Arc Menu&lt;/a&gt; &lt;a href=&quot;https://extensions.gnome.org/&quot;&gt;Gnome Extension&lt;/a&gt; had an update.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href=&quot;https://extensions.gnome.org/extension/1228/arc-menu/&quot;&gt;Arc Menu&lt;/a&gt; in action:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-19_22-03-15_arc_menu.png&quot; alt=&quot;2020-01-19_22-03-15_arc_menu.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Normally I can update it via either Google Chrome or Firefox using the &lt;a href=&quot;https://extensions.gnome.org/&quot;&gt;Gnome Extensions website&lt;/a&gt; however tonight when I tried the update an error occurred.&lt;/p&gt;

&lt;p&gt;Literrally an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Error&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-19_20-52-06_arc_error.png&quot; alt=&quot;2020-01-19_20-52-06_arc_error.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing Menu&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The menu was also missing and I tried to reinstall, reboot and all the usual turn it off and back on again tricks but to no avail.&lt;/p&gt;

&lt;p&gt;After some duckduckgo’ing (I am trying &lt;a href=&quot;https://duckduckgo.com/&quot;&gt;duckduckgo&lt;/a&gt; as my default search these days over the evil Google), I came across the solution of restarting Gnome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So for anyone else that needs this solution or for me if/when this happens again here is what to do when you see the error.&lt;/p&gt;

&lt;p&gt;Press &lt;kbd&gt;Alt&lt;/kbd&gt;+&lt;kbd&gt;F2&lt;/kbd&gt; to bring up a dialog then type &lt;kbd&gt;r&lt;/kbd&gt; to restart the Gnome process.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-19_22-03-14_restart_gnome.png&quot; alt=&quot;2020-01-19_22-03-14_restart_gnome.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After a refresh, the menu should be working and the error will be gone.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-19_20-53-27_arc_success.png&quot; alt=&quot;2020-01-19_20-53-27_arc_success.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>VueJS GistPad Interactive Playground</title>
   <link href="https://solrevdev.com/2020/01/16/vuejs-gistpad-interactive-playground.html"/>
   <updated>2020-01-16T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/16/vuejs-gistpad-interactive-playground</id>
   <content type="html">&lt;p&gt;Recently I installed a VS Code extension called &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vsls-contrib.gistfs&quot;&gt;GistPad&lt;/a&gt; about which the marketplace docs go on to say:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;GistPad is a Visual Studio Code extension that allows you to manage GitHub Gists entirely within the editor. You can open, create, delete, fork, star and clone gists, and then seamlessly begin editing files as if they were local.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is a great extension and I am using Gists way more now.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-16_11-23-46_gistpad.png&quot; alt=&quot;2020-01-16_11-23-46_gistpad.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To install the extension launch VS Code Quick Open (&lt;kbd&gt;Ctrl&lt;/kbd&gt;+&lt;kbd&gt;P&lt;/kbd&gt;), paste the following command, and press enter.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ext install vsls-contrib.gistfs&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=vsls-contrib.gistfs&quot;&gt;marketplace docs&lt;/a&gt; are a great place to start learning about what it can do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GistPad Interactive Playgrounds&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another neat feature is interactive playgrounds which again the marketplace docs explain :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you’re building web applications, and want to create a quick playground environment in order to experiment with HTML, CSS or JavaScript (or Sass/SCSS, Less, Pug and TypeScript), you can right-click the Your Gists node and select New Playground or New Secret Playground. This will create a new gist, seeded with an HTML, CSS and JavaScript file, and then provide you with a live preview Webview, so that you can iterate on the code and visually see how it behaves.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I am a big fan of &lt;a href=&quot;https://vuejs.org&quot;&gt;VueJS&lt;/a&gt; so I decided to spin up a new interactive playground choosing VueJS from the menu that appears.&lt;/p&gt;

&lt;p&gt;That produces a nice hello world style template that you can use to get started with.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-16_11-24-59_gistpad_playground.png&quot; alt=&quot;2020-01-16_11-24-59_gistpad_playground.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UK Police Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rather than displaying weather data or some random dummy data in my playground, I decided to use crime data for Oxfordshire from &lt;a href=&quot;https://data.police.uk/docs/&quot;&gt;Data.Police.UK&lt;/a&gt; which seemed an interesting dataset to play around with.&lt;/p&gt;

&lt;p&gt;I started by reading the docs and looking at the example request which takes pairs of lat/long coordinates to describe an area:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://data.police.uk/api/crimes-street/all-crime?poly=52.268,0.543:52.794,0.238:52.130,0.478&amp;amp;date=2017-01&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I then found &lt;a href=&quot;https://www.doogal.co.uk/polylines.php&quot;&gt;this site&lt;/a&gt; which allowed me to draw an area and then get those lat/lon coordinates.&lt;/p&gt;

&lt;p&gt;Then looking at the sample JSON response back from the API I then had enough to get started on my VueJS GistPad Interactive Playground:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;category&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;anti-social-behaviour&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;location_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Force&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;location&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;latitude&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;52.640961&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;street&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;884343&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;On or near Wharf Street North&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;longitude&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-1.126371&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;context&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;outcome_status&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;persistent_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;54164419&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;location_subtype&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;month&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2017-01&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;category&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;anti-social-behaviour&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;location_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Force&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;location&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;latitude&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;52.633888&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;street&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;883425&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
                &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;On or near Peacock Lane&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
            &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;longitude&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;-1.138924&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;context&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;outcome_status&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;persistent_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;54165316&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;location_subtype&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;month&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2017-01&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;VueJS GistPad Interactive Playground&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Right-clicking in the GistPad tab in VSCode showed me a menu allowing me to create either a public or private interactive playground.&lt;/p&gt;

&lt;p&gt;The generated template is plenty to get started with.&lt;/p&gt;

&lt;p&gt;It gives you 3 files to edit and a preview pane that refreshes whenever you make a change which is an excellent developer inner loop.&lt;/p&gt;

&lt;p&gt;So after some trial and error, these were my 3 files all &lt;a href=&quot;https://gist.github.com/solrevdev/41a7adb028bb10c741153f58b36d01fe&quot;&gt;associated with a GitHub Gist&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The end result&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The GistPad toolbar has an icon that allows you to open a console to view the output of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;console.log&lt;/code&gt; statements and I soon had a working version.&lt;/p&gt;

&lt;p&gt;If you would like to see my Police Data sample try this link:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gist.github.com/solrevdev/41a7adb028bb10c741153f58b36d01fe&quot;&gt;https://gist.github.com/solrevdev/41a7adb028bb10c741153f58b36d01fe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If VueJS isn’t your thing then react.js is an option and typescript versions of these two are also available.&lt;/p&gt;

&lt;p&gt;The extension is open for more templates to be added to it and can be submitted to them.&lt;/p&gt;

&lt;p&gt;All in all, it’s an excellent experience.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>How to remove the .NET Core Runtime and SDK</title>
   <link href="https://solrevdev.com/2020/01/15/how-to-remove-the-net-core-runtime-and-sdk.html"/>
   <updated>2020-01-15T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/15/how-to-remove-the-net-core-runtime-and-sdk</id>
   <content type="html">&lt;p&gt;Today I noticed a windows machine that I look after had absolutely loads of versions of the dotnetcore framework installed on it.&lt;/p&gt;

&lt;p&gt;It seemed like every major and minor version from 1.0 to the latest 3.1 and many previews in-between had been installed.&lt;/p&gt;

&lt;p&gt;To see if your machine is the same try this command in your terminal:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet &lt;span class=&quot;nt&quot;&gt;--list-sdks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Microsoft has a page titled &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/versions/remove-runtime-sdk-versions?tabs=windows&quot;&gt;How to remove the .NET Core Runtime and SDK&lt;/a&gt; which explains how to remove older versions.&lt;/p&gt;

&lt;p&gt;There is also a &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/additional-tools/uninstall-tool?tabs=windows&quot;&gt;tool to help uninstall these versions&lt;/a&gt;  available that is worth a look.&lt;/p&gt;

&lt;p&gt;So I downloaded and installed the tool and ran a command to see list what could be uninstalled for me.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet-core-uninstall list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-15_12_27_19.png&quot; alt=&quot;2020-01-15_12_27_19.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The tool is smart in that it knows which versions are required by Visual Studio.&lt;/p&gt;

&lt;p&gt;So I began to uninstall the undeeded and safe to remove dotnetcore SDK’s on the system.&lt;/p&gt;

&lt;p&gt;I started by removing all preview versions of the dotnetcore sdk.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet-core-uninstall remove &lt;span class=&quot;nt&quot;&gt;--sdk&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--all-previews&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-15_12_27_48.png&quot; alt=&quot;2020-01-15_12_27_48.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I then re-ran the tool to ensure that these were uninstalled and to see what versions were left.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet-core-uninstall list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then I built and ran my final command to remove the older versions that were not needed by Visual Studio.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet-core-uninstall remove &lt;span class=&quot;nt&quot;&gt;--sdk&lt;/span&gt; 2.2.300 2.2.102 2.2.100 2.1.801 2.1.701 2.1.700 2.1.604 2.1.602 2.1.601 2.1.600 2.1.511 2.1.509 2.1.508 2.1.507 2.1.505 2.1.504 2.1.503 2.1.502 2.1.500 2.1.403 2.1.402 2.1.401 2.1.400 2.1.302 2.1.301 2.1.300 2.1.201 2.1.200 2.1.104 2.1.103 2.1.102 2.1.101 2.1.100 2.1.4 2.1.3 2.1.2 1.1.7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-15_12_42_37.png&quot; alt=&quot;2020-01-15_12_42_37.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;One final check…&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet-core-uninstall list
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-15_13_17_44.png&quot; alt=&quot;2020-01-15_13_17_44.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Exclude all hits from known bots and spiders</title>
   <link href="https://solrevdev.com/2020/01/13/exclude-all-hits-from-known-bots-and-spiders.html"/>
   <updated>2020-01-13T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/13/exclude-all-hits-from-known-bots-and-spiders</id>
   <content type="html">&lt;p&gt;Today I decided to take a look at my &lt;a href=&quot;https://analytics.google.com/&quot;&gt;Google Analytics&lt;/a&gt; for this website and I had way more traffic than a site like mine ought to have.&lt;/p&gt;

&lt;p&gt;Drilling down into the stats I noticed that most of the traffic must be from either bots or spiders.&lt;/p&gt;

&lt;p&gt;Google Analytics does have a setting though to filter those out.&lt;/p&gt;

&lt;p&gt;Log in to your analytics and go to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;View Settings&lt;/code&gt; where there will be a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Exclude all hits from known bots and spiders&lt;/code&gt; checkbox.&lt;/p&gt;

&lt;p&gt;Make sure that is checked.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-13_exclude-bots-and-spiders.png&quot; alt=&quot;2020-01-13_exclude-bots-and-spiders.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>error MSB4236 The SDK Microsoft.NET.Sdk specified could not be found</title>
   <link href="https://solrevdev.com/2020/01/11/error-msb4236-the-sdk-microsoftnetsdk-specified-could-not-be-found.html"/>
   <updated>2020-01-11T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/11/error-msb4236-the-sdk-microsoftnetsdk-specified-could-not-be-found</id>
   <content type="html">&lt;h3 id=&quot;background&quot;&gt;Background&lt;/h3&gt;

&lt;p&gt;Recently I was working on a website built before dotnetcore was even a thing. It was targeting an older version of the original .NET Framework.&lt;/p&gt;

&lt;p&gt;I am slowly modernizing it. Firstly I upgraded the nuget packages and re-targeted it to .NET Framework version 4.8.&lt;/p&gt;

&lt;p&gt;Next, as the solution was split into many projects I was able to migrate many of these projects to be netstandard.&lt;/p&gt;

&lt;p&gt;The idea is that all that is left is to think about upgrading the website to a Razor Pages aspnetcore project from the classic model view controller website.&lt;/p&gt;

&lt;h3 id=&quot;missing-sdk-build-error&quot;&gt;Missing SDK Build Error&lt;/h3&gt;

&lt;p&gt;While I was doing this a build script was failing.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-11-15.00.04.png&quot; alt=&quot;2020-01-11-15.00.04.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The error was &lt;em&gt;error MSB4236 The SDK Microsoft.NET.Sdk specified could not be found&lt;/em&gt; and is because the project now includes dotnetcore projects that need building.&lt;/p&gt;

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

&lt;p&gt;After some googling the answer was to upgrade the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Microsoft Visual Studio 2019 Build Tools&lt;/code&gt; installed on that server.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-11-14.57.39.png&quot; alt=&quot;2020-01-11-14.57.39.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.NET Core Build Tools&lt;/code&gt; and/or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Web development build tools&lt;/code&gt; and install&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2020-01-11-14.59.06.png&quot; alt=&quot;2020-01-11-14.59.06.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Once this was done the build worked.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>SSL error on downlevel windows versions</title>
   <link href="https://solrevdev.com/2020/01/10/err_http2_inadequate_transport_security.html"/>
   <updated>2020-01-10T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2020/01/10/err_http2_inadequate_transport_security</id>
   <content type="html">&lt;p&gt;The other day I was going to test and debug some code on a Windows Server 2012RC machine.&lt;/p&gt;

&lt;p&gt;When running my asp.net core 3.1 razor pages website I encountered the exception:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This site worked fine elsewhere so to try and narrow down the problem I created a brand new asp.net core website to see if it was something in my code that was the issue but I had the same error showing up in google chrome.&lt;/p&gt;

&lt;p&gt;After some googling, I tried to reset the servers self-signed SSL certificate by using the following closing the browser in between but that had no effect:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet dev-certs https &lt;span class=&quot;nt&quot;&gt;--clean&lt;/span&gt;
dotnet dev-certs https &lt;span class=&quot;nt&quot;&gt;--trust&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I created a &lt;a href=&quot;https://github.com/aspnet/AspNetCore.Docs/issues/16434&quot;&gt;github issue&lt;/a&gt; and the ever-helpful @guardrex came to my rescue again and pointed me in the right direction.&lt;/p&gt;

&lt;p&gt;It is a &lt;a href=&quot;https://github.com/dotnet/aspnetcore/issues/16811&quot;&gt;known bug&lt;/a&gt; and there is an open Github issue for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workaround&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So here for others and me if this happens again is my workaround:&lt;/p&gt;

&lt;p&gt;A specific down-level machine (Windows Server 2012R2) was causing the exception and because I knew which machine, I had access to its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Environment.MachineName&lt;/code&gt; which I use later to programmatically decide which &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ListenOptions.Protocols&lt;/code&gt; Kestrel should load.&lt;/p&gt;

&lt;p&gt;HTTP/1 on the down-level machine but HTTP/2 elsewhere.&lt;/p&gt;

&lt;p&gt;So, I created an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appsettings.{the-environment.machinename-value-here}.json&lt;/code&gt; file alongside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;appsettings.Development.json&lt;/code&gt; with the following HTTP/1 settings rather than the default HTTP/2 settings that are loaded by default elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;appsettings.machinename.json&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Then in Program.cs I modified &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CreateHostBuilder&lt;/code&gt; to read the above custom appsettings.json file.&lt;/p&gt;

&lt;p&gt;Note the line containing: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config.AddJsonFile($&quot;appsettings.{Environment.MachineName}.json&quot;, optional: true);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Program.cs&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHostBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateHostBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDefaultBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureAppConfiguration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hostingContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddJsonFile&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;appsettings.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Environment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MachineName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optional&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddCommandLine&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ConfigureWebHostDefaults&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;webBuilder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;webBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UseStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

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

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>homebrews cellar version of mono breaks omnisharp</title>
   <link href="https://solrevdev.com/2019/12/20/homebrew-mono-breaks-omnisharp.html"/>
   <updated>2019-12-20T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2019/12/20/homebrew-mono-breaks-omnisharp</id>
   <content type="html">&lt;p&gt;So today, I raised a &lt;a href=&quot;https://github.com/OmniSharp/omnisharp-vscode/issues/3477&quot;&gt;GitHub issue&lt;/a&gt; because after I had opened the result of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet new mvc&lt;/code&gt; in VSCode the problems window would have approximately 120 issues and the code editor window would be full of red squiggles.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://pbs.twimg.com/media/EMJhNcRXkAEgE_-.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I was running the very &lt;a href=&quot;https://dotnet.microsoft.com/download/dotnet-core/3.0&quot;&gt;latest version of dotnetcore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://pbs.twimg.com/media/EMJhyLSWwAIxryE.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;And the very latest version of &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VSCode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://pbs.twimg.com/media/EMJiAp1WoAA5Y6-.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;I had not changed anything in the .csproj file. It was fresh from running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet new mvc&lt;/code&gt; from the terminal.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://pbs.twimg.com/media/EMOFqFlX0AA2KFt.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;So, I raised an issue over on &lt;a href=&quot;https://github.com/OmniSharp/omnisharp-vscode/issues/3477&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/github-issue-3477.png&quot; alt=&quot;github-issue-3477.png&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Big thanks to the rapid response and answer from &lt;a href=&quot;https://github.com/filipw&quot;&gt;@filipw&lt;/a&gt;, who discovered that it was the &lt;a href=&quot;https://brew.sh/&quot;&gt;homebrew&lt;/a&gt; &lt;a href=&quot;https://docs.brew.sh/Formula-Cookbook&quot;&gt;cellar&lt;/a&gt; version of mono that was the issue and that intstalling the &lt;a href=&quot;https://www.mono-project.com/download/stable/&quot;&gt;stable version of mono&lt;/a&gt; was the fix.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://pbs.twimg.com/media/EMOMjtmXkAA6BG7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://pbs.twimg.com/media/EMOMjtiW4AAKpZ2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>HTTP Error 500.30 - ANCM In-Process Start Failure</title>
   <link href="https://solrevdev.com/2019/12/10/HTTP-Error-500.30-ANCM-In-Process-Start-Failure.html"/>
   <updated>2019-12-10T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2019/12/10/HTTP-Error-500.30-ANCM-In-Process-Start-Failure</id>
   <content type="html">&lt;p&gt;I host an &lt;a href=&quot;https://dot.net/&quot;&gt;aspnetcore&lt;/a&gt; website on a Windows Server 2012 R2 running IIS on Amazon AWS and it’s generally fast and stable.&lt;/p&gt;

&lt;p&gt;However, the past two nights the server has restarted unexpectedly leaving the website down with the following error message:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTTP Error 500.30 - ANCM In-Process Start Failure
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first night a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IISRESET&lt;/code&gt; command was all that was needed to get the site running again, however, last night it did the same thing.&lt;/p&gt;

&lt;p&gt;Looking at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Event Viewer&lt;/code&gt; I noticed the following:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Application &apos;/LM/W3SVC/2/ROOT&apos; with physical root &apos;C:\Path\To\Website&apos; failed to load clr and managed application. Managed server didn&apos;t initialize after 120000 ms.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, doing some googling I came across an article suggesting that &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/azure-iis-errors-reference?view=aspnetcore-3.1#an-x86-app-is-deployed-but-the-app-pool-isnt-enabled-for-32-bit-apps&quot;&gt;An x86 app is deployed but the app pool isn’t enabled for 32-bit app&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This suggests that:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;For an x86 framework-dependent deployment (&amp;lt;PlatformTarget&amp;gt;x86&amp;lt;/PlatformTarget&amp;gt;), enable the IIS app pool for 32-bit apps. In IIS Manager, open the app pool&apos;s Advanced Settings and set Enable 32-Bit Applications to True.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Following those instructions the setting in IIS I changed then looked like this:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/J5OvzM2.png&quot; alt=&quot;In IIS Manager, open the app pool&apos;s Advanced Settings and set Enable 32-Bit Applications to True&quot; /&gt;&lt;/p&gt;

&lt;p&gt;An &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IISRESET&lt;/code&gt; for good measure and the site is back up again.&lt;/p&gt;

&lt;p&gt;I still need to see why Windows is restarting at approximately the same time each night the past few nights but if it does I hope to have solved the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTTP Error 500.30 - ANCM In-Process Start Failure&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;Success? 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unable to locate package dotnet-sdk-3.1</title>
   <link href="https://solrevdev.com/2019/12/07/unable-to-locate-package-dotnet-sdk-3.1.html"/>
   <updated>2019-12-07T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2019/12/07/unable-to-locate-package-dotnet-sdk-3.1</id>
   <content type="html">&lt;p&gt;Every time there is a new release of &lt;a href=&quot;https://dotnet.microsoft.com/download&quot;&gt;dotnetcore&lt;/a&gt; I need to get it updated on the three environments where I develop and deploy code: macOS, Windows and Linux (Ubuntu).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt; and &lt;a href=&quot;https://chocolatey.org/&quot;&gt;Chocolatey&lt;/a&gt; update the version of dotnetcore for me automatically, sometimes there is a delay but they will eventually update them.&lt;/p&gt;

&lt;p&gt;However, for Linux each release of &lt;a href=&quot;https://dotnet.microsoft.com/download&quot;&gt;dotnetcore&lt;/a&gt; I always have to manually intervene and install it manually.&lt;/p&gt;

&lt;p&gt;If you follow the &lt;a href=&quot;https://docs.microsoft.com/en-gb/dotnet/core/install/linux-package-manager-ubuntu-1904]&quot;&gt;instructions&lt;/a&gt; from Microsoft  you will get the following error message:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Unable to locate package dotnet-sdk-3.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The issue is that page targets Ubuntu version 19.04 and I am running Ubuntu version 19.10 (Eoan Ermine).&lt;/p&gt;

&lt;p&gt;So, If you are me from the future wanting to know how to get the latest version installed  here is what you need to do:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;curl https://packages.microsoft.com/keys/microsoft.asc | &lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-key add -
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-add-repository https://packages.microsoft.com/ubuntu/19.10/prod
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;dotnet-sdk-3.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Fetch wont send or receive any cookies 🍪</title>
   <link href="https://solrevdev.com/2019/05/20/fetch-wont-send-or-receive-any-cookies.html"/>
   <updated>2019-05-20T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2019/05/20/fetch-wont-send-or-receive-any-cookies</id>
   <content type="html">&lt;p&gt;Today I think I have fixed a bug that has niggled away at me for ages. 🍪&lt;/p&gt;

&lt;p&gt;A severe case of  &lt;a href=&quot;https://blog.codinghorror.com/the-works-on-my-machine-certification-program/&quot;&gt;‘works on my machine’&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have some code that consumes an external API that once authenticated would then, later on, make another API call to fetch the end users recent images.&lt;/p&gt;

&lt;p&gt;This worked great…&lt;/p&gt;

&lt;p&gt;Except for some users who reported they once logged on would not see their images.&lt;/p&gt;

&lt;p&gt;However, for the longest time, I could not recreate this. It worked on my machine, It worked on macOS, Windows and Linux, It worked on safari, chrome and firefox. It worked on an iPhone 6s.&lt;/p&gt;

&lt;p&gt;So, I added &lt;a href=&quot;https://github.com/serilog/serilog-aspnetcore&quot;&gt;logging&lt;/a&gt;, then more logging. I even signed up for a trial &lt;a href=&quot;https://www.browserstack.com/&quot;&gt;Browserstack&lt;/a&gt; account to try as many different browsers and devices I could and still could not recreate the issue.&lt;/p&gt;

&lt;p&gt;Then eventually while testing something else out I managed to recreate the bug using Apple’s iOS simulator using an iPhone 6S running iOS 11.2.&lt;/p&gt;

&lt;p&gt;Being able to recreate the bug really is half the battle when it comes to bug fixing code.&lt;/p&gt;

&lt;p&gt;So, armed with some breakpoints and a clearer idea of what was going on, I was eventually able to get the bug fixed and a working app pushed out to production.&lt;/p&gt;

&lt;p&gt;The bug?&lt;/p&gt;

&lt;p&gt;Well, I am not 100% sure how to explain it but the front end has some JavaScript code that uses the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&quot;&gt;fetch API&lt;/a&gt; to call an &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-2.2&quot;&gt;ApiController&lt;/a&gt; that checks for a session variable and based on that value returns data back to the front end client.&lt;/p&gt;

&lt;p&gt;For most browsers and my development environment, the following code was enough to make that call and get the correct data back:&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;apiUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But, then for some browsers, I needed to modify the fetch API call to specify that cookies and credentials must be sent along with the request also.&lt;/p&gt;

&lt;p&gt;This is the code that does this and is what I committed as the fix.&lt;/p&gt;

&lt;div class=&quot;language-javascript highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nx&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;apiUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;credentials&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The documentation for fetch does point to the issue somewhat&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;By default, &lt;strong&gt;fetch won’t send or receive any cookies&lt;/strong&gt; from the server, resulting in unauthenticated requests if the site relies on maintaining a user session (to send cookies, the credentials init option must be set).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Since Aug 25, 2017. The spec changed the default credentials policy to same-origin. Firefox changed since 61.0b13.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;– &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch&quot;&gt;Using Fetch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why this was only needed for certain browsers I am unsure but my fix seems to work.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The imported project was not found</title>
   <link href="https://solrevdev.com/2019/05/13/imported-project-microsoft-common-props-not-found.html"/>
   <updated>2019-05-13T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2019/05/13/imported-project-microsoft-common-props-not-found</id>
   <content type="html">&lt;p&gt;On my Ubuntu disco dingo laptop, There was a bug affecting VS Code’s IntelliSense that I had just been putting up with for a good few days.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The imported project “/usr/lib/mono/xbuild/15.0/Microsoft.Common.props” was not found&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There was no such problem on macOS or Windows however as I like to write code on my Linux daily driver laptop it became harder and harder to ignore.&lt;/p&gt;

&lt;p&gt;It’s a bug that I &lt;a href=&quot;https://github.com/OmniSharp/omnisharp-vscode/issues/3049&quot;&gt;raised over on GitHub&lt;/a&gt; and while the full logs and environment details are over there in more detail, for brevity I will show what I believe is the main problem here:&lt;/p&gt;

&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[warn]: OmniSharp.MSBuild.ProjectManager
        Failed to load project file &apos;/home/solrevdev/Code/scratch/testconsole/testconsole.csproj&apos;.
/home/solrevdev/Code/scratch/testconsole/testconsole.csproj(1,1)
Microsoft.Build.Exceptions.InvalidProjectFileException: The imported project &quot;/usr/lib/mono/xbuild/15.0/Microsoft.Common.props&quot; was not found. Confirm that the path in the &amp;lt;Import&amp;gt; declaration is correct, and that the file exists on disk.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Despite the error showing up in the OmniSharp logs I actually think the problem is with the version or build of mono that I had installed not having the assets needed for MSBuild.&lt;/p&gt;

&lt;p&gt;So fed up of not getting IntelliSense in either VS Code or VS Code insiders I decided to try something I perhaps should have tried on day one.&lt;/p&gt;

&lt;p&gt;That was to re-install or update my version of Mono from the official &lt;a href=&quot;https://www.mono-project.com/download/stable/#download-lin-ubuntu&quot;&gt;download page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The instructions for doing this I borrowed and adapted are from there and are as follows:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;gnupg ca-certificates
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-key adv &lt;span class=&quot;nt&quot;&gt;--keyserver&lt;/span&gt; hkp://keyserver.ubuntu.com:80 &lt;span class=&quot;nt&quot;&gt;--recv-keys&lt;/span&gt; 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;deb https://download.mono-project.com/repo/ubuntu stable-bionic main&quot;&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/mono-official-stable.list
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt update

&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;mono-devel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;🎉 This seems to have worked!&lt;/p&gt;

&lt;p&gt;I now have IntelliSense again and the warning has gone away!&lt;/p&gt;

&lt;p&gt;As I mentioned in the GitHub issue I will leave the bug open for a while as while the commands worked for me they may not persist and there may be more to it than that.&lt;/p&gt;

&lt;p&gt;In fact, I’ve just read a &lt;a href=&quot;https://github.com/OmniSharp/omnisharp-vscode/issues/2604#issuecomment-429814128&quot;&gt;similar issue&lt;/a&gt;  that suggests that OmniSharp shouldn’t be using the MSBuild assets from mono and should be using the dotnet sdk version instead.&lt;/p&gt;

&lt;p&gt;That makes sense to me. So reinstalling mono while apparently working may not be the top solution.&lt;/p&gt;

&lt;p&gt;I may try using &lt;a href=&quot;https://github.com/OmniSharp/omnisharp-vscode/issues/2604#issuecomment-429814128&quot;&gt;this command&lt;/a&gt; at some point but for now, as I said everything seems to work ok.&lt;/p&gt;

&lt;p&gt;This post is here in case this happens again, it’s nice to google a problem and find your own blog has the answer plus if anyone else benefits then even better.  👌&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Forcing HTTP to HTTPS redirect on IIS via Amazon Elastic Load Balancers</title>
   <link href="https://solrevdev.com/2019/05/13/force-http-to-https-redirect-via-aws-elb.html"/>
   <updated>2019-05-13T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2019/05/13/force-http-to-https-redirect-via-aws-elb</id>
   <content type="html">&lt;p&gt;Today I replaced an ageing asp.net web forms web application with a new static site which for now is just a landing page with a contact form and in doing so needed to force any insecure HTTP requests to HTTPS.&lt;/p&gt;

&lt;p&gt;A bit of a gotcha was an error on the redirect and the issue was the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HTTP_X_FORWARDED_PROTO&lt;/code&gt; header also needed to be forwarded along with the redirect.&lt;/p&gt;

&lt;p&gt;For example :&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;conditions&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;logicalGrouping=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MatchAny&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;input=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{HTTP_X_FORWARDED_PROTO}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;^http$&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;input=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{HTTPS}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;on&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/conditions&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next up, I needed to redirect any users who were going to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.aspx&lt;/code&gt; pages that no longer existed to a custom 404 HTML page not found page.&lt;/p&gt;

&lt;p&gt;This just needed adding to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.config&lt;/code&gt; like so :&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;nt&quot;&gt;&amp;lt;system.web&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;compilation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;customErrors&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;mode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;On&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;redirectMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ResponseRewrite&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;&amp;lt;error&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;statusCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;404&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;redirect=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;404.html&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/customErrors&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;/system.web&amp;gt;&lt;/span&gt;

   &lt;span class=&quot;nt&quot;&gt;&amp;lt;system.webServer&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;httpErrors&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;errorMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Custom&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;&amp;lt;remove&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;statusCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;404&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
         &lt;span class=&quot;nt&quot;&gt;&amp;lt;error&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;statusCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;404&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/404.html&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;responseMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ExecuteURL&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/httpErrors&amp;gt;&lt;/span&gt;
   &lt;span class=&quot;nt&quot;&gt;&amp;lt;/system.webServer&amp;gt;&lt;/span&gt;

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

&lt;p&gt;So here it is for the next time I have to do something similar, This is the full &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web.config&lt;/code&gt; that needs to in the root folder of the site.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;system.web&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;compilation&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;targetFramework=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;4.0&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;customErrors&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;mode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;On&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;redirectMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ResponseRewrite&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;error&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;statusCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;404&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;redirect=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;404.html&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/customErrors&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/system.web&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;system.webServer&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;httpErrors&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;errorMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Custom&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;remove&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;statusCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;404&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;error&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;statusCode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;404&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/404.html&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;responseMode=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ExecuteURL&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/httpErrors&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;rewrite&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;rules&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;rule&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HTTPS Rule behind AWS Elastic Load Balancer&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;stopProcessing=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(.*)&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ignoreCase=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;conditions&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;logicalGrouping=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;MatchAny&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;input=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{HTTP_X_FORWARDED_PROTO}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;^http$&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                        &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;input=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{HTTPS}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;on&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/conditions&amp;gt;&lt;/span&gt;
                    &lt;span class=&quot;nt&quot;&gt;&amp;lt;action&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Redirect&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://{HTTP_HOST}{REQUEST_URI}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;redirectType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Found&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class=&quot;nt&quot;&gt;&amp;lt;/rule&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;/rules&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/rewrite&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/system.webServer&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And to test that the site works I entered it onto &lt;a href=&quot;https://www.whynopadlock.com/&quot;&gt;https://www.whynopadlock.com/&lt;/a&gt; which gave me the confidence that all was well.&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Host ASP.NET Core on Windows with IIS</title>
   <link href="https://solrevdev.com/2019/03/11/host-aspnetcore-on-iis.html"/>
   <updated>2019-03-11T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2019/03/11/host-aspnetcore-on-iis</id>
   <content type="html">&lt;p&gt;A recent push to production broke an ASP.NET Core application I have running and it took me a while to find out the underlying problem.&lt;/p&gt;

&lt;p&gt;The error was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;502 - Web server received an invalid response while acting as a gateway or proxy server&lt;/code&gt; with nothing obvious in the logs that helped me.&lt;/p&gt;

&lt;p&gt;First I published a last known good version of my application by creating a new branch in git and resetting it to the last known good commit I had.&lt;/p&gt;

&lt;p&gt;The idea being I can deploy from this branch while I investigated and once fixed I could delete the branch.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git reset e64c51bf1c3bdde753ff2d8fd8b18d4d902b8b5b &lt;span class=&quot;nt&quot;&gt;--hard&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then digging around the changes between branches in git I noticed that either Visual Studio and/or my MSBuild based deployment script had added a property pointing to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostingModel=&quot;InProcess&quot;&lt;/code&gt; to my web.config and a property referencing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreModuleV2&lt;/code&gt; to my .csproj file.&lt;/p&gt;

&lt;p&gt;So with some time spent on both StackOverflow and the &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-2.2&quot;&gt;ASP.NET Core documentation&lt;/a&gt; sites it was apparent that in order to have the more performant&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InProcess&lt;/code&gt; hosting model via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AspNetCoreModuleV2&lt;/code&gt; module the &lt;a href=&quot;https://dotnet.microsoft.com/download/thank-you/dotnet-runtime-2.2.2-windows-hosting-bundle-installer&quot;&gt;.NET Core 2.2 Runtime &amp;amp; Hosting Bundle for Windows (v2.2.2)&lt;/a&gt; needs to be installed.&lt;/p&gt;

&lt;p&gt;After many combinations and deployments, I ended up with this successful combination of settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project File&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;Project&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Sdk=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.NET.Sdk.Web&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TargetFramework&amp;gt;&lt;/span&gt;netcoreapp2.2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TargetFramework&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AspNetCoreHostingModel&amp;gt;&lt;/span&gt;InProcess&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AspNetCoreHostingModel&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;AspNetCoreModuleName&amp;gt;&lt;/span&gt;AspNetCoreModuleV2&lt;span class=&quot;nt&quot;&gt;&amp;lt;/AspNetCoreModuleName&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;TieredCompilation&amp;gt;&lt;/span&gt;true&lt;span class=&quot;nt&quot;&gt;&amp;lt;/TieredCompilation&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;LangVersion&amp;gt;&lt;/span&gt;latest&lt;span class=&quot;nt&quot;&gt;&amp;lt;/LangVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/PropertyGroup&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;Include=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Microsoft.AspNetCore.App&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/Project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Web.Config&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;&amp;lt;!--
    Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
  --&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;system.webServer&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;rewrite&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;rules&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;rule&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;HTTPS rewrite behind ELB rule&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;stopProcessing=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;match&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;^(.*)$&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ignoreCase=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;conditions&amp;gt;&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;input=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{HTTP_X_FORWARDED_PROTO}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;pattern=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;^http$&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;ignoreCase=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;/conditions&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;nt&quot;&gt;&amp;lt;action&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Redirect&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;redirectType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Found&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;url=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://{SERVER_NAME}{URL}&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;&amp;lt;/rule&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;/rules&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/rewrite&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;handlers&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;aspNetCore&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;path=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;verb=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;*&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;modules=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;AspNetCoreModuleV2&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;resourceType=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unspecified&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;/handlers&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;aspNetCore&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;processPath=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnet&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;arguments=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.\web.dll&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;stdoutLogEnabled=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;stdoutLogFile=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;.\logs\stdout&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;forwardWindowsAuthToken=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;false&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;hostingModel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;InProcess&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/system.webServer&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So after the install of &lt;a href=&quot;https://dotnet.microsoft.com/download/thank-you/dotnet-runtime-2.2.2-windows-hosting-bundle-installer&quot;&gt;.NET Core 2.2 Runtime &amp;amp; Hosting Bundle for Windows (v2.2.2)&lt;/a&gt; and one reboot later all was well again.  🙌&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Ubuntu 18.10 and the .net framework sdk 2.2 gives a package not found error</title>
   <link href="https://solrevdev.com/2019/01/09/ubuntu-18.10-netcore-sdk-2.2-ubuntu-package-not-found.html"/>
   <updated>2019-01-09T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2019/01/09/ubuntu-18.10-netcore-sdk-2.2-ubuntu-package-not-found</id>
   <content type="html">&lt;p&gt;This is something I have had to do before so I am blogging here for next time.&lt;/p&gt;

&lt;p&gt;I have just upgraded my laptop from Ubuntu 18.04 to 18.10 so that I could check out the latest Gnome desktop environment and one of my first tasks was to update the .net framework to the latest 2.2 sdk.&lt;/p&gt;

&lt;p&gt;However despite following the instructions from here: &lt;a href=&quot;https://dotnet.microsoft.com/download/linux-package-manager/ubuntu18-04/sdk-2.2.102&quot;&gt;https://dotnet.microsoft.com/download/linux-package-manager/ubuntu18-04/sdk-2.2.102&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dpkg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; packages-microsoft-prod.deb

&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;add-apt-repository universe
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;apt-transport-https
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get update
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;dotnet-sdk-2.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I would then receive a package not found error on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get install dotnet-sdk-2.2&lt;/code&gt; line.&lt;/p&gt;

&lt;p&gt;The fix seems to be to copy these manually.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; https://packages.microsoft.com/config/ubuntu/18.04/prod.list
&lt;span class=&quot;nb&quot;&gt;sudo mv &lt;/span&gt;prod.list /etc/apt/sources.list.d/microsoft-prod.list
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt update
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So once I had entered the following all was well. Phew!&lt;/p&gt;

&lt;p&gt;I must say the drop in resources and general snappiness with the latest gnome on ubuntu 18.10 is noticeable.  🙌&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Using System.CommandLine to build a command line application.</title>
   <link href="https://solrevdev.com/2018/12/20/using-system-commandline-to-build-a-command-line-application-and-nuget-config.html"/>
   <updated>2018-12-20T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2018/12/20/using-system-commandline-to-build-a-command-line-application-and-nuget-config</id>
   <content type="html">&lt;p&gt;Using System.CommandLine to build a command line application.&lt;/p&gt;

&lt;p&gt;I am writing a command line application and in order to parse arguments and display output to the console I found an experimental library called &lt;a href=&quot;https://github.com/dotnet/command-line-api&quot;&gt;System.Commandline&lt;/a&gt; written by the dotnet team that works really well.&lt;/p&gt;

&lt;p&gt;The point for this post was that while this worked great locally I had a brain freeze when it came to getting &lt;a href=&quot;https://bitbucket.org/product/features/pipelines&quot;&gt;bitbucket pipelines&lt;/a&gt; to build this properly due to it having a custom MyGet feed for the as yet unreleased library.&lt;/p&gt;

&lt;p&gt;So here is the sample Nuget.Config file I had to create alongside the solution file to get bitbucket pipelines to build this correctly.&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;packageRestore&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;enabled&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;automatic&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;True&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/packageRestore&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;packageSources&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;nuget&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://api.nuget.org/v3/index.json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;dotnet-core&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://dotnet.myget.org/F/dotnet-core/api/v3/index.json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;system-commandline&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;https://dotnet.myget.org/F/system-commandline/api/v3/index.json&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/packageSources&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;disabledPackageSources&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;activePackageSource&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;add&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;All&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;(Aggregate source)&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/activePackageSource&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Borrowing from the &lt;a href=&quot;https://github.com/dotnet/command-line-api/wiki&quot;&gt;wiki&lt;/a&gt; this is really how simple this is to use.&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;intOption&quot;&amp;gt;An option whose argument is parsed as an int&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;boolOption&quot;&amp;gt;An option whose argument is parsed as a bool&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;fileOption&quot;&amp;gt;An option whose argument is parsed as a FileInfo&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;intOption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boolOption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FileInfo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fileOption&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;The value for --int-option is: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;intOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;The value for --bool-option is: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;boolOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;The value for --file-option is: &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fileOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FullName&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;null&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dotnet run &lt;span class=&quot;nt&quot;&gt;--&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--int-option&lt;/span&gt; 123
The value &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--int-option&lt;/span&gt; is: 0
The value &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--bool-option&lt;/span&gt; is: False
The value &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--file-option&lt;/span&gt; is: null
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The dotnet team have done some really great work this year 🙌&lt;/p&gt;

&lt;p&gt;Success 🎉&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>HTTPS/SSL via this GitHub Pages site with Fly.io</title>
   <link href="https://solrevdev.com/2017/08/31/http-ssl-via-github-pages-with-flyio.html"/>
   <updated>2017-08-31T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2017/08/31/http-ssl-via-github-pages-with-flyio</id>
   <content type="html">&lt;p&gt;HTTPS/SSL via this GitHub Pages site with Fly.io&lt;/p&gt;

&lt;p&gt;For a while now anything I host on Amazon Web Services I use Amazon’s fantastic certificate manager to generate an SSL certificate on my behalf for free then associate that with an Elastic Load Balancer ensuring that all traffic to sites I host on AWS are being served from HTTPS and SSL.&lt;/p&gt;

&lt;p&gt;I also host a site on Firebase and that also comes with a free SSL/HTTPS certificate even for custom domains.&lt;/p&gt;

&lt;p&gt;However, this blog and &lt;a href=&quot;https://thedrunkfist.com/&quot;&gt;https://thedrunkfist.com/&lt;/a&gt; are both blogs that use Jekyll and Github Pages for hosting.&lt;/p&gt;

&lt;p&gt;That in itself isn’t a problem as long as you don’t have a custom domain which I do for these.&lt;/p&gt;

&lt;p&gt;So, for a while, I’ve had the choice of waiting for Github to come up with a solution, use CloudFlare to host all my DNS or take another look at let’s encrypt.&lt;/p&gt;

&lt;p&gt;And I have been WAY too busy to set any of that up.&lt;/p&gt;

&lt;p&gt;Unfortunately, this has not only been nagging me as I do not practise why I preach but &lt;a href=&quot;https://www.troyhunt.com/life-is-about-to-get-harder-for-websites-without-https&quot;&gt;time is ticking&lt;/a&gt; in terms of how browsers are going to treat non-secure sites moving forward.&lt;/p&gt;

&lt;p&gt;But yesterday while waiting for an engineer to come I came across a site called &lt;a href=&quot;https://fly.io/&quot;&gt;https://fly.io/&lt;/a&gt; which I think works like CloudFlare but seemed way easier to use as within 5 minutes on my phone I had pointed an ALIAS record to this GitHub hosted blog and it was done!&lt;/p&gt;

&lt;p&gt;I then added some middle-where to enforce HTTPS/SSL (redirect HTTP to HTTPS) and then added my Google Analytics code for tracking and by then DNS had propagated and each page was secure.&lt;/p&gt;

&lt;p&gt;Very impressed!&lt;/p&gt;

&lt;p&gt;The only issue I had was with my other site where I had a staic.thedrunkfist.com CNAME record pointing to an AWS S3 bucket for uploaded images and assets which if I changed the image URL from HTTP to HTTPS had a different Amazon SSL certificate therefore not coming from my domain.&lt;/p&gt;

&lt;p&gt;But with some small changes to how I linked to that all was fixed.&lt;/p&gt;

&lt;p&gt;I.E http://static.domain.com/1.jpg to https://s3.amazonaws.com/static.domain.com/1.jpg&lt;/p&gt;

&lt;p&gt;I cannot recommend &lt;a href=&quot;https://fly.io/&quot;&gt;https://fly.io/&lt;/a&gt; enough.&lt;/p&gt;

&lt;p&gt;I was able to port 2 blogs hosted on GitHub Pages to HTTPS for free in 10 minutes total without giving control of all my DNS records.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Force SSL on IIS hosted ASP.NET website under AWS ELB</title>
   <link href="https://solrevdev.com/2016/10/07/force-ssl-aws-elb.html"/>
   <updated>2016-10-07T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/10/07/force-ssl-aws-elb</id>
   <content type="html">&lt;p&gt;How to force HTTPS / SSL on ASP.NET websites behind AWS ELB&lt;/p&gt;

&lt;p&gt;I love HTTPS / SSL and think that all websites should be served this way. Including this blog which I am working on.&lt;/p&gt;

&lt;p&gt;It is currently hosted on GitHub Pages so that is my current barrier to getting this done.&lt;/p&gt;

&lt;p&gt;However whereas once it was a premium expensive service it is getting easier and cheaper and one way 
is to use Amazon Web Services and the &lt;a href=&quot;https://aws.amazon.com/certificate-manager/&quot;&gt;Certificate Manager&lt;/a&gt; service 
where you can provision a wildcard certificate.&lt;/p&gt;

&lt;p&gt;Once I set that up I pointed my SSL certificate to an &lt;a href=&quot;https://aws.amazon.com/elasticloadbalancing/&quot;&gt;Amazon Elastic Load Balancer&lt;/a&gt; which then 
routes traffic to an EC2 instance.&lt;/p&gt;

&lt;p&gt;One nice thing you may want to do is to then force HTTPS/SSL on all requests.&lt;/p&gt;

&lt;p&gt;For ASP.NET websites hosted on IIS you can add some rewrite rules into the Web.Config file that will do that for you.&lt;/p&gt;

&lt;p&gt;As I will need to use this code snippet again I will leave it here for myself and others to use.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Add this xml rewrite rule to your Web.Config file bearing in mind you may aleady have the system.webServer node in your config file.&lt;/p&gt;

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

</content>
 </entry>
 
 <entry>
   <title>Locked out of Windows Server 2012</title>
   <link href="https://solrevdev.com/2016/08/02/rdp-grace-period.html"/>
   <updated>2016-08-02T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/08/02/rdp-grace-period</id>
   <content type="html">&lt;p&gt;An issue I had a while back was a development lab version of Microsoft Windows Server 2012 locking me out via RDP or any other means due to RDP licensing and a grace period expiring.&lt;/p&gt;

&lt;p&gt;It was not a server in my room but a proper rack mounted headless server.&lt;/p&gt;

&lt;p&gt;Diagnosing what was wrong took ages but it turns out the fix was to delete a registry key and rebooting the server.&lt;/p&gt;

&lt;p&gt;Being a good devops guy I scheduled a task to delete that key again and reboot the server so this would not happen again.&lt;/p&gt;

&lt;p&gt;All good!&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Then today the same thing happened. &lt;strong&gt;nightmare&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What happened to my my scheduled task?&lt;/p&gt;

&lt;p&gt;Well I had a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reg delete&lt;/code&gt; batch file which was also configured to run with elevated privalages so everything was setup to run correctly, however the kicker was on rebooting, the admin account I was using lost it’s full control access to the registry hive.&lt;/p&gt;

&lt;p&gt;For now with an elevated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;regedit&lt;/code&gt; I gave the admin account full control to the parent node and this should be enough.&lt;/p&gt;

&lt;p&gt;Later I will look at setting the permissions via the batch script before running the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reg delete&lt;/code&gt; command but i will check the next time it reboots.&lt;/p&gt;

&lt;p&gt;So for those looking for the key to delete here it is:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;reg delete &quot;HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\GracePeriod&quot; /f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is a &lt;a href=&quot;https://support.software.dell.com/vworkspace/kb/113932&quot;&gt;known bug&lt;/a&gt; it seems.&lt;/p&gt;

&lt;p&gt;I have a blog post road map but this was such a gotcha I wanted to document it!&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Visual Studio 2015 Update 3 install issue</title>
   <link href="https://solrevdev.com/2016/08/01/dotnet-core-install-fix.html"/>
   <updated>2016-08-01T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/08/01/dotnet-core-install-fix</id>
   <content type="html">&lt;p&gt;I am upgrading a project to use Visual Studio 2015 Community Edition and one of the updates it recommends was not installing with an error relating to the Visual Studio 2015 Update 3 update.&lt;/p&gt;

&lt;p&gt;After uninstalling and re-installing both the update and Visual Studio itself I eventually had a look on Google for the following error:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Setup has detected that Visual Studio 2015 Update 3 may not be completely installed. Please repair Visual Studio 2015 Update 3, then install this product again.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After seeing that I was not the only one with this issue I decided to follow &lt;a href=&quot;https://swimmingpooldotnet.wordpress.com/2016/07/23/setup-has-detected-that-visual-studio-2015-update-3-may-not-be-completely-installed-please-repair-visual-studio-2015-update-3-then-install-this-product-again/&quot;&gt;this fix from this site&lt;/a&gt; which works a treat so thank you very much internet!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&quot;DotNetCore.1.0.0-VS2015Tools.Preview2.exe&quot; SKIP_VSU_CHECK=1

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

&lt;p&gt;As the original post mentions do run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmd.exe&lt;/code&gt; process as an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;administrator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Some minor tweaks with Jekyll</title>
   <link href="https://solrevdev.com/2016/07/29/minor-jekyll-tweaks.html"/>
   <updated>2016-07-29T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/07/29/minor-jekyll-tweaks</id>
   <content type="html">&lt;p&gt;I do have a blog post roadmap but each post idea does involve a level of research and thought and seeing as it is 2:30am here in the UK and lets just say I wanted a quick post created on my mobile phone involving little thinking.&lt;/p&gt;

&lt;p&gt;I have made a few minor tweaks to to this website. Firstly each post when listed on the home page used ro render the entire post and not a snippet or teaser which in Jekyll speak is excerts.&lt;/p&gt;

&lt;p&gt;However all being well,  This paragraph will not be on the home page.&lt;/p&gt;

&lt;p&gt;So, I used Jekyll variables and used the strip html function and a truncate words setting to show some teasers/snippets/excerts.&lt;/p&gt;

&lt;p&gt;An advantage of this is less html page weight on the home page and especially on mobile devices allows the user to scan articles.&lt;/p&gt;

&lt;p&gt;Tags and Categories I think were also not setup correctly.  That should now be fixed and allows me in future to create a page or search function based on tags or categories.&lt;/p&gt;

&lt;p&gt;Finally as I imported all my content from blogger one issue I had was posts that were originally created by a feed reader that added tbe pipe charachter in the title causing display issues and to fix you either have to escape it or remove it as its a reserved character.&lt;/p&gt;

&lt;p&gt;The post titles were used on the Archive page which for now lists all posts.&lt;/p&gt;

&lt;p&gt;Finally this post written just before sleep and direct in markdown and not spellchecked.&lt;/p&gt;

&lt;p&gt;Cheers&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Upcoming blog posts road map</title>
   <link href="https://solrevdev.com/2016/07/27/blog-post-roadmap.html"/>
   <updated>2016-07-27T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/07/27/blog-post-roadmap</id>
   <content type="html">&lt;p&gt;I have been extremely busy recently with, well, pretty much everything, including this week’s marathon training schedule on top of caring for mum and an upcoming visit to the Oxord John Radcliffe hospital with her.&lt;/p&gt;

&lt;p&gt;So, I have been working on various things code wise and have a few blog posts in either Trello as ideas or as draft posts which I will push out soon.&lt;/p&gt;

&lt;p&gt;To hold myself accountable I will list a roadmap of these posts in no particular order:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Adding SSL/HTTPS to this site. Why and how including options such as CloudFlare, Amazon Certificate Manager and Route53 DNS.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Travis-CI for building and pushing these Jekyll driven sites to perhaps Amazon S3 hosting or others apart from my current GitHub Hosting.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Setting up TeamCity, Octopus Deploy and FluentMigrator for CI for non static sites after a few years away from this ecosystem.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;ScriptCS and Rosyln for extending applications and an alternative to Powershell plus it’s cross platform&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Push notifications and how I hate email again.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Another post about sleep and training and how it helps in life and helps to code.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is actually just the ones I can be bothered to copy and paste now as its late and I need my beauty sleep!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Deleting nearline storage via gsutil</title>
   <link href="https://solrevdev.com/2016/07/13/deleting-nearline-storage-gsutil.html"/>
   <updated>2016-07-13T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/07/13/deleting-nearline-storage-gsutil</id>
   <content type="html">&lt;p&gt;In order to save some cash after seeing some recurring Google invoices for their &lt;a href=&quot;https://cloud.google.com/storage/docs/storage-classes&quot;&gt;nearline storage&lt;/a&gt; buckets prompted me to realise that I was paying for storage that was essentially just a test of what I at the time considered to be a match for Amazon’s &lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;S3&lt;/a&gt; and &lt;a href=&quot;https://aws.amazon.com/glacier/&quot;&gt;Glacier&lt;/a&gt; storage.&lt;/p&gt;

&lt;p&gt;Now they do have a nice &lt;a href=&quot;https://console.cloud.google.com/&quot;&gt;console web app&lt;/a&gt; and an &lt;a href=&quot;https://itunes.apple.com/us/app/google-cloud-console/id1005120814?mt=8&quot;&gt;iOS app&lt;/a&gt; to manage these but attempts at deleting my buckets of data did not work.&lt;/p&gt;

&lt;p&gt;Probably due to the size of the storage.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;So, a quick search and I found they have a &lt;a href=&quot;https://cloud.google.com/sdk/docs/&quot;&gt;CLI SDK&lt;/a&gt; which would enable me to run a delete command from the Mac Terminal.&lt;/p&gt;

&lt;p&gt;The following are instructions for Mac but the &lt;a href=&quot;https://cloud.google.com/sdk/docs/&quot;&gt;docs&lt;/a&gt; should cover others including windows.&lt;/p&gt;

&lt;hr /&gt;

&lt;ul&gt;
  &lt;li&gt;Download the &lt;a href=&quot;https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-116.0.0-darwin-x86_64.tar.gz&quot;&gt;Python&lt;/a&gt; package&lt;/li&gt;
  &lt;li&gt;Extract it to a folder in your home directory such as  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/google-cloud-sdk&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Run the install script &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./google-cloud-sdk/install.sh&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Initialize the SDK &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./google-cloud-sdk/bin/gcloud init&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Welcome! This command will take you through the configuration of gcloud.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Your current configuration has been set to: [default]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;To continue, you must login. Would you like to login (Y/n)?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;Follow the rest of the setup and then once you have chosen the project that contains the bucket you want to delete you can run the following command however do note this will &lt;strong&gt;delete all your data&lt;/strong&gt; so do know that is what you are doing here!&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$ gsutil rm -r gs://INSERT-NAME-OF-BUCKET/&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;p&gt;And this is what you should see :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.solrevdev.com.s3.amazonaws.com/blog/gsutil.gif_1.gif&quot; alt=&quot;alt text&quot; title=&quot;gsutil deleting a bucket&quot; /&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Xamarin Studio and ServiceStack.Text IsMono update</title>
   <link href="https://solrevdev.com/2016/07/12/xamarin-servicestack-is-mono.html"/>
   <updated>2016-07-12T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/07/12/xamarin-servicestack-is-mono</id>
   <content type="html">&lt;p&gt;In my &lt;a href=&quot;/2016/07/11/xamarin-servicestack-mono-platform-helper.html&quot;&gt;last post&lt;/a&gt; I &lt;a href=&quot;https://twitter.com/solrevdev/status/752628079776919552&quot;&gt;tweeted&lt;/a&gt; out a link about me using a custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IsRunninOnMono()&lt;/code&gt; method and enjoying using the Service Stack library with Xamarin on a Mac to develop on.&lt;/p&gt;

&lt;p&gt;Well, happily I got a great reply from &lt;a href=&quot;http://mythz.servicestack.net&quot;&gt;@demisbellot&lt;/a&gt; from Service Stack who pointed out the&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ServiceStack.Text&lt;/code&gt; library has an alternative to my my solution baked in.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;a href=&quot;https://twitter.com/&quot;&gt;@solrevdev&lt;/a&gt; FYI you can also
use&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if (Env.IsMono) {}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;which already provides this&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thanks for the &lt;a href=&quot;https://twitter.com/demisbellot/status/752630173497864193&quot;&gt;reply&lt;/a&gt; &lt;a href=&quot;http://mythz.servicestack.net&quot;&gt;@demisbellot&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I shall use that later today as it is a much better solution and the base &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Env.*&lt;/code&gt; class has loads of useful methods that I have missed.&lt;/p&gt;

&lt;p&gt;For reference here is the &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack.Text/blob/master/src/ServiceStack.Text/Env.cs#L20&quot;&gt;source code&lt;/a&gt; used.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://static.solrevdev.com.s3.amazonaws.com/blog/service-stack-dot-text-is-mono_1.png&quot; alt=&quot;alt text&quot; title=&quot;ServiceStack-Text IsMono Image&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Again, this blog post has been written on my mobile phone using the following tools in addition to my &lt;strong&gt;Jekyll&lt;/strong&gt; driven, &lt;strong&gt;Git&lt;/strong&gt; stored and &lt;strong&gt;GitHub&lt;/strong&gt; static hosted website.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;This post was been written in &lt;a href=&quot;Octopage - Blogging with Jekyll, Markdown and GitHub pages by Huang Tuo
https://appsto.re/gb/rk9UM.i&quot;&gt;Octopage for iOS&lt;/a&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Markdown&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Images hosted on S3 uploaded via &lt;a href=&quot;http://appcrawlr.com/ios/cloudcat-your-aws-s3-in-your-po&quot;&gt;CloudCat&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Text checked by &lt;a href=&quot;http://www.gingersoftware.com/grammarcheck&quot;&gt;Ginger&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Its faster to write on an actual computer but it does make coming up with draft blog posts as soon as you have the idea easy.&lt;/p&gt;

&lt;p&gt;It is really good for taking a screenshot on your phone, using the phones image editing tools then uploading to Amazon S3 then finishing off the blog post when you would normally be checking twitter or social media.&lt;/p&gt;

&lt;p&gt;I am hoping this will drive me to write more blog posts this year than I did when it was hosted on &lt;strong&gt;blogger&lt;/strong&gt; and eventually my writing will improve with the benefit of more traffic and better SEO.&lt;/p&gt;

&lt;p&gt;Open Source and contributing to projects is another challenge for me this yesr including releasing some of the projects I have been working on.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Xamarin Studio and a Platform Helper for Mac and Mono</title>
   <link href="https://solrevdev.com/2016/07/11/xamarin-servicestack-mono-platform-helper.html"/>
   <updated>2016-07-11T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/07/11/xamarin-servicestack-mono-platform-helper</id>
   <content type="html">&lt;p&gt;Since I decided to move away from &lt;a href=&quot;http://www.microsoft.com/en-gb/windows&quot;&gt;Microsoft Windows&lt;/a&gt; with the purchase of my &lt;a href=&quot;http://www.apple.com/uk/macbook-pro/&quot;&gt;Macbook Pro&lt;/a&gt; around 4 years go I have been a big fan of not having to spin up a virtual machine with Windows on just to run &lt;a href=&quot;https://www.visualstudio.com/&quot;&gt;Microsoft Visual Studio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now I have used Visual Studio all the way back to the VBScript ASP and Visual Basic days and it is without doubt a fantastic IDE.&lt;/p&gt;

&lt;p&gt;But then &lt;a href=&quot;http://www.monodevelop.com/&quot;&gt;MonoDevelop&lt;/a&gt; came along and the &lt;a href=&quot;http://www.mono-project.com/&quot;&gt;Mono framework&lt;/a&gt; and I no longer had to exclusively stay in Visual Studio all day.&lt;/p&gt;

&lt;p&gt;Fast forward many years and we now have the quite amazing Xamarin Studio. An IDE that rivals Visual Studio and can run natively on the Mac and even allow you to alongside ASP.NET MVC websites, Console Apps, Windows Services (via TopShelf) also build iOS and Android apps. Even Windows mobile apps if you want to waste your time ;)&lt;/p&gt;

&lt;p&gt;So yes. If you have not checked it out do kick the tyres of &lt;a href=&quot;https://www.xamarin.com/studio&quot;&gt;Xamarin Studio&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Now, one issue I have at the moment and it’s probably a config error but something that’s not high on my list of things to fix is &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack/wiki/Logging#nlog&quot;&gt;NLog&lt;/a&gt; on the Mac.&lt;/p&gt;

&lt;p&gt;It works on Windows but not the Mac.&lt;/p&gt;

&lt;p&gt;So to fix that and to explore some C# features I have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PlatformHelper.cs&lt;/code&gt; class that tells me if I am running on Mac or Windows.&lt;/p&gt;

&lt;p&gt;I then set the &lt;a href=&quot;https://github.com/ServiceStack/&quot;&gt;ServiceStack&lt;/a&gt; LogFactory to use a ConsoleLogger rather than try to write it elsewhere.&lt;/p&gt;

&lt;p&gt;And again, I really have to say how complete, fast and so nice to use the &lt;a href=&quot;https://servicestack.net/&quot;&gt;ServiceStack Framework&lt;/a&gt; is.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Feel free to use the code I am using:&lt;/p&gt;

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

</content>
 </entry>
 
 <entry>
   <title>ServiceStack OrmLite transaction support</title>
   <link href="https://solrevdev.com/2016/07/09/servicestack-transactions.html"/>
   <updated>2016-07-09T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/07/09/servicestack-transactions</id>
   <content type="html">&lt;p&gt;I am a big fan of the &lt;a href=&quot;https://github.com/ServiceStack/ServiceStack.OrmLite&quot;&gt;ServiceStack OrmLite&lt;/a&gt; framework.&lt;/p&gt;

&lt;p&gt;I have  been working on a fairly big project and have abstracted the data access away into a separate class library and wanted to have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Commit()&lt;/code&gt; method that allowed multiple repositories’s to do their thing and to save across tables in a transactional way.&lt;/p&gt;

&lt;p&gt;The ServiceStack OrmLite API is very nice and maps to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;IDbConnection&lt;/code&gt;   interface nicely. Do check it out.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/ServiceStack/Assets/master/img/ormlite/OrmLiteApi.png&quot; alt=&quot;alt text&quot; title=&quot;ServiceStack OrmLite Interface Image&quot; /&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Sometimes sleep is best</title>
   <link href="https://solrevdev.com/2016/07/08/sometimes-sleep-is-best.html"/>
   <updated>2016-07-08T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/07/08/sometimes-sleep-is-best</id>
   <content type="html">&lt;p&gt;&lt;img src=&quot;http://static.solrevdev.com.s3.amazonaws.com/blog/gyroscope/2016-07-08-sleep_1.jpg&quot; alt=&quot;alt text&quot; title=&quot;Sleep graph from Gyroscope App&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I had planned to crack open the laptop but after a day of looking after my mum, fighting with the first instance of malware on my Macbook Pro and a tiring kettlebell session I am pleased that I managed to get some refreshing sleep instead.&lt;/p&gt;

&lt;p&gt;Ever since I taught myself how to code back in the 1990’s I have had issues with sleep so I am so pleased I managed to sleep again. Sleep is coming easier the more I train.&lt;/p&gt;

&lt;p&gt;The malware was I suspect from a chrome extension and removing that and installing again seems to have worked.&lt;/p&gt;

&lt;p&gt;The malware was a variant of VpnApps and neither &lt;a href=&quot;https://www.sophos.com/en-us.aspx&quot;&gt;Sophos&lt;/a&gt; or &lt;a href=&quot;https://www.malwarebytes.com/&quot;&gt;Malwarebytes&lt;/a&gt; could shift it.&lt;/p&gt;

&lt;p&gt;Oh well, today a fresh day and have sleep behind me as the image from my &lt;a href=&quot;https://gyrosco.pe/&quot;&gt;Gyroscope app&lt;/a&gt; shows.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Blogging on mobile devices</title>
   <link href="https://solrevdev.com/2016/07/07/blogging-on-mobile-devices.html"/>
   <updated>2016-07-07T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/07/07/blogging-on-mobile-devices</id>
   <content type="html">&lt;p&gt;So, when I am at my laptop I can draft a blog post using my text editor of choice using the markdown markup language and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit&lt;/code&gt; the file to git.&lt;/p&gt;

&lt;p&gt;Then using Jekyll i can build the static pages and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt; will deploy the new post.&lt;/p&gt;

&lt;p&gt;That is a lovely workflow for a developer and keeps me away from wordpress or a heavy content management system.&lt;/p&gt;

&lt;p&gt;However as I am going to host my images on Amazon S3 as that is where my old blogger images used to live I want to upload screenshots from my phone and I want to write a post using a markdown editor and publish on the move&lt;/p&gt;

&lt;p&gt;In fact just like this!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/b20lG6r.jpg&quot; alt=&quot;alt text&quot; title=&quot;Octopage&quot; /&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/dD4k1x4.jpg&quot; alt=&quot;alt text&quot; title=&quot;Markdown&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I can even embed some code snippets like this:&lt;/p&gt;

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

</content>
 </entry>
 
 <entry>
   <title>New website</title>
   <link href="https://solrevdev.com/2016/07/06/my-new-jekyll-git-website.html"/>
   <updated>2016-07-06T00:00:00+00:00</updated>
   <id>https://solrevdev.com/2016/07/06/my-new-jekyll-git-website</id>
   <content type="html">&lt;p&gt;So, I have had a blog for quite a long time and have hosted it on various platforms such as &lt;a href=&quot;https://en.wikipedia.org/wiki/Posterous&quot;&gt;Posterous&lt;/a&gt;, &lt;a href=&quot;https://home.passle.net/&quot;&gt;Passle&lt;/a&gt;, &lt;a href=&quot;http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html&quot;&gt;Amazon AWS S3&lt;/a&gt; and finally ended up on &lt;a href=&quot;https://www.blogger.com/home&quot;&gt;Blogger&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have also hosted it under various domain names before settling on the one you are on now.&lt;/p&gt;

&lt;p&gt;I decided to refresh the website and blog and wanted to have control of my content so I decided to &lt;a href=&quot;http://import.jekyllrb.com/docs/blogger/&quot;&gt;import my blogger posts with Jekyll&lt;/a&gt; and am storing the content in &lt;a href=&quot;https://git-scm.com/&quot;&gt;Git&lt;/a&gt; and it’s currently being hosted via &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub pages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Blogger platform despite the nice iOS app is a little dated so I wanted a nice refresh and fast static site so I am really happy with being able to write new blog posts in any text editor I like using the &lt;a href=&quot;https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet&quot;&gt;Markdown&lt;/a&gt; syntax and just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt; when I have generated the site the new post via &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All in all, I am very happy so far.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;h2 id=&quot;about-this-site&quot;&gt;About this site&lt;/h2&gt;

&lt;p&gt;Some facts about the setup of this website include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Built for &lt;a href=&quot;http://jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Developed on GitHub and hosted for free on &lt;a href=&quot;https://pages.github.com&quot;&gt;GitHub Pages&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Coded with &lt;a href=&quot;http://sublimetext.com&quot;&gt;Sublime Text 2&lt;/a&gt;, an amazing code editor&lt;/li&gt;
  &lt;li&gt;Based on the great theme &lt;a href=&quot;http://hyde.getpoole.com&quot;&gt;Hyde&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Learn more and contribute on about the theme on &lt;a href=&quot;https://github.com/poole&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Have questions or suggestions? Feel free to &lt;a href=&quot;https://github.com/solrevdev/solrevdev.github.io&quot;&gt;open an issue on GitHub&lt;/a&gt; or &lt;a href=&quot;https://twitter.com/solrevdev&quot;&gt;ask me on Twitter&lt;/a&gt;.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>full-time carer and very very amateur boxer</title>
   <link href="https://solrevdev.com/2016/02/15/full-time-carer-and-very-very-amateur.html"/>
   <updated>2016-02-15T21:29:00+00:00</updated>
   <id>https://solrevdev.com/2016/02/15/full-time-carer-and-very-very-amateur</id>
   <content type="html">So this is just a little placeholder for a blog post I want to write about my transition from being a full-time developer and DevOps guy into being a full-time carer for my elderly mother and my brother who had a stroke. And how going teetotal learning how to box and clean eating has changed my life for the better.&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I still like to program with C# but now using a Mac and Xamarin Studio, Write front-end code using sublime text and still like to play with Amazon Web services, technology like Docker, etc.&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-family: &amp;quot;helvetica neue light&amp;quot; , , &amp;quot;helvetica&amp;quot; , &amp;quot;arial&amp;quot; , sans-serif;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: &amp;quot;helvetica neue light&amp;quot; , , &amp;quot;helvetica&amp;quot; , &amp;quot;arial&amp;quot; , sans-serif;&quot;&gt;I listen to podcasts about technology and software and still read lots of blogs about code infrastructure and all things development, tech and DevOps.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But now I also subscribe to boxing and mixed martial arts podcasts and took a keen interest in fighting and clean eating and living.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It has helped me physically and mentally, and I want to write a lengthier blog post about this when I have time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I haven&apos;t written a blog post for a very long time, so this will have to do for now!&lt;/div&gt;</content>
 </entry>
 
 <entry>
   <title>C# Cup of T Mug</title>
   <link href="https://solrevdev.com/2009/07/24/c-cup-of-t-mug.html"/>
   <updated>2009-07-24T11:06:00+00:00</updated>
   <id>https://solrevdev.com/2009/07/24/c-cup-of-t-mug</id>
   <content type="html">&lt;p&gt;I must have &lt;a href=&quot;http://www.zazzle.com/c_cup_of_t_mug-168503670567870702&quot;&gt;one of these&lt;/a&gt;…&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://lh5.ggpht.com/_WnPb2kqqhc4/SmmVpQvGYVI/AAAAAAAAAbg/uApWRzxvgfw/s1600-h/c_cup_of_t_mug%5B11%5D.jpg&quot;&gt;&lt;img
            style=&quot;display: inline; margin-left: 0px; margin-right: 0px&quot; title=&quot;c_cup_of_t_mug&quot; alt=&quot;c_cup_of_t_mug&quot;
            src=&quot;http://lh3.ggpht.com/_WnPb2kqqhc4/SmmVp1eqvhI/AAAAAAAAAbk/sb139fZ1v-4/c_cup_of_t_mug_thumb%5B9%5D.jpg?imgmax=800&quot; width=&quot;240&quot;
            height=&quot;240&quot; /&gt;&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Subsonic simple repository and auto migrations</title>
   <link href="https://solrevdev.com/2009/07/14/subsonic-simple-repository-and-auto.html"/>
   <updated>2009-07-14T21:43:00+00:00</updated>
   <id>https://solrevdev.com/2009/07/14/subsonic-simple-repository-and-auto</id>
   <content type="html">&lt;p&gt;Rob Conery has posted a &lt;a href=&quot;http://subsonicproject.com/docs/Using_SimpleRepository&quot; target=&quot;_blank&quot;&gt;video and a blog post&lt;/a&gt; explaining one the cooler
    features of the new Subsonic 3.0 ORM product which is the Simple Repository that can also create the database schema on the fly as you drive out the domain
    model of your application.&lt;/p&gt;
&lt;p&gt;I think this has pretty much convinced me to use &lt;a href=&quot;http://subsonicproject.com&quot; target=&quot;_blank&quot;&gt;subsonic&lt;/a&gt; in the side project I am working on
    alongside &lt;a href=&quot;http://git-scm.com/&quot; target=&quot;_blank&quot;&gt;GIT&lt;/a&gt; and &lt;a href=&quot;http://agilezen.com&quot; target=&quot;_blank&quot;&gt;Kanban&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Kanban agile project management</title>
   <link href="https://solrevdev.com/2009/07/11/kanban-agile-project-management.html"/>
   <updated>2009-07-11T13:37:00+00:00</updated>
   <id>https://solrevdev.com/2009/07/11/kanban-agile-project-management</id>
   <content type="html">&lt;p&gt;Today at the Gym I caught up with some podcasts and listened to &lt;a
        href=&quot;http://www.hanselman.com/blog/HanselminutesPodcast170KanbanBoardsForAgileProjectManagementWithZenAuthorNateKohari.aspx&quot;
        target=&quot;_blank&quot;&gt;Hanselminutes Podcast 170 – Kanban Boards for Agile Project Management with Zen Author Nate Kohari&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This approach to lean management of software projects is very interesting and differs from the SCRUM time boxed approach that I have worked with before. &lt;/p&gt;
&lt;p&gt;I have decided to give this approach a go with a side project I have been meaning to work on and have signed up for the free account over at the &lt;a
        href=&quot;http://agilezen.com/&quot; target=&quot;_blank&quot;&gt;Zen Project Management site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For a bit more of an overview on Kanban head over to &lt;a href=&quot;http://jamesshore.com/Blog/Kanban-Systems.html&quot; target=&quot;_blank&quot;&gt;James Shore’s overview on
        Kanban systems&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Video of Scott Guthrie on ASP.NET MVC in Reading</title>
   <link href="https://solrevdev.com/2009/07/09/video-of-scott-guthrie-on-aspnet-mvc-in.html"/>
   <updated>2009-07-09T22:51:00+00:00</updated>
   <id>https://solrevdev.com/2009/07/09/video-of-scott-guthrie-on-aspnet-mvc-in</id>
   <content type="html">&lt;p&gt;Mike Ormand has uploaded the videos for the ASP.NET MVC session that I went to in Reading the other week.&lt;/p&gt;
&lt;p&gt;&lt;a title=&quot;Scott Guthrie at Vista Squad on ASP.NET MVC Part 1 - mike ormond - Channel 9&quot;
        href=&quot;http://channel9.msdn.com/posts/mike+ormond/Scott-Guthrie-at-Vista-Squad-on-ASPNET-MVC/&quot;&gt;Scott Guthrie at Vista Squad on ASP.NET MVC Part 1 - mike
        ormond - Channel 9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a title=&quot;Scott Guthrie at Vista Squad on ASP.NET MVC Part 2 - mike ormond - Channel 9&quot;
        href=&quot;http://channel9.msdn.com/posts/mike%20ormond/Scott-Guthrie-at-Vista-Squad-on-ASPNET-MVC-Part-2/&quot;&gt;Scott Guthrie at Vista Squad on ASP.NET MVC Part
        2 - mike ormond - Channel 9&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Windows 7 RTM to be released on MSDN on Monday?</title>
   <link href="https://solrevdev.com/2009/07/09/windows-7-rtm-to-be-released-on-msdn-on.html"/>
   <updated>2009-07-09T07:11:00+00:00</updated>
   <id>https://solrevdev.com/2009/07/09/windows-7-rtm-to-be-released-on-msdn-on</id>
   <content type="html">&lt;p&gt;I hope all these are true as I have been holding off installing the time restricted beta’s and release candidates in favour of installing the real thing…&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.v3.co.uk/v3/news/2245489/windows-ready-rtm&quot; target=&quot;_blank&quot;&gt;Windows 7 rumoured to be ready for RTM&lt;/a&gt; &lt;br /&gt;&lt;a
        href=&quot;http://www.computerworld.com/s/article/9135199/Reports_Windows_7_heads_to_RTM_July_13?taxonomyId=89&amp;amp;intsrc=kc_top&amp;amp;taxonomyName=operating_systems&quot;
        target=&quot;_blank&quot;&gt;Reports: Windows 7 heads to RTM July 13&lt;/a&gt; &lt;br /&gt;&lt;a href=&quot;http://news.cnet.com/8301-13860_3-10279973-56.html&quot; target=&quot;_blank&quot;&gt;Will
        Windows 7 be finalized next week?&lt;/a&gt; &lt;br /&gt;&lt;a href=&quot;http://www.techtree.com/India/News/Windows_7_Moves_to_Next_Phase_on_July_13/551-104044-580.html&quot;
        target=&quot;_blank&quot;&gt;Windows 7 Moves to Next Phase on July 13&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Git!</title>
   <link href="https://solrevdev.com/2009/07/08/git.html"/>
   <updated>2009-07-08T22:03:00+00:00</updated>
   <id>https://solrevdev.com/2009/07/08/git</id>
   <content type="html">&lt;p&gt;I mentioned in my last post about &lt;a href=&quot;http://www.bbc.co.uk/glow/&quot; target=&quot;_blank&quot;&gt;BBC’s Glow framework&lt;/a&gt; source code being hosted with &lt;a
        href=&quot;http://git-scm.com/&quot; target=&quot;_blank&quot;&gt;Git&lt;/a&gt;/&lt;a href=&quot;http://github.com/&quot; target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Well &lt;a href=&quot;http://blog.wekeroad.com/&quot; target=&quot;_blank&quot;&gt;Rob Conery&lt;/a&gt; the creator of the excellent &lt;a href=&quot;http://subsonicproject.com/&quot;
        target=&quot;_blank&quot;&gt;ORM Subsonic&lt;/a&gt; has posted a blog post and &lt;a href=&quot;http://blog.wekeroad.com/subsonic/subsonic-working-with-git/&quot; target=&quot;_blank&quot;&gt;video
        on how to get started with the Git version control system.&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>BBC Glow JavaScript framework released.</title>
   <link href="https://solrevdev.com/2009/07/08/bbc-glow-javascript-framework-released.html"/>
   <updated>2009-07-08T17:09:00+00:00</updated>
   <id>https://solrevdev.com/2009/07/08/bbc-glow-javascript-framework-released</id>
   <content type="html">&lt;p&gt;Surprisingly the BBC have released an open source JavaScript library called &lt;a href=&quot;http://www.bbc.co.uk/glow/&quot; target=&quot;_blank&quot;&gt;Glow&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;The difference between this and other JavaScript libraries is that the BBC’s library looks like it supports older or ‘Level 2’ browsers. &lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.bbc.co.uk/glow/development/source.shtml&quot; target=&quot;_blank&quot;&gt;Source Code&lt;/a&gt; &lt;br /&gt;&lt;a href=&quot;http://www.bbc.co.uk/glow/docs/&quot;
        target=&quot;_blank&quot;&gt;Documentation&lt;/a&gt; &lt;br /&gt;&lt;a href=&quot;http://www.bbc.co.uk/glow/demos/&quot; target=&quot;_blank&quot;&gt;Demo’s&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;On a side note the BBC are using &lt;a href=&quot;http://git-scm.com/documentation&quot; target=&quot;_blank&quot;&gt;Git&lt;/a&gt; and &lt;a href=&quot;http://github.com/guides/home&quot;
        target=&quot;_blank&quot;&gt;GitHub&lt;/a&gt; to host the source code. I really must have a play with Git as a source control provider.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Today is jQuery day!</title>
   <link href="https://solrevdev.com/2009/07/01/today-is-jquery-day.html"/>
   <updated>2009-07-01T07:46:00+00:00</updated>
   <id>https://solrevdev.com/2009/07/01/today-is-jquery-day</id>
   <content type="html">&lt;p&gt;The more I use jQuery the more I like it but one thing that would really help is intellisense in Visual Studio. Well that is actually possible it seems after
    &lt;a href=&quot;http://webdevdotnet.blogspot.com/2009/06/aspnet-mvc-jquery-part-1-adding-jquery.html&quot; target=&quot;_blank&quot;&gt;reading a really helpful post that explains
        how to actually make that happen.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://webdevdotnet.blogspot.com/&quot; target=&quot;_blank&quot;&gt;same guy&lt;/a&gt; that wrote that post (who’s RSS feed has now been added to my every expanding
    Google Reader subscriptions list) has also written a cool post that &lt;a
        href=&quot;http://webdevdotnet.blogspot.com/2009/06/aspnet-mvc-jquery-part-2-zebra-striping.html&quot; target=&quot;_blank&quot;&gt;explains how to use jQuery to give tables
        the striped alternating row colour effect known as zebra striping&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;Its a technique I have used with both server side C# or JavaScript in the past but jQuery cuts down the amount of code to a &lt;a
        href=&quot;http://webdevdotnet.blogspot.com/2009/06/aspnet-mvc-jquery-part-2-zebra-striping.html&quot; target=&quot;_blank&quot;&gt;couple of lines of JavaScript&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another good read is a post on how to create the share a page with various social networking sites such as facebook or digg etc. &lt;a
        href=&quot;http://www.queness.com/post/309/create-a-digg-style-post-sharing-tool-with-jquery&quot; target=&quot;_blank&quot;&gt;This excellent tutorial again uses jQuery to
        achieve this&lt;/a&gt;.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Scott Guthrie - ASP.NET MVC Special Event</title>
   <link href="https://solrevdev.com/2009/06/30/scott-guthrie-aspnet-mvc-special-event.html"/>
   <updated>2009-06-30T23:41:00+00:00</updated>
   <id>https://solrevdev.com/2009/06/30/scott-guthrie-aspnet-mvc-special-event</id>
   <content type="html">&lt;p&gt;I’m off to Microsoft in Reading on Friday to see the ASP.NET MVC session by &lt;a href=&quot;http://weblogs.asp.net/scottgu/&quot; target=&quot;_blank&quot;&gt;Scott Guthrie&lt;/a&gt;.
    Fantastic stuff!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://scott-mvc.eventbrite.com/&quot; target=&quot;_blank&quot;&gt;Still 86 places available as well….&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>ReSharper 4.1 Released</title>
   <link href="https://solrevdev.com/2008/09/02/resharper-41-released.html"/>
   <updated>2008-09-02T21:30:00+00:00</updated>
   <id>https://solrevdev.com/2008/09/02/resharper-41-released</id>
   <content type="html">&lt;p&gt;Jet Brains have a minor point release to one of my favourite productivity tools ReSharper. &lt;/p&gt;
&lt;p&gt;Since installing it I have noticed that my memory footprint for devenv.exe is much much smaller and the whole IDE and coding experience feels noticeably
    snappier with Visual Studio.NET 2008 SP1.&lt;/p&gt;
&lt;p&gt;Hopefully I will notice the same benefits when I next switch back to coding with Visual Studio.NET 2005.&lt;/p&gt;
&lt;p&gt;For further details on the release you can visit the &lt;a href=&quot;http://www.jetbrains.com/resharper/releaseNotes41.html&quot;&gt;release notes&lt;/a&gt; page on the Jet
    Brains site.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.jetbrains.com/resharper/releaseNotes41.html&quot;&gt;http://www.jetbrains.com/resharper/releaseNotes41.html&lt;/a&gt;&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Google Chrome</title>
   <link href="https://solrevdev.com/2008/09/02/google-chrome.html"/>
   <updated>2008-09-02T21:18:00+00:00</updated>
   <id>https://solrevdev.com/2008/09/02/google-chrome</id>
   <content type="html">&lt;p&gt;A new web browser has arrived. &lt;a href=&quot;http://www.google.com/chrome&quot;&gt;Google Chrome&lt;/a&gt; has just been released in beta. &lt;/p&gt;
&lt;p&gt;Here are my initial thoughts:-&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;The installer worked and it imported all of my Firefox settings without a problem. &lt;br /&gt;&lt;/li&gt;
    &lt;li&gt;The design is simple and clean and feels less cluttered than IE or Firefox. &lt;br /&gt;&lt;/li&gt;
    &lt;li&gt;The tabs work really well and each tab sits within its own process so that hopefully any pages or sites that would normally crash the entire browser
        will only require the tab closing and thus not losing any work or pages that you have left open. &lt;br /&gt;&lt;/li&gt;
    &lt;li&gt;It is really fast! It launches faster than IE and renders faster than Firefox. The rendering engine appears to be WebKit rather than the Mozilla engine.
        &lt;br /&gt;&lt;/li&gt;
    &lt;li&gt;It&apos;s small! By that I mean that it&apos;s memory footprint in task manager is at least 3 times smaller than my Firefox footprint and that is with just the
        one tab open. &lt;br /&gt;&lt;/li&gt;
    &lt;li&gt;I already miss my Firefox extensions!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far so good. &lt;/p&gt;
&lt;p&gt;I shall be testing all the sites I&apos;ve worked on and actually use it as my main browser for a little while.&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>runat != "server"</title>
   <link href="https://solrevdev.com/2008/07/19/runat_19.html"/>
   <updated>2008-07-19T19:01:00+00:00</updated>
   <id>https://solrevdev.com/2008/07/19/runat_19</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://lh5.ggpht.com/hellosmithy/SII6A3ZbhpI/AAAAAAAAASU/VC-mSQ095t8/s1600-h/runatserver%5B3%5D.jpg&quot;&gt;&lt;img
            style=&quot;border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px&quot; height=&quot;240&quot; alt=&quot;runatserver&quot;
            src=&quot;http://lh5.ggpht.com/hellosmithy/SII6BV-BHjI/AAAAAAAAASY/kEtQM0gIbS0/runatserver_thumb%5B1%5D.jpg?imgmax=800&quot; width=&quot;240&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a title=&quot;http://www.cafepress.com/cp/customize/designer.aspx?clear=true&amp;amp;number=286179670#&quot;
        href=&quot;http://www.cafepress.com/cp/customize/designer.aspx?clear=true&amp;amp;number=286179670#&quot;&gt;http://www.cafepress.com/cp/customize/designer.aspx?clear=true&amp;amp;number=286179670#&lt;/a&gt;
&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>CSS Grid Layouts</title>
   <link href="https://solrevdev.com/2008/06/20/css-grid-layouts.html"/>
   <updated>2008-06-20T17:31:00+00:00</updated>
   <id>https://solrevdev.com/2008/06/20/css-grid-layouts</id>
   <content type="html">&lt;p&gt;Today I started to build a new website and have been given the design from the graphic designer along with all the images that are needed to build the site.
&lt;/p&gt;
&lt;p&gt;The next step is to turn the design into XHTML and CSS and create a .NET Master Page for this new design. &lt;/p&gt;
&lt;p&gt;Some earlier reading and research has pointed me to some nice CSS frameworks that can help me out with this.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://developer.yahoo.com/yui/grids/&quot; target=&quot;_blank&quot;&gt;YUI Grids CSS&lt;/a&gt; &lt;br /&gt;&lt;a href=&quot;http://code.google.com/p/blueprintcss/&quot;
        target=&quot;_blank&quot;&gt;Blueprint&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Both of these frameworks allow you to split up your page into a grid system allowing you to create containers, navigation bars, headers, footers and such
    like and cover all the browser inconsistencies including IE6.&lt;/p&gt;
&lt;p&gt;Hopefully using one of these frameworks will give the site a nice consistent look and feel and one that is standards compliant also.&lt;/p&gt;
&lt;p&gt;Not sure which one I will use if any but I will post my findings in a future post. &lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Users Stories, BDD and Rediscovering Business Benefits</title>
   <link href="https://solrevdev.com/2008/06/14/users-stories-bdd-and-rediscovering.html"/>
   <updated>2008-06-14T13:30:00+00:00</updated>
   <id>https://solrevdev.com/2008/06/14/users-stories-bdd-and-rediscovering</id>
   <content type="html">&lt;p&gt;On a &lt;a href=&quot;http://blog.odeworld.co.uk/&quot; target=&quot;_blank&quot;&gt;project&lt;/a&gt; I worked on recently the team used the &lt;a href=&quot;http://en.wikipedia.org/wiki/Scrum_%28development%29&quot; target=&quot;_blank&quot;&gt;SCRUM&lt;/a&gt; methodology for building the software. &lt;/p&gt;  &lt;p&gt;Scrum is just one of the many flavours of &lt;a href=&quot;http://en.wikipedia.org/wiki/Agile_software_development&quot; target=&quot;_blank&quot;&gt;agile software development&lt;/a&gt; and for the most part it worked really well. &lt;/p&gt;  &lt;p&gt;The easy side of any agile project for me are all the good things such as getting the &lt;a href=&quot;http://www.codeplex.com/treesurgeon&quot; target=&quot;_blank&quot;&gt;development tree&lt;/a&gt; setup in source control, getting a &lt;a href=&quot;http://confluence.public.thoughtworks.org/display/CCNET/Welcome+to+CruiseControl.NET&quot; target=&quot;_blank&quot;&gt;continuous integration server&lt;/a&gt; working and practicing techniques such as &lt;a href=&quot;http://en.wikipedia.org/wiki/Test-driven_development&quot; target=&quot;_blank&quot;&gt;TDD&lt;/a&gt; with tools such as &lt;a href=&quot;http://www.nunit.org/index.php&quot; target=&quot;_blank&quot;&gt;NUnit&lt;/a&gt;, &lt;a href=&quot;http://www.testdriven.net/&quot; target=&quot;_blank&quot;&gt;TestDriven.NET&lt;/a&gt;, &lt;a href=&quot;http://www.ayende.com/projects/rhino-mocks/downloads.aspx&quot; target=&quot;_blank&quot;&gt;RhinoMocks&lt;/a&gt;, &lt;a href=&quot;http://selenium.openqa.org/&quot;&gt;Selenium&lt;/a&gt;  and &lt;a href=&quot;http://nant.sourceforge.net/&quot; target=&quot;_blank&quot;&gt;NAnt&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;What wasn&apos;t quite as easy for the team was the writing of user stories, these are better than &lt;a href=&quot;http://en.wikipedia.org/wiki/Big_Design_Up_Front&quot; target=&quot;_blank&quot;&gt;BDUP&lt;/a&gt; specification documents but are still not a walk in the park to get right.&lt;/p&gt;  &lt;p&gt;This is still something that I try and continue to improve my knowledge on and a couple of articles I&apos;ve read today that have a slightly different take on user story writing than I&apos;ve previously read. &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href=&quot;http://www.derickbailey.com/2008/05/29/UserStoryAndAcceptanceCriteriaFormatting.aspx&quot;&gt;User Story And Acceptance Criteria Formatting&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href=&quot;http://ayende.com/Blog/archive/2008/06/13/My-BDD-experiment.aspx&quot;&gt;My BDD experiment&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href=&quot;http://iansrobinson.com/2008/06/05/rediscovering-business-benefits/&quot;&gt;Rediscovering Business Benefits &lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;img alt=&quot;music note&quot; src=&quot;http://spaces.live.com/rte/emoticons/music_note.gif&quot; align=&quot;absmiddle&quot; border=&quot;0&quot; width=&quot;14&quot; height=&quot;14&quot; /&gt; While writing this, I was listening to &quot;Web 2.0 - Part 2 of 3&quot; by ThoughtWorks&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>ASP.NET MVC with NHibernate and Spring</title>
   <link href="https://solrevdev.com/2008/06/14/aspnet-mvc-with-nhibernate-and-spring.html"/>
   <updated>2008-06-14T13:05:00+00:00</updated>
   <id>https://solrevdev.com/2008/06/14/aspnet-mvc-with-nhibernate-and-spring</id>
   <content type="html">&lt;p&gt;I&apos;ve just finished reading a couple of fantastic articles written by &lt;a href=&quot;http://devlicio.us/blogs/billy_mccafferty/&quot; target=&quot;_blank&quot;&gt;Billy
        McCafferty&lt;/a&gt; that cover &lt;a href=&quot;http://www.asp.net/mvc/&quot; target=&quot;_blank&quot;&gt;ASP.NET MVC&lt;/a&gt;, &lt;a href=&quot;http://en.wikipedia.org/wiki/NHibernate&quot;
        target=&quot;_blank&quot;&gt;NHibernate&lt;/a&gt; and &lt;a href=&quot;http://www.springframework.net/&quot; target=&quot;_blank&quot;&gt;Spring&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;http://www.codeproject.com/KB/architecture/NHibernateBestPractices.aspx&quot; target=&quot;_blank&quot;&gt;NHibernate Best Practices with ASP.NET&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;http://devlicio.us/blogs/billy_mccafferty/archive/2008/04/21/asp-net-mvc-best-practices-with-nhibernate-and-spring-net.aspx&quot;
            target=&quot;_blank&quot;&gt;S#arp Architecture: ASP.NET MVC with NHibernate and Spring&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I get some downtime I&apos;m going to have a proper look at Billy&apos;s &lt;a href=&quot;http://www.codeplex.com/SharpArchitecture&quot; target=&quot;_blank&quot;&gt;S#arp
        Architecture&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img height=&quot;14&quot; alt=&quot;music note&quot; src=&quot;http://spaces.live.com/rte/emoticons/music_note.gif&quot; width=&quot;14&quot; align=&quot;absMiddle&quot; border=&quot;0&quot; /&gt; While writing this, I
    was listening to &amp;quot;Sorting out Internationalization with Michael Kaplan&amp;quot; by Scott Hanselman&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>ReSharper 4.0 Keyboard Shortcut Cheatsheet</title>
   <link href="https://solrevdev.com/2008/06/14/resharper-40-keyboard-shortcut.html"/>
   <updated>2008-06-14T12:47:00+00:00</updated>
   <id>https://solrevdev.com/2008/06/14/resharper-40-keyboard-shortcut</id>
   <content type="html">&lt;p&gt;The company credit card has been dusted off again and this time to upgrade my ReSharper license from the older 2.x version to the new 4.x version.&lt;/p&gt;  &lt;p&gt;The new version has full support for C# 3.0 and LINQ and has ASP.NET performance improvements.&lt;/p&gt;  &lt;p&gt;All of the shortcuts and productivity gains I used in 2.x work in exactly the same way although most of my current projects are built in Visual Studio 2005 so I&apos;m not getting the full benefit of the new version yet.&lt;/p&gt;  &lt;p&gt;This will be changing soon as some upcoming &lt;a href=&quot;http://en.wikipedia.org/wiki/Greenfield_project&quot; target=&quot;_blank&quot;&gt;greenfield projects&lt;/a&gt; will allow me to use Visual Studio 2008, &lt;a href=&quot;http://www.asp.net/mvc/&quot; target=&quot;_blank&quot;&gt;ASP.NET MVC&lt;/a&gt; and many other cool new things!&lt;/p&gt;  &lt;p&gt;I am going to print out the &lt;a href=&quot;http://www.jetbrains.com/resharper/docs/ReSharper40DefaultKeymap.pdf&quot; target=&quot;_blank&quot;&gt;keyboard shortcut cheatsheet pdf&lt;/a&gt; and stick it above my desk.&lt;/p&gt;  &lt;p&gt;&lt;img alt=&quot;music note&quot; src=&quot;http://spaces.live.com/rte/emoticons/music_note.gif&quot; align=&quot;absmiddle&quot; border=&quot;0&quot; width=&quot;14&quot; height=&quot;14&quot; /&gt; While writing this, I was listening to &quot;Sorting out Internationalization with Michael Kaplan&quot; by Scott Hanselman&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Windows Sysinternals tools</title>
   <link href="https://solrevdev.com/2008/06/13/windows-sysinternals-tools.html"/>
   <updated>2008-06-13T09:14:00+00:00</updated>
   <id>https://solrevdev.com/2008/06/13/windows-sysinternals-tools</id>
   <content type="html">&lt;p&gt;The Windows Sysinternals team create a superb suite of &lt;a title=&quot;system administration utilities&quot; href=&quot;http://technet.microsoft.com/en-gb/sysinternals/0e18b180-9b7a-4c49-8120-c47c5a693683.aspx&quot; target=&quot;_blank&quot;&gt;system administration utilities&lt;/a&gt; which you would normally download and install onto your local machine. &lt;/p&gt;  &lt;p&gt;This suite of tools includes a replacement for the windows task manager called &lt;a href=&quot;http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx&quot; target=&quot;_blank&quot;&gt;Process Explorer&lt;/a&gt; which also helps debug file and registry operations.&lt;/p&gt;  &lt;p&gt;While this suite of tools is incredibly useful as it is they have now released online versions of them at &lt;a title=&quot;http://live.sysinternals.com/&quot; href=&quot;http://live.sysinternals.com/&quot;&gt;http://live.sysinternals.com/&lt;/a&gt; meaning that you can simply point your browser to that link and run any of the tools without installing it locally. &lt;/p&gt;  &lt;p&gt;The main advantage here is that you don&apos;t have to keep updated as they release new versions.&lt;/p&gt;  &lt;p&gt;I guess this is another example of &lt;a href=&quot;http://en.wikipedia.org/wiki/Cloud_computing&quot; target=&quot;_blank&quot;&gt;computing in the cloud&lt;/a&gt;!&lt;/p&gt;</content>
 </entry>
 
 <entry>
   <title>Microsoft should get rid of the runat="server" tag from ASP.NET!</title>
   <link href="https://solrevdev.com/2008/06/12/microsoft-should-get-rid-of-runat-tag.html"/>
   <updated>2008-06-12T13:54:00+00:00</updated>
   <id>https://solrevdev.com/2008/06/12/microsoft-should-get-rid-of-runat-tag</id>
   <content type="html">&lt;p&gt;I write ASP.NET code most of the time and do so with Microsoft Visual Studio.NET 2003/2005/2008 and it has become really tiresome to add the
    runat=&amp;quot;server&amp;quot; tag all the time!&lt;/p&gt;
&lt;p&gt;I wouldn&apos;t mind if there were runat=&amp;quot;client&amp;quot; or runat=&amp;quot;pub&amp;quot;&amp;#160; options but there isn&apos;t.&lt;/p&gt;
&lt;p&gt;So please Microsoft stop us from having to use it!&lt;/p&gt;</content>
 </entry>
 

</feed>
