<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="https://shazwazza.com/rss/xslt"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>Shazwazza</title>
    <link>https://shazwazza.com/</link>
    <description>My blog which is pretty much just all about coding</description>
    <generator>Articulate, blogging built on Umbraco</generator>
    <image>
      <url>/media/0libq25y/frog.png?rmode=max&amp;v=1da0e911f4e6890</url>
      <title>Shazwazza</title>
      <link>https://shazwazza.com/</link>
    </image>
    <item>
      <guid isPermaLink="false">1336</guid>
      <link>https://shazwazza.com/post/letting-ai-agents-loose-on-examine/</link>
      <category>Examine</category>
      <title>Letting AI agents loose on Examine</title>
      <description>&lt;p&gt;I've been maintaining &lt;a href="https://github.com/Shazwazza/Examine"&gt;Examine&lt;/a&gt; for a very long time now. It's the search and indexing library that sits underneath every Umbraco site, and like any project that's been around long enough, it has plenty of hot paths that could be faster, a steady trickle of issues that need triaging, and a backlog of &amp;quot;I'll get to that eventually&amp;quot; performance work that, well… I never actually get to.&lt;/p&gt;
&lt;p&gt;So a couple of months ago I thought I'd try an experiment - what if I stopped doing all of that myself and instead let a bunch of AI agents do it for me, on a schedule, while I got on with everything else? This post is about how that went. I've shipped a few releases off the back of it and some of the hot-path improvements are genuinely a bit ridiculous.&lt;/p&gt;
&lt;h2 id="so-what-are-these-agentic-workflows"&gt;So what are these &amp;quot;agentic workflows&amp;quot;?&lt;/h2&gt;
&lt;p&gt;The thing I installed is &lt;a href="https://github.com/githubnext/agentics"&gt;GitHub Next's Agentic Workflows&lt;/a&gt; - a set of markdown-defined workflows that run in GitHub Actions and drive an AI agent to do actual maintenance work on your repo. They're not chat bots. They run on a schedule, they have persistent memory, they open real pull requests, and they leave the &amp;quot;should this ship?&amp;quot; decision to me.&lt;/p&gt;
&lt;p&gt;I ended up with a few of them running on Examine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Perf Improver&lt;/strong&gt; - runs daily, hunts for performance bottlenecks, writes benchmarks to actually prove the improvement, and opens a draft PR with the before/after numbers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Efficiency Improver&lt;/strong&gt; - its scrappier sibling, focused on the smaller allocation-and-LINQ-state-machine wins that add up over time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Daily Issue Triage&lt;/strong&gt; - goes through untriaged issues, sets types, applies labels, spots duplicates, and leaves a tidy triage report for me.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agentic Maintenance&lt;/strong&gt; - keeps the whole setup ticking along.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each one is just a markdown file in &lt;code&gt;.github/workflows/&lt;/code&gt; with a description, a schedule, some &lt;code&gt;safe-outputs&lt;/code&gt; limits (things like &amp;quot;you may open at most 4 PRs per run, and they must be drafts&amp;quot;) and a big prompt describing how to behave. That's it. The nice part is that the guardrails are declarative, so the agent can't merge its own PRs, it can't touch protected files, and it can only comment so many times per run. All of that is baked in.&lt;/p&gt;
&lt;h2 id="how-it-actually-works-day-to-day"&gt;How it actually works day to day&lt;/h2&gt;
&lt;p&gt;The bit that makes this more than a gimmick is the persistent memory. Every run, the Perf Improver reads its own notes - which build/test/benchmark commands it validated, what's on its optimisation backlog, what it worked on last time, and which suggestions I've already ticked off. Then it does a couple of tasks in a round-robin fashion so it's not endlessly poking at the same corner of the codebase.&lt;/p&gt;
&lt;p&gt;It also keeps a single rolling &amp;quot;Monthly Activity&amp;quot; issue open with a checklist of what needs my attention. So my side of it is pretty simple: the agent opens a draft PR with measured before/after numbers, I read it and run CI, and if I'm happy with it I merge. That's the whole loop. I'm the reviewer, the agent is the workhorse doing the grind I never had time for.&lt;/p&gt;
&lt;h2 id="so-what-did-they-actually-get-done"&gt;So what did they actually get done?&lt;/h2&gt;
&lt;p&gt;Over roughly the last two months, here's what these workflows actually got up to on Examine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;124&lt;/strong&gt; successful Perf Improver runs and &lt;strong&gt;9&lt;/strong&gt; successful Efficiency Improver runs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;20&lt;/strong&gt; successful Daily Issue Triage runs quietly keeping the issue tracker tidy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;24&lt;/strong&gt; performance/efficiency PRs reviewed and merged (13 from Perf Improver, 11 from Efficiency Improver) between late May and the end of June&lt;/li&gt;
&lt;li&gt;And the bit that actually matters - &lt;strong&gt;three releases shipped&lt;/strong&gt; off this work: &lt;strong&gt;v3.8.0&lt;/strong&gt;, &lt;strong&gt;v3.9.0&lt;/strong&gt; and the &lt;strong&gt;v4.0.0-beta.7&lt;/strong&gt; pre-release&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last point is really the whole thing. This isn't a pile of speculative branches rotting in a fork somewhere - it's code that went through my review, passed CI, and is now sitting in NuGet packages that real Umbraco sites are running.&lt;/p&gt;
&lt;h2 id="show-me-the-numbers"&gt;Show me the numbers&lt;/h2&gt;
&lt;p&gt;Right, this is the part I actually get excited about. One of my favourite things about the Perf Improver is that its prompt tells it to only attempt improvements it can actually measure - establish a baseline first, make the change, then measure again and document both numbers. For the algorithmic hot-path stuff that means benchmarks, so along the way it built out a proper &lt;a href="https://benchmarkdotnet.org/"&gt;BenchmarkDotNet&lt;/a&gt; suite that compares the current source against the published NuGet packages (3.0.1 through 3.3.0), which means I can show you real, reproducible deltas instead of hand waving.&lt;/p&gt;
&lt;h3 id="the-full-text-search-hot-path"&gt;The full-text search hot path&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ManagedQuery&lt;/code&gt; is &lt;em&gt;the&lt;/em&gt; primary full-text search entry point in Examine - it's what runs on basically every search. The agent noticed it had no dedicated benchmark, wrote one, and then stacked up a series of small, individually measured changes: a volatile factory cache in &lt;code&gt;SearchContext.GetFieldValueType&lt;/code&gt;, an early return in the extract-terms check, and killing off some redundant &lt;code&gt;ConcurrentDictionary&lt;/code&gt; lookups in &lt;code&gt;AddDocument&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here's the current source vs the most recent 3.3.0 release, on a 1,000-document index:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ManagedQueryAllFields&lt;/td&gt;
&lt;td&gt;3.3.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;11.42 ms&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,323 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ManagedQueryAllFields&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;2.17 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;371 KB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That's about &lt;strong&gt;5.3x faster&lt;/strong&gt; and roughly &lt;strong&gt;3.6x less memory allocated&lt;/strong&gt; on the single most travelled code path in the whole library. On the busiest thing Examine does. I'll happily take that.&lt;/p&gt;
&lt;h3 id="building-queries"&gt;Building queries&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;GroupedAnd&lt;/code&gt; / &lt;code&gt;GroupedOr&lt;/code&gt; / &lt;code&gt;GroupedNot&lt;/code&gt; are the workhorses of the query builder. A little &lt;code&gt;string[]&lt;/code&gt; fast-path (skipping a defensive &lt;code&gt;.ToArray()&lt;/code&gt; copy when the caller already handed it a &lt;code&gt;string[]&lt;/code&gt;) plus some allocation trimming got this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CreateQueryOnly&lt;/td&gt;
&lt;td&gt;3.3.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;3,995 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;8.34 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CreateQueryOnly&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;319 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;2.20 KB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GroupedAndStringArray&lt;/td&gt;
&lt;td&gt;3.3.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21,377 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;21.10 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GroupedAndStringArray&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;16,659 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;14.34 KB&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;CreateQuery()&lt;/code&gt; baseline dropping from 8.34 KB down to 2.2 KB is a lovely little win, and the grouped clauses are about 25% faster with a third of the allocation shaved off.&lt;/p&gt;
&lt;h3 id="constructing-a-valueset"&gt;Constructing a ValueSet&lt;/h3&gt;
&lt;p&gt;Every single document you index goes through a &lt;code&gt;ValueSet&lt;/code&gt; constructor. The old path allocated an intermediate dictionary and a generator state machine per field, which is exactly the kind of thing you don't notice until you're bulk indexing a big site. The agent got rid of both:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Mean&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Allocated&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FromDictionary5Fields&lt;/td&gt;
&lt;td&gt;3.3.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1,183 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2,200 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FromDictionary5Fields&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;226 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;592 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FromDictionary20Fields&lt;/td&gt;
&lt;td&gt;3.3.0&lt;/td&gt;
&lt;td style="text-align: right;"&gt;4,007 ns&lt;/td&gt;
&lt;td style="text-align: right;"&gt;6,544 B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FromDictionary20Fields&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Source&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;654 ns&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;&lt;strong&gt;1,520 B&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That's roughly &lt;strong&gt;5-6x faster&lt;/strong&gt; and about &lt;strong&gt;4x less allocated&lt;/strong&gt; on indexing, and when you're rebuilding the index on a large Umbraco site that adds up fast.&lt;/p&gt;
&lt;p&gt;The really nice touch is that these benchmark result tables now live in &lt;code&gt;&amp;lt;remarks&amp;gt;&lt;/code&gt; doc-comments right next to the benchmark code, so the numbers are versioned in the repo alongside the thing they measure. The agent did that bit too.&lt;/p&gt;
&lt;h2 id="was-it-actually-worth-it"&gt;Was it actually worth it?&lt;/h2&gt;
&lt;p&gt;For me the thing that makes it work is the measurement. Everything comes to me as a small, focused, draft PR with the numbers attached, so I can look at it, sanity check it, run CI, and decide in a couple of minutes. It's not zero effort - I still read every change before it goes anywhere near main - but the ratio is fantastic. I'm getting a steady stream of well-measured, single-purpose performance PRs on a library I care about, on paths I'd genuinely never have found the time to optimise by hand, and I've shipped real releases because of it. For a project I maintain around everything else, that's a pretty great deal.&lt;/p&gt;
&lt;p&gt;If you maintain a repo with a backlog you never get to, especially performance work that needs benchmarks to justify it, I'd recommend giving &lt;a href="https://github.com/githubnext/agentics"&gt;GitHub Next's agentics&lt;/a&gt; a go.&lt;/p&gt;
&lt;p&gt;You can see all of it out in the open on the &lt;a href="https://github.com/Shazwazza/Examine"&gt;Examine repo&lt;/a&gt; - the &lt;code&gt;[perf-improver]&lt;/code&gt; and &lt;code&gt;[efficiency-improver]&lt;/code&gt; PRs, the benchmark suite, and the releases they fed into. And there's a nice bonus here for me too: &lt;a href="https://examinex.online/"&gt;ExamineX&lt;/a&gt;, my managed, cloud-hosted Examine search offering, runs on this exact same internal plumbing. So all this work the agents have been doing to tighten up Examine's query and indexing hot paths feeds straight through into ExamineX - the underlying engine gets faster and leaner, and every ExamineX site gets those wins for free without changing a thing. If you'd rather have your search running as a managed service instead of hosting Lucene indexes on your own servers, that's what ExamineX is there for. 🙂&lt;/p&gt;
</description>
      <pubDate>Fri, 03 Jul 2026 17:45:20 Z</pubDate>
      <a10:updated>2026-07-03T17:45:20Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1290</guid>
      <link>https://shazwazza.com/post/articulate-430-with-support-for-markdown-code-snippets-and-syntax-highlighting/</link>
      <category>Articulate</category>
      <title>Articulate 4.3.0 with support for markdown code snippets and syntax highlighting</title>
      <description>&lt;p&gt;I'm happy to announce that &lt;a href="https://github.com/Shazwazza/Articulate/releases/tag/v4.3.0"&gt;Articulate 4.3.0 is shipped&lt;/a&gt; and includes a great new feature that I've been wanting/needing:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The ability to create markdown based posts with support for GitHub style code fences/snippets and syntax highlighting for (almost) any coding language.&lt;/strong&gt; &lt;a href="https://github.com/Shazwazza/Articulate/issues/341"&gt;See #341&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now I can finally do away with using Live Writer for my blog and having to manually add css classes to the html for syntax highlighting... yes that's what I've been doing ‍♂️&lt;/p&gt;
&lt;h2 id="upgrading"&gt;Upgrading&lt;/h2&gt;
&lt;p&gt;Once you've updated to the 4.3.0 release, you'll probably need to change the Property Editor of the &lt;code&gt;Articulate Markdown&lt;/code&gt; Data Type to be &lt;code&gt;Articulate Markdown editor&lt;/code&gt; since it's most likely currently configured to the default/basic Umbraco markdown editor.&lt;/p&gt;
&lt;p&gt;You'll then need to update your Post.cshtml theme file to include the correct Prism dependencies. For example, here's the &lt;a href="https://github.com/Shazwazza/Articulate/commit/40ada4e5cfa65bc6e9eae73c35dfae057453bda7#diff-0227666dd80b3592a91378b549d779105eeb896db0c56bd84f5ec2c0ae6c2d46"&gt;update/diff to the VAPOR theme&lt;/a&gt; which adds these dependencies. It's just ensuring that the Prism stylesheet is added to the header and the Prism.js dependencies are appended to the Post.cshtml file.&lt;/p&gt;
&lt;p&gt;Once that's all done, your set!&lt;/p&gt;
&lt;h2 id="creating-markdown-posts"&gt;Creating markdown posts&lt;/h2&gt;
&lt;p&gt;If you didn't already know, Articulate has always had a &lt;a href="https://github.com/Shazwazza/Articulate/wiki/Creating-a-blog-post#using-markdown-from-your-browser-works-great-with-mobile-phones-too"&gt;browser based markdown editor to create posts&lt;/a&gt;. You can simply go to your Articulate root URL and go to the path: &lt;code&gt;a-new&lt;/code&gt; to load the markdown editor. Previously this editor would require you to authenticate at the end of writing your post (if you weren't already authenticated) but now it requires authentication up-front.&lt;/p&gt;
&lt;p&gt;Once it's loaded, it's just a text editor and you can use your all the normal markdown syntax. You can even upload or take photos with the editor &lt;/p&gt;
&lt;p&gt;Of course you can just use the back office markdown editor to create or update posts too but I find for quickly getting a post written and published it's faster to use the &lt;code&gt;/a-new&lt;/code&gt; editor... and it works on mobile.&lt;/p&gt;
&lt;h2 id="using-code-fences-with-syntax-highlighting"&gt;Using code fences with syntax highlighting&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://docs.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks"&gt;GitHub's documentation&lt;/a&gt; shows how this works. The typical code fence is 3x back-ticks above and below your code snippet. If you want to add language specific syntax highlighting you can use 3x back-ticks + the language name/alias. For example, c# would be: &lt;code&gt;```cs&lt;/code&gt; (or c# or csharp) and JavaScript would be &lt;code&gt;```js&lt;/code&gt;. &lt;a href="https://docs.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks#syntax-highlighting"&gt;GitHub's implementation&lt;/a&gt; is different from what Articulate uses so it may not be a perfect 1:1 result but should be fairly close. Articulate is using a combination of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xoofx/markdig"&gt;Markdig&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ilich/Markdig.Prism"&gt;Markdig.Prism&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://prismjs.com/"&gt;Prism.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="rendered-examples"&gt;Rendered Examples&lt;/h3&gt;
&lt;p&gt;There are just rendered examples based on the default prism styles. I wrote this blog post with the Articulate markdown editor so you can see the results.&lt;/p&gt;
&lt;p&gt;Here's an example of a rendered csharp code fence:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;/// &amp;lt;summary&amp;gt;
/// This is an example of the ArticulateComposer
/// &amp;lt;/summary&amp;gt;
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class ArticulateComposer : IUserComposer
{
    public void Compose(Composition composition)
    {
        composition.RegisterUnique&amp;lt;ArticulateRoutes&amp;gt;();
        composition.RegisterUnique&amp;lt;ContentUrls&amp;gt;();
        composition.RegisterUnique&amp;lt;ArticulateDataInstaller&amp;gt;();
        composition.RegisterUnique&amp;lt;ArticulateTempFileSystem&amp;gt;(
            x =&amp;gt; new ArticulateTempFileSystem(&amp;quot;~/App_Data/Temp/Articulate&amp;quot;));

        // TODO: Register remaining services....
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's an example of a rendered js code fence:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;(function () {
    'use strict';

    /**
     * An example of the articulateOptionsManagementController
     * @param {any} $scope
     * @param {any} $element
     * @param {any} $timeout
     */
    function articulateOptionsManagementController($scope, $element, $timeout) {

        var vm = this;
        vm.viewState = &amp;quot;list&amp;quot;;
        vm.selectedGroup = null;
        
        // TODO: Fill in the rest....
    }

    var articulateOptionsMgmtComponent = {
        templateUrl: '../../App_Plugins/Articulate/BackOffice/PackageOptions/articulatemgmt.html',        
        controllerAs: 'vm',
        controller: articulateOptionsManagementController
    };

    angular.module(&amp;quot;umbraco&amp;quot;)
        .component('articulateOptionsMgmt', articulateOptionsMgmtComponent);
})();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's an example of a rendered ruby code fence:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-ruby"&gt;class Dog  
  def initialize(breed, name)  
    # Instance variables  
    @breed = breed  
    @name = name  
  end  
  
  def bark  
    puts 'Ruff! Ruff!'  
  end  
  
  def display  
    puts &amp;quot;I am of #{@breed} breed and my name is #{@name}&amp;quot;  
  end  
end
&lt;/code&gt;&lt;/pre&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:10:26 Z</pubDate>
      <a10:updated>2023-03-23T15:10:26Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1296</guid>
      <link>https://shazwazza.com/post/auto-upgrade-your-nuget-packages-with-azure-pipelines-or-github-actions/</link>
      <category>ASP.Net</category>
      <category>Umbraco</category>
      <title>Auto upgrade your Nuget packages with Azure Pipelines or GitHub Actions</title>
      <description>&lt;p&gt;Before we start I just want to preface this with some  &lt;strong&gt;warnings &lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This works for me, it might not work for you&lt;/li&gt;
&lt;li&gt;To get this working for you, you may need to tweak some of the code referenced&lt;/li&gt;
&lt;li&gt;This is not under any support or warranty by anyone&lt;/li&gt;
&lt;li&gt;Running Nuget.exe update command outside of Visual Studio will overwrite your files so there is a manual review process (more info below)&lt;/li&gt;
&lt;li&gt;This is only for ASP.NET Framework using &lt;em&gt;packages.config&lt;/em&gt; – Yes I know that is super old and I should get with the times, but this has been an ongoing behind the scenes project of mine for a long time. When I need this for Package Reference projects, ASP.NET Core/5, I’ll update it but there’s nothing stopping you from tweaking this to work for you&lt;/li&gt;
&lt;li&gt;This only works for a specified csproj, not an entire sln – it could work for that but I’ve not tested, there would be a few tweaks to make that work&lt;/li&gt;
&lt;li&gt;&lt;span style="text-decoration: line-through;"&gt;This does not yet work for GitHub actions but the concepts are all here and could probably very easily be converted&lt;/span&gt; &lt;strong&gt;UPDATE:&lt;/strong&gt; This works now!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that’s out of the way …&lt;/p&gt;
&lt;h2&gt;How do I do it?&lt;/h2&gt;
&lt;p&gt;With a lot of PowerShell :) This also uses a few methods from the &lt;a rel="noopener" href="https://github.com/microsoft/PowerShellForGitHub" target="_blank"&gt;PowerShellForGitHub&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;The process is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run a pipeline/action on a schedule (i.e. each day)&lt;/li&gt;
&lt;li&gt;This checks against your source code for the installed version for a particular package&lt;/li&gt;
&lt;li&gt;Then it checks with Nuget (using your Nuget.config file) to see what the latest stable version is&lt;/li&gt;
&lt;li&gt;If there’s a newer version:&lt;/li&gt;
&lt;li&gt;Create a new branch&lt;/li&gt;
&lt;li&gt;Run a Nuget update against your project&lt;/li&gt;
&lt;li&gt;Build the project&lt;/li&gt;
&lt;li&gt;Commit the changes&lt;/li&gt;
&lt;li&gt;Push the changes&lt;/li&gt;
&lt;li&gt;Create a PR for review&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Azure Pipelines/GitHub Actions YAML&lt;/h2&gt;
&lt;p&gt;The only part of the YAML that needs editing is the variables, here's what they mean:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ProjectFile = The relative path to your csproj that you want to upgrade&lt;/li&gt;
&lt;li&gt;PackageFile = The relative path to your packages.config file for this project&lt;/li&gt;
&lt;li&gt;PackageName = The Nuget package name you want upgraded&lt;/li&gt;
&lt;li&gt;GitBotUser = The name used for the Git commits&lt;/li&gt;
&lt;li&gt;GitBotEmail = The email used for the Git commits&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For Azure Pipelines, these are also required:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHubOwner = The repository owner (i.e. &lt;a href="https://github.com/Shazwazza/Articulate" title="https://github.com/Shazwazza/Articulate"&gt;https://github.com/&lt;strong&gt;Shazwazza&lt;/strong&gt;/Articulate&lt;/a&gt; )&lt;/li&gt;
&lt;li&gt;GitHubRepository = The repository name (i.e. &lt;a href="https://github.com/Shazwazza/Articulate" title="https://github.com/Shazwazza/Articulate"&gt;https://github.com/Shazwazza/&lt;strong&gt;Articulate&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;There is one Secret setting you need to create in Azure Pipelines: &lt;em&gt;GitHubAccessToken &lt;/em&gt;which is a GitHub PAT that you need to create that has access to this repository.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then there are some variables to assist with testing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DisableUpgradeStep = If true will just check if there’s an upgrade available and exit&lt;/li&gt;
&lt;li&gt;DisableCommit = If true will run the upgrade and will exit after that (no commit, push or PR)&lt;/li&gt;
&lt;li&gt;DisablePush = If true will run the upgrade + commit and will exit after that (no push or PR)&lt;/li&gt;
&lt;li&gt;DisablePullRequest = If true will run the upgrade + commit + push and will exit after that (no PR)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each step in the yaml build more or less either calls Git commands or PowerShell functions. The PowerShell functions are loaded as part of a PowerShell Module which is committed to the repository. This module’s functions are auto-loaded by PowerShell because the first step configures the PowerShell environment variable &lt;em&gt;PSModulePath&lt;/em&gt; to include the custom path. Once that is in place, all functions exposed by the module are auto-loaded.&lt;/p&gt;
&lt;p&gt;In these examples you’ll see that I’m referencing Umbraco Cloud names and that’s because I’m using this on Umbraco Cloud for my own website and the examples are for the &lt;em&gt;UmbracoCms&lt;/em&gt; package. But this should in theory work for all packages!&lt;/p&gt;
&lt;h2&gt;Show me the code&lt;/h2&gt;
&lt;p&gt;&lt;a rel="noopener" href="https://github.com/Shazwazza/NugetAutoUpgrade/tree/main/Examples/NET_Framework/WebApplication" target="_blank"&gt;The code for all of this is here&lt;/a&gt; in a new GitHub repo and here’s how you use it:&lt;/p&gt;
&lt;p&gt;You can copy the folder structure in the repository as-is. Here's an example of what my site's repository folder structure is to make this work (everything except the src folder is in the GitHub repo above):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[root]
&lt;ul&gt;
&lt;li&gt;auto-upgrader.devops.yml &lt;em&gt;(If you are using Azure Pipelines)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;.github
&lt;ul&gt;
&lt;li&gt;workflows
&lt;ul&gt;
&lt;li&gt;auto-upgrader.gh.yml &lt;em&gt;(If you are using GitHub Actions)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;build
&lt;ul&gt;
&lt;li&gt;PowershellModules
&lt;ul&gt;
&lt;li&gt;AutoUpgradeFunctions.psd1&lt;/li&gt;
&lt;li&gt;AutoUpgradeFunctions.psm1&lt;/li&gt;
&lt;li&gt;AutoUpgradeFunctions&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;src
&lt;ul&gt;
&lt;li&gt;Shazwazza.Web
&lt;ul&gt;
&lt;li&gt;Shazwazza.Web.csproj&lt;/li&gt;
&lt;li&gt;packages.config&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of the steps have descriptive display names and it should be reasonably self documenting.&lt;/p&gt;
&lt;p&gt;The end result is a PR, here’s one that was generated by this process:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://shazwazza.com/media/8d8d9550b2c26c6/image.png?width=600&amp;amp;height=353&amp;amp;mode=max" alt=""&gt;&lt;/p&gt;
&lt;h2&gt;Nuget overwrites&lt;/h2&gt;
&lt;p&gt;Nuget.exe works differently than Nuget within Visual Studio’s Package Manager Console. All of those special commands like Install-Package, Update-Package, etc… are all PowerShell module commands built into Visual Studio and they are able to work with the context of Visual Studio. This allows those commands to try to be a little smarter when running Nuget updates and also allows the legacy Nuget features like running PowerShell scripts on install/update to run. This script just uses Nuget.exe and it’s less smart especially for these legacy .NET Framework projects. As such, it will just overwrite all files in most cases (it does detect file changes it seems but isn’t always accurate).&lt;/p&gt;
&lt;p&gt;With that  &lt;strong&gt;warning &lt;/strong&gt;it is very important to make sure you review the changed files in the PR and revert or adjust any changes you need before applying the PR.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You’ll see a note in the PowerShell script about Nuget overwrites. There are other options that can be used like "Ignore" and "IgnoreAll" but all my tests have showed that for some reason this setting will end up deleting a whole bunch of files so the default overwrite setting is used.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;Get out there and try it! Would love some feedback on this if/when you get a change to test it.&lt;/p&gt;
&lt;div class="post"&gt;
&lt;div class="body"&gt;
&lt;div id="4866d04c-80a7-43ab-a84e-d2ee7756afb6" class="postBody"&gt;
&lt;p&gt;&lt;em&gt;PackageReference&lt;/em&gt; support with .NET Framework projects could also be done (but IMO this is low priority) along with being able to upgrade the entire SLN instead of just the csproj.&lt;/p&gt;
&lt;p&gt;Then perhaps some attempts at getting a NET Core/5 version of this running. In theory that will be easier since it will mostly just be &lt;em&gt;dotnet&lt;/em&gt; commands.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt; &lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:10:22 Z</pubDate>
      <a10:updated>2023-03-23T15:10:22Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1186</guid>
      <link>https://shazwazza.com/post/powershell-script-to-create-an-umbraco-package-in-umbraco-s-native-file-format/</link>
      <category>Umbraco</category>
      <title>Powershell script to create an Umbraco package in Umbraco’s native file format</title>
      <description>&lt;p&gt;&lt;a href="http://shazwazza.com/media/articulate/windows-live-writer-powershell-script-to-create-an-umbraco-p_8a19-logo-powershell-umbraco_2.png"&gt;&lt;img title="logo-powershell-umbraco" style="float: left; margin: 0px 10px 10px 0px; display: inline" alt="logo-powershell-umbraco" src="http://shazwazza.com/media/articulate/windows-live-writer-powershell-script-to-create-an-umbraco-p_8a19-logo-powershell-umbraco_thumb.png" width="150" align="left" height="150"&gt;&lt;/a&gt;Since I like using Powershell for my build scripts for various projects I thought it would be handy to create a Powershell script to create an Umbraco package in it’s native package format. I’ve added this script template to my &lt;a href="https://github.com/Shazwazza/UmbracoScripts" target="_blank"&gt;GitHub Umbraco Scripts&lt;/a&gt; repository which you can see here: &lt;a title="http://bit.ly/1kM9g9g" href="http://bit.ly/1kM9g9g"&gt;http://bit.ly/1kM9g9g&lt;/a&gt;&lt;/p&gt; &lt;p&gt;I’ve tried to add a bit of documentation to this script and in theory you should only need to set up the paths properly. You’ll also need to ensure that you initially create the package in the back office of Umbraco in order to generate the &lt;em&gt;createdPackages.config &lt;/em&gt;as mentioned in the script notes.&lt;/p&gt; &lt;p&gt;I use this script in &lt;a href="https://github.com/Shazwazza/Articulate" target="_blank"&gt;Articulate’s&lt;/a&gt; build script so at least I can also provide an example of using it which you can see here: &lt;a title="https://github.com/Shazwazza/Articulate/blob/master/build/build.ps1" href="https://github.com/Shazwazza/Articulate/blob/master/build/build.ps1"&gt;https://github.com/Shazwazza/Articulate/blob/master/build/build.ps1&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:15 Z</pubDate>
      <a10:updated>2023-03-23T15:08:15Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1240</guid>
      <link>https://shazwazza.com/post/articulate-104-released/</link>
      <category>Articulate</category>
      <title>Articulate 1.0.4 released</title>
      <description>&lt;p&gt;I’ve finally got around to releasing &lt;a href="https://github.com/Shandem/Articulate" target="_blank"&gt;Articulate&lt;/a&gt; 1.0.4 today. Want to say a big thanks to all those who submitted pull requests, you guys rock! There’s a few nice fixes in this release but most importantly it fixes the issues with multi-tenancy when domains are assigned in Umbraco.&lt;/p&gt; &lt;p&gt;Here’s the release notes on GitHub with links to each issue fixed:&lt;/p&gt; &lt;p&gt;&lt;a title="https://github.com/Shandem/Articulate/releases/tag/1.0.4" href="https://github.com/Shandem/Articulate/releases/tag/1.0.4"&gt;https://github.com/Shandem/Articulate/releases/tag/1.0.4&lt;/a&gt;&lt;/p&gt; &lt;p&gt;You can download the Umbraco package from there or from Our here:&lt;/p&gt; &lt;p&gt;&lt;a title="http://our.umbraco.org/projects/starter-kits/articulate" href="http://our.umbraco.org/projects/starter-kits/articulate"&gt;http://our.umbraco.org/projects/starter-kits/articulate&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Unfortunately I haven’t got around to getting this on Nuget yet – That’s because it has it’s own challenges since I’d like to perform a full Umbraco data install via Nuget… coming soon I hope :)&lt;/p&gt; &lt;p&gt;Enjoy!&lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:15 Z</pubDate>
      <a10:updated>2023-03-23T15:08:15Z</a10:updated>
    </item>
    <item>
      <guid isPermaLink="false">1233</guid>
      <link>https://shazwazza.com/post/this-blog-now-powered-by-articulate/</link>
      <category>Articulate</category>
      <category>Umbraco</category>
      <title>This blog now powered by Articulate</title>
      <description>&lt;p&gt;&lt;img src="https://raw.githubusercontent.com/Shandem/Articulate/master/Logo.png"&gt;&lt;/p&gt; &lt;p&gt;I’ve recently decided to build a new open source blog engine powered by &lt;a href="http://umbraco.com" target="_blank"&gt;Umbraco&lt;/a&gt; called &lt;a href="https://github.com/Shandem/Articulate" target="_blank"&gt;Articulate&lt;/a&gt;. There’s a few reasons why i wanted to do this:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;Since my full time job is an Umbraco core developer, I spend most of my time &lt;em&gt;building&lt;/em&gt; Umbraco and get very little time for &lt;em&gt;using&lt;/em&gt; Umbraco. I wanted to change this dilemma and really &lt;em&gt;use&lt;/em&gt; Umbraco and utilize many of the features we’ve been creating and try to push some of it’s boundaries. For me this is a perfect way to find inspiration for new Umbraco features and enhancements.&lt;/li&gt; &lt;li&gt;I’ve wanted to move my blog away from &lt;a href="http://www.dotnetblogengine.net/" target="_blank"&gt;BlogEngine.Net&lt;/a&gt; to Umbraco for quite some time (&lt;em&gt;&lt;a href="http://en.wikipedia.org/wiki/Eating_your_own_dog_food" target="_blank"&gt;dogfooding&lt;/a&gt;&lt;/em&gt;) but I really needed to have every feature that I use in BlogEngine.Net available&lt;/li&gt; &lt;li&gt;I wanted to make the blog experience as simple as possible in Umbraco&lt;/li&gt; &lt;li&gt;I wanted to be able to write blog posts directly from my mobile phone &lt;em&gt;easily&lt;/em&gt; using the web&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;I’ve got some &lt;a href="https://github.com/Shandem/Articulate/wiki" target="_blank"&gt;documentation&lt;/a&gt; written and I’ll keep updating this over time but hopefully there’s enough there to get you started. Since Articulate is open source and hosted on GitHub, any community contributions are hugely welcomed :) Whether that’s core code additions, fixes, documentation, etc… any contribution is a big help. &lt;/p&gt; &lt;p&gt;Articulate templates are based on themes and I think it would be super awesome if people started creating their own themes and releasing them as their own packages or including them in the Articulate core for release. &lt;a href="https://github.com/Shandem/Articulate/wiki/Themes" target="_blank"&gt;Creating themes&lt;/a&gt; is really easy and in fact a few of the themes included with Articulate are open source MIT licensed themes migrated over from the &lt;a href="http://ghost.org" target="_blank"&gt;Ghost blogging platform&lt;/a&gt; (which is also very easy to do).&lt;/p&gt; &lt;p&gt;The first version of Articulate is out and can be downloaded either from the &lt;a href="http://our.umbraco.org/projects/starter-kits/articulate" target="_blank"&gt;Umbraco package repository&lt;/a&gt; or from &lt;a href="https://github.com/Shandem/Articulate/releases" target="_blank"&gt;GitHub&lt;/a&gt;. There are a few minor bugs in this release that have been &lt;a href="https://github.com/Shandem/Articulate/issues?state=closed" target="_blank"&gt;reported and fixed&lt;/a&gt; and I’ll have a newer version out this week. &lt;/p&gt;</description>
      <pubDate>Thu, 23 Mar 2023 15:08:14 Z</pubDate>
      <a10:updated>2023-03-23T15:08:14Z</a10:updated>
    </item>
  </channel>
</rss>