<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Justin James</title><link>https://digitaldrummerj.me/</link><description>Recent content on Justin James</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>Justin James</managingEditor><webMaster>Justin James</webMaster><copyright>Copyright 2015-2026. All content is the property of Justin James and digitaldrummerj.me.</copyright><lastBuildDate>Sat, 01 Feb 2025 13:00:00 +0000</lastBuildDate><atom:link href="https://digitaldrummerj.me/index.xml" rel="self" type="application/rss+xml"/><item><title>Testing Our Node Profanity Filter</title><link>https://digitaldrummerj.me/nodejs-profanity-filter-testing/</link><pubDate>Sat, 01 Feb 2025 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/nodejs-profanity-filter-testing/</guid><category>node</category><description>&lt;p&gt;In our previous post, we create a &lt;a href="/nodejs-profanity-filter/" &gt;profanity filter in NodeJS&lt;/a&gt; but the one thing we did not do was add any automated tests. I am huge fan of automated testing so in this article we are going to use Jest to add some automated tests to our profanity filter.&lt;/p&gt;
&lt;h2 id="install-jest"&gt;Install Jest&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install jest --save-dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="create-our-test-filter"&gt;Create Our Test Filter&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a folder called tests&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a file called profanityFilter.test.js&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the profanityFilter.test.js file to import our API and create our describe that will hold our tests&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;./profanityFilter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Adjust path as necessary
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Custom Profanity Filter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="create-our-first-test"&gt;Create Our First Test&lt;/h2&gt;
&lt;p&gt;Within the describe statement that we added above, add the following code for our 1st test to make sure we do not have multiple blank lines&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;should remove multiple blank lines but keep valid punctuation&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Line 1\n\n\nLine 2...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expectedOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Line 1&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Line 2...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedOutput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="run-our-tests"&gt;Run Our Tests&lt;/h2&gt;
&lt;p&gt;Run the following command to run our tests&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx jest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For our API, since I have the type set as module, when you run Jest you may get the error below that had me scratching your head as it was unexpected.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; FAIL tests/profanityFilter.test.js
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ● Test suite failed to run
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Jest encountered an unexpected token
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; By default &lt;span class="s2"&gt;&amp;#34;node_modules&amp;#34;&lt;/span&gt; folder is ignored by transformers.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Here&lt;span class="s1"&gt;&amp;#39;s what you can do:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; • If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; • To have some of your &amp;#34;node_modules&amp;#34; files transformed, you can specify a custom &amp;#34;transformIgnorePatterns&amp;#34; in your config.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; • If you need a custom transformation specify a &amp;#34;transform&amp;#34; option in your config.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the &amp;#34;moduleNameMapper&amp;#34; config option.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; You&amp;#39;&lt;/span&gt;ll find more details and examples of these config options in the docs:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://jestjs.io/docs/configuration
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; For information about custom transformations, see:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; https://jestjs.io/docs/code-transformation
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Details:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; C:&lt;span class="se"&gt;\W&lt;/span&gt;ork&lt;span class="se"&gt;\r&lt;/span&gt;epos&lt;span class="se"&gt;\o&lt;/span&gt;urApi&lt;span class="se"&gt;\t&lt;/span&gt;ests&lt;span class="se"&gt;\p&lt;/span&gt;rofanityFilter.test.js:1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Object.&amp;lt;anonymous&amp;gt;&amp;#34;&lt;/span&gt;:function&lt;span class="o"&gt;(&lt;/span&gt;module,exports,require,__dirname,__filename,jest&lt;span class="o"&gt;){&lt;/span&gt;import filter from &lt;span class="s1"&gt;&amp;#39;./profanityFilter&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // Adjust path as necessary
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ^^^^^^
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; SyntaxError: Cannot use import statement outside a module
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; at Runtime.createScriptFromCode &lt;span class="o"&gt;(&lt;/span&gt;node_modules/jest-runtime/build/index.js:1505:14&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To fix this issue, instead of running &lt;code&gt;npx jest&lt;/code&gt; we need to run node and turn on th experimental-vm-modules&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;node --experimental-vm-modules node_modules/jest/bin/jest.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will run your tests and the tests should pass. You will, however, notice the 1st two lines in the output have a warning since we are using an experimental flag.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;(&lt;/span&gt;node:90036&lt;span class="o"&gt;)&lt;/span&gt; ExperimentalWarning: VM Modules is an experimental feature and might change at any &lt;span class="nb"&gt;time&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;(&lt;/span&gt;Use &lt;span class="sb"&gt;`&lt;/span&gt;node --trace-warnings ...&lt;span class="sb"&gt;`&lt;/span&gt; to show where the warning was created&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; PASS tests/profanityFilter.test.js
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Custom Profanity Filter
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; √ should remove multiple blank lines but keep valid punctuation &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;68&lt;/span&gt; ms&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Test Suites: &lt;span class="m"&gt;1&lt;/span&gt; passed, &lt;span class="m"&gt;1&lt;/span&gt; total
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Tests: &lt;span class="m"&gt;1&lt;/span&gt; passed, &lt;span class="m"&gt;1&lt;/span&gt; total
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Snapshots: &lt;span class="m"&gt;0&lt;/span&gt; total
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Time: 0.444 s, estimated &lt;span class="m"&gt;1&lt;/span&gt; s
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we are ready to write our remaining test cases.&lt;/p&gt;
&lt;p&gt;Here is a quick brainstorm of the things that I would test:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Verify that all of the added banned words are removed from the output&lt;/li&gt;
&lt;li&gt;Verify that th excluded from the custom filter is not removed from the output&lt;/li&gt;
&lt;li&gt;Verify that a mix of \r\n and \n is converted to \n and does not return multiple blank lines&lt;/li&gt;
&lt;li&gt;Verify that extra spaces at the beginning and end is removed&lt;/li&gt;
&lt;li&gt;Verify that * is replaced with an empty string&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Having these tests cases will ensure that our profanity filter continues to work as we make changes.&lt;/p&gt;
&lt;p&gt;Happy testing!!!&lt;/p&gt;</description></item><item><title>Create A Profanity Filter in Node</title><link>https://digitaldrummerj.me/nodejs-profanity-filter/</link><pubDate>Wed, 22 Jan 2025 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/nodejs-profanity-filter/</guid><category>node</category><description>&lt;p&gt;I was recently working with a client on a Q&amp;amp;A submission form for student assemblies that they are running and the client needed to strip out any profanity from the question submission.&lt;/p&gt;
&lt;!--more --&gt;
&lt;p&gt;We decided that we want this in the API versus on the submission form itself so that the students couldn&amp;rsquo;t just open up the browser developer tools and figure out how to skirt around the filter. The API is written in NodeJS Express.&lt;/p&gt;
&lt;p&gt;I could have rolled my own profanity filter but I figured someone had to have already done this and low and behold they have. I need the library to be able to add additional words. I found an npm package called &lt;a href="https://www.npmjs.com/package/bad-words" target="_blank" &gt;bad-words&lt;/a&gt; that had exactly what I was looking for.&lt;/p&gt;
&lt;h2 id="express-api-server"&gt;Express API Server&lt;/h2&gt;
&lt;p&gt;Our Node server will be using express.&lt;/p&gt;
&lt;h3 id="create-project-and-install-express"&gt;Create Project and Install Express&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;npm init&lt;/code&gt; and follow the prompts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open up your package.json file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a type attribute to the list right below the main&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;type&amp;#34;: &amp;#34;module&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Express&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install express dotenv body-parser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a file called index.js&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to index.js&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;express&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;bodyParser&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;body-parser&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;dotenv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Middleware
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Johnny Five Is Alive!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Start server
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Server is running on http://localhost:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open up your terminal and run &lt;code&gt;node ./index.js&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You should see a response that says &amp;ldquo;Johnny Five is alive!&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="add-profanity-filter"&gt;Add Profanity Filter&lt;/h2&gt;
&lt;p&gt;We are now ready to add our API.&lt;/p&gt;
&lt;h3 id="create-our-api"&gt;Create our API&lt;/h3&gt;
&lt;p&gt;In the index.js file we are going to add a new endpoint &lt;code&gt;/api/question&lt;/code&gt;. The endpoint will have a request body that is json and contains a firstName and question property.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/api/question&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// LOGIC TO SAVE GOES HERE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Question submitted successfully&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Error submitting question:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Failed to submit question&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="create-our-profanity-filter"&gt;Create Our Profanity Filter&lt;/h3&gt;
&lt;p&gt;We are now ready to create our profanity filter. We are going to start by installing the bad-words library as they already have a good profanity filter that allows us to extend it with additional words and custom filters.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open your terminal and run the npm install command&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install bad-words
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a file called profanityFilter.js&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the profanityFilter.js.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Filter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bad-words&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;placeHolder&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="integrate-it-into-our-api"&gt;Integrate it into Our API&lt;/h3&gt;
&lt;p&gt;Now lets integrate the profanity filter into our API.&lt;/p&gt;
&lt;p&gt;Go to index.js and in the /api/question, add the following two lines after the &lt;code&gt;const { firstName, question } = req.body;&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;firstNameSanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;profanityFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;questionSanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;profanityFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The profanity filter is now working. However, there are three enhancements to the filter that we want to add:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We want the filter to remove the bad words instead of replacing with an asterisk&lt;/li&gt;
&lt;li&gt;We want to want to add extra words into the filter&lt;/li&gt;
&lt;li&gt;We want to be able to filter multiple word bad words&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="enhance-the-filter-results-by-removing-asterisks"&gt;Enhance the Filter Results by Removing Asterisks&lt;/h4&gt;
&lt;p&gt;By default, the filter replaces the bad words with an asterisk but we want to remove the word completely. The bad words library does not support replacing the bad words with an empty string, so we are going to have to do it after the bad words filter has been called.&lt;/p&gt;
&lt;p&gt;To remove all asterisks, after we call the &lt;code&gt;clean&lt;/code&gt; method for the first name and question, we can call the built-in JavaScript replaceAll method&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;firstNameSanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firstNameSanitized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;questionSanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;questionSanitized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have replaced all of the asterisks with an empty character for the question that can contain multiple lines, there is a potential that we might have multiple blank lines in a row and we want to remove those.&lt;/p&gt;
&lt;p&gt;Below we are going to standardize the line breaks to make sure that they are all \n and that we don&amp;rsquo;t have any \r\n and then replace 3 blank lines with 2 lines.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;questionSanitized&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\r\n/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Normalize CRLF to LF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\n{3,}/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\n\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Replace multiple blank lines with one
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// Remove leading/trailing whitespace
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="add-new-words-to-the-filter"&gt;Add New Words to the Filter&lt;/h4&gt;
&lt;p&gt;To add to the default word list for the bad words library, we need to create an array of words and then call the addWords method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bannedWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;skill issue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;skill 1issue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sk!ll issue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ez&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Filter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bad-words&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Filter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;placeHolder&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bannedWords&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="create-custom-filter-to-handle-multiple-word-bad-words"&gt;Create Custom Filter To Handle Multiple Word Bad Words&lt;/h4&gt;
&lt;p&gt;The next enhancement we are going to do is write a custom filter to remove multiple word bad words. Luckily, the bad words library allows us to easily create custom filters.&lt;/p&gt;
&lt;p&gt;To create a custom filter, we need to create a class that extends Filter.&lt;/p&gt;
&lt;p&gt;We will call super.clean to run the default bad words filter, and then we will loop through our banned words and replace any of them with an asterisk.&lt;/p&gt;
&lt;p&gt;In our custom filter, we can also add the remove all asterisks and extra blanks instead of having them within our api method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CustomFilter&lt;/span&gt; &lt;span class="kr"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// run default bad-words filter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Apply additional custom bad word filtering
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;bannedWords&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phrase&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sb"&gt;`\\b&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\W+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;[\\W]*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;\\b`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;gi&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\r\n/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Normalize CRLF to LF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\n{3,}/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\n\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One of the issues with our custom filter is that it matches on partial words so we also need to have an exclude words from custom filter list.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create an exclude words array&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;excludedWordsFromCustomFilter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ez&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next we need to add a filter statement inside of our custom filter before the forEach&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;bannedWords&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phrase&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;excludedWordsFromCustomFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="tell-our-profanity-filter-to-use-our-custom-filter"&gt;Tell Our Profanity Filter to Use Our Custom Filter&lt;/h4&gt;
&lt;p&gt;The last thing we need to do is tell our profanity filter to use our custom filter.&lt;/p&gt;
&lt;p&gt;To do this instead of saying &lt;code&gt;new Filter&lt;/code&gt; we will say &lt;code&gt;new CustomFilter&lt;/code&gt; in profanityFilter.js&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CustomFilter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;placeHolder&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our profanity filter is now complete.&lt;/p&gt;
&lt;p&gt;Below is the full API if you just want to copy the whole thing.&lt;/p&gt;
&lt;h2 id="complete-api"&gt;Complete API&lt;/h2&gt;
&lt;p&gt;Filename: index.js&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;express&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;bodyParser&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;body-parser&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;dotenv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;profanityFilter&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;./profanityFilter.js&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Middleware
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bodyParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Question Submission Endpoint
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/api/question&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;First name and question are required&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;firstNameSanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;profanityFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstNameSanitized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;questionSanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;profanityFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;questionSanitized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;firstNameSanitized&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;questionSanitized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Invalid Submission&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// LOGIC TO SAVE GOES HERE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Question submitted successfully&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Error submitting question:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Failed to submit question&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Johnny Five Is Alive!&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Start server
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`Server is running on http://localhost:&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Export the Express API
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="complete-profanity-filter"&gt;Complete Profanity Filter&lt;/h2&gt;
&lt;p&gt;Filename: profanityFilter.js&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Filter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bad-words&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bannedWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;skill issue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sk1ll issue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;skill 1ssue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sk1ll 1ssue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sk!ll issue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sk!ll !ssue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;skill !ssue&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ez&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// This will be excluded from the custom filter but handled by `super.clean`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;excludedWordsFromCustomFilter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ez&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;kys&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;L&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CustomFilter&lt;/span&gt; &lt;span class="kr"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Apply the default bad-words filter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Apply additional custom bad word filtering
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;bannedWords&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bannedWord&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;excludedWordsFromCustomFilter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bannedWord&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// Exclude specific words
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bannedWord&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sb"&gt;`\\b&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;bannedWord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\W+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;[\\W]*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;\\b`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;gi&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\r\n/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Normalize CRLF to LF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\n{3,}/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\n\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;CustomFilter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;placeHolder&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Add custom banned words to the filter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addWords&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;bannedWords&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Export the filter for use in other files
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Use Powershell on Mac</title><link>https://digitaldrummerj.me/mac-powershell-prompt/</link><pubDate>Mon, 13 May 2024 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/mac-powershell-prompt/</guid><category>powershell</category><description>&lt;p&gt;This post is a Mac version of a post I wrote a couple of years ago titled &lt;a href="/next-level-powershell-prompt" &gt;Take Your Windows Terminal and Powershell to the next level&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/powershell/powershell-prompt-example.png" alt="Michael&amp;rsquo;s PowerShell Terminal"&gt;&lt;/p&gt;
&lt;p&gt;Let’s walk through the steps I took to get my shell working like Michael&amp;rsquo;s on Mac.&lt;/p&gt;
&lt;p&gt;Several items need to be installed for the profile to work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nerd Font - this is the font that provides the icons for folders and files&lt;/li&gt;
&lt;li&gt;Starship - The minimal, blazing-fast, and infinitely customizable prompt for any shell!&lt;/li&gt;
&lt;li&gt;Terminal-Icons - PowerShell module to display the icons from the Nerd Font&lt;/li&gt;
&lt;li&gt;devtoolbox - a PowerShell module with a bunch of useful aliases for dev tools that Michael released&lt;/li&gt;
&lt;li&gt;PSReadLine - a PowerShell module that allows predictive suggestions as you type a command&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="font-install"&gt;Font Install&lt;/h2&gt;
&lt;p&gt;Nerd Fonts patches developer targeted fonts with a high number of glyphs (icons). Specifically to add an increased number of extra glyphs from popular ‘iconic fonts’ such as Font Awesome, Devicons, Octicons, and others.&lt;/p&gt;
&lt;p&gt;I am using the CaskaydiaCove Nerd Font from &lt;a href="https://www.nerdfonts.com/font-downloads" target="_blank" &gt;Nerd Font&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To install:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open Terminal or whatever shell you are using&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update brew with the nerd fonts&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew tap homebrew/cask-fonts
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;If you are not using brew, you can install it at &lt;a href="https://brew.sh" target="_blank" &gt;https://brew.sh&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the font&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install --cask font-caskaydia-cove-nerd-font
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="powershell-install"&gt;Powershell Install&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;After the font is installed, you need to install Powershell&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install powershell/tap/powershell
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To Launch Powershell, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pwsh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="powershell-profile-setup"&gt;Powershell Profile Setup&lt;/h2&gt;
&lt;p&gt;Next, we need to tell Mac Terminal to use the CaskaydiaCove Nerd Font that was just installed.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Terminal, Open settings by pressing Command+, or clicking on the Terminal menu and selecting Settings&lt;/li&gt;
&lt;li&gt;Click the + to add a new Profile or Duplicate your existing profile&lt;/li&gt;
&lt;li&gt;Name the new profile Powershell&lt;/li&gt;
&lt;li&gt;Click the Change&amp;hellip; button under Font&lt;/li&gt;
&lt;li&gt;Click All Fonts&lt;/li&gt;
&lt;li&gt;Find CaskaydiaCove Nerd Font and Select it&lt;/li&gt;
&lt;li&gt;For the style, select Regular&lt;/li&gt;
&lt;li&gt;Click the Font Window&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we need to tell Terminal to automatically launch Powershell as our default Terminal shell&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Under the settings for the Powershell profile, click the Shell tab&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under Startup, check the Run command: box&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/usr/local/microsoft/powershell/7/pwsh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Uncheck Run inside shell&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the Ask Before closing, add the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-pwsh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pwsh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Close the Settings&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Quit Terminal and Relaunch it&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="use-brew-in-powershell"&gt;Use brew in Powershell&lt;/h2&gt;
&lt;p&gt;In order to use brew in Powershell you need to run the brew shellenv command to set up the profile environment variables.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run: &lt;code&gt;vi $profile&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following to the file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$(/opt/homebrew/bin/brew shellenv) | Invoke-Expression
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Refresh the Powershell profile by running&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;You might need to restart Terminal for the profile changes to be picked up&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="starship"&gt;Starship&lt;/h2&gt;
&lt;p&gt;Next, we need to install &lt;a href="https://starship.rs/" target="_blank" &gt;Starship&lt;/a&gt;, the minimal, blazing-fast, and infinitely customizable prompt for any shell!&lt;/p&gt;
&lt;p&gt;You install Starship using&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -sS https://starship.rs/install.sh &lt;span class="p"&gt;|&lt;/span&gt; sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Note: I had issues installing starship with brew and Powershell not recognizing starship command, so using manual install above.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now that we have Starship installed, we need to add it to our PowerShell profile.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open PowerShell&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the profile by typing &lt;code&gt;vi $profile&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following line at the top of the profile and save it&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Invoke-Expression&lt;/span&gt; &lt;span class="p"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;starship&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;powershell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="terminal-icons"&gt;Terminal-Icons&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/devblackops/Terminal-Icons" target="_blank" &gt;Terminal Icons&lt;/a&gt; is a PowerShell module to show file and folder icons in the terminal.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To install, open PowerShell and run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="nb"&gt;Terminal-Icons&lt;/span&gt; &lt;span class="n"&gt;-Scope&lt;/span&gt; &lt;span class="n"&gt;CurrentUser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To use the Terminal Icons, add the following line to your PowerShell profile&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="nb"&gt;Terminal-Icons&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="devtoolbox"&gt;devtoolbox&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/builders-club/devtoolbox" target="_blank" &gt;devtoolbox&lt;/a&gt; is a set of aliases for common developer command for docker, docker-compose, and git.&lt;/p&gt;
&lt;p&gt;You can install devtoolbox from the &lt;a href="https://www.powershellgallery.com/packages/devtoolbox/" target="_blank" &gt;PowerShell Gallery&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="n"&gt;-Name&lt;/span&gt; &lt;span class="n"&gt;devtoolbox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To use the devtoolbox now that we have installed it, open up your PowerShell profile and add the following statement.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="n"&gt;devtoolbox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="psreadline-predictions"&gt;PSReadLine Predictions&lt;/h2&gt;
&lt;p&gt;For the predictive suggests when typing commands, we need to install PSReadline.&lt;/p&gt;
&lt;p&gt;In a PowerShell administrator shell, run&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="n"&gt;PSReadLine&lt;/span&gt; &lt;span class="n"&gt;-RequiredVersion&lt;/span&gt; &lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;2&lt;/span&gt; &lt;span class="n"&gt;-Force&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Needed to use -Force to have it install PSReadLine 2.2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To our PowerShell profile, we need to add the following lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="n"&gt;PSReadLine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-PredictionSource&lt;/span&gt; &lt;span class="nb"&gt;History
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-PredictionViewStyle&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-EditMode&lt;/span&gt; &lt;span class="n"&gt;Windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="update-function"&gt;update function&lt;/h2&gt;
&lt;p&gt;The update function is designed to make it easy to update powershell to the latest version&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;update&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="n"&gt;upgrade&lt;/span&gt; &lt;span class="n"&gt;powershell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I would suggest that you run the &lt;code&gt;update&lt;/code&gt; command and install the latest cross platform version. Once you get the cross-platform version installed, you need to install the PowerShell modules above and create the profile.&lt;/p&gt;
&lt;h2 id="profile-ended"&gt;profile ended&lt;/h2&gt;
&lt;p&gt;The last piece is to set up the profile function to launch the profile into VSCode quickly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nv"&gt;$profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now your prompt is just like Michael&amp;rsquo;s.&lt;/p&gt;
&lt;h2 id="full-powershell-profile"&gt;Full PowerShell Profile&lt;/h2&gt;
&lt;p&gt;There is a couple of bonus functions like getting a random fact, a dad joke, convert a temp to C, and convert a temp to F.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Originally posted by &lt;a href="https://gist.github.com/MichaelJolley/e65a1a46477a85ecff7f660eee02fa25" target="_blank" &gt;Michael Jolley&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Invoke-Expression&lt;/span&gt; &lt;span class="p"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;starship&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;powershell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="nb"&gt;Terminal-Icons&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="n"&gt;devtoolbox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="n"&gt;PSReadLine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-PredictionSource&lt;/span&gt; &lt;span class="nb"&gt;History
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-PredictionViewStyle&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-EditMode&lt;/span&gt; &lt;span class="n"&gt;Windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;fact&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;irm &lt;/span&gt;&lt;span class="n"&gt;-Uri&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;uselessfacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;jsph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="k"&gt;?&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Select &lt;/span&gt;&lt;span class="n"&gt;-ExpandProperty&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;joke&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;irm &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;icanhazdadjoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;-Headers&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;select &lt;/span&gt;&lt;span class="n"&gt;-ExpandProperty&lt;/span&gt; &lt;span class="n"&gt;joke&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;2C&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Temp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$Temperature&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Temperature&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;2F&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Temp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$Temperature&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Temperature&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;update&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;brew&lt;/span&gt; &lt;span class="n"&gt;upgrade&lt;/span&gt; &lt;span class="n"&gt;powershell&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nv"&gt;$profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Setup Streamdeck With Companion Plug-in</title><link>https://digitaldrummerj.me/companion-streamdeck-plugin/</link><pubDate>Sat, 19 Aug 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/companion-streamdeck-plugin/</guid><category>companion</category><category>streamdeck</category><description>&lt;p&gt;When using &lt;a href="https://bitfocus.io/companion" target="_blank" &gt;BitFocus Companion&lt;/a&gt; and one or more &lt;a href="https://www.elgato.com/us/en/s/welcome-to-stream-deck" target="_blank" &gt;Streamdecks&lt;/a&gt;, you have two ways to setup Companion to interact with the Streamdecks.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first option is to use the Streamdeck natively use Companion. For this to work you do not need to install the Streamdeck software and can not have the Streamdeck software running if you have it installed. The major downside of this configuration is that you are not able to use any Streamdeck plug-ins and can only use what Companion offers. The upside to this configuration is that it allows you to use multiple Streamdecks and have them work independently of each other.&lt;/li&gt;
&lt;li&gt;The second option is to use the Streamdeck Companion plug-in which allows you to use Streamdeck plug-ins and requires that the Streamdeck software is installed and running. The downside to this configuration is if you are using multiple Streamdecks then when you change the page in Companion it will change the page on all of the Streamdeck. The upside is that all of the StreamDeck plug-ins do work.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This article is going to focus on how you set up Companion to work with the StreamDeck Plugin.&lt;/p&gt;
&lt;h2 id="configuration-companion"&gt;Configuration Companion&lt;/h2&gt;
&lt;p&gt;The first thing we are going to do is configure Companion to use the Streamdeck plugin.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open up your Companion Setting GUI. Mine is at &lt;a href="http://localhost:8888/settings" target="_blank" &gt;http://localhost:8888/settings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Find the Surfaces section and disable the &amp;ldquo;Enable connected Streamdecks&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/companion/plugin/companion-turn-on-plugin.png" alt="Companion Enable connected Streamdecks Setting"&gt;&lt;/p&gt;
&lt;h2 id="install-streamdeck-companion-plugin"&gt;Install Streamdeck Companion Plugin&lt;/h2&gt;
&lt;p&gt;Next we are going to install the Companion button plugin for your Streamdeck. If you do not already have the Streamdeck software installed, you will need to download it from &lt;a href="https://www.elgato.com/us/en/s/downloads" target="_blank" &gt;https://www.elgato.com/us/en/s/downloads&lt;/a&gt; and install it before you can install the Companion button plugin.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Once you have the software installed, open up the Streamdeck software&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the top right corner, you want to click on the Store icon. It is the one with the + sign on it.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/companion/plugin/streamdeck-store.png" alt="Streamdeck Store Icon"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the store is open, click on the Plugins option on the left&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/companion/plugin/streamdeck-store-plugins.png" alt="Streamdeck Store Plugins"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the searchbar type, Companion and then install the Companion button plugin if it is not already installed.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/companion/plugin/streamdeck-store-companion-plugin.png" alt="Streamdeck Companion Plugin Search and Install"&gt;&lt;/p&gt;
&lt;div role="alert" class="alert alert-danger"&gt;&lt;strong&gt;Note:&lt;/strong&gt; If the button for the plugin says uninstall then you already have it installed and can go to the next section.&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the install ask you to install the Companion profile, please install it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="adding-companion-buttons-to-your-streamdeck"&gt;Adding Companion Buttons to Your Streamdeck&lt;/h2&gt;
&lt;p&gt;Next, in the Streamdeck software we need to add buttons that are of Companion button that point to a specific Companion button and page within Companion. It does not matter if you create an additional page of Companion buttons, a sub-folder of Companion buttons or a Streamdeck profile that has all of the Companion buttons, they all work the same way once you add the buttons.&lt;/p&gt;
&lt;div role="alert" class="alert alert-success"&gt;&lt;strong&gt;Note:&lt;/strong&gt; You will want to leave one button in Companion empty so that you can put a button on your Streamdeck that changes your Streamdeck page or profile out of your Companion profile&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;When you install the Companion profile, if you switch to that profile, it may have all of the buttons already configured on it for you.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the Companion profile did not install or does not have all of the buttons configured for you, follow the steps below.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Streamdeck software, search for Companion in the list of possible actions.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/companion/plugin/streamdeck-companion-button-action.png" alt="Streamdeck Companion action search"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then drag the Companion button on to the Streamdeck in the position that you want the Companion button to show up&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once you have dragged the button onto the Streamdeck, you will need to set the button number that it references and the page number. By default the page number is set to dynamic which means that you will be able to navigate to different Companion pages. If you want to stay on a single page for that Streamdeck, then set the page number to the page that you want that Streamdeck to always stay on.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/companion/plugin/streamdeck-companion-button-set-button-and-page.png" alt="Streamdeck Companion Button set button number and page number"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Repeat the previous two steps for all of the Companion buttons that you want on your Streamdeck.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now your Streamdeck will work with all of the Companion buttons that you have configured.&lt;/p&gt;</description></item><item><title>Jenkins - Automatically Versioning Your Application</title><link>https://digitaldrummerj.me/jenkins-automatically-versioning-your-application/</link><pubDate>Mon, 24 Jul 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/jenkins-automatically-versioning-your-application/</guid><category>jenkins</category><description>&lt;p&gt;In a previous post, I showed how to use GitVersion with Jenkins but unfortunately as I got further into my Jenkins usage, I was not able to get GitVersion to consistently work across pull request, tags and main branch. Several times I thought I had it working only to find out as I fixed it for one of the places I needed to use it that I broke it for the other places. It felt like I was spending more time trying to get GitVersion working then I did on getting the whole build working. So it was time for a different solution.&lt;/p&gt;
&lt;p&gt;GitVersion is what we were using to determine the version number to use for our nuget packages and releases. It made it so that we did not have to remember each sprint to increment our version number which we routinely forgot to do until we started packaging a release and then it just delayed us as the build needed to run with the new version number.&lt;/p&gt;
&lt;p&gt;The replacement tool we decide on was jx-release-version. jx-release-version just worked out of the box the first time in all of my scenarios (pull request, tags, and main branch).&lt;/p&gt;
&lt;p&gt;Below we will go through the code I needed to add to my Jenkinsfile to get jx-release-version to give me the version number to use.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I am on Windows and have not tested it on a linux Jenkins agent.&lt;/p&gt;
&lt;p&gt;The first thing that I needed to do was download and unzip the Windows install of jx-release-version from &lt;a href="https://github.com/jenkins-x-plugins/jx-release-version/releases" target="_blank" &gt;https://github.com/jenkins-x-plugins/jx-release-version/releases&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once the zip downloaded, I unzipped jx-release-version to c:\jx-release-version on my Jenkins controller.&lt;/p&gt;
&lt;p&gt;The next thing I needed to do was to add a stage to my Jenkinsfile to use jx-release-version to get the version number and make it an environment variable that I can use in future stages.&lt;/p&gt;
&lt;p&gt;In the script below, we are checking if the Jenkins environment variable TAG_NAME is set and if it is then we are tell jx-release-version to use the tag as the version number. For our Git tags, they are in the format of v1.0.0 and jx-release-version will strip out the starting v. If the build is not from a tag then we increment the minor version by default and it will pick up the version from the latest tag we created. Once we get the version number f rom jx-release-version, we set the versionNumber environment variable by calling &lt;code&gt;env.versionNumber&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I am using my Jenkinsfile in a Multi-Branch build so the TAG_NAME might not be set if you are not using a Multi-Branch build.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;stage&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Set Version Number&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;isTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TAG_NAME&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isTag&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;tagVersionOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="nl"&gt;returnStdout:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;script:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@\\jx-release-version\\jx-release-version -next-version=manual:${env.TAG_NAME}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versionNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;${tagVersionOutput.trim()}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="nl"&gt;returnStdout:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;script:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;@\\jx-release-version\\jx-release-version -next-version=increment:minor&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;versionNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versionNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;${versionNumber}-ci${env.BUILD_NUMBER}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;versionNumber: ${env.versionNumber}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have the versionNumber environment variable, we can use it in any of the other stages in the Jenkinsfile. For Windows, we can use it in a bat file using &lt;code&gt;%versionNumber%&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Build&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;bat&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; echo %versionNumber%
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And just like that we have semantic versioning working in our Jenkins build.&lt;/p&gt;</description></item><item><title>Cypress - Scale Your Network Mocks By Centralizing Them</title><link>https://digitaldrummerj.me/cypress-scale-mocks-by-centralizing-them/</link><pubDate>Wed, 19 Jul 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-scale-mocks-by-centralizing-them/</guid><category>cypress</category><category>testing</category><description>&lt;p&gt;As your number of Cypress test grows, if you are mocking out your API calls, it can easily and quickly become hard to maintain which is exactly what happened on one of my projects.&lt;/p&gt;
&lt;p&gt;We were mocking all of our API calls so that we could focus our tests on UI behaviors and interactions. The code for the mocks was typically in each test which was fine for a few tests but now with over 1,000 Cypress tests, you can just imagine how much duplicate code there was and the difficulty in maintaining it..&lt;/p&gt;
&lt;p&gt;It was difficult to find an existing mock for a new tests, so many times the developer writing the tests just created a new mock and a new fixture file.&lt;/p&gt;
&lt;p&gt;It was even more time consuming as the API responses changed over time and we had to find all of the places that a mock was used and update the response for the new API response.&lt;/p&gt;
&lt;p&gt;It was also timing consuming to switch from the old way of mocking Cypress with cy.server to cy.intercept.&lt;/p&gt;
&lt;p&gt;To combat these issues, we changed our code to centralize the calls to cy.intercept so that we only had 1 place it was being used, so if we ever needed to change the Cypress method being used or any update any of the parameters to cy.intercept.&lt;/p&gt;
&lt;p&gt;From there, we also created files to hold all of our code to mock out an API. We split the files by the API endpoints so that we could easily figure out where an API mock was located. In these files, we also needed the ability to change the response, status code, url, and the alias for cy.wait.&lt;/p&gt;
&lt;p&gt;This post is about how we accomplished these goals and made a way more manageable mock setup for us.&lt;/p&gt;
&lt;h2 id="cyintercept-helper"&gt;Cy.Intercept Helper&lt;/h2&gt;
&lt;p&gt;The first thing we need to create is a helper file that encapsulates the calls to cy.intercept.&lt;/p&gt;
&lt;p&gt;In the helper below, we are checking to see if the response ends in .json and if it does, then we assume we are going to use a fixture file and needed to use the fixture parameter. If it does not end in .json, then we assume that we need to use the body parameter.&lt;/p&gt;
&lt;p&gt;You will also see that we are removing the @ from the alias. We did this as our aliases values start with @ so that we can use them with cy.wait without having to constantly adding the @ symbol to the name.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; cypress/mocks/api-helper.js&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;string&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;@&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="mocks"&gt;Mocks&lt;/h2&gt;
&lt;p&gt;Now that we had our api helper file created, we needed to create our API mocks. Below are two samples: 1.) Blog API that uses inline value and 2.) Post API that uses a fixture file.&lt;/p&gt;
&lt;h3 id="1-mock-returns-inline-response"&gt;1. Mock Returns Inline Response&lt;/h3&gt;
&lt;p&gt;In this mock, we need to import the api helper from above. Then we define our aliases that we are going to use for our cy.wait command. We make the alias name that same as the method name to make it easy for developers to know which alias to use for the mock. Lastly, we needed to define our API mock method with the default response, status, url, and alias.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; cypress/mocks/api/mock-api-blogs.js&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;mockRoute&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;../api-helper&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aliases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;getBlog&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@blog&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getBlog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;200&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/api/v1/blog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;alias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aliases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getBlog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;mockRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-mock-returns-response-from-fixture-file"&gt;2. Mock Returns Response From Fixture File&lt;/h3&gt;
&lt;p&gt;This mock is exactly like the previous mock but instead of inline response, we are telling our mock which fixture file to return. Cypress expects your fixture file to in the fixtures directory, so we just need to set the value of the response to the file location under the fixture directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; cypress/mocks/api/mock-api-posts.js&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;mockRoute&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;../api-helper&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aliases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;getPosts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;@getPosts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;posts/post.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;200&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/api/v1/posts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;alias&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aliases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getgetPosts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;mockRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is what our fixture file looks like. It is just the response that the API would return. Typically I create these by grabbing the response from the Chrome Dev Tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; cypress/fixtures/posts/post.json&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;results&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;011b8bbf-5930-4925-9f3d-f8d81c1fc2f3&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Post 1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;BlogId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;5d7e0d56-8fd2-4563-a97c-8bd5de312509&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Post 2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;BlogId&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="test-that-uses-mock"&gt;Test That Uses Mock&lt;/h2&gt;
&lt;p&gt;Now that we have our API helper and our mocks created, we can use them in a test. We start by importing the mock that we want to use and then call the mock method, do the action that calls the API that is mocked (cy.visit in the sample test) and then call cy.wait using our mocks alias.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File:&lt;/strong&gt; cypress/e2e/test.js&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;postMocks&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;../../mocks/api/mock-api-posts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Posts Tests&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Post Sample Tests&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;postMocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPosts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/posts&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postMocks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aliases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getPosts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;tests&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this setup, we have be able to reduce the number of different mocks by over 70%. We can also quickly find all of the places that a particicular API mock is used if we need to update it.&lt;/p&gt;</description></item><item><title>Cypress - Creating Your Test Strategy</title><link>https://digitaldrummerj.me/cypress-creating-your-test-strategy/</link><pubDate>Mon, 17 Jul 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-creating-your-test-strategy/</guid><category>cypress</category><category>testing</category><description>&lt;p&gt;One of the most essential things before jumping into writing tests is to develop your test strategy. I know it sounds like a daunting task. If you are reading this post, you are most likely a developer. We developers&amp;hellip;.we just want to write code, not spend time coming up and documenting something like a test strategy. However, not having a test strategy is like driving a car in a new city without a GPS and hoping you will reach your destination as quickly as possible. You may get there eventually without the GPS, but it is most likely going to be a lot of wrong turns, having to stop for directions (which most likely you won&amp;rsquo;t do until you are really, really lost if you ever stop to ask) and a lot of backtracking and starting over. I don&amp;rsquo;t know about you, but I prefer the most direct route and a GPS to guide me.&lt;/p&gt;
&lt;p&gt;In this post, you will discover the questions you need to answer to develop your test strategy, and once you answer those questions, you will have your GPS guide. These questions are how we decided on our testing strategy when we started and has taken us from zero UI tests in 2019 to around 1,100 tests as of the writing of this post.&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s jump right into it.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;First, give yourself a big pat on the back for deciding that automating your tests is essential. It is a big step to go from manual tests (or no tests) to automated tests, and once you go automated, you never want to go back to manual&amp;hellip;ever again.&lt;/p&gt;
&lt;p&gt;With Cypress, you have multiple types of tests (Component, End to End, and UI with network calls mocked) that you can write. There is not one correct answer for which type to start with or which one is more important. It all depends on your situation, goals, and application. And yes, I know I gave the depends answer that we all dislike. I wish I could give a straight answer for the type or types to start with, but if I did that, it would not be the best for you as the reader since we all have different testing goals, and depending on those goals that are certain test types that are better than others. Eventually, all of the types of tests may be important to your application tests or some types may never become important based on your testing goals.&lt;/p&gt;
&lt;p&gt;For an existing application that you want to write automated tests for, regardless of the types of tests that you write, I would suggest that you consider all of your existing code to be working as expected and write tests as you make changes to those existing features or add in new features. This will allow you to build up your testing library while continuing to deliver new features.&lt;/p&gt;
&lt;h2 id="two-big-questions-need-to-be-answered-to-figure-out-the-type-of-tests-you-should-be-writing-initially"&gt;Two big questions need to be answered to figure out the type of tests you should be writing initially.&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Where do most of your bugs come from in your UI?&lt;/li&gt;
&lt;li&gt;What is your intent with moving from manual testing (or no testing) to automated testing?&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="1-where-do-most-of-your-bugs-come-from-in-your-ui"&gt;1. Where do most of your bugs come from in your UI?&lt;/h2&gt;
&lt;p&gt;Typically, when a team starts looking into automated testing, it is due to having too many customer bugs being reported in production because you do not have enough time to manually test all of the essential things before deploying to production, and you are spending more time releasing hotfixes than working on new features. So our initial goals usually are to increase our customer&amp;rsquo;s confidence that the product we are delivering to them can be counted on; we are catching bugs before they make their way into production, so we spend way less time and energy creating hotfixes; having the confidence that when we deploy to production, our application does what we say it does.&lt;/p&gt;
&lt;p&gt;That is why our first question is about where the majority of your bugs in the UI are coming from so that you can maximize the return on your automated test investment as quickly as possible. The quicker we can get wins with our automated tests, the easier it is for the team, management, and customers to see the value in writing automated tests, and the more likely you will be given the continued time to write automated tests.&lt;/p&gt;
&lt;p&gt;Here are the typical answers I hear for where the bugs are coming from (at a high level) and what type of tests you might want to start with.&lt;/p&gt;
&lt;h3 id="11-communication-with-your-api-an-api-that-you-own-and-created-for-your-ui"&gt;1.1: Communication with your API (an API that you own and created for your UI)&lt;/h3&gt;
&lt;p&gt;If most of your bugs are with the interactions between your UI and your API, then end-to-end testing, or E2E, as often referred to, would be the place I would consider looking into first. E2E tests are meant to test the whole flow of your application from your UI to API to your data store (e.g. database). Most of the time, nothing is mocked out in E2E tests. However, if you have a tough to test 3rd party API (something you don&amp;rsquo;t own and didn&amp;rsquo;t create) or have rate limits, or it is an expensive 3rd party API then I have seen those mocked out in the E2E tests.&lt;/p&gt;
&lt;p&gt;E2E tests also typically require inserting data into the data store by either calling the API to load the test data or going through the UI screen to create the data. For the performance of your tests, it is recommended to call the API to insert the test data needed for your tests instead of going through all of the UI screens to insert the data.&lt;/p&gt;
&lt;h3 id="12-ui-behaviors-and-interactions"&gt;1.2: UI behaviors and interactions&lt;/h3&gt;
&lt;p&gt;If the majority of your bugs are coming from the behaviors that happen as part of the user interactions in your UI, then I would look at mocking the API calls that you are making by using &lt;a href="https://docs.cypress.io/api/commands/intercept" target="_blank" &gt;cy.intercept&lt;/a&gt; so that you can focus on what your UI is doing as the user is clicking around in your UI.&lt;/p&gt;
&lt;p&gt;Here are some examples of what I mean by behaviors and interactions and tests you might run:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User clicks on a button that should launch a dialog. Is the correct dialog opened and did the dialog have the right action buttons (if they are configurable in code to what shows)?&lt;/li&gt;
&lt;li&gt;As a user types or paste into an input field, non-allowed characters are removed from the input field. Did it actually strip out the non-allowed characters?&lt;/li&gt;
&lt;li&gt;An input field should have a min and max limit. Are the correct attribute on the input field to enforce this?&lt;/li&gt;
&lt;li&gt;Depending on permissions, an element on the page will be shown or hidden. This is where mocking the API calls comes in handy. You can change the permissions as part of what the mock returns, so simulate the different permissions a user might have and then test if an element is shown or hidden as you expect. Your API should validate the permissions again to ensure someone didn&amp;rsquo;t open the developer tools and turn on something, but this at least ensures your UI is right.&lt;/li&gt;
&lt;li&gt;A grid with sortable columns and ensuring that the sorting A-Z and Z-A are working. If it is server-side sorting, then make sure that the correct API call is made, which you can test with a mock. This particular test has saved us several times, as we have broken this feature more than once on some of our grids.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are just a few of the many behaviors and interactions in our UI that we are testing for. This is where my team focused most of our automated tests since the UI behaviors were where about 95% of our bugs came from. In 2019, we started out with zero automated UI tests and as of the writing of this post, we have about 1100 Cypress tests now. Yes, 1100 UI tests. Writing automated tests is considered part of our definition of done and our management is 100% backing us that automated tests are non-negotiable but have left it up to us as a development team to figure out what tests and how many of them we need (i.e. there is no random code coverage % that we need to hit just to hit&amp;hellip;..).&lt;/p&gt;
&lt;p&gt;When we started writing Cypress tests, our product was a little over a year old, so we had a lot of code already written and a lot of features that we had already committed to delivering. So we couldn&amp;rsquo;t just stop what we were doing to focus on writing Cypress tests, but management was willing to split off a dev or two to work on the testing strategy, write some initial tests to validate the automated testing strategy, and train the team on writing Cypress tests. This allowed the team to keep moving forward while we figured out the testing strategy and validated it.&lt;/p&gt;
&lt;h3 id="13-a-3rd-party-api-that-you-are-interfacing-with"&gt;1.3: A 3rd party API that you are interfacing with?&lt;/h3&gt;
&lt;p&gt;For interacting with a 3rd party, if your UI is directly contacting the 3rd party API, then I would for sure use Cypress for testing, but in most cases, the interaction with a 3rd party API requires some kind of authentication or a secret key so your interactions to the 3rd party API are done from within your API so that you do not leak your secret keys. If your interaction to the 3rd party API is done from within your API, then I would write API tests using the testing framework for your API technology (e.g. XUnit if I am using .NET for the API).&lt;/p&gt;
&lt;p&gt;If your communication and interaction with a 3rd party API is done directly from your UI, then you can either write end-to-end tests with nothing mocked or tests with your API calls mocked. Personally, I would mock my API calls as much as I can so that I can focus on just the 3rd party API interactions.&lt;/p&gt;
&lt;h3 id="14-individual-components-within-your-application"&gt;1.4: Individual components within your application?&lt;/h3&gt;
&lt;p&gt;If the individual reusable components are where the majority of your bugs are coming from and you are using one of the &lt;a href="https://docs.cypress.io/guides/component-testing/overview#Supported-Frameworks" target="_blank" &gt;supported UI frameworks&lt;/a&gt; then I would write &lt;a href="https://docs.cypress.io/guides/component-testing/overview" target="_blank" &gt;component tests&lt;/a&gt; so that you can test the components in isolation.&lt;/p&gt;
&lt;p&gt;The only reason that we are not using Component tests is that they did not exist in 2019 when we started using Cypress and since we already have UI tests that include testing our components, it has not made sense ROI wise for us to start with component testing. However, if we were starting today, we would have used component testing in addition to our UI interaction and behavior tests.&lt;/p&gt;
&lt;h2 id="2-what-is-your-goal-with-moving-from-manual-testing-or-no-testing-to-automated-testing"&gt;2. What is your goal with moving from manual testing (or no testing) to automated testing?&lt;/h2&gt;
&lt;p&gt;Typically, when starting our journey with automated testing, we have some pain points with our existing test strategy that we are trying to solve. I find that they tend to fall into the following categories:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Want to reduce bugs that you are currently having&lt;/li&gt;
&lt;li&gt;Want to reduce the time it takes to run through QA?&lt;/li&gt;
&lt;li&gt;Want to automate the testing of edge cases that are hard to manually test?&lt;/li&gt;
&lt;li&gt;Want to test happy path to make sure everything is working as expected when no errors are being generated&lt;/li&gt;
&lt;li&gt;Want to test out potential errors that may be returned from your API and make sure the UI handles them properly&lt;/li&gt;
&lt;li&gt;Want to perform full end-to-end testing to make sure everything is communicating as expected between the different layers of your application without having anything mocked out&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="21-want-to-reduce-bugs-that-you-are-currently-having"&gt;2.1 Want to reduce bugs that you are currently having&lt;/h3&gt;
&lt;p&gt;If this is your primary goal, then follow the answers to question number one above for the type of tests to start with in your testing strategy. I suggest starting by writing tests as you fix bugs instead of trying to retrofit and add tests for all the bugs you have already fixed.&lt;/p&gt;
&lt;h3 id="22-want-to-reduce-the-time-it-takes-to-run-through-qa"&gt;2.2 Want to reduce the time it takes to run through QA?&lt;/h3&gt;
&lt;p&gt;To reduce testing time, I would look at your current tests and see which ones take the longest to set up and execute and are in critical features. Then see if you can effectively automate the setup and test or not to make sure that you actually shorten the testing time without spending so much time writing the tests that it would take years before you see a return on the testing time (i.e. more testing saving than testing writing time). Unfortunately, sometimes it is more expensive to automate certain tests than it is to manually perform the tests.&lt;/p&gt;
&lt;p&gt;As well, the reason that I also look at the tests for critical features is to make sure we are testing the most important features first and not just testing the features that take the longest to test.&lt;/p&gt;
&lt;p&gt;Also, if you are currently manually testing, if your test steps are not documented, one of the first steps would be to document those steps so that your initial automated tests match the tests you are manually doing today. The documentation could be as simple as a Google Document or Word document. It is more important that they are documented than how they are documented. Bravo to you and your team if you already have your manual test steps documented.&lt;/p&gt;
&lt;h3 id="23--want-to-automate-the-testing-of-edge-cases-that-are-hard-to-manually-test"&gt;2.3 Want to automate the testing of edge cases that are hard to manually test?&lt;/h3&gt;
&lt;p&gt;Before diving into testing edge cases, I would first look at the impact if the edge case was hit. I have seen several times that a team wants to test edge cases just to make sure they are covered and when we looked at the impact if the edge case was hit, it was either no impact or so small that it really did not make sense to actually test it compared to other tests that needed to be written.&lt;/p&gt;
&lt;p&gt;Suppose the edge case is hit and it would cause impact. In that case, the first step is to understand the steps that it would take to make the edge case happen and then see which test type would allow you to test the edge case (Component test, end-to-end test, or UI behavior/interaction test).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Most of the time, though, this is different from where most teams start with their automated testing since it is typically challenging to get everything set up to make the edge case happen and most of the time they are low impact.&lt;/p&gt;
&lt;h3 id="24--want-to-test-happy-path-to-make-sure-everything-is-working-as-expected-when-no-errors-are-being-generated"&gt;2.4 Want to test happy path to make sure everything is working as expected when no errors are being generated&lt;/h3&gt;
&lt;p&gt;This is where most teams start, followed right behind it with testing potential errors that can be returned for their API calls. When you deploy your application to production, you want to know that your UI is doing what it is supposed to be doing when everything is playing nicely together. Having happy path tests gives you the confidence that everything is working.&lt;/p&gt;
&lt;p&gt;The one caution I will give about happy path tests is that it is very very easy to go overboard and test everything possible. Just because you can doesn&amp;rsquo;t mean that you should. Test the critical features and ones that are impactful if they are not working correctly.&lt;/p&gt;
&lt;h3 id="25-want-to-test-out-potential-errors-that-may-be-returned-from-your-api-and-make-sure-the-ui-handles-them-properly"&gt;2.5 Want to test out potential errors that may be returned from your API and make sure the UI handles them properly&lt;/h3&gt;
&lt;p&gt;These kinds of tests are perfect for using &lt;a href="https://docs.cypress.io/api/commands/intercept" target="_blank" &gt;cy.intercept&lt;/a&gt; to mock out your API calls so that you can quickly generate the error and then make sure that your UI handles the error gracefully.&lt;/p&gt;
&lt;h3 id="26-want-to-perform-full-end-to-end-testing-to-make-sure-everything-is-communicating-as-expected-between-the-different-layers-of-your-application-without-having-anything-mocked-out"&gt;2.6 Want to perform full end-to-end testing to make sure everything is communicating as expected between the different layers of your application without having anything mocked out&lt;/h3&gt;
&lt;p&gt;This answer here is obvious that end-to-end testing will be the answer. In my experience, most teams do not start with end-to-end testing as it is the most complicated and time-consuming to get set up. You have to run your API, data store, and UI before kicking off your Cypress tests. You will also need to seed any data needed for tests to pass to your data store before running each test and then do the cleanup work to ensure that tests are not interacting with each other.&lt;/p&gt;
&lt;p&gt;End-to-end testing is also typically the slowest test to run since you are communicating across the network with your API and data store. End-to-end tests are typically reserved for critical features where it is important to ensure everything is communicating.&lt;/p&gt;
&lt;h2 id="wrap-up"&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;As much as I know you want to jump into writing code, having a test strategy is key to be able to quickly hit your testing goals. Your test strategy is your GPS guide to your goals and will get you there as fast as possible. No matter how much we think we know better than the GPS, it is amazing how accurate the GPS is.&lt;/p&gt;</description></item><item><title>ASP.NET Core Environment Variables With Semicolon in Jenkins</title><link>https://digitaldrummerj.me/jenkins-aspnet-environment-semicolon/</link><pubDate>Tue, 16 May 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/jenkins-aspnet-environment-semicolon/</guid><category>jenkins</category><category>aspnet core</category><category>dotnet</category><description>&lt;p&gt;In my ASP.NET Core project, I am using an environment variable in the form of &lt;code&gt;MyProject:MyVariableName&lt;/code&gt; and needed to create a Jenkins pipeline but I could not get it to recognize that format. It kept making the environment variable name just &lt;code&gt;MyVariableName&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Below is the code from the Jenkinsfile that I tried that was causing the issue.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;stage(&amp;#39;Run All Test&amp;#39;) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; environment {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; MyProject:MyVariableName = &amp;#34;Some Value&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Turns out that Jenkins does not recognize the &lt;code&gt;:&lt;/code&gt; in environment variable names. After much searching, I ran across this &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?tabs=basicconfiguration&amp;amp;view=aspnetcore-7.0#non-prefixed-environment-variables" target="_blank" &gt;article&lt;/a&gt; that mentioned that not all platforms support &lt;code&gt;:&lt;/code&gt; in the environment names and that for those environmments you can use a double underscore &lt;code&gt;__&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;So I updated the Jenkinsfile code above with the double underscore &lt;code&gt;__&lt;/code&gt; and this make Jenkins see the environment variable name as &lt;code&gt;MyProject:MyVariableName&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;stage(&amp;#39;Run All Test&amp;#39;) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; environment {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; MyProject__MyVariableName = &amp;#34;Some Value&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Using GitVersion on Jenkins</title><link>https://digitaldrummerj.me/jenkins-gitversion/</link><pubDate>Tue, 02 May 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/jenkins-gitversion/</guid><category>jenkins</category><description>&lt;div role="alert" class="alert alert-danger"&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This article is out of date. Click &lt;a href="/jenkins-automatically-versioning-your-application" &gt;here&lt;/a&gt; for the new article.&lt;/div&gt;
&lt;p&gt;For my continuous integration builds I use Gitversion to determine that SemVer to use for a particular build and release. For pre-release builds the version would look like something like 0.1.5-ci0004 where the -ci is appended to the version number and the 4 is the build number in the CI system. Then for a release release version, it would just be the version number of 0.1.5 without the -ci or build number at the end.&lt;/p&gt;
&lt;p&gt;I am in the process of moving several builds to Jenkins and needed to get Gitversion working on Jenkins. I am totally new to Jenkins so even though Gitversion has &lt;a href="https://gitversion.net/docs/reference/build-servers/jenkins" target="_blank" &gt;documentation&lt;/a&gt; on how to set up Jenkins correctly it took me a bit to figure out where those options were. My initial attempts ended up with the pre-release being 0.1.5-origin-master-0004 instead of 0.1.5-ci0004.&lt;/p&gt;
&lt;p&gt;Below are the options that you need to add into Jenkins and what they are called.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;For Branches to build, make the branch specifier&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+refs/heads/*:refs/remotes/@{remote}/*
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/gitversion-jenkins/git-branches.png" alt="Branches to Build Branch Specifier"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add Clean before checkout and check Delete untracked nested repositories&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/gitversion-jenkins/git-clean-before-checkout.png" alt="Git Before Checkout"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add Advanced clone behaviors and then check both Fetch tags and Honor refspec on initial clone. Leave Path of the r eference repo to use during clone and Timeout fields empty and do not check Shallow clone&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/gitversion-jenkins/git-advanced-clone-behaviors.png" alt="Git Advanced Clone Behaviors"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add Prune stale remote-tracking branches&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/gitversion-jenkins/git-prune-remote-tracking.png" alt="Git Prune Remote Tracking Branch"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add Check out to specific local branch and leave the branch name empty&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/gitversion-jenkins/git-checkout-specific-local-branch.png" alt="Git Check out to specific local branch"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that your Git checkout is setup correctly, before you can run gitversion you need to add the EnvInject plugin so that you can put the gitversion information onto environment variables that you can use in other build steps.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click on Manage Jenkins&lt;/li&gt;
&lt;li&gt;Click on Manage Plugins&lt;/li&gt;
&lt;li&gt;In the avaiable plugins list search for EnvInject&lt;/li&gt;
&lt;li&gt;Install the plugin&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you are ready to add the build steps to execute your Gitversion command and then inject them into your environment variables.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;For the build steps, add an Execute Windows batch command and set the command text to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gitversion /output buildserver
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As part of the output you will see Gitversion output such as&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_Major=0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_Minor=1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_Patch=5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_PreReleaseTag=ci.4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_PreReleaseTagWithDash=-ci.4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_PreReleaseLabel=ci
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_PreReleaseLabelWithDash=-ci
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_PreReleaseNumber=4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_WeightedPreReleaseNumber=55004
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_BuildMetaData=
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_BuildMetaDataPadded=
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_FullBuildMetaData=Branch.master.Sha.440219f44061cd2a8d1d8b21ad2dcd1580f5ecd3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_MajorMinorPatch=0.1.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_SemVer=0.1.5-ci.4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_LegacySemVer=0.1.5-ci4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_LegacySemVerPadded=0.1.5-ci0004
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_AssemblySemVer=0.1.5.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_AssemblySemFileVer=0.1.5.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_FullSemVer=0.1.5-ci.4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_InformationalVersion=0.1.5-ci.4+Branch.master.Sha.440219f44061cd2a8d1d8b21ad2dcd1580f5ecd3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_BranchName=master
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_EscapedBranchName=master
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_Sha=440219f44061cd2a8d1d8b21ad2dcd1580f5ecd3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_ShortSha=440219f
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_NuGetVersionV2=0.1.5-ci0004
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_NuGetVersion=0.1.5-ci0004
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_NuGetPreReleaseTagV2=ci0004
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_NuGetPreReleaseTag=ci0004
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_VersionSourceSha=cc7a999cb8118c8c098a8f45f507403342a64435
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_CommitsSinceVersionSource=4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_CommitsSinceVersionSourcePadded=0004
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_UncommittedChanges=0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitVersion_CommitDate=2022-02-09
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After the build step for gitversion, add an Inject environment variables step and set the Properties File Path to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gitversion.properties
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now you can use the Gitversion references into other build steps such as a nuget package build where it will use the environment variable %GitVersion_NuGetVersionV2%.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet pack MyNugetPackage.sln /p:PackageVersion&lt;span class="o"&gt;=&lt;/span&gt;%GitVersion_NuGetVersionV2% --version-suffix &lt;span class="s2"&gt;&amp;#34;pr%BUILD_NUMBER%&amp;#34;&lt;/span&gt; -c Release
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will see in the log output that the command execute was:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet pack MyNugetPackage.sln /p:PackageVersion=0.1.5-ci0004 --version-suffix &amp;#34;pr10&amp;#34; -c Release
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Use C# to Convert PNG to Base64</title><link>https://digitaldrummerj.me/csharp-png-to-base64/</link><pubDate>Mon, 01 May 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/csharp-png-to-base64/</guid><category>dotnet</category><description>&lt;p&gt;For something I was working on I created a quick script to convert all PNG files in a directory to a base64 representation of the image. Since I was going to need to do this multiple times as we iterated over the image designs to get one that we were happy with, I decided to write a quick script using C# and use &lt;a href="https://www.linqpad.net/" target="_blank" &gt;Linqpad&lt;/a&gt; to run the script.&lt;/p&gt;
&lt;p&gt;For the script output, I needed to output the following TypeScript object for the project that I needed the base64 values for.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;LG1&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;base64 of image. removed for readability here&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;LG10&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;base64 of images. removed for readability here&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is the &lt;a href="https://www.linqpad.net/" target="_blank" &gt;Linqpad&lt;/a&gt; script that I wrote.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;directoryPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@&amp;#34;Directory with the PNG iamges&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;pngFiles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directoryPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;*.png&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;StringBuilder&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;StringBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;const images = {\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;pngFile&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pngFiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;imageBytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAllBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pngFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;base64String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToBase64String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imageBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetFileNameWithoutExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pngFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;-&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;_&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AppendFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\t\&amp;#34;{0}\&amp;#34;: \&amp;#34;{1}\&amp;#34;,\n&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base64String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Remove the trailing comma and newline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;\n}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// add newline before the closing bracket&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Print out the output string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;Dump&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that I have the TypeScript object in the Linqpad output window I can just copy and paste it into my TypeScript code.&lt;/p&gt;</description></item><item><title>Better Way to Manage Database Views in EF Core Migrations</title><link>https://digitaldrummerj.me/efcore-view-migrations/</link><pubDate>Thu, 23 Feb 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/efcore-view-migrations/</guid><category>ef core</category><category>dotnet</category><description>&lt;p&gt;When using database views that are not directly managed by Entity Framework Core (EF Core), it is a good practice to still version control and I like to do this by including the scripts to add/drop the views in an EF Core migration script. It is easy enough to use the migration builder sql method to call the sql needed to add and drop your view but as the project grows this becomes way harder to manage. One of my projects has 638 migration scripts (it has been going on for like 5 years now), so you can imagine how difficult it can be to find the previous version of the view when you need to drop a migration script and revert back to the previous version of the view.&lt;/p&gt;
&lt;p&gt;Recently, we started managing our view in a different way that has made it easier to find the different versions of the view and simplified our migration scripts. In this post, we are going to look at the implementation we did to manage our views.&lt;/p&gt;
&lt;p&gt;For the view management we do need a blank EF Core migration script which we can create by running&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet ef migrations add YourMigrationScriptName
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create you a blank migration script with the Up and Down Methods&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore.Migrations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyApp.Migrations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;YourMigrationScriptName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Migration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MigrationBuilder&lt;/span&gt; &lt;span class="n"&gt;migrationBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MigrationBuilder&lt;/span&gt; &lt;span class="n"&gt;migrationBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before in our migration script we would create public static methods to get the view create and drop statements and then call then in the Up method. Then in the Down method we would make a call to the previous migration scripts static methods for the view drop and create statements.&lt;/p&gt;
&lt;p&gt;Instead we now create a class per view that will contain all of the versions of the view with methods to get the create statements for the version and the create statement for the previous version. Then in our migration script we can just make a call to the view&amp;rsquo;s class to get the right sql.&lt;/p&gt;
&lt;p&gt;To make this easy to work with, in each view&amp;rsquo;s class we have created:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;VersionNumber&lt;/code&gt; enum to hold each version number. We made the version number that same as the date in each migration scripts file name&lt;/li&gt;
&lt;li&gt;A method called &lt;code&gt;GetDropSql&lt;/code&gt; that returns the view drop statement&lt;/li&gt;
&lt;li&gt;A property for each version number that contains the view create sql for that version&lt;/li&gt;
&lt;li&gt;A method called &lt;code&gt;GetCreateSqlPrevious&lt;/code&gt; that takes in a version number and calls the correct property for the previous version of the view.&lt;/li&gt;
&lt;li&gt;A method called &lt;code&gt;GetCreateSql&lt;/code&gt; that takes in a version number and calls the correct property for that version of the view&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Below is an example of what our view files look like. I create two versions of the view called v20230222 and v20221230.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyApp.Views&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyView&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// View version numbers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;VersionNumber&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;v20230222&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;v20221230&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ViewName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;vw_MyView&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Get the previous view create statement for a version number&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;GetCreateSqlPrevious&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VersionNumber&lt;/span&gt; &lt;span class="n"&gt;versionNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;versionNumber&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;VersionNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v20230222&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v20230222&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;VersionNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v20221230&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ApplicationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unknown Version Number&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Get the view create statement for the version number&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;GetCreateSql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VersionNumber&lt;/span&gt; &lt;span class="n"&gt;versionNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;versionNumber&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;VersionNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v20230222&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v20230222&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;VersionNumber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;v20221230&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;v20221230&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ApplicationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Unknown Version Number&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;GetDropSql&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$@&amp;#34;DROP VIEW IF EXISTS {ViewName}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;v20221230&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$@&amp;#34;CREATE VIEW [dbo].{ViewName} WITH SCHEMABINDING AS ......&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;v20221230&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;$@&amp;#34;CREATE VIEW [dbo].{ViewName} WITH SCHEMABINDING AS ......&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have our view class, in our migration script in the Up and Down method we could use to the &lt;code&gt;migrationBuilder.Sql&lt;/code&gt; method to make the calls into the &lt;code&gt;MyView&lt;/code&gt; class that we created above.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The reason that we drop our views and then create them instead of just altering the views is due to using schema binding on our views to ensure we don&amp;rsquo;t drop a column that they are using and Sql Server not allowing alter with schema binding.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;MyApp.Views&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;MyApp.Migrations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;partial&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;YourMigrationScriptName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Migration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MigrationBuilder&lt;/span&gt; &lt;span class="n"&gt;migrationBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;migrationBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$&amp;#34;exec(&amp;#39;{MyView.GetDropSql().Replace(&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;)}&amp;#39;)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;migrationBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$&amp;#34;exec(&amp;#39;{MyView.GetCreateSql(MyView.VersionNumber.v20230222).Replace(&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;)}&amp;#39;)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Down&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MigrationBuilder&lt;/span&gt; &lt;span class="n"&gt;migrationBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;migrationBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$&amp;#34;exec(&amp;#39;{MyView.GetDropSql().Replace(&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;)}&amp;#39;)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;migrationBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$&amp;#34;exec(&amp;#39;{MyView.GetCreateSqlPrevious(MyView.VersionNumber.v20230222).Replace(&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;)}&amp;#39;)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Having this process allows us to manage our views in the same manner that we do for all of our schema changes to either push changes to our database with the &lt;code&gt;ef database update&lt;/code&gt; command or by creating a sql script from the migrations and then running the script against our database.&lt;/p&gt;</description></item><item><title>Xunit Create Generic Exception Tests</title><link>https://digitaldrummerj.me/xunit-generic-exception-tests/</link><pubDate>Thu, 16 Feb 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/xunit-generic-exception-tests/</guid><category>dotnet</category><category>testing</category><description>&lt;p&gt;Using XUnit, I was creating a bunch of exception test methods that did the same exact test with the only difference being the type of exception being thrown and I thought there has to be a way to use an XUnit theory to only write a single test method and validate the various exception scenarios.&lt;/p&gt;
&lt;p&gt;Luckily, it was really easy to accomplish the goal of having a single test method that validated multiple types of exceptions using an XUnit theory but it took a bit to figure how to accomplish this goal.&lt;/p&gt;
&lt;p&gt;To validate an exception with XUnit you normally make an &lt;code&gt;Assert.Throws&lt;/code&gt; calls and tell the type of exception like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ApplicationException&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Throws&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ApplicationException&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Your_Method_Call&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works great to catch a single exception type. However, what if you needed to test different scenarios in an XUnit theory that throw different exception types.&lt;/p&gt;
&lt;p&gt;One option would be to create a test method for each exception type you need to assert but this creates a good amount of duplicate code.&lt;/p&gt;
&lt;p&gt;Another option would be to make all of your exceptions types the same but that makes your code less obvious and it is not great practice to not throw the proper exception type.&lt;/p&gt;
&lt;p&gt;That leaves us with the best option which is to use one of the overloads of the Asset.Throws method where you can specify the exception type. Since all exceptions inherit for &lt;code&gt;Exception&lt;/code&gt;, we can get the type of exception from the Exception that we pass in through the XUnit Theory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Throws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Your_Method_Call&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now lets look at the complete example where we setup the test data to use in the XUnit Theory &lt;code&gt;MemberData&lt;/code&gt; attribute and then write the XUnit &lt;code&gt;Theory&lt;/code&gt; test method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[]&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ExceptionData&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[]&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// argument1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// argument2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Argument1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Exception Message&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// exceptedException&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;[Theory]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;[MemberData(nameof(ExceptionData))]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;ShouldThrowException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;argument1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;argument2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;expectedException&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Throws&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Your_Method_Call&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotNull&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expectedException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>How to Setup a Development Environment for Companion v3 and ZoomOSC/ZoomISO Module</title><link>https://digitaldrummerj.me/companion-3-dev-environment-for-zoomosc/</link><pubDate>Fri, 03 Feb 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/companion-3-dev-environment-for-zoomosc/</guid><category>companion</category><description>&lt;p&gt;&lt;a href="https://bitfocus.io/companion" target="_blank" &gt;Bitfocus Companion&lt;/a&gt; version 3 is in beta and they have made it way easier to do module development. In this post, we are going to get your computer set up to be able to do Companion module development.&lt;/p&gt;
&lt;p&gt;This post will be broken into 3 parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get Companion Module on Your Computer&lt;/li&gt;
&lt;li&gt;Compile Companion Module&lt;/li&gt;
&lt;li&gt;Use Your Version of Companion Module in Your Companion Instance&lt;/li&gt;
&lt;li&gt;Pull Changes from Module Into Your Copy of the Module&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="part-1-get-companion-module-on-your-computer"&gt;Part 1: Get Companion Module on Your Computer&lt;/h2&gt;
&lt;p&gt;The module we will be using for this post is the &lt;a href="https://GitHub.com/bitfocus/companion-module-zoom-osc-iso" target="_blank" &gt;ZoomOSC / ZoomISO&lt;/a&gt; module.&lt;/p&gt;
&lt;p&gt;The code for all of the Companion modules is hosted on a GitHub repository. In part 1, we will be creating a GitHub account, create a copy of the companion module in your GitHub account and download the companion module code onto your machine.&lt;/p&gt;
&lt;h3 id="create-github-account"&gt;Create GitHub Account&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;If you already have a GitHub account you can skip this step&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is free to get a GitHub account. Go to &lt;a href="https://GitHub.com/signup" target="_blank" &gt;https://GitHub.com/signup&lt;/a&gt; and follow the prompts to create your account&lt;/p&gt;
&lt;p&gt;Now you are ready to set up your GitHub account and download the code&lt;/p&gt;
&lt;h3 id="get-companion-module-code"&gt;Get Companion module code&lt;/h3&gt;
&lt;p&gt;You will need to create a copy of the GitHub repository called a Fork, so that we have our own copy of the module in GitHub.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;From GitHub docs: A fork is a copy of a repository that you manage. Forks let you make changes to a project without affecting the original repository. You can fetch updates from or submit changes to the original repository with pull requests.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to &lt;a href="https://github.com/bitfocus/companion-module-zoom-osc-iso" target="_blank" &gt;https://github.com/bitfocus/companion-module-zoom-osc-iso&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Fork button on the right side of the screen&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/companion/gh-fork.png" alt="Github Create Fork"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once you click the create fork button, it will create your fork and then navigate your to the url for your fork.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="create-companion-module-dev-directory"&gt;Create Companion module dev directory&lt;/h3&gt;
&lt;p&gt;For all of your module development, you will need to create a single directory on your machine that any Companion module that you want to develop will go into&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a directory called companion-module-dev somewhere on your computer&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="get-companion-module-onto-your-computer"&gt;Get Companion module onto your computer&lt;/h3&gt;
&lt;p&gt;Now that we have the directory that will contain our Companion modules, we need to actually get them onto your computer. We will install the Git command line tools so that you can clone (e.g. copy) the module onto your machine.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to &lt;a href="https://git-scm.com/downloads" target="_blank" &gt;https://git-scm.com/downloads&lt;/a&gt; and downlod the Git install for your OS&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the installer for Git and follow the prompts. I just stick with the defaults&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and navigate to the companion-module-dev folder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to set up your Git name and email that will be used if you submit changes to the code&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global user.name &lt;span class="s2"&gt;&amp;#34;Your GitHub Name (e.g. First Name Last Name)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git config --global user.email &lt;span class="s2"&gt;&amp;#34;Your GitHub email&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the companion-module-dev folder run the following command to download the source code&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone &lt;span class="o"&gt;[&lt;/span&gt;Your GitHub Fork Url&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;for example, my GitHub fork is &lt;a href="https://github.com/digitaldrummerj/companion-module-zoom-osc-iso" target="_blank" &gt;https://github.com/digitaldrummerj/companion-module-zoom-osc-iso&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The git clone created a directory in the companion-module-dev folder called companion-module-zoom-osc-iso&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the terminal, go into the companion-module-zoom-osc-iso driectory&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the terminal within the companion-module-zoom-osc-iso directory run the following command to add a reference back to the original repository so that later on you can easily pull in code changes to the module&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git remote add upstream https://github.com/bitfocus/companion-module-zoom-osc-iso
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="part-2-development-environment-setup"&gt;Part 2: Development Environment Setup&lt;/h2&gt;
&lt;p&gt;Now that we have a copy of the module code on GitHub and on our computer, we need to set up the development environment for Companion.&lt;/p&gt;
&lt;h3 id="node-install"&gt;Node Install&lt;/h3&gt;
&lt;p&gt;The first thing we need to do is install Node 18.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to &lt;a href="https://github.com/Schniz/fnm#installation" target="_blank" &gt;https://github.com/Schniz/fnm#installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Follow the install instructions for your OS&lt;/li&gt;
&lt;li&gt;Follow the &lt;a href="https://github.com/Schniz/fnm#shell-setup" target="_blank" &gt;Shell Setup&lt;/a&gt; steps for your terminal type&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that fnm is installed, we need to tell it which version of node to use&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In a terminal window, install Node by running&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fnm install &lt;span class="m"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tell fnm to use Node 18 that we just installed&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fnm use &lt;span class="m"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tell fnm that Node 18 is our default Node version&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fnm default &lt;span class="m"&gt;18&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lastly, install enable the corepack&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;corepack &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="code-editor-install"&gt;Code Editor Install&lt;/h3&gt;
&lt;p&gt;For editing the companion module code I like to use &lt;a href="https://code.visualstudio.com/download" target="_blank" &gt;Visual Studio Code&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="part-3-use-your-version-of-companion-module-in-your-companion-instance"&gt;Part 3: Use Your Version of Companion Module in Your Companion Instance&lt;/h2&gt;
&lt;h3 id="install-dependencies"&gt;Install Dependencies&lt;/h3&gt;
&lt;p&gt;Before we can use our Companion module in Companion, we need to install the dependencies for the module and compile the module.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Before we can install the module dependencies, we need to install Yarn. In your terminal windows run the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --global yarn
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Note: we are using Yarn 1.0 because that it what Companion is using and we want to stay on the same version as they are using.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now that we got Yarn installed, we can install our module&amp;rsquo;s dependencies by running the following command in your module&amp;rsquo;s directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;yarn install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The module built after the yarn install. If you need to build the module again, you can run the following command&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;yarn dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="install-companion"&gt;Install Companion&lt;/h3&gt;
&lt;p&gt;We are now ready to install Companion v3&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to &lt;a href="https://bitfocus.io/companion" target="_blank" &gt;https://bitfocus.io/companion&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Sign in if you have an account or Sign up if you need to create an account&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After you login, click on the Download link on the left menu or navigate to &lt;a href="https://user.bitfocus.io/download" target="_blank" &gt;https://user.bitfocus.io/download&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/companion/companion-download-link.png" alt="Download Link in Menu"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For v3, there are two options: Beta or Experimental. The Beta is built every 6 hours. The experimental build is the latest and greatest.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/companion/companion-download.png" alt="Download Options"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After you download Companion, run the installer and take all of the defaults.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WARNING: If you have Companion v2 installed on the same machine as you are installing Companion v3 on, you will share the configuration between your v2 and v3 instances but the commands and connections names are not the same between v2 and v3, so you will need a set of buttons for each version.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="start-companion"&gt;Start Companion&lt;/h3&gt;
&lt;p&gt;Now that we have Companion installed, we are ready to tell Companion that we want to use your development modules and then start Companion&lt;/p&gt;
&lt;h3 id="tell-companion-about-your-development-modules"&gt;Tell Companion About Your Development Modules&lt;/h3&gt;
&lt;p&gt;After you launch Companion, follow the steps below to tell Companion about your development modules&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click on the Cog (step 1 in the image)&lt;/li&gt;
&lt;li&gt;Click on the Select for the Developer modules path (step 2 in the image)&lt;/li&gt;
&lt;li&gt;Click Launch GUI (step 3 in the image)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/companion/companion-cog.png" alt="Companion Launch"&gt;&lt;/p&gt;
&lt;h2 id="part-4-pull-changes-from-module-into-your-copy-of-the-module"&gt;Part 4: Pull Changes from Module Into Your Copy of the Module&lt;/h2&gt;
&lt;p&gt;As the module developers make changes, you will want to pull those changes into your forked repository.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and navigate to your module&amp;rsquo;s directory that contains your forked repository&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first thing is to fetch all of the changes from the original repository. Note that commits to the original repository will be stored in a local branch called, upstream/master&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git fetch upstream
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure that you are on your fork&amp;rsquo;s main branch&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git checkout main
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Merge the changes from the upstream/main into your local master branch. This will bring your fork&amp;rsquo;s master branch into sync with the upstream repository without losing your local changes. If you have made any changes that create a conflict, you will obviously have to resolve those before you can complete the merge.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git merge upstream/master
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build the module&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;yarn dev
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Get NDI working with OBS 29 on Apple Silicon</title><link>https://digitaldrummerj.me/obs29-ndi-apple-silicon/</link><pubDate>Sun, 29 Jan 2023 13:05:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/obs29-ndi-apple-silicon/</guid><category>obs</category><description>&lt;p&gt;I just got a Mac Mini M2 and needed to get OBS 29 (latest version) working with NDI but no matter what I did I kept getting the following error everytime I started OBS.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/obs-mac/obs-ndi-error.png" alt="OBS NDI Error on Startup Missing NDI Runtime"&gt;&lt;/p&gt;
&lt;h2 id="initial-install"&gt;Initial Install&lt;/h2&gt;
&lt;p&gt;Initially, I tried the following as this is what the &lt;a href="https://obsproject.com/forum/resources/obs-ndi-newtek-ndi%e2%84%a2-integration-into-obs-studio.528/" target="_blank" &gt;OBS NDI plugin&lt;/a&gt; recommended but it is what led to the error above.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://obsproject.com/download" target="_blank" &gt;OBS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Install the &lt;a href="https://ndi.palakis.fr/runtime/ndi-runtime-4.5.1-macOS.pkg" target="_blank" &gt;OBS NDI 4.5 Runtime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Install the &lt;a href="https://github.com/DDRBoxman/obs-ndi/releases/tag/4.10.1" target="_blank" &gt;obs-ndi-4.10.1-macos-arm64.pkg&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Upon launching OBS, you will get the NDI runtime is missing error&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/obs-mac/obs-ndi-error.png" alt="OBS NDI Error on Startup Missing NDI Runtime"&gt;&lt;/p&gt;
&lt;h2 id="fixing-the-ndi-runtime-missing-error"&gt;Fixing the NDI Runtime Missing Error&lt;/h2&gt;
&lt;p&gt;No matter how many times I download the OBS NDI 4.5 runtime, the error kept popping up.&lt;/p&gt;
&lt;p&gt;To fix the error, I needed to close OBS, install the NDI Tools from NewTek and copy the libndi_advanced.dylib to the /usr/local/lib folder as libndi.4.dylib&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Quit OBS&lt;/li&gt;
&lt;li&gt;Install the &lt;a href="https://www.ndi.tv/tools/" target="_blank" &gt;NDI Tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Reboot your Mac&lt;/li&gt;
&lt;li&gt;Open a terminal and run the following command to copy the libndi lib to the right place&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cp &lt;span class="s2"&gt;&amp;#34;/Applications/NDI Video Monitor.app/Contents/Frameworks/libndi_advanced.dylib&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/usr/local/lib/libndi.4.dylib&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="verify-ndi-is-working"&gt;Verify NDI Is Working&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Open OBS and the NDI Runtime missing error should not appear&lt;/li&gt;
&lt;li&gt;Under the tools menu you have the NDI Output settings option&lt;/li&gt;
&lt;li&gt;In a scene you can add an NDI Source&lt;/li&gt;
&lt;/ol&gt;
&lt;div role="alert" class="alert alert-warning"&gt;&lt;strong&gt;Note:&lt;/strong&gt; I also found that when sending NDI Audio to another computer, that the audio had a lot of static on it but only in the right channel. The fix was to go into the OBS Audio setting on the computer sending the NDI output and set the Audio -&amp;gt; General -&amp;gt; Channels to Mono.&lt;/div&gt;</description></item><item><title>How to Completely Uninstall OBS on Mac</title><link>https://digitaldrummerj.me/obs-mac-uninstall/</link><pubDate>Sun, 29 Jan 2023 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/obs-mac-uninstall/</guid><category>obs</category><description>&lt;p&gt;As part of troubleshooting why on my Mac Mini M2 NDI was not workign with OBS, I needed to completely uninstall OBS (application, plugins, configurations, etc) so that there was no traces of OBS on the system and then re-install OBS.&lt;/p&gt;
&lt;h2 id="uninstall-obs-application"&gt;Uninstall OBS Application&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Open finder&lt;/li&gt;
&lt;li&gt;Click on Applications&lt;/li&gt;
&lt;li&gt;Find OBS&lt;/li&gt;
&lt;li&gt;Right click on OBS and click on Move to Trash.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="uninstall-obs-plugins-and-configurations"&gt;Uninstall OBS Plugins and Configurations&lt;/h2&gt;
&lt;p&gt;To uninstall the plugins and configurations, open terminal and run the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/obs-studio
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="install-obs"&gt;Install OBS&lt;/h2&gt;
&lt;p&gt;Now that OBS is completely removed from your system you are ready to install &lt;a href="https://obsproject.com" target="_blank" &gt;OBS&lt;/a&gt; and any &lt;a href="https://obsproject.com/forum/resources/categories/obs-studio-plugins.6/" target="_blank" &gt;OBS plugins&lt;/a&gt; that you use.&lt;/p&gt;</description></item><item><title>EF Core - Configurations That Apply to All Tables</title><link>https://digitaldrummerj.me/ef-core-global-configurations/</link><pubDate>Mon, 06 Jun 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ef-core-global-configurations/</guid><category>aspnet core</category><category>ef core</category><description>&lt;p&gt;In our &lt;a href="/ef-core-split-model-config" &gt;previous post&lt;/a&gt;, we split all of the entity configurations by table into their own configuration mapping file. The next step is to create a base class that all of the configuration mappings inherit from where we can put configurations that all entities should get.&lt;/p&gt;
&lt;p&gt;An example of where we will use the global configuration is the &lt;a href="/ef-core-soft-deletes" &gt;soft deletes&lt;/a&gt; that we implement previously where we want to exclude all of the rows that have the IsDeleted flag set to true but we do not want to have to remember to add that code to all of the entities and we don&amp;rsquo;t want to have to remember to add it to every query.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; This code in this post is based off of the code from the previous post. If you have not gone through the previous post, you can download the code &lt;a href="https://github.com/digitaldrummerj/efcore-examples/tree/feature/4-entity-config-by-table" target="_blank" &gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first thing that we need to do is create our base interface and abstract class that all of our entity maps will inherit from.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the EntityFramework\Maps directory, created a directory named base&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the EntityFramework\Maps\base directory, create a new interface named IEntityMapBase.cs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the IEntityBaseMapBase.cs file add the following code.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Maps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEntityMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEntityTypeConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TEntity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The interface inherits from the EF Core IEntityTypeConfiguration, just like our BlogMap and PostMap did in our previous post.&lt;/p&gt;
&lt;p&gt;Now we need to create our implementation of the interface we just created. We will be making it an abstract class since we do not want to be able to directly instantiate the class and only want to use it as a base class.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the EntityFramework\Maps\Base directory, create a class named EntityMapBase.cs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the EntityMapBase.cs file, we need to override the configuration method&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore.Metadata.Builders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Maps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntityMapBase&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEntityMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TEntity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IEntityBase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityTypeBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next, we are going to update our BlogMap and PostMap to inherit from the EntityMapBase that we just created. Then we will implement the global configuration for the isDeleted query filter.&lt;/p&gt;
&lt;p&gt;In the BlogMap and PostMap, change the base class to inherit from EntityMapBase and call the base configure method like so:&lt;/p&gt;
&lt;p&gt;file: EntityFramework\Maps\BlogMap.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostMap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EntityMapBase&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityTypeBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// existing configuration code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;file: EntityFramework\Maps\PostMap.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostMap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EntityMapBase&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityTypeBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// existing configuration code&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we are ready to move the isDeleted query filter into our EntityMapBase .&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;code&gt;EntityMapBase.Configure&lt;/code&gt; method, add the following code to specify the global filter for isDeleted = false&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasQueryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the BlogMap and PostMap, we can remove the isDeleted query filter as it is in the base class now.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Just like that we now have the ability to specify global entity configurations just by inheriting our entity map from EntityMapBase and within our entity calling base.Configure(builder)&lt;/p&gt;
&lt;p&gt;In our next post in our EF Core series, we are going to take a look at indexes. You have seen some indexes in our code but we are going to dive a bit deeper into index and look at non-unique indexes, unique index, and multiple property indexes.&lt;/p&gt;</description></item><item><title>EF Core - Split Model Configuration Into Files By Table</title><link>https://digitaldrummerj.me/ef-core-split-model-config/</link><pubDate>Wed, 18 May 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ef-core-split-model-config/</guid><category>aspnet core</category><category>ef core</category><description>&lt;p&gt;With EF Core, you configure your entities (e.g. tables) using the OnModelCreating in your database context. It is easy to just put all of the configurations into that OnModelCreating method which for just a few entities works great. However, as the number of entities grows, OnModelCreating easily becomes unwieldy with thousands of lines of configuration code.&lt;/p&gt;
&lt;p&gt;In this post, I am going to show you how you can move the configuration for each of your entities to it&amp;rsquo;s own file and just have to put a single line of code in the OnModelCreating for each entity. This will greatly simplify the management and maintenance of the entity configuration code.&lt;/p&gt;
&lt;h2 id="problem-we-are-solving"&gt;Problem We Are Solving&lt;/h2&gt;
&lt;p&gt;Lets take a look at an example of the OnModelCreation method. As you can see below with just 2 entities, we are already looking at 26 lines of configuration code for just a couple of indexes, a query filter, and an enum based column.&lt;/p&gt;
&lt;p&gt;If we stopped at just 26 lines of code, no big deal but as the number of entities grows so does the amount of configuration code. In fact, one of the projects I am currently working on has over 2,000 lines of configuration code.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;OnModelCreating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelBuilder&lt;/span&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasQueryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsUnique&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasDefaultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Draft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasConversion&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasQueryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsUnique&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Url&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;After we are done with moving the configurations for each entities into their own file, the OnModelCreating method will only contain a single line for each entity that we need to configure. This is way easier to manage.&lt;/p&gt;
&lt;p&gt;Here is what the OnModelCreating will look like after our implementation&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;OnModelCreating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelBuilder&lt;/span&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BlogMap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PostMap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="solution"&gt;Solution&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This post builds on the sample code from our &lt;a href="/ef-core-audit-columns" &gt;previous post&lt;/a&gt;. You can download this code &lt;a href="https://github.com/digitaldrummerj/efcore-examples/tree/feature/2-audit-fields" target="_blank" &gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In order to move the configuration for each entity to its own file, we need to create a class for each entity that implements Microsoft.EntityFrameworkCore.IEntityTypeConfiguration.&lt;/p&gt;
&lt;h3 id="blog-configuration"&gt;Blog Configuration&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create directory Maps&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Maps directory, create the file BlogMap.cs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the BlogMap.cs file&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore.Metadata.Builders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Maps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogMap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEntityTypeConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityTypeBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasQueryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsUnique&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasDefaultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Draft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasConversion&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="post-configuration"&gt;Post Configuration&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the Maps directory, create the file PostMap.cs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the PostMap.cs file&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore.Metadata.Builders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Maps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostMap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEntityTypeConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityTypeBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasQueryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsUnique&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Url&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="database-context-update"&gt;Database Context Update&lt;/h3&gt;
&lt;p&gt;Now we need to update our database context to use the new BlogMap and PostMap classes.&lt;/p&gt;
&lt;p&gt;Open our model context and replace the code within the OnModelCreating method with the following code.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BlogMap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PostMap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We have gone from having lots of entity configuration code in the database context OnModelCreating to have just a few lines. Also, having a config file for each entity, makes it possible to quickly see the entity&amp;rsquo;s configuration.&lt;/p&gt;
&lt;p&gt;We can also enhance our implementation which we are going to do in our &lt;a href="/ef-core-global-configurations" &gt;next post&lt;/a&gt;, to have a base class that all entities inherit from where we can put global configurations and not have to worry about adding them to each entity.&lt;/p&gt;</description></item><item><title>EF Core - Use Enum as Column Value</title><link>https://digitaldrummerj.me/ef-core-enum-as-column-value/</link><pubDate>Sun, 27 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ef-core-enum-as-column-value/</guid><category>aspnet core</category><category>ef core</category><description>&lt;p&gt;When dealing with databases, there are times when you want a column to only allow certain values. When using EF Core, the easiest way to accomplish this goal is to use an enum as the type for the property.&lt;/p&gt;
&lt;p&gt;In this post, we will add a new column to an entity that is of an enum type, set the default value, and create the migration script.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post uses the sample code from our &lt;a href="/ef-core-audit-columns" &gt;previous post&lt;/a&gt;. You can download this code &lt;a href="https://github.com/digitaldrummerj/efcore-examples/tree/feature/2-audit-fields" target="_blank" &gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first thing we need to do is create an enum.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file name Status.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Status.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To the Status.cs add the following code to create our enum&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Draft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Published&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that our enum is created, we need to add the new property to our entity that is of the enum Status.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up our Blog entity and add a new property&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;[Required]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;[MaxLength(25)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we need to tell EF Core how to handle the enum.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up our BlogMap.cs and add the following to the Configure to set the default value and convert it to a string&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasDefaultValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SyncStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Draft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasConversion&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Also, need to add the using for EF Core to the BlogMap.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last thing we need to do is create our migration script to add the new column.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to generate the migration script&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet ef migrations add AddStatusField -o .&lt;span class="se"&gt;\E&lt;/span&gt;ntityFramework&lt;span class="se"&gt;\M&lt;/span&gt;igrations&lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then we need to run the script against our database.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet ef database update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now our application will only allow the enum values to be entered through EF Core for our Status column.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/efcore-examples/tree/feature/3-enum-columns" target="_blank" &gt;See Code for Post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>EF Core - Audit Fields - Track Created By and Last Updated By Values Automatically</title><link>https://digitaldrummerj.me/ef-core-audit-columns/</link><pubDate>Sat, 26 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ef-core-audit-columns/</guid><category>aspnet core</category><category>ef core</category><description>&lt;p&gt;In several applications I work on, in addition to the soft deletes that we implemented in the &lt;a href="/ef-core-soft-deletes" &gt;previous post&lt;/a&gt;, we also have a requirement to implement audit tracking. Audit tracking is the tracking of who created the record, the date the record was created, who was the last person to update the record, and the last updated date of the record.&lt;/p&gt;
&lt;p&gt;In this post, we will implement audit tracking so that the audit fields are automatically added onto our entities and EF Core automatically sets the values when save our entity.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post is designed as a step-by-step walkthrough of the implementation. If you just want to look at the finished code, get it &lt;a href="https://github.com/digitaldrummerj/efcore-examples/tree/feature/2-audit-fields" target="_blank" &gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="example-project"&gt;Example Project&lt;/h2&gt;
&lt;p&gt;In our &lt;a href="/ef-core-soft-deletes" &gt;previous post&lt;/a&gt; on soft deletes, we create a sample project and implemented soft deletes for our entities. In this post, we are going to build on that code from the &lt;a href="/ef-core-soft-deletes" &gt;previous post&lt;/a&gt;. You can either go through the previous post or you can download the finished code from the previous post &lt;a href="https://github.com/digitaldrummerj/efcore-examples/tree/feature/1-soft-deletes" target="_blank" &gt;here&lt;/a&gt; and then check out the branch feature/1-soft-deletes.&lt;/p&gt;
&lt;h2 id="add-audit-fields-to-entities"&gt;Add Audit Fields to Entities&lt;/h2&gt;
&lt;p&gt;Since all of our entities that need to have audit tracking enable are inheriting from EntityBase.cs, we just need to add the audit tracking fields to the IEntityBase.cs and EntityBase.cs.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open IEntityBase.cs and add the following properties&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;CreatedOn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;CreatedBy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;UpdatedOn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UpdatedBy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open EntityBase.cs and implement the following properties&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;CreatedOn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;CreatedBy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;UpdatedOn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UpdatedBy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we need to create a new EF Core migration script for the audit field and update our database to run the migration script to add the audit fields.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up a terminal and navigate to the EntityFrameworkExample project folder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to generate the migration script&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet ef migrations add AddAuditFields -o .&lt;span class="se"&gt;\E&lt;/span&gt;ntityFramework&lt;span class="se"&gt;\M&lt;/span&gt;igrations
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now that we have the migration script, we can run it against the database to update our tables to add the new audit fields&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt; &lt;span class="n"&gt;ef&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="add-current-user-services"&gt;Add Current User Services&lt;/h2&gt;
&lt;p&gt;We need to create a current user session that will get the currently logged in user. For this example, we will be using Windows Authentication but any authentication scheme that is supported by ASP.NET Core should work.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a directory named Authentication&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create an interface named IUserSession.cs in the Authentication directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;IUserSession.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In IUserSession.cs add the following code&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Authentication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IUserSession&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LoginName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsAuthenticated&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we need to implement the IUserSession&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a class named UserSession.cs in the Authentication directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;UserSession.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To the UserSession.cs add the following code to implement the IUserSession.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Authentication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserSession&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IUserSession&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LoginName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsAuthenticated&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next, we are going to create an interface for the current user and implement it so that we can get the currently logged in user from the ASP.NET HttpContext&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file named ICurrentUserService.cs in the Authentication directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ICurrentUserService.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To the ICurrentUserservice.cs add the following code&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Authentication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ICurrentUserService&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IUserSession&lt;/span&gt; &lt;span class="n"&gt;GetCurrentUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we need to implement the ICurrentUserService.cs&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file named CurrentUserService.cs in the Authentication directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;CurrentUserService.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To the CurrentUserService.cs add the following code to get the logged in user information&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Authentication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CurrentUserService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICurrentUserService&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;CurrentUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt; &lt;span class="n"&gt;httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IUserSession&lt;/span&gt; &lt;span class="n"&gt;GetCurrentUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UserSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IUserSession&lt;/span&gt; &lt;span class="n"&gt;currentUser&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UserSession&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IsAuthenticated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAuthenticated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;LoginName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_httpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="add-current-user-to-database-context"&gt;Add Current User To Database Context&lt;/h2&gt;
&lt;p&gt;Now that we have our current user service implemented, we need to pass it into the database context so that when we run add, update and delete operations we can get the current user and set the values of the audit fields.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the EntityFrameworkExampleContext.cs, we need to add the ICurrentUserService to the constructor&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ICurrentUserService&lt;/span&gt; &lt;span class="n"&gt;_currentUserService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EntityFrameworkExampleContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ICurrentUserService&lt;/span&gt; &lt;span class="n"&gt;currentUserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_currentUserService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentUserService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Also, need to add the namespace for the ICurrentUserService&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Authentication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then we need to update all of the calls to &lt;code&gt;ChangeTracker.SetAuditProperties&lt;/code&gt; to pass in the &lt;code&gt;_currentUserService&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ChangeTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetAuditProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_currentUserService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="update-database-context-to-fill-in-audit-fields"&gt;Update Database Context to Fill In Audit Fields&lt;/h2&gt;
&lt;p&gt;For our database context, when a SaveChanges method is called, it uses the SetAuditProperties extension method to look at the records being changed and decides which audit fields to set.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For adding a new record, it sets both the created and last updated fields.&lt;/li&gt;
&lt;li&gt;For update and delete, it only sets the last updated fields.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Replace the code in ChangeTrackerExtension.cs with the following code&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Authentication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore.ChangeTracking&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChangeTrackerExtensions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;SetAuditProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;ChangeTracker&lt;/span&gt; &lt;span class="n"&gt;changeTracker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ICurrentUserService&lt;/span&gt; &lt;span class="n"&gt;currentUserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;changeTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EntityEntry&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;changeTracker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;IEntityBase&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EntityState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deleted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EntityState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Added&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EntityState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Modified&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentUserService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCurrentUser&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;LoginName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityEntry&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IEntityBase&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEntityBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;EntityState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Added&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatedOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreatedBy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedBy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;EntityState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Modified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedBy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;EntityState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deleted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UpdatedBy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EntityState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Modified&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="authentication-configuration"&gt;Authentication Configuration&lt;/h2&gt;
&lt;p&gt;In order to get the current user information, we need to set up ASP.NET Core authentication. Since this is a sample application, we are going to use Windows authentication but you can use any authentication that ties into ASP.NET Core Authentication scheme.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add the Microsoft.AspNetCore.Authentication.Negotiate package&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add package Microsoft.AspNetCore.Authentication.Negotiate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the Program.cs file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the using statement for the Microsoft.AspNetCore.Authentication.Negotiate package&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Authentication.Negotiate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right after the &lt;code&gt;builder.Services&lt;/code&gt; call, add the call to AddAuthentication and AddAuthorization&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddAuthentication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NegotiateDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationScheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddNegotiate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddAuthorization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FallbackPolicy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultPolicy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To our dependency injection, we need to add the IUserSession, ICurrentUserService, and IHttpContextAccessor&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IUserSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserSession&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICurrentUserService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CurrentUserService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IHttpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpContextAccessor&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To the add add UseAuthentication and UseAuthorization&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseAuthentication&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the Blog and Post controllers and add the &lt;code&gt;[Authorize]&lt;/code&gt; attribute to the class to tell ASP.NET Core that it needs to run the authentication for those two controllers.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="add-user-controller"&gt;Add User Controller&lt;/h2&gt;
&lt;p&gt;The last thing we are going to implement is a controller to get our current user information, so that you can make sure that the authentication is working properly.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the controllers directory, create a file named UserController.cs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the UserController.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Authentication&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Authorization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Controllers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Route(&amp;#34;api/[controller]&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;)]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Produces(&amp;#34;application/json&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [ApiController]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Authorize]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ICurrentUserService&lt;/span&gt; &lt;span class="n"&gt;_currentUserService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;UserController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICurrentUserService&lt;/span&gt; &lt;span class="n"&gt;currentUserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_currentUserService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentUserService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [HttpGet]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_currentUserService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCurrentUser&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are now ready to test that the audit tracking works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run the application and go to the Swagger UI (/swagger)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the GET operation for the UserController (/api/user/) and make sure that it returns back your currently logged in user&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the Blog, run the post operation to insert a new Blog. You only need to fill out the Name and Url fields for the post.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;EF Core Advanced Features&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/advancedefcore.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the create Blog, post results, you will see that the created and update fields are automatically populated by our database context&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;EF Core Advanced Features&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/advancedefcore.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;posts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;isDeleted&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;createdOn&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2022-03-26T19:47:59.2757602-07:00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;createdBy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;DESKTOP-RPV8VMV\\digit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;updatedOn&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2022-03-26T19:47:59.2757602-07:00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;updatedBy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;DESKTOP-RPV8VMV\\digit&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you run an update call, you will see that the timestamp has been updated and if you run the API with a different user that the updated by is also updated.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;EF Core Advanced Features&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/advancedefcore.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;posts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;isDeleted&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;createdOn&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2022-03-26T19:47:59.2757602-07:00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;createdBy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;DESKTOP-RPV8VMV\\digit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;updatedOn&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2022-03-26T20:47:59.2757602-07:00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;updatedBy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;DESKTOP-RPV8VMV\\digit&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Whew, you made it. I know it was a lot of steps and code to walk through, but you did it and completed the implementation of EF Core audit tracking. Audit tracking once you know how to implement it is not overly difficult to implement but figuring out all of the moving pieces takes a bit of time. Hopefully, this post has made it easier for you to quickly implement audit tracking.&lt;/p&gt;
&lt;p&gt;If you found this post helpful, please share this post on Twitter and Facebook using the share buttons below.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/efcore-examples/tree/feature/2-audit-fields" target="_blank" &gt;See Code for Post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>EF Core - Implement Soft Delete</title><link>https://digitaldrummerj.me/ef-core-soft-deletes/</link><pubDate>Thu, 24 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ef-core-soft-deletes/</guid><category>aspnet core</category><category>ef core</category><description>&lt;p&gt;In several applications I work on, we have a requirement that we are not allowed to delete any data out of the database physically. Instead, when we need to delete a record, we set an IsDeleted column to true and exclude the row from our queries.&lt;/p&gt;
&lt;p&gt;In this post, we will be implementing soft deletes on an example project.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post is designed as a step-by-step walkthrough of the implementation. If you just want to look at the finished code, get it &lt;a href="https://github.com/digitaldrummerj/efcore-examples/tree/feature/1-soft-deletes" target="_blank" &gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="create-sample-project"&gt;Create Sample Project&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and navigate to where you want to store the example project we are going to create&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run dotnet new to create our WebApi project&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet new webapi -o EntityFrameworkExample
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into the EntityFrameworkExample directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; EntityFrameworkExample
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a .gitignore file using the dotnet-ignore global tool&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet-ignore get -n visualstudio.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;If you do not have the dotnet-ignore global dotnet tool installed, check out my post on how to install the tool, &lt;a href="/dotnet-global-ignore/" &gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the EntityFramework Core Sql Server package&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add package Microsoft.EntityFrameworkCore.SqlServer
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="create-tables"&gt;Create Tables&lt;/h2&gt;
&lt;p&gt;Now that our example project is created, we need to create our data model. We are going to create two tables, Blog and Post.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create directory EntityFramework&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;EntityFramework
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a directory in EntityFramework called Entities&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Entities
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create Blog.cs in EntityFramework\Entities\&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Blog.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the Blog.cs file to create a Blogs table with two columns and a relationship with the Post table.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel.DataAnnotations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel.DataAnnotations.Schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;[Table(&amp;#34;Blogs&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Required]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [MaxLength(255)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Required]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [MaxLength(2048)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [InverseProperty(nameof(Blog))]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Posts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create Post.cs in EntityFramework\Entities directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Post.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to Post.cs to create the Post table with several columns and the relationship to the Blog table.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel.DataAnnotations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel.DataAnnotations.Schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;[Table(&amp;#34;Posts&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Required]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [MaxLength(255)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Required]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [MaxLength(100)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsDraft&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;BlogId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [ForeignKey(nameof(BlogId))]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Blog&lt;/span&gt; &lt;span class="n"&gt;Blog&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="create-context"&gt;Create Context&lt;/h2&gt;
&lt;p&gt;Now that our entities are created, we are ready to create our database context for Entity Framework.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the EntityFramework directory, create a file called EntityFrameworkExampleContext.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;EntityFrameworkExampleContext.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the EntityFrameworkExampleContext.cs file, add the following code to tell EntityFramework about the Blog and Post table.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntityFrameworkExampleContext&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DbContext&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;EntityFrameworkExampleContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptions&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Blog&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="add-context-to-aspnet-core-startup"&gt;Add Context to ASP.NET Core Startup&lt;/h2&gt;
&lt;p&gt;Now that we have our tables and EF Core context file, we are ready to add the context to the ASP.NET Core startup.&lt;/p&gt;
&lt;p&gt;In Program.cs, we need to add the database context to the Services.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add the following namespaces&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the connection string before the call to &lt;code&gt;var app = builder.Build();&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;connectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;(localdb)\mssqllocaldb;Database=EntityFrameworkExample;ConnectRetryCount=0;Trusted_Connection=True;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EntityFrameworkExampleContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;If you have Visual Studio installed, it comes with the localdb.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="using-docker-for-sql-server"&gt;Using Docker for Sql Server&lt;/h3&gt;
&lt;p&gt;If you do not have Visual Studio installed, you can use Docker to run an instance of Visual Studio and then update the connection string to connect to the docker instance.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker pull mcr.microsoft.com/mssql/server:2019-latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --name sql_2019_1436 -e &lt;span class="s2"&gt;&amp;#34;ACCEPT_EULA=Y&amp;#34;&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;#34;SA_PASSWORD=1Secure*Password1&amp;#34;&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;#34;MSSQL_PID=Enterprise&amp;#34;&lt;/span&gt; -p 1436:1433 -d mcr.microsoft.com/mssql/server:2019-latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you are using docker, you will need to update your connection string in Program.cs to point localhost,1436, and the sa username and password.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;connectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Server=localhost,1436;Database=EntityFrameworkExample;ConnectRetryCount=0;User ID=sa;Password=1Secure*Password1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div role="alert" class="alert alert-danger"&gt;In production, do not use sa for your database connection and do not hard code the connection in your Program.cs.&lt;/div&gt;
&lt;h2 id="create-controllers-for-blog-and-post"&gt;Create Controllers for Blog and Post&lt;/h2&gt;
&lt;p&gt;Next, we need to create a controller for Blog and Post to be able to perform CRUD operations. We are going to use the dotnet cli to scaffold the controller.&lt;/p&gt;
&lt;p&gt;We have a few packages to add:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add package Microsoft.EntityFrameworkCore.Design
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add package Microsoft.EntityFrameworkCore.Design
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we need to install the ASP.NET Code Generator global tool.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet tool install -g dotnet-aspnet-codegenerator
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, we are ready to scaffold the Blog controller.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet aspnet-codegenerator controller -name BlogController -async -api -m Blog -dc EntityFrameworkExampleContext -outD ir Controllers
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and the Post controller&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet aspnet-codegenerator controller -name PostController -async -api -m Post -dc EntityFrameworkExampleContext -outDir Controllers
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="create-database"&gt;Create Database&lt;/h2&gt;
&lt;p&gt;Before running our WebApi, we need to create our database and database tables. I will use EntityFramework&amp;rsquo;s migration tool to create scripts that are then applied to our database.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;We need to install the EF Core tools&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet tool install --global dotnet-ef
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To create our database migration script and save it to the EntityFramework\Migrations\ directory, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet ef migrations add InitialCreate -o .&lt;span class="se"&gt;\E&lt;/span&gt;ntityFramework&lt;span class="se"&gt;\M&lt;/span&gt;igrations
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To run the script against our database, we need to call the database update command for dotnet ef.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet ef database update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are now ready to start the project and navigate to the /swagger url and test out the controller. You should see the following Swagger screen.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/efcore/soft-deletes/swagger.png" alt="Swagger UI"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Right now, it does not have soft deletes enabled, but before implementing soft deletes, we want to make sure that the basic API is working.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="add-isdeleted-column-to-tables"&gt;Add IsDeleted Column to Tables&lt;/h2&gt;
&lt;p&gt;To make it easy to make sure that all tables get the IsDeleted field added to them, we are going to create a base class that will contain the IsDeleted and Id columns so that we do not have to remember to add it to each table.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create directory EntityFramework\Entities\Base&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Base
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Base directory, create a file named IEntityBase.cs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;IEntityBase.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The IEntityBase.cs will be an interface that will define our default columns and needs the following code for the Id and IsDeleted columns&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEntityBase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Base directory, create a file named EntityBase.cs which will be used to implement the Id and IsDeleted columns. We will use an abstract class to ensure that we never directly create a new EntityBase class.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;EntityBase.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the EntityBase.cs to implement the Id and IsDeleted column. The Id column will be defined as an auto-incrementing column for Sql Server.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel.DataAnnotations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel.DataAnnotations.Schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntityBase&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEntityBase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that we have our base class for our entities, we need to update Blog and Post to use the base class.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Update the class definition for Blog to inherit from EntityBase&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Blog&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EntityBase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From the Blog class remove the Id definition since it is now included in the base class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update the class definition for Post to inherit from EntityBase&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EntityBase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From the Post class remove the Id definition since it is now included in the base class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We need to create a migration for adding the IsDeleted column to Blog and Post.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet ef migrations add AddIsDeleted -o .&lt;span class="se"&gt;\E&lt;/span&gt;ntityFramework&lt;span class="se"&gt;\M&lt;/span&gt;igrations&lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, we need to apply the migration script to our database&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet ef database update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="update-database-context-to-override-delete-operation"&gt;Update Database Context to Override Delete Operation&lt;/h2&gt;
&lt;p&gt;Now we are ready to change how EF Core runs the delete operations so that it sets the IsDeleted flag to true and leaves the record in the database. We will do this by updating our DbContext to override the save methods to change all delete operations from an entity that inherits from IEntityBase into an update operation that sets the IsDeleted to true.&lt;/p&gt;
&lt;p&gt;Before we update our DbContext file, we are going to create an extension method that will take in an EF Core ChangeTracker and modify the entities that EF Core is going to delete.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the directory, EntityFramework\Extensions&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Extensions
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the file, EntityFramework\Extensions\ChangeTrackerExtension.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ChangeTrackerExtension.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to ChangeTrackerExtension.cs to get all entities that inherit from IEntityBase and are part of the delete operation and then update the entity to have the IsDeleted set to true and make it an update operation instead so that the record stays in the database.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore.ChangeTracking&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChangeTrackerExtensions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;SetAuditProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;ChangeTracker&lt;/span&gt; &lt;span class="n"&gt;changeTracker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;changeTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EntityEntry&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;changeTracker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;IEntityBase&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;EntityState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deleted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityEntry&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;entities&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IEntityBase&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEntityBase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EntityState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Modified&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we are ready to update our DbContext to use the ChangeTracker Extension we just created. We need to override four methods in our DbContext file (EntityFrameworkExampleContext.cs) to call the ChangeTracker before saving the changes.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;SaveChangesAsync (CancellationToken cancellationToken)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ChangeTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetAuditProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SaveChangesAsync (bool acceptAllChangesOnSuccess, CancellationToken cancellationToken)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;acceptAllChangesOnSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ChangeTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetAuditProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveChangesAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acceptAllChangesOnSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SaveChanges&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ChangeTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetAuditProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SaveChanges(bool acceptAllChangesOnSuccess)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;acceptAllChangesOnSuccess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ChangeTracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetAuditProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;acceptAllChangesOnSuccess&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the using statement for the ChangeTrackerExtension to the Context file&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Extensions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are now ready to test that the IsDelete works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run the application and go to the Swagger UI (/swagger)&lt;/li&gt;
&lt;li&gt;On the Blog, run the post operation to insert a new Blog&lt;/li&gt;
&lt;li&gt;Grab the Id value from the Blog&lt;/li&gt;
&lt;li&gt;Run the delete operation for the Blog Id&lt;/li&gt;
&lt;li&gt;Run the get call for the Blogs and notice that the record is still there, but the IsDelete is set to true.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="filter-out-deleted-rows"&gt;Filter Out Deleted Rows&lt;/h2&gt;
&lt;p&gt;At this point, our soft delete is working, but in most cases, we want to filter out records with IsDelete set to true. We could do this by adding a where clause onto all of our queries, but there is a much easier way which is to use an EF Core Global filter for each entity.&lt;/p&gt;
&lt;p&gt;Even though we could add the global filter onto each entity, there is a high likely hood that you will forget to do it at least once. Instead, we can implement an EntityTypeConfiguration that allows us to add default configurations for each entity that inherits from it and configurations specific to that entity like indexes.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the directory EntityFramework\Maps&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Maps
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the directory EntityFramework\Maps\Base&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Base
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the EntityFramework\Maps\Base\IEntityMapBase.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;IEntityMapBase.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the IEntityMapBase.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Maps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEntityMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEntityTypeConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TEntity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the file EntityFramework\Maps\Base\EntityMapBase.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;EntityMapBase.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the EntityMapBase.cs to set the HasQueryFilter property.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore.Metadata.Builders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Maps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EntityMapBase&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEntityMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TEntity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IEntityBase&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityTypeBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TEntity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasQueryFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDeleted&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we are ready to create the EntityMap configuration for our Blog and Post entities.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the file EntityFramework\Maps\BlogMap.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;BlogMap.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the BlogMap.cs to implement the EntityMap. Right now, we are not adding more configurations like indexes, but if you did need to do that, you would add it in the Configure method after the call to the base configures.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore.Metadata.Builders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Maps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BlogMap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EntityMapBase&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityTypeBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Blog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the file EntityFramework\Maps\PostMap.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PostMap.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the PostMap.cs to implement the EntityMap. Right now, we are not adding more configurations like indexes, but if you did need to do that, you would add it in the Configure method after the call to the base configure.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Entities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore.Metadata.Builders&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Maps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostMap&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EntityMapBase&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EntityTypeBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, we need to tell our DbContext (EntityFrameworkExampleContext), about the EntityMap configuration for our Blog and Post entities by overriding the OnModelCreating method&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kd"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;OnModelCreating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelBuild&lt;/span&gt; &lt;span class="n"&gt;er&lt;/span&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BlogMap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApplyConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;PostMap&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the using statement to the DbContext for the BlogMap and PostMap&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;EntityFrameworkExample.Maps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you start up the application and run the get call for Blog, you will see that it no longer returns the record with the IsDelete set to true as it did before. If you need to write a query to return the IsDeleted is true records, on the individual queries, you would add a call to IgnoreQueryFilters() as part of the query.&lt;/p&gt;
&lt;p&gt;Whew, you made it. I know it was a lot of steps and code to walk through, but you did it and completed the implementation of EF Core soft deletes. If you found this post helpful, please share this post on Twitter and Facebook using the share buttons below.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/efcore-examples/tree/feature/1-soft-deletes" target="_blank" &gt;See Code for Post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>ASP.NET Core - AutoMapper - Handle When Property Names Are Not Same Between Objects</title><link>https://digitaldrummerj.me/aspnet-core-automapper-property-names-not-same/</link><pubDate>Wed, 23 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/aspnet-core-automapper-property-names-not-same/</guid><category>aspnet core</category><description>&lt;p&gt;So far in our AutoMapper series all of the property names between the input and output objects have been the same. There are times though that the property names will not match and you will need to tell AutoMapper how to map the properties else the configuration will not be valid.&lt;/p&gt;
&lt;p&gt;In this post, we will take a look at the AutoMapper configuration need to map between property names that do not match.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/aspnet-core-automapper-example/tree/feature/2-automapper-testing" target="_blank" &gt;get code from previous post&lt;/a&gt;. Once you have code downloaded, checkout the branch feature/2-automapper-testing&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before we can look at the AutoMapper configuration, we need to make the property names different between the input (WeatherForecast) and output (WeatherForecastViewModel). In the code below, we are going to update the WeatherForecastViewModel to change the Summary property name to SummaryText.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AutoMapperExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeatherForecastViewModel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TemperatureC&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TemperatureF&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string?&lt;/span&gt; &lt;span class="n"&gt;SummaryText&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that our property names do not match, we need to update our MappingProfile to tell AutoMapper how to map between the differently named properties. Luckily, AutoMapper will still handle all of the property names that are the same, so we only need to tell AutoMapper about the properties that are different.&lt;/p&gt;
&lt;p&gt;To our mapping between WeatherForecast and WeatherForecastViewModel we need to add a call to ForMember and tell it how to map the output to the input property for Summary and SummaryText.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;CreateMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WeatherForecastViewModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ForMember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SummaryText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReverseMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Just like that AutoMapper is able to map Summary to SummaryText. For each property name that do not match, you will need to add a call to ForMember.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/aspnet-core-automapper-example/tree/feature/3-automapper-names-not-same" target="_blank" &gt;See Code for Post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>ASP.NET Core - AutoMapper - Test Mapping Configuration</title><link>https://digitaldrummerj.me/aspnet-core-automapper-testing/</link><pubDate>Tue, 22 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/aspnet-core-automapper-testing/</guid><category>aspnet core</category><description>&lt;p&gt;In our &lt;a href="/aspnet-core-automapper" &gt;previous post&lt;/a&gt;, we used AutoMapper to convert from a entity to a view model. In our example code, it only works as expected in happy path mode where the configuration is valid and there is no way to know the configuration is not valid until we run the application.&lt;/p&gt;
&lt;p&gt;In this post, we are going to look at how with just a few lines of code, we can write a unit test to validate our AutoMapper mapping profile.&lt;/p&gt;
&lt;p&gt;In our working AutoMapper example in the previous post, if we change the WeatherForecastViewModel Summary property name to SummaryText and then run the xunit tests that are going to create in a moment, we will get the following error that lets us know that we have properties that are not mapped between the two objects.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/automapper/automapper-config-test-failed.png" alt="AutoMapper profile config test failure"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you changed the WeatherForecastViewModel Summary property name, go ahead and change it back to Summary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now we are going to create our XUnit project and mapping profile test.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In our project directory, create a new folder called AutoMapperExample.UnitTests&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into AutoMapperExample.UnitTests&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create our XUnit project using the dotnet cli&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet new xunit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a reference to the AutoMapperExample.API project&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add reference ..&lt;span class="se"&gt;\A&lt;/span&gt;utoMapperExample.API&lt;span class="se"&gt;\A&lt;/span&gt;utoMapperExample.csproj
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the project in your code editor&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Delete the file UnitTest1.cs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new file named MappingProfileTests.cs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the MappingProfileTests.cs to load our mapping profile and assets that the configuration is valid&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AutoMapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Xunit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AutoMapperExample.UnitTests&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MappingProfileTests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt; [Fact]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;ValidateMappingConfigurationTest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MapperConfiguration&lt;/span&gt; &lt;span class="n"&gt;mapperConfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MapperConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;MappingProfile&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IMapper&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Mapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mapperConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConfigurationProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AssertConfigurationIsValid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you are able to validate that the AutoMapper mapping profile is valid as part of your testing and automated builds.&lt;/p&gt;
&lt;p&gt;In our &lt;a href="/aspnet-core-automapper-property-names-not-same" &gt;next post&lt;/a&gt; on AutoMapper, we will take a look at what to do when property names are not the same between the input and output.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/aspnet-core-automapper-example/tree/feature/2-automapper-testing" target="_blank" &gt;See Code for Post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>ASP.NET Core - AutoMapper - Easily Convert One Object to Another</title><link>https://digitaldrummerj.me/aspnet-core-automapper/</link><pubDate>Mon, 21 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/aspnet-core-automapper/</guid><category>aspnet core</category><description>&lt;p&gt;&lt;strong&gt;Mapping code is boring. Testing mapping code is even more boring.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AutoMapper is a simple library built to map one object to another and takes out all of the fuss of mapping one object to another.&lt;/p&gt;
&lt;p&gt;In this post, we will create a sample ASP.NET Core API project, install and wire up AutoMapper, and update the sample Weather Forecast Controller to return a view model that was converted from our concrete object to a view model object.&lt;/p&gt;
&lt;h2 id="what-is-automapper"&gt;What is AutoMapper?&lt;/h2&gt;
&lt;p&gt;AutoMapper is an object to object mapper that works by transforming an input object of one type into an output object of a different type.&lt;/p&gt;
&lt;p&gt;What makes AutoMapper interesting is that it provides conventions to take the work out of figuring out how to map type A to type B. As long as type B follows AutoMapper’s established convention, almost zero configuration is needed to map two types.&lt;/p&gt;
&lt;h2 id="why-use-automapper"&gt;Why use AutoMapper?&lt;/h2&gt;
&lt;p&gt;As I said before, mapping code is boring and testing mapping code is even more boring.&lt;/p&gt;
&lt;p&gt;Without AutoMapper, you have to write lots of for loops to transform the input to an output of a different type and then to be able to reverse it back to the original type. You also have to write tests for all of that code and maintain it as you add new properties to the objects.&lt;/p&gt;
&lt;p&gt;Instead of creating all of those mapping loops, AutoMapper provides simple configuration of types, as well as simple testing of mappings. As you will see in the next section, one you set up AutoMapper, it only takes a single line of code to convert between types.&lt;/p&gt;
&lt;h2 id="how-to-use-automapper"&gt;How to use AutoMapper?&lt;/h2&gt;
&lt;p&gt;We are going to create a sample project, install AutoMapper, and then use AutoMapper to convert an object to a view model.&lt;/p&gt;
&lt;h3 id="create-sample-project-and-install-automapper"&gt;Create Sample Project and Install AutoMapper&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;The code and configuration below is written using ASP.NET 6.0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create sample project&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet new webapi -o AutoMapperExample
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go into the AutoMapperExample directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; AutoMapperExample
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You need to install AutoMapper&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add package AutoMapper
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For ASP.NET Core Dependency Injection, you need to install the AutoMapper Dependency Injection package&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="create-view-model"&gt;Create View Model&lt;/h3&gt;
&lt;p&gt;Now that we have our example project and AutoMapper installed, we are going to modify the dotnet webapi WeatherForecast example that came with our example project, to return a WeatherForecastViewModel instead of the concrete WeatherForecast object.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file named WeatherForecastViewModel.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;WeatherForecastViewModel.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the WeatherForecastViewModel.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AutoMapperExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeatherForecastViewModel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TemperatureC&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TemperatureF&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string?&lt;/span&gt; &lt;span class="n"&gt;Summary&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="create-automapper-mapping-configuration"&gt;Create AutoMapper Mapping Configuration&lt;/h3&gt;
&lt;p&gt;Next, we need to tell AutoMapper that we want it to mapper one type to another type and to also be able to reverse the mapping.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file named MappingProfile.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;MappingProfile.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to create a mapping between WeatherForecast to WeatherForecastViewModel and then reverse it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AutoMapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AutoMapperExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MappingProfile&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;MappingProfile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;CreateMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WeatherForecastViewModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReverseMap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="tell-aspnet-core-about-automapper-configuration"&gt;Tell ASP.NET Core About AutoMapper Configuration&lt;/h3&gt;
&lt;p&gt;Now that we have our mapping profile set up, we need to tell ASP.NET about the mapping and add AutoMapper to the DependencyInjection so that any time we request an IMapper it will give us an AutoMapper instance.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the Project.cs file, you will need to add the call to AddAutoMapper&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddAutoMapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MappingProfile&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You will also need to add the namespace to the top of the file to reference the MappingProfile class&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AutoMapperExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="return-viewmodel-from-controller"&gt;Return ViewModel from Controller&lt;/h3&gt;
&lt;p&gt;The last item we need to do is update our WeatherForecastController to return a view model instead of the concrete class.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up the Controllers\WeatherForecastController.cs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We need to add an _mapper class variable and wire it up in the constructor&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecastController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IMapper&lt;/span&gt; &lt;span class="n"&gt;_mapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;WeatherForecastController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecastController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;IMapper&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_mapper&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, we need to update the Get method to return a view model and use AutoMapper to convert the WeatherForecast to WeatherForecastViewModel&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;[HttpGet(Name = &amp;#34;GetWeatherForecast&amp;#34;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecastViewModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;weatherForecasts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;WeatherForecast&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;TemperatureC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;55&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Summary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Summaries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Summaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecastViewModel&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;weatherForecasts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now launch the project with F5 and navigate to /swagger&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the WeatherForecast get method and you will see that it returns the WeatherForecastViewModel and that AutoMapper does all of the mapping work for us and even ignores that fact that there is no Summary property in the ViewModel.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclussion"&gt;Conclussion&lt;/h2&gt;
&lt;p&gt;Now that everything is working, next time you need to create a mapping, you just need to create the view model, add the mapping to our MappingProfile and then call the Map function to convert between objects.&lt;/p&gt;
&lt;p&gt;AutoMapper as configured above only works as expected if our input and output objects have the same property names and the mapping configuration is correct.&lt;/p&gt;
&lt;p&gt;In &lt;a href="/aspnet-core-automapper-testing" &gt;part 2&lt;/a&gt; in the series, we will look at how to write a unit test for our mapping configuration to make sure that profile is correct. Then in &lt;a href="/aspnet-core-automapper-property-names-not-same" &gt;part 3&lt;/a&gt; of this series, we will look at how to configure our mapping when property names do not match between the input and output object.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/aspnet-core-automapper-example/tree/feature/1-automapper-get-started" target="_blank" &gt;See Code for Post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>VS Code - Favorite Extension - Github Pull Request and Issues</title><link>https://digitaldrummerj.me/vscode-github-pull-request-and-issues-extension/</link><pubDate>Sun, 20 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vscode-github-pull-request-and-issues-extension/</guid><category>vscode</category><category>productivity</category><description>&lt;p&gt;In this series, we are looking at some of my favorite VS Code extensions.&lt;/p&gt;
&lt;p&gt;Extensions in VS Code are invaluable to speed up your work and make you more productive.&lt;/p&gt;
&lt;p&gt;In this post, we will look at the GitHub Pull Request and Issues extension that allows you to stay where you are writing your code and manage your GitHub pull requests and issues right from VS Code&lt;/p&gt;
&lt;h2 id="what-is-included"&gt;What is Included&lt;/h2&gt;
&lt;p&gt;This extension provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Listing and browsing PRs and Issues&lt;/li&gt;
&lt;li&gt;Reviewing PRs with in-editor commenting&lt;/li&gt;
&lt;li&gt;Validate PRs with easy checkouts&lt;/li&gt;
&lt;li&gt;“Start working on issue” action creates a branch for you&lt;/li&gt;
&lt;li&gt;Create issues from “todo” comments&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="demo"&gt;Demo&lt;/h2&gt;
&lt;p&gt;Once you have the extension installed a new icon will show up on the sidebar that will show a list of pull requests and issues.&lt;/p&gt;
&lt;figure&gt;&lt;img src="/images/vscode-extensions/gh-pr-issues/demo1.gif"
alt="Pull Request Demo"&gt;&lt;figcaption&gt;
&lt;p&gt;Pull Request Demo&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;&lt;img src="/images/vscode-extensions/gh-pr-issues/demo2.gif"
alt="Create Issue from Todo Comment Demo"&gt;&lt;figcaption&gt;
&lt;p&gt;Create Issue from Todo Comment Demo&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="more-details-and-install"&gt;More Details and Install&lt;/h2&gt;
&lt;p&gt;You can read more details about the usage and install GitHub Pull Request and Issues Extension at the &lt;a href="https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github" target="_blank" &gt;VS Code Market Place&lt;/a&gt;&lt;/p&gt;</description></item><item><title>VS Code - Favorite Extension - Angular 2 Switcher</title><link>https://digitaldrummerj.me/vscode-angular2-switcher-extension/</link><pubDate>Sat, 19 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vscode-angular2-switcher-extension/</guid><category>vscode</category><category>productivity</category><description>&lt;p&gt;In this series, we are looking at some of my favorite VS Code extensions.&lt;/p&gt;
&lt;p&gt;Extensions in VS Code are invaluable to speed up your work and make you more productive.&lt;/p&gt;
&lt;p&gt;In this post, we will look at Angular2 Switcher which provides shortcuts to switch between the TypeScript, StyleSheet, Spec, and Html files for your Angular files.&lt;/p&gt;
&lt;h2 id="why-you-need-angular2-switcher"&gt;Why You Need Angular2 Switcher&lt;/h2&gt;
&lt;p&gt;When coding using Angular, I frequently switch between my template (html), stylesheet (scss), and typescript (.ts). To switch files, I have to open up the sidebar, mousing over to the file, and clicking to open it. However, I find mousing aggravates my shoulder, so anything I can do to avoid mousing is awesome.&lt;/p&gt;
&lt;p&gt;With Angular2 Switcher, you get keyboard shortcuts to switch between the files.&lt;/p&gt;
&lt;h2 id="usage"&gt;Usage&lt;/h2&gt;
&lt;p&gt;Here are the keyboard shortcuts for Windows and Mac.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th style="text-align: center"&gt;Windows&lt;/th&gt;
&lt;th style="text-align: center"&gt;Mac&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;go to &lt;code&gt;.html&lt;/code&gt;&lt;br&gt;&lt;br&gt;if on &lt;code&gt;.html&lt;/code&gt; then go to previous&lt;/td&gt;
&lt;td style="text-align: center"&gt;alt+o&lt;/td&gt;
&lt;td style="text-align: center"&gt;shift+alt+o&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;go to &lt;code&gt;.css&lt;/code&gt;&lt;br&gt;&lt;br&gt;if on &lt;code&gt;.css&lt;/code&gt; then go to previous&lt;/td&gt;
&lt;td style="text-align: center"&gt;alt+i&lt;/td&gt;
&lt;td style="text-align: center"&gt;shift+alt+i&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;go to &lt;code&gt;.ts&lt;/code&gt;&lt;br&gt;&lt;br&gt;if on &lt;code&gt;.ts&lt;/code&gt; then go to previous&lt;/td&gt;
&lt;td style="text-align: center"&gt;alt+u&lt;/td&gt;
&lt;td style="text-align: center"&gt;shift+alt+u&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;go to &lt;code&gt;.spec.ts&lt;/code&gt;&lt;br&gt;&lt;br&gt;if on &lt;code&gt;.spec.ts&lt;/code&gt; then go to previous&lt;/td&gt;
&lt;td style="text-align: center"&gt;alt+p&lt;/td&gt;
&lt;td style="text-align: center"&gt;shift+alt+p&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;If you have a StreamDeck, you can also add buttons for the shortcuts so that you dont have to remember the shortcuts. Yes, I know you are leaving the keyboard to use the StreamDeck, but pushing a button doesn&amp;rsquo;t bother me like mousing does.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="more-details-and-install"&gt;More Details and Install&lt;/h2&gt;
&lt;p&gt;You can read more details about the usage and install Angular2 Switcher at the &lt;a href="https://marketplace.visualstudio.com/items?itemName=infinity1207.angular2-switcher" target="_blank" &gt;VS Code Market Place&lt;/a&gt;&lt;/p&gt;</description></item><item><title>VS Code - Favorite Extension - markdownlint</title><link>https://digitaldrummerj.me/vscode-markdown-lint-extension/</link><pubDate>Fri, 18 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vscode-markdown-lint-extension/</guid><category>vscode</category><category>productivity</category><description>&lt;p&gt;In this series, we are taking a look at some of my favorite VS Code extensions.&lt;/p&gt;
&lt;p&gt;Extension in VS Code are invaluable to speed up your work and make you more productive.&lt;/p&gt;
&lt;p&gt;In this post, we are going to look at Markdown Lint which provides Markdown/CommonMark linting and style checking for markdown files.&lt;/p&gt;
&lt;p&gt;Markdownlint has make my markdown output much more predictable and easier for others dev to follow.&lt;/p&gt;
&lt;h2 id="why-you-need-markdownlint"&gt;Why You Need Markdownlint&lt;/h2&gt;
&lt;p&gt;Markdown is designed to be easy to read, write, and understand which it succeeds at. However, its flexibility is both a benefit and a drawback. There are many possible styles, so formatting can be inconsistent which impacts how your markdown parser is rendering your markdown.&lt;/p&gt;
&lt;h2 id="usage"&gt;Usage&lt;/h2&gt;
&lt;p&gt;Markdownlint includes 50 rules to encourage standards and consistency for Markdown files.&lt;/p&gt;
&lt;p&gt;By default, markdownlint will run as you are typing and any lines that violate one of markdownlint&amp;rsquo;s rules will trigger a warning in the editor. Warnings are indicated by a wavy underline and can also be seen by pressing Ctrl+Shift+M/⇧⌘M to open the Errors and Warnings dialog.&lt;/p&gt;
&lt;h2 id="demo"&gt;Demo&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/vscode-extensions/markdown-lint/demo.png" alt="demo"&gt;&lt;/p&gt;
&lt;h2 id="more-details-and-install"&gt;More Details and Install&lt;/h2&gt;
&lt;p&gt;You can read more details about usage, rules, and install Markdown Link at the &lt;a href="https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint" target="_blank" &gt;VS Code Market Place&lt;/a&gt;&lt;/p&gt;</description></item><item><title>VS Code - Favorite Extension - Path Intellisense</title><link>https://digitaldrummerj.me/vscode-path-intellisense-extension/</link><pubDate>Thu, 17 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vscode-path-intellisense-extension/</guid><category>vscode</category><category>productivity</category><description>&lt;p&gt;In this series, we are taking a look at some of my favorite VS Code extensions.&lt;/p&gt;
&lt;p&gt;Extension in VS Code are invaluable to speed up your work and make you more productive.&lt;/p&gt;
&lt;p&gt;In this post, we are going to look at Path Intellisense which autocompletes file names.&lt;/p&gt;
&lt;p&gt;Having intellisense as you type a path name make it so much easier to quickly import files.&lt;/p&gt;
&lt;h2 id="demo"&gt;Demo&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/vscode-extensions/path-intellisense/demo.gif" alt="demo"&gt;&lt;/p&gt;
&lt;h2 id="more-details-and-install"&gt;More Details and Install&lt;/h2&gt;
&lt;p&gt;You can read more details about usage and install Path Intellisense at the &lt;a href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense" target="_blank" &gt;VS Code Market Place&lt;/a&gt;&lt;/p&gt;</description></item><item><title>VS Code - Favorite Extension - FoldPlus</title><link>https://digitaldrummerj.me/vscode-foldplus-extension/</link><pubDate>Wed, 16 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vscode-foldplus-extension/</guid><category>vscode</category><category>productivity</category><description>&lt;p&gt;In this series, we are taking a look at some of my favorite VS Code extensions.&lt;/p&gt;
&lt;p&gt;Extension in VS Code are invaluable to speed up your work and make you more productive.&lt;/p&gt;
&lt;p&gt;In this post, we are going to look at Fold Plus which enables you to quickly expand or collapse sections within a file.&lt;/p&gt;
&lt;div role="alert" class="alert alert-success"&gt;This is one of my most used extensions.&lt;/div&gt;
&lt;h2 id="what-fold-plus-does"&gt;What Fold Plus Does&lt;/h2&gt;
&lt;p&gt;It is such a great productivity boost to be able to quickly collapse or expand just the sections that I care about in a file using a single command.&lt;/p&gt;
&lt;p&gt;Fold Plus adds several additional commands into VS Code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fold All Same Level as Cursor&lt;/li&gt;
&lt;li&gt;Fold All Same Level as Parent&lt;/li&gt;
&lt;li&gt;Fold All with Selected Text&lt;/li&gt;
&lt;li&gt;Fold All Keep Selected Text&lt;/li&gt;
&lt;li&gt;Fold All Keep Cursor Live&lt;/li&gt;
&lt;li&gt;Fold Parent&lt;/li&gt;
&lt;li&gt;Fold Children&lt;/li&gt;
&lt;li&gt;Unfold All Lines Matching&lt;/li&gt;
&lt;li&gt;Unfold All Same Level as Cursor&lt;/li&gt;
&lt;li&gt;Unfold All with Selected Text&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="demo"&gt;Demo&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/vscode-extensions/foldplus/demo.gif" alt="demo"&gt;&lt;/p&gt;
&lt;h2 id="usage"&gt;Usage&lt;/h2&gt;
&lt;p&gt;To use Fold Plus, pull up the command pallete, type Fold or Unfold and pick the fold command you want.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I use Fold All Same Level as Cursor and Unfold All Same Level as Cursor the most&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="more-details-and-install"&gt;More Details and Install&lt;/h2&gt;
&lt;p&gt;You can read more details about the commands and install Fold Plus at the &lt;a href="https://marketplace.visualstudio.com/items?itemName=dakara.dakara-foldplus" target="_blank" &gt;VS Code Market Place&lt;/a&gt;&lt;/p&gt;</description></item><item><title>VS Code Favorite Extension Series - Add Only</title><link>https://digitaldrummerj.me/vscode-addonly-extension/</link><pubDate>Tue, 15 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vscode-addonly-extension/</guid><category>vscode</category><category>productivity</category><description>&lt;p&gt;Extension in VS Code are invaluable to speed up your work and make you more productive. In this series, we are taking a look at some of my favorite VS Code extensions.&lt;/p&gt;
&lt;p&gt;In this post, we are going to look at Add Only to quickly add or remove a &lt;code&gt;.Only&lt;/code&gt; to your tests.&lt;/p&gt;
&lt;h2 id="usage"&gt;Usage&lt;/h2&gt;
&lt;p&gt;I write and run a lot of UI tests and one of the things I am constantly doing is adding and removing &lt;code&gt;.only&lt;/code&gt; to run sets of tests. The Add Only extensions puts an &amp;ldquo;Add Only&amp;rdquo; or &amp;ldquo;Remove Only&amp;rdquo; option right above the describe, context, test, and it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You need to have CodeLens enabled for this to work&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="demo"&gt;Demo&lt;/h2&gt;
&lt;p&gt;&lt;img src="/images/vscode-extensions/add-only/demo.gif" alt="demo"&gt;&lt;/p&gt;
&lt;h2 id="more-details-and-install"&gt;More Details and Install&lt;/h2&gt;
&lt;p&gt;You can read more details about Add Only and install Add Only at &lt;a href="https://marketplace.visualstudio.com/items?itemName=ub1que.add-only" target="_blank" &gt;VS Code Market Place&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>ASP.NET - Health Checks - Generic Endpoint</title><link>https://digitaldrummerj.me/aspnet-core-health-checks-generic-endpoint/</link><pubDate>Mon, 14 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/aspnet-core-health-checks-generic-endpoint/</guid><category>aspnet core</category><description>&lt;p&gt;In the previous post in the series, we have implemented our health checks that return a json response and allow us to selectively run the health checks based on tags.&lt;/p&gt;
&lt;p&gt;In this post, we are going to build on the previous post and update our endpoint mappings to use a generic endpoint builder so that we only have our filters and json response code in a single place.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you have not implemented the example health check from the previous post, you can &lt;a href="https://github.com/digitaldrummerj/aspnet-core-health-checks-example/tree/feature/3-filter" target="_blank" &gt;download code from previous post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file called HealthCheckExtension.cs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the HealthCheckExtension.cs to create a custom health check endpoint that will map a health check to the url and tag filter provided.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net.Mime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Diagnostics.HealthChecks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Json.Serialization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Diagnostics.HealthChecks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HealthCheckExtensions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;IEndpointConventionBuilder&lt;/span&gt; &lt;span class="n"&gt;MapCustomHealthChecks&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IEndpointRouteBuilder&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;endpointUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;checkName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;tagToFilter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;endpointConventionBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;endpointUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GetFilter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tagToFilter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ResponseWriter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CreateJsonResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;checkName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MediaTypeNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;endpointConventionBuilder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckRegistration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GetFilter&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckRegistration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;filterPredicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;filterPredicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="n"&gt;filterPredicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;filterPredicate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;JsonSerializerOptions&lt;/span&gt; &lt;span class="n"&gt;GetJsonSerializerOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;jsonSerializerOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JsonSerializerOptions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;WriteIndented&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PropertyNamingPolicy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonNamingPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CamelCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DefaultIgnoreCondition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonIgnoreCondition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WhenWritingNull&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;jsonSerializerOptions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;CreateJsonResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthReport&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;checkName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;checkName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Info&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthStatus&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;GetJsonSerializerOptions&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Program.cs file replace the app.UseEndpoints with the following code that wires up the health checks using the MapCustomHealthChecks extensions that we created above&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseEndpoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// runs all health checks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapCustomHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/health&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;All Checks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// runs health checks with tag Example&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapCustomHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/health/example&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Example Checks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Example&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// runs health checks with tag Example2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapCustomHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/health/example2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Example Checks 2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Example2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now our health check endpoints will all follow the same standard and are easier to wire up.&lt;/p&gt;</description></item><item><title>ASP.NET - Selectively Run Health Checks</title><link>https://digitaldrummerj.me/aspnet-core-health-checks-filter/</link><pubDate>Sun, 13 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/aspnet-core-health-checks-filter/</guid><category>aspnet core</category><description>&lt;p&gt;In &lt;a href="/aspnet-core-health-checks" &gt;part 1&lt;/a&gt; we create our basic ASP.NET Core health check and then in &lt;a href="/aspnet-core-health-checks-json" &gt;part 2&lt;/a&gt; we changed from plain text &amp;ldquo;Healthy&amp;rdquo; or &amp;ldquo;Unhealthy&amp;rdquo; to a json response that let us know the stauts of each health check.&lt;/p&gt;
&lt;p&gt;In our previous post, we have only have a single health check so it has been ok to have all health checks run when we hit our health check endpoint. However, if we had multiple health checks we are going to want to be able to run them by themselves as well as run the whole suite.&lt;/p&gt;
&lt;p&gt;In this post, we will are going to add the ability to selectively run our health checks and create endpoints that will run either a set of health checks or all of the health checks.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you have not implemented the example health check from &lt;a href="/aspnet-core-health-checks" &gt;part 1&lt;/a&gt; and &lt;a href="/aspnet-core-health-checks-json" &gt;part 2&lt;/a&gt;, please do so first or &lt;a href="https://github.com/digitaldrummerj/aspnet-core-health-checks-example/tree/feature/2-json-response" target="_blank" &gt;download code from previous post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To demonstrate selectively running of health checks, we need to add a second health check.&lt;/p&gt;
&lt;p&gt;Create second health check called ExampleHealthCheck2.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ExampleHealthCheck2.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Add the following code to the ExampleHealthCheck2.cs file. This is a copy of the ExampleHealthCheck.cs with the messages changed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AspNetCoreHealthCheckExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Diagnostics.HealthChecks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleHealthCheck2Async&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHealthCheck&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Healthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Health Check 2 Msg Here.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Registration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FailureStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Unhealthy Check 2 Msg Here.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The next task we need to do is add tags when we tell ASP.NET Core about our health checks by adding the tags parameter and giving 1 or more tags names.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHealthChecks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleHealthCheckAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Example&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Example&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHealthChecks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleHealthCheck2Async&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Example2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Example2&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The last item we need to do is to create new health check endpoints that only run health checks that match a given tag by using the Predicate parameter.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/health&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ResponseWriter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HealthCheckExtensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteResponse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/health/example&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Example&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ResponseWriter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HealthCheckExtensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteResponse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/health/example2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Predicate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Example2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ResponseWriter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HealthCheckExtensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteResponse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Now when you navigate to the &lt;code&gt;/health&lt;/code&gt; endpoint it will run all health checks and when you navigate to &lt;code&gt;/health/example&lt;/code&gt; and &lt;code&gt;/health/example2&lt;/code&gt; it will only run the health checks that match the tag.&lt;/p&gt;
&lt;p&gt;In our next post in this series, we will update our endpoint mapping to use a generic endpoint that provides filtering and returns json with a single line of code.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/aspnet-core-health-checks-example/tree/feature/3-filter" target="_blank" &gt;Download Code Example&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also read more about ASP.NET Core Health Checks in the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks" target="_blank" &gt;docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>ASP.NET - Health Checks - Generate Better Response Than Just Text</title><link>https://digitaldrummerj.me/aspnet-core-health-checks-json/</link><pubDate>Sat, 12 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/aspnet-core-health-checks-json/</guid><category>aspnet core</category><description>&lt;p&gt;In our &lt;a href="/aspnet-core-health-checks" &gt;previous post&lt;/a&gt;, we added a simple health check to our ASP.NET Core application. Although we only added a single health check, you can add multiple health checks and have multiple run as once. Regardless if you run a single or multiple health checks, the implementation out of the box, just returns a &amp;ldquo;Healthy&amp;rdquo; or &amp;ldquo;Unhealthy&amp;rdquo; string, which is not overall helpful to know which component actually failed.&lt;/p&gt;
&lt;p&gt;In this post, we are going to update our health check response to return a json record that will let us know the status of each health check that is run as well as the overall health status of the application.&lt;/p&gt;
&lt;!-- markdownlint-disable-next-line MD040 --&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{
&amp;#34;status&amp;#34;: &amp;#34;Healthy&amp;#34;,
&amp;#34;duration&amp;#34;: &amp;#34;00:00:00.0066738&amp;#34;,
&amp;#34;info&amp;#34;:
[
{
&amp;#34;key&amp;#34;: &amp;#34;ExampleHealthCheckAsync&amp;#34;,
&amp;#34;description&amp;#34;: &amp;#34;Health Msg Here.&amp;#34;,
&amp;#34;duration&amp;#34;: &amp;#34;00:00:00.0010113&amp;#34;,
&amp;#34;status&amp;#34;: &amp;#34;Healthy&amp;#34;,
&amp;#34;data&amp;#34;: {}
}
]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To update the health check to return json instead of plain text, we do not actually need to update the health check itself. The update will all be in how we configure the health check in Program.cs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you have not implemented the example health check from the &lt;a href="/aspnet-core-health-checks" &gt;previous post&lt;/a&gt;, please do so first or &lt;a href="https://github.com/digitaldrummerj/aspnet-core-health-checks-example/tree/feature/1-plain-text-response" target="_blank" &gt;download code from previous post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="create-custom-health-check-response"&gt;Create Custom Health Check Response&lt;/h2&gt;
&lt;p&gt;To create our custom json response, we are going to create a method that takes in a HealthReport, loops through the report and create a json resppnse.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create file HealthCheckExtensions&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HealthCheckExtensions.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to HealthCheckExtensions.cs to generate our response in JSON&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net.Mime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Diagnostics.HealthChecks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Json.Serialization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HealthCheckExtensions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;WriteResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HealthReport&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;jsonSerializerOptions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;JsonSerializerOptions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;WriteIndented&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;PropertyNamingPolicy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonNamingPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CamelCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;DefaultIgnoreCondition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonIgnoreCondition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WhenWritingNull&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalDuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Info&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HealthStatus&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;jsonSerializerOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContentType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MediaTypeNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="enable-custom-response"&gt;Enable Custom Response&lt;/h2&gt;
&lt;p&gt;To enable the custom response, we need to update Program.cs to enable routing and then use endpoints.&lt;/p&gt;
&lt;p&gt;In Program.cs, remove the following line from our previous setup&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/health&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In Program.cs, add the following lines to use the new HealthCheckExtensions.WriteResponse. If you are using app.UseAuthorization, you need to put the app.UseRouting() above it and the app.UseEndpoints below it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseRouting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// app.UseAuthorization();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseEndpoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endpoints&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;health&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ResponseWriter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HealthCheckExtensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteResponse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now when run your application and go to /health the response we look similar to:&lt;/p&gt;
&lt;!-- markdownlint-disable-next-line MD040 --&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;{
&amp;#34;status&amp;#34;: &amp;#34;Healthy&amp;#34;,
&amp;#34;duration&amp;#34;: &amp;#34;00:00:00.0066738&amp;#34;,
&amp;#34;info&amp;#34;:
[
{
&amp;#34;key&amp;#34;: &amp;#34;ExampleHealthCheckAsync&amp;#34;,
&amp;#34;description&amp;#34;: &amp;#34;Health Msg Here.&amp;#34;,
&amp;#34;duration&amp;#34;: &amp;#34;00:00:00.0010113&amp;#34;,
&amp;#34;status&amp;#34;: &amp;#34;Healthy&amp;#34;,
&amp;#34;data&amp;#34;: {}
}
]
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The returned json is way more informative and helpful than the plain text response. It will become even more helpful when you have multiple health checks running at the same time and need to see the status of each one to figure out the failure.&lt;/p&gt;
&lt;p&gt;Also, right now, when you run the health checks, it will run all of the health checks. We only have a single health check, but as this grows into multiple health checks, it would be nice to be able to run specific health checks. In our &lt;a href="/aspnet-health-checks-filters" &gt;next post&lt;/a&gt;, this is exactly what we will be doing as we create filters to run only specific health checks.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/aspnet-core-health-checks-example/tree/feature/2-json-response" target="_blank" &gt;Download Code Example&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also read more about ASP.NET Core Health Checks in the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks" target="_blank" &gt;docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>ASP.NET Core - Add Health Checks</title><link>https://digitaldrummerj.me/aspnet-core-health-checks/</link><pubDate>Fri, 11 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/aspnet-core-health-checks/</guid><category>aspnet-core</category><description>&lt;p&gt;If your ASP.NET Core application communicates with any 3rd party systems, it is beneficial to have health checks to determine if your connection to the 3rd party system is healthy, degraded, or unhealthy.&lt;/p&gt;
&lt;p&gt;With ASP.NET Core, Microsoft references Microsoft.AspNetCore.Diagnostics.HealthChecks package implicitly for ASP.NET Core apps. This means that everything you need architecture wise is available and you just need to create your actual health check code.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: AspNetCore.Diagnostics.HealthChecks isn&amp;rsquo;t maintained or supported by Microsoft.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="create-health-check"&gt;Create Health Check&lt;/h2&gt;
&lt;p&gt;The first thing we need to do is create our actual health check.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All of the code in this post is using ASP.NET Core 6.0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the file ExampleHealthCheck.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ExampleHealthCheck.cs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the ExampleHealthCheck.cs file&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AspNetCoreHealthCheckExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Diagnostics.HealthChecks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleHealthCheckAsync&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHealthCheck&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;CheckHealthAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HealthCheckContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Healthy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Health Msg Here.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;HealthCheckResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Registration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FailureStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;Unhealth Msg Here.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Line 12-13:&lt;/strong&gt; If healthy, return a status of 200 and text response of &amp;ldquo;Healthy&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Line 17-19:&lt;/strong&gt; If unhealthy, return a status of 503 and text response of &amp;ldquo;Unhealthy&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="tell-aspnet-core-about-health-check"&gt;Tell ASP.NET Core About Health Check&lt;/h2&gt;
&lt;p&gt;In Program.cs, you need to register the health check service before the builder.build() line.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddHealthChecks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddCheck&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ExampleHealthCheckAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Example&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will also need to add the namespace for our health check to the top of Program.cs&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;AspNetCoreHealthCheckExample&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lastly, we need to setup the endpoint url for the health check in Program.cs before the app.Run() line&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-csharp" data-lang="csharp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MapHealthChecks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/health&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, when you run your application (F5) and navigate to /health, you can run your health check, which will return a &amp;ldquo;Healthy&amp;rdquo; text response. If you want to simulate an unhealthy response, just throw an exception into the try block of your health check.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That was pretty easy and quick to add the ability to run a health check into our application. The hard part comes next, which is to write the code to check if our application is healthy or not.&lt;/p&gt;
&lt;p&gt;However, I find that just having a text response of &amp;ldquo;Healthy&amp;rdquo; or &amp;ldquo;Unhealthy&amp;rdquo; is not super helpful. It is incredibly unhelpful when you run multiple health checks at once in which one fails, and you have no idea which one was the &amp;ldquo;Unhealthy&amp;rdquo; one. In our &lt;a href="/aspnet-core-health-checks-json" &gt;next post&lt;/a&gt; in this series, we will update our healthy response to return JSON to see the status of each of the health checks, how long it took to run, and the description of what it is doing.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Healthy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;00:00:00.0066738&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;info&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ExampleHealthCheckAsync&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Health Msg Here.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;00:00:00.0010113&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Healthy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="https://github.com/digitaldrummerj/aspnet-core-health-checks-example/tree/feature/1-plain-text-response" target="_blank" &gt;Download Code Example&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also read more about ASP.NET Core Health Checks in the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks" target="_blank" &gt;docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Hugo - Add Google Analytics</title><link>https://digitaldrummerj.me/hugo-google-analytics/</link><pubDate>Thu, 10 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-google-analytics/</guid><category>hugo</category><category>blogging</category><description>&lt;p&gt;It is really helpful to understand how your site is performing with respect to which posts are getting the most views, how long are people staying on your site, where in the world they are viewing your site from and where did your traffic come from.&lt;/p&gt;
&lt;p&gt;To get analytics for this site, I am using Google Analytics which is free.&lt;/p&gt;
&lt;p&gt;In this post, we will enable Google Analytics on our Hugo site.&lt;/p&gt;
&lt;h2 id="create-your-google-analytics-account"&gt;Create Your Google Analytics Account&lt;/h2&gt;
&lt;p&gt;The first thing you need to do is setup your Google Analytics account.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an Analytics account at &lt;a href="https://www.google.com/analytics/" target="_blank" &gt;https://www.google.com/analytics/&lt;/a&gt;
&lt;ol&gt;
&lt;li&gt;To create an account, click Get started today&lt;/li&gt;
&lt;li&gt;If you already have a Google Analytics account, click Sign in to Analytics&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Set up your &amp;ldquo;Property&amp;rdquo;, give it a name, and point it to the URL of your site you plan on tracking&lt;/li&gt;
&lt;li&gt;You should see a measurement id on the property settings. If you do not, check out the &lt;a href="https://support.google.com/analytics/answer/9539598?hl=en&amp;amp;ref_topic=9303319" target="_blank" &gt;Google help doc&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="configure-hugo"&gt;Configure Hugo&lt;/h2&gt;
&lt;p&gt;Thankfully, Hugo has a built-in template for Google Analytics. To use the template, we need to include it in our page and add our measurement id to our config file.&lt;/p&gt;
&lt;h3 id="header"&gt;Header&lt;/h3&gt;
&lt;p&gt;In our header file (e.g. where the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tag is), we need to add the following code to render the Google Analytics code&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-go_html_template" data-lang="go_html_template"&gt;{{ template &amp;#34;_internal/google_analytics.html&amp;#34; . }}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="hugo-config"&gt;Hugo Config&lt;/h3&gt;
&lt;p&gt;In your config.toml file we need to add our Google Analytics measurement id.&lt;/p&gt;
&lt;p&gt;At the top level of your config.toml file, you need to add the following line.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;googleAnalytics&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;G-1234567890&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;G-1234567890&lt;/code&gt; with your Google Analytics measurement id.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once you rebuild your site, everything should work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You will be tracking on all urls since the googleAnalytics value in your config.toml is not empty. To only track on your production url, you can run Hugo with &lt;a href="https://gohugo.io/getting-started/configuration/" target="_blank" &gt;multiple configuration files&lt;/a&gt; and only populate the googleAnalytic value in your production configuration.&lt;/p&gt;</description></item><item><title>Hugo - Set Default Front Matter When Creating Post</title><link>https://digitaldrummerj.me/hugo-default-front-matter/</link><pubDate>Wed, 09 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-default-front-matter/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;Did you know that when you create a new post in Hugo that you can set the default front matter set for you?&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t know either until after I had done over 50 posts where I created all of my posts files and front matter by hand. Whereas I could have had Hugo create the files for me and set default front matter that I wanted.&lt;/p&gt;
&lt;p&gt;In this post, we will look at how you can also set your default front matter for your post.&lt;/p&gt;
&lt;h2 id="my-default-front-matter"&gt;My Default Front Matter&lt;/h2&gt;
&lt;p&gt;For my post, my default front matter is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;categories: [&amp;#34;&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;published: {{ dateFormat &amp;#34;2006-01-02&amp;#34; .Date }}T13:00:00Z
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;draft: true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: &amp;#34;{{ replace .Name &amp;#34;-&amp;#34; &amp;#34; &amp;#34; | title }}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;url: &amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;!--more--&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="create-default-front-matter"&gt;Create Default Front Matter&lt;/h2&gt;
&lt;p&gt;To create our default front matter we need to create an archetypes file for your posts section.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Archetypes files are content templates that contain preconfigured front matter.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the directory layouts\archetypes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the file layouts\archetypes\posts.md&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In your posts.md file, here is my front matter&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;categories: [&amp;#34;&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;published: {{ dateFormat &amp;#34;2006-01-02&amp;#34; .Date }}T13:00:00Z
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;draft: true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: &amp;#34;{{ replace .Name &amp;#34;-&amp;#34; &amp;#34; &amp;#34; | title }}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;url: &amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;!--more--&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="create-new-post"&gt;Create New Post&lt;/h2&gt;
&lt;p&gt;Now that you have your posts archetype template created, the last thing to do is create a new post which you can do by running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo new posts/my-post.md
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Your my-post.md file will look like that following with the date updated for the day you created the file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;categories: [&amp;#34;&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;published: 2022-03-09T13:00:00Z
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;draft: true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: &amp;#34;My Post&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;url: &amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;!--more--&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Git: Delete Merged Local Branches That No Longer Exist with Powershell</title><link>https://digitaldrummerj.me/git-remove-local-merged-branches-powershell/</link><pubDate>Tue, 08 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/git-remove-local-merged-branches-powershell/</guid><category>powershell</category><category>git</category><description>&lt;p&gt;Previously, I wrote about &lt;a href="/git-remove-local-merged-branches/" &gt;how to delete git branches that have been merged and no longer exist on the remote using git bash&lt;/a&gt;. Using git bash worked just fine for this. However, my normal shell is PowerShell and I want to stay in PowerShell.&lt;/p&gt;
&lt;p&gt;In this post, we will look at how to use PowerShell instead to delete your local git branches that have been merged and no longer exist on the remote.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open PowerShell and navigate to your git repository that you want to clean up&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure you are on the main branch&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;checkout&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fetch the latest from the git&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;pull&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;-prune&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;See the list of local git branches&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Delete all local branches that have been merged to main branch&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt; &lt;span class="n"&gt;-vv&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;where &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt; &lt;span class="o"&gt;-match&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;\[origin/.*: gone\]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt; &lt;span class="n"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;StringSplitOptions&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;RemoveEmptyEntries&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Sometimes it may give you an error that the the branch is not fully merged and you will need to change the -d to a -D&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;See the list of local git branches that remain&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Hugo - Dynamically Add Copy Code Snippet Button</title><link>https://digitaldrummerj.me/hugo-add-copy-code-snippet-button/</link><pubDate>Mon, 07 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-add-copy-code-snippet-button/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;I have many posts on this site that include code snippets for the reader to cut and paste into their code. From a usability perspective, instead of always making the user select lines from the code snippet to copy and paste, we can add a copy button to the code snippet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;example without line numbers:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/copy-button/example.png" alt="copy button example"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;example with line numbers:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/copy-button/example-with-line-num.png" alt="copy button example with line numbers"&gt;&lt;/p&gt;
&lt;p&gt;This post will look at how to implement the copy button, as you see in the above images, any time that we detect a code snippet on a post.&lt;/p&gt;
&lt;h2 id="requirements"&gt;Requirements&lt;/h2&gt;
&lt;p&gt;There are a few requirements to make this work.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Automatically add the copy button without having to add specific to a post&lt;/li&gt;
&lt;li&gt;Works with both line numbers on and line numbers off&lt;/li&gt;
&lt;li&gt;Only copy the code and not the line numbers&lt;/li&gt;
&lt;li&gt;Copy button says &amp;ldquo;Copied&amp;rdquo; after clicking on it and then goes back to copy after a few seconds&lt;/li&gt;
&lt;li&gt;Work cross-browser&lt;/li&gt;
&lt;li&gt;Copy button is located within the code snippet section&lt;/li&gt;
&lt;li&gt;The type of code snippet is added to the top left above the code snippet&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To make all of this work, we need to configure the Hugo highlighter, add some styles to the highlighter, write some JavaScript to add the copy button dynamically and style the copy button.&lt;/p&gt;
&lt;h2 id="hugo-highlighter-site-configuration"&gt;Hugo Highlighter Site Configuration&lt;/h2&gt;
&lt;p&gt;The first thing we will do is configure the Hugo syntax highlighter.&lt;/p&gt;
&lt;h3 id="highlighter-configuration"&gt;Highlighter Configuration&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="hl"&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;markup&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;markup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;anchorLineNos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;codeFences&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;guessSyntax&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hl_Lines&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;lineAnchors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;lineNoStart&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;lineNos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;lineNumbersInTable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;noClasses&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;noHl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;monokailight&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;tabWidth&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Line 10 (lineNumbersInTable): When adding line numbers like this example, use a table instead of inlining the numbers. This is important to be able to copy just the code and not the line number.&lt;/li&gt;
&lt;li&gt;Line 11 (noClasses): Determines if CSS classes are used or not. We can set it to false to indicate that we want to use CSS classes&lt;/li&gt;
&lt;li&gt;Line 13 (style): This is the highlighter color scheme. You can see all of the color schemes &lt;a href="https://xyproto.github.io/splash/docs/all.html" target="_blank" &gt;here&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="highlighter-color-scheme"&gt;Highlighter Color Scheme&lt;/h3&gt;
&lt;p&gt;When you have the &lt;code&gt;noClasses&lt;/code&gt; set to false, you need to create a style for the color schema. In this case, I am using the monokailight color schema.&lt;/p&gt;
&lt;p&gt;To generate the CSS:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open a terminal and navigate to your project&lt;/li&gt;
&lt;li&gt;Run the command below and replace the style with the one you want.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo gen chromastyles --style monokailight assets&lt;span class="se"&gt;\c&lt;/span&gt;ss&lt;span class="se"&gt;\s&lt;/span&gt;yntax.css
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you are using the monokailight style, I found that the background of the code snippet was too dim and that the &lt;code&gt;.err&lt;/code&gt; class was set to red and triggered for random things like the : in https: for links. In my syntax.css file, I changed the following two classes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;/* Background */&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;chroma&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#272822&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#f1f1f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;/* Error */&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;chroma&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;err&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#272822&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#f1f1f1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="add-copy-button-on-fly-to-code-snippet"&gt;Add Copy Button on Fly to Code Snippet&lt;/h2&gt;
&lt;p&gt;Next, we will create the JavaScript code that will add the copy button and copy the code snippet to the clipboard.&lt;/p&gt;
&lt;p&gt;Start by creating the file assets\js\copy-code-button.js&lt;/p&gt;
&lt;p&gt;Once the file is created, we need to add a function to create the button and then find all elements with a &lt;code&gt;.highlight&lt;/code&gt; CSS class and add a copy button to each one.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JavaScript" data-lang="JavaScript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createCopyButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;button&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;copy-code-button&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;button&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Copy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;click&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;copyCodeToClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstChild&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;div&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;highlight-wrapper&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;.highlight&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;createCopyButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The last bit of JavaScript that we need to create is the function to copy the code snippet to the clipboard, change the button to say &amp;ldquo;Copied&amp;rdquo; and then back to Copy after 2 seconds.&lt;/p&gt;
&lt;p&gt;This JavaScript will also work with line numbers as we set the line numbers to be in a table and with our querySelector, we are just grabbing the elements that have the actual code we want to be copied to the clipboard.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We are also working around if the clipboard API not being present in the copyCodeBlockExecCommand function&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-JavaScript" data-lang="JavaScript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;copyCodeToClipboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;codeToCopy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;:last-child &amp;gt; .chroma &amp;gt; code&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;clipboard-write&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;granted&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;prompt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codeToCopy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;copyCodeBlockExecCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codeToCopy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;copyCodeBlockExecCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codeToCopy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Copied!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Copy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;copyCodeBlockExecCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;codeToCopy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textArea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;textArea&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentEditable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readOnly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;false&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;copyable-text-area&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;codeToCopy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertBefore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstChild&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createRange&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectNodeContents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSelection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeAllRanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;sel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setSelectionRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;999999&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;execCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;copy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;highlightDiv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textArea&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="making-the-copy-button-look-pretty"&gt;Making the Copy Button Look Pretty&lt;/h2&gt;
&lt;p&gt;Now let&amp;rsquo;s make our Copy button look pretty and styled as we want it.&lt;/p&gt;
&lt;p&gt;Create the file assets\css\copy-code-button.css and add the following code to it. The CSS Code sets the border, padding, margin, and positioning of the copy button.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;highlight-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;/* Start: Turn off individual column border, margin, and padding when line numbers are showing */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;highlight-wrapper&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;lntd&lt;/span&gt; &lt;span class="nt"&gt;pre&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;chroma&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;lntd&lt;/span&gt; &lt;span class="nt"&gt;pre&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="kc"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#ccc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;chroma&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;lntd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;first-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;chroma&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;lntd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;last-child&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;/* End: Turn off individual column border, margin, and padding when line numbers are showing */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;highlight&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;highlight&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;chroma&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;static&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-top-left-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-top-right-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-bottom-left-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-bottom-right-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;copy-code-button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;-29&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.25&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="kc"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#232326&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-top-left-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-top-right-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-bottom-right-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-bottom-left-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;copy-code-button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;hover&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;copy-code-button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;focus&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;copy-code-button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;active&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;copy-code-button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#222225&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#b3b3b3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;copyable-text-area&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;-1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;.01&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;chroma&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-lang&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;-29&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;black&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-top-left-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-top-right-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-bottom-left-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-bottom-right-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;letter-spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.25&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="kc"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#232326&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="add-css-and-javascript-files-to-site"&gt;Add CSS and JavaScript Files to Site&lt;/h2&gt;
&lt;p&gt;Now that we have configured the Hugo highlighter, created our JavaScript code and CSS for the copy button, we need to add the CSS and JavaScript file to the site.&lt;/p&gt;
&lt;p&gt;The CSS files will go into the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; and the JavaScript files will go at the bottom of the footer.html file. We will also minify them, which is why we put them into the asset folder earlier.&lt;/p&gt;
&lt;p&gt;In your header, add the following lines after your site&amp;rsquo;s CSS import. This will minify your CSS in the Assets folder and add the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; reference to the CSS file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;findRE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;pre&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$syntax&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="na"&gt;.Get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;css/syntax.css&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$syntax&lt;/span&gt;&lt;span class="na"&gt;.RelPermalink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$copyCss&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="na"&gt;.Get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;css/copy-code-button.css&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$copyCss&lt;/span&gt;&lt;span class="na"&gt;.RelPermalink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In your footer, add the following after the rest of your JavaScript code input.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;findRE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;lt;pre&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$jsCopy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="na"&gt;.Get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;js/copy-code-button.js&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$jsCopy&lt;/span&gt;&lt;span class="na"&gt;.RelPermalink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now any time you navigate to a page on your site with a code snippet (e.g. &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tag), a copy button will be added to the code snippet, and the code snippet will be copied to the clipboard.&lt;/p&gt;
&lt;h2 id="inspiration-for-copy-button"&gt;Inspiration for Copy Button&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Inspiration for the copy code button came from the following posts.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.dannyguo.com/blog/how-to-add-copy-to-clipboard-buttons-to-code-blocks-in-hugo/" target="_blank" &gt;https://www.dannyguo.com/blog/how-to-add-copy-to-clipboard-buttons-to-code-blocks-in-hugo/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://aaronluna.dev/blog/add-copy-button-to-code-blocks-hugo-chroma/" target="_blank" &gt;https://aaronluna.dev/blog/add-copy-button-to-code-blocks-hugo-chroma/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Hugo - Add Preview When Sharing to Twitter</title><link>https://digitaldrummerj.me/hugo-preview-when-sharing-twitter/</link><pubDate>Sun, 06 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-preview-when-sharing-twitter/</guid><category>hugo</category><category>blogging</category><description>&lt;p&gt;When you share your blog post on Twitter, you can attach photos, videos and media experiences to Tweets, which helps to drive traffic to your site.&lt;/p&gt;
&lt;p&gt;This is called a Twitter card and looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/twitter-card/example.png" alt="twitter card example"&gt;&lt;/p&gt;
&lt;p&gt;There are two types of Twitter Cards that we are concerned with for our site&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary" target="_blank" &gt;Summary Card&lt;/a&gt;: Title, Description, and Thumbnail (optional)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary-card-with-large-image" target="_blank" &gt;Summary Card with Large Image&lt;/a&gt;: Similar to the Summary Card but with a prominently featured image&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To create the Twitter card, you need a bit of HTML in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your site.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;twitter:card&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;summary&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;twitter:title&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hugo - Add Contact Form Using Formspree&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;twitter:description&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;When creating your blog, it is a good idea to have a contact me form. With static sites though you do not have server side processing available to send you an email with from the contact me form submission. To overcome not having server side processing available, we are going to use Formspress. Formspree is free for up to 50 submissions per month.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;twitter:site&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;@digitaldrummerj&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="summary-card"&gt;Summary Card&lt;/h2&gt;
&lt;p&gt;Luckily, for us, Hugo has an internal template to generate the Twitter card and you just need to include it in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of your site. By default, the Hugo template will create a summary card without any images but in the next section we will go through how you can include images.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;_internal/twitter_cards.html&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you have deployed, your site, you can test the Twitter card using &lt;a href="https://cards-dev.twitter.com/validator" target="_blank" &gt;https://cards-dev.twitter.com/validator&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="summary-card-with-large-image"&gt;Summary Card with Large Image&lt;/h2&gt;
&lt;p&gt;When you include an image in your Twitter card, Hugo will make your Twitter card a large image Twitter card.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Hugo internal template does not support a small summary card with a thumbnail. If you want a summary card with a thumbnail, you can create your own Twitter card template based on the &lt;a href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/twitter_cards.html" target="_blank" &gt;Hugo Twitter card template source code&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The summary card with large image look like:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/twitter-card/example-with-image.png" alt="example with image"&gt;&lt;/p&gt;
&lt;p&gt;In order to include an image, Hugo looks in a number of places to find the image&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First Hugo looks in your post front matter for an images value (&lt;code&gt;images: [&amp;quot;&amp;quot;]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Then Hugo searchs for image page resources with feature, cover or thumbnail in their name. These are images files under a directory that matches the post (e.g. content\posts\first-post)&lt;/li&gt;
&lt;li&gt;If not images are found, Hugo looks for images (&lt;code&gt;images: [&amp;quot;&amp;quot;]&lt;/code&gt;) in your site config&lt;/li&gt;
&lt;li&gt;If no image if found, then an image-less Twitter &lt;code&gt;summary&lt;/code&gt; card is used instead of summary_large_image&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To learn more about Twitter cards, check out their &lt;a href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards" target="_blank" &gt;docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now when you share a post from your site on Twitter, it will give the Twitter Card preview instead of just the link.&lt;/p&gt;</description></item><item><title>Hugo - Add Contact Form Using Formspree</title><link>https://digitaldrummerj.me/hugo-contact-form-formspree/</link><pubDate>Sat, 05 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-contact-form-formspree/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;When creating your blog, it is a good idea to have a contact me form. With static sites though you do not have server side processing available to send you an email with from the contact me form submission.&lt;/p&gt;
&lt;p&gt;To overcome not having server side processing available, we are going to use &lt;a href="https://formspree.io/" target="_blank" &gt;Formspress&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Formspree is free for up to 50 submissions per month.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;First thing you need to do is sign up for a Formspree account at &lt;a href="https://formspree.io/register" target="_blank" &gt;https://formspree.io/register&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you have your account created, you need to create a form at &lt;a href="https://formspree.io/forms" target="_blank" &gt;https://formspree.io/forms&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/contact-form/create-new-form.png" alt="create new form"&gt;&lt;/p&gt;
&lt;p&gt;Once you create the form, Formspree will give you this page that gives you several code options for the form. For my site, I am using the HTML option.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/contact-form/form-info.png" alt="form code options"&gt;&lt;/p&gt;
&lt;p&gt;Now that we have our Formspree form created, we need to create our contact us page.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create content\search.md&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the front matter to define the title and layout&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: &amp;#34;Contact&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;layout: &amp;#34;contact&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Have a question you want answered? Is there something you would like me to write about on the blog? Just use the form below to reach out to me. If you are asking about a particular blog entry, please share the URL of the entry you are asking about.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To create the layout, create the file layouts\page\contact.html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the contact.html page, add the following code to add the contact page content and then replace the comment with the HTML for Formspree.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;main&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.Content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- Formspree Html Here --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="recaptcha-spam-filtering"&gt;reCAPTCHA Spam Filtering&lt;/h2&gt;
&lt;p&gt;reCAPTCHA utilizes machine learning to distinguish between humans and automated &amp;ldquo;bots&amp;rdquo; with just a single click.&lt;/p&gt;
&lt;p&gt;By default, reCAPTCHA is turned off. To turn it on go under the from settings and turn on reCAPTCHA.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/contact-form/reCAPTCHA.png" alt="reCAPTCHA example"&gt;&lt;/p&gt;
&lt;h2 id="honeypot-spam-filtering"&gt;Honeypot Spam filtering&lt;/h2&gt;
&lt;p&gt;Sometimes you can fool automated form scrapers by adding a hidden &amp;ldquo;honeypot&amp;rdquo; input with the name _gotcha. This is an input field that normal visitors won&amp;rsquo;t fill out because it&amp;rsquo;s hidden with CSS. However an algorithm scraping forms might not know to ignore the field, and might fill it with spammy content.&lt;/p&gt;
&lt;p&gt;When Formspree receives a submission with the _gotcha field filled in then Formspree assume a bot submitted the form and they silently ignore the submission.&lt;/p&gt;
&lt;p&gt;Add this input field to your form to turn on Honeypot Spam Filtering&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;_gotcha&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;display:none&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now when you go to /contact and submit the form it will email you the form submission.&lt;/p&gt;</description></item><item><title>Hugo - Create a 404 Page</title><link>https://digitaldrummerj.me/hugo-create-404-page/</link><pubDate>Fri, 04 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-create-404-page/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;Every website should have a 404 page for when a user tries to go to a page that does not exist.&lt;/p&gt;
&lt;p&gt;Hugo out of the box does not provide you with a nice 404 page but we can create a 404 page just like any other page. Creating the page though is the easy part. The hard part is getting the 404 page to display when a user goes to an invalid url as it depends on your website host to set up the 404 page redirect0000.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For this site, I am hosting it using Netlify and they automatically pick up my 404 page.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at how to create our 404 page.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In your layouts directory, create a file called 404.html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next you need to design your 404 page, below is a simple template.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;main&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Page not found&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Go to Home Page&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we are ready to test our 404 page. Unfortunately, Hugo server does not wire up a 404 page. This means that locally you have to view your page as a normal page and then when you deploy to Netlify it you can try to view a bogus page and it will show the 404 page. To view locally navigate to /404.html&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Hugo - Minify JS and CSS</title><link>https://digitaldrummerj.me/hugo-asset-pipeline/</link><pubDate>Thu, 03 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-asset-pipeline/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;When you think about a website&amp;rsquo;s performance, you should minify your CSS and JavaScript file so that they download as quickly as possible. This still holds true even with static sites like Hugo. Luckily, Hugo can minify files out of the box using what Hugo calls &lt;a href="https://gohugo.io/hugo-pipes/introduction/" target="_blank" &gt;Asset Pipeline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this post, I will show you how to take advantage of the Hugo Asset Pipeline to minify your CSS and JavaScript.&lt;/p&gt;
&lt;p&gt;To have Hugo minify your files, you need to put your CSS and JavaScript files that you want to minify in the assets at either the root of your site or the root of your theme.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hugo processes only CSS and JavaScript files in the assets folder while the static folder is just served as is.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means the first thing you need to do is move your CSS and JavaScript files to be minified to the assets\css and assets\js folder, respectively.&lt;/p&gt;
&lt;p&gt;Once you move the files into the assets folder, you can get a reference to them using &lt;code&gt;resources.Get&lt;/code&gt; and passing the location within the assets folder.&lt;/p&gt;
&lt;p&gt;To get a reference to the assets\css\clean-blog.css file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$css&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="na"&gt;.Get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;css/clean-blog.css&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$css&lt;/span&gt;&lt;span class="na"&gt;.RelPermalink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The reference above for clean-blog.css is not minified though. To minify clean-blog.css, we need to pipe it to &lt;code&gt;minify&lt;/code&gt;. Hugo will automatically reference the clean-blog.min.css when you use the RelPermalink&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$css&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="na"&gt;.Get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;css/clean-blog.css&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$css&lt;/span&gt;&lt;span class="na"&gt;.RelPermalink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For JavaScript files, it works the same way as a CSS file does. To minify js/clean-blog.js and get a reference to js/clean-blog.min.js, use the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go-html-template" data-lang="go-html-template"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="na"&gt;.Get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;js/clean-blog.js&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;minify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;$js&lt;/span&gt;&lt;span class="na"&gt;.RelPermalink&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can minify your CSS and JavaScript files any time you want.&lt;/p&gt;</description></item><item><title>Take your Windows Terminal and PowerShell to the next level</title><link>https://digitaldrummerj.me/next-level-powershell-prompt/</link><pubDate>Wed, 02 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/next-level-powershell-prompt/</guid><category>powershell</category><description>&lt;p&gt;I was watching the &lt;a href="https://www.twitch.tv/baldbeardedbuilder" target="_blank" &gt;Bald Bearded Builder, Michael Jolley&lt;/a&gt;, and I had shell envy as I watched him use PowerShell terminal. His terminal showed the git status, node version, folder/file icons, cool color scheme, and predictive completion when typing commands based on his PowerShell history.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/powershell/powershell-prompt-example.png" alt="Michael&amp;rsquo;s PowerShell Terminal"&gt;&lt;/p&gt;
&lt;p&gt;I asked Michael in chat if he would be willing to share his PowerShell profile which he was nice enough to do right then and there on stream.&lt;/p&gt;
&lt;p&gt;However, even with his PowerShell profile in hand, it still took me a bit of time to figure out what I needed to do to take advantage of the profile.&lt;/p&gt;
&lt;p&gt;Let’s walk through the steps I took to get my shell working like Michael&amp;rsquo;s.&lt;/p&gt;
&lt;p&gt;Several items need to be installed for the profile to work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nerd Font - this is the font that provides the icons for folders and files&lt;/li&gt;
&lt;li&gt;Starship - The minimal, blazing-fast, and infinitely customizable prompt for any shell!&lt;/li&gt;
&lt;li&gt;Terminal-Icons - PowerShell module to display the icons from the Nerd Font&lt;/li&gt;
&lt;li&gt;devtoolbox - a PowerShell module with a bunch of useful aliases for dev tools that Michael released&lt;/li&gt;
&lt;li&gt;PSReadLine - a PowerShell module that allows predictive suggestions as you type a command&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="font-install"&gt;Font Install&lt;/h2&gt;
&lt;p&gt;Nerd Fonts patches developer targeted fonts with a high number of glyphs (icons). Specifically to add an increased number of extra glyphs from popular ‘iconic fonts’ such as Font Awesome, Devicons, Octicons, and others.&lt;/p&gt;
&lt;p&gt;I am using the CaskaydiaCove Nerd Font from &lt;a href="https://www.nerdfonts.com/font-downloads" target="_blank" &gt;Nerd Font&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To install:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Windows Terminal as an Administrator&lt;/li&gt;
&lt;li&gt;Create a Powershell tab&lt;/li&gt;
&lt;li&gt;Install the Chocolatey package&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;choco install -y cascadia-code-nerd-font
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;If you are not using Chocolatey, you can install it at &lt;a href="https://chocolatey.org/install" target="_blank" &gt;https://chocolatey.org/install&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;After the font is installed, I needed this registry entry to tell Windows that the font is a TrueTypeFont&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Console\TrueTypeFont&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-ItemProperty&lt;/span&gt; &lt;span class="n"&gt;-Path&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="n"&gt;-Name&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;000&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;-Value&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;CaskaydiaCove Nerd Font&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, we need to tell &lt;a href="https://www.microsoft.com/en-us/p/windows-terminal/9n0dx20hk701?activetab=pivot:overviewtab" target="_blank" &gt;Windows Terminal&lt;/a&gt; to use the CaskaydiaCove Nerd Font that was just installed.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Windows Terminal, Open settings by pressing ctrl+, or clicking on the down arrow in the title bar and select Settings&lt;/li&gt;
&lt;li&gt;On the bottom of the left navbar, click on Open JSON file&lt;/li&gt;
&lt;li&gt;Find the defaults sections under profiles and add the &amp;ldquo;font&amp;rdquo; section below&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;profiles&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;defaults&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Put settings here that you want to apply to all profiles
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;font&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;face&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;CaskaydiaCove Nerd Font&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;weight&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;normal&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="starship"&gt;Starship&lt;/h2&gt;
&lt;p&gt;Next, we need to install &lt;a href="https://starship.rs/" target="_blank" &gt;Starship&lt;/a&gt;, the minimal, blazing-fast, and infinitely customizable prompt for any shell!&lt;/p&gt;
&lt;p&gt;You install Starship using &lt;a href="https://chocolatey.org" target="_blank" &gt;Chocolatey&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;choco install -y starship
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have Starship installed, we need to add it to our PowerShell profile.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open PowerShell&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the profile by typing &lt;code&gt;notepad $profile&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following line at the top of the profile and save it&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Invoke-Expression&lt;/span&gt; &lt;span class="p"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;starship&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;powershell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="terminal-icons"&gt;Terminal-Icons&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/devblackops/Terminal-Icons" target="_blank" &gt;Terminal Icons&lt;/a&gt; is a PowerShell module to show file and folder icons in the terminal.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To install, open PowerShell and run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="nb"&gt;Terminal-Icons&lt;/span&gt; &lt;span class="n"&gt;-Scope&lt;/span&gt; &lt;span class="n"&gt;CurrentUser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To use the Terminal Icons, add the following line to your PowerShell profile&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="nb"&gt;Terminal-Icons&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="devtoolbox"&gt;devtoolbox&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/builders-club/devtoolbox" target="_blank" &gt;devtoolbox&lt;/a&gt; is a set of aliases for common developer command for docker, docker-compose, and git.&lt;/p&gt;
&lt;p&gt;You can install devtoolbox from the &lt;a href="https://www.powershellgallery.com/packages/devtoolbox/" target="_blank" &gt;PowerShell Gallery&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="n"&gt;-Name&lt;/span&gt; &lt;span class="n"&gt;devtoolbox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To use the devtoolbox now that we have installed it, open up your PowerShell profile and add the following statement.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="n"&gt;devtoolbox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="psreadline-predictions"&gt;PSReadLine Predictions&lt;/h2&gt;
&lt;p&gt;For the predictive suggests when typing commands, we need to install PSReadline.&lt;/p&gt;
&lt;p&gt;In a PowerShell administrator shell, run&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Install-Module&lt;/span&gt; &lt;span class="n"&gt;PSReadLine&lt;/span&gt; &lt;span class="n"&gt;-RequiredVersion&lt;/span&gt; &lt;span class="mf"&gt;2.2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;2&lt;/span&gt; &lt;span class="n"&gt;-Force&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Needed to use -Force to have it install PSReadLine 2.2&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To our PowerShell profile, we need to add the following lines:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="n"&gt;PSReadLine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-PredictionSource&lt;/span&gt; &lt;span class="nb"&gt;History
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-PredictionViewStyle&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-EditMode&lt;/span&gt; &lt;span class="n"&gt;Windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="update-function"&gt;update function&lt;/h2&gt;
&lt;p&gt;The update function is designed to make it easy to update powershell to the latest version&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;update&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;iex &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;amp; { &lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;irm &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;aka&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;install-powershell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ps1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; } -UseMSI&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I would suggest that you run the &lt;code&gt;update&lt;/code&gt; command and install the latest cross platform version. Once you get the cross-platform version installed, you need to install the PowerShell modules above and create the profile.&lt;/p&gt;
&lt;h2 id="profile-ended"&gt;profile ended&lt;/h2&gt;
&lt;p&gt;The last piece is to set up the profile function to launch the profile into VSCode quickly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nv"&gt;$profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now your prompt is just like Michael&amp;rsquo;s.&lt;/p&gt;
&lt;h2 id="full-powershell-profile"&gt;Full PowerShell Profile&lt;/h2&gt;
&lt;p&gt;There is a couple of bonus functions like getting a random fact, a dad joke, convert a temp to C, and convert a temp to F.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Originally posted by &lt;a href="https://gist.github.com/MichaelJolley/e65a1a46477a85ecff7f660eee02fa25" target="_blank" &gt;Michael Jolley&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-PowerShell" data-lang="PowerShell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Invoke-Expression&lt;/span&gt; &lt;span class="p"&gt;(&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;starship&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt; &lt;span class="n"&gt;powershell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="nb"&gt;Terminal-Icons&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="n"&gt;devtoolbox&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Import-Module&lt;/span&gt; &lt;span class="n"&gt;PSReadLine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-PredictionSource&lt;/span&gt; &lt;span class="nb"&gt;History
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-PredictionViewStyle&lt;/span&gt; &lt;span class="n"&gt;ListView&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Set-PSReadLineOption&lt;/span&gt; &lt;span class="n"&gt;-EditMode&lt;/span&gt; &lt;span class="n"&gt;Windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;fact&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;irm &lt;/span&gt;&lt;span class="n"&gt;-Uri&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;uselessfacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;jsph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="k"&gt;?&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;en&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Select &lt;/span&gt;&lt;span class="n"&gt;-ExpandProperty&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;joke&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;irm &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;icanhazdadjoke&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;-Headers&lt;/span&gt; &lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;select &lt;/span&gt;&lt;span class="n"&gt;-ExpandProperty&lt;/span&gt; &lt;span class="n"&gt;joke&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;2C&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Temp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$Temperature&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Temperature&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;2F&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Alias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Temp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$Temperature&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Temperature&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;update&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;iex &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;amp; { &lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;irm &lt;/span&gt;&lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;//&lt;/span&gt;&lt;span class="n"&gt;aka&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;install-powershell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ps1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; } -UseMSI&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;profile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nv"&gt;$profile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Hugo - Integrate Google Search</title><link>https://digitaldrummerj.me/hugo-Integrate-google-search/</link><pubDate>Tue, 01 Mar 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-Integrate-google-search/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;As your site content grows, finding content will become harder and you will need to add the ability to search your content.&lt;/p&gt;
&lt;p&gt;You could create your own client search using something like &lt;a href="https://lunrjs.com" target="_blank" &gt;lunr.js&lt;/a&gt; but then you have to maintain the search code and deal with any issues that come up. To me this is a distraction from the real goal of publishing content on your site.&lt;/p&gt;
&lt;p&gt;So instead, I prefer to harness the power of Google to search my site using the handy search argument &lt;code&gt;site:&lt;/code&gt; to tell Google to just search my site for the search term.&lt;/p&gt;
&lt;p&gt;In this post, we are going to create a search page that will take the reader to Google for the search results.&lt;/p&gt;
&lt;h2 id="create-search-layout"&gt;Create Search Layout&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the layouts\search\single.html page&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Define our main in the single.html page&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the single.html page, we need to get a form for our search that has a single input box for our keyword and then have it call a JavaScript function called google_search (we will write that next)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;search&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;onsubmit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;google_search(); return false;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;form-group form-group-lg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;form-control&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;google-search&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Enter search term and hit enter&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the top of the single.html file we need to create the google_search function that will open a new Window that go to Google without keyword search and the site: as part of the search so that it only searches your site. We are getting the baseurl from the config.toml file through the .Site.BaseURL.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Javascript&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;text/javascript&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;google_search&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;google-search&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;https://www.google.com/search?q=&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;+site:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{{ .Site.BaseURL | absLangURL }}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="create-search-page"&gt;Create Search Page&lt;/h3&gt;
&lt;p&gt;The last thing that we need to do is create our search page to use the html that we created above.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the file content\search.md&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following front matter to the search.md file to tell Hugo that this page uses the layout\search\single.html for its layout&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: &amp;#34;Search&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;type: &amp;#34;search&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now when you navigate to /search, type in your search term and press enter it will open a new tab that goes to Google and searches your site.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: To test locally, you will need to use your site&amp;rsquo;s domain url and not localhost as Google ignores the &lt;code&gt;site:&lt;/code&gt; parameter for localhost&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Hugo - Add Additional Non-Blog Pages</title><link>https://digitaldrummerj.me/hugo-add-additional-pages/</link><pubDate>Mon, 28 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-add-additional-pages/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;So far in our &lt;a href="/categories/hugo" &gt;Hugo series&lt;/a&gt; we have only looked only blog related page such as posts and archives but typically you have other pages such as an about, search, or contact us.&lt;/p&gt;
&lt;p&gt;In this post, we are going to take a look at how you can add an about page.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In future post, we will look at the search and contact pages. They follow the same instructions but the layout/content is much different than just a headshot and bio.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;All of our additional pages go in the content folder. The additional pages are written in markdown and have a layout file to control the look and feel of the page.&lt;/p&gt;
&lt;h2 id="create-about-page"&gt;Create About Page&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at how to create our about page (&lt;a href="/about" &gt;see my about page&lt;/a&gt;)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the file &lt;code&gt;content\about.md&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following front matter to set the page title, description and layout to use for the page&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: &amp;#34;About&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Description: &amp;#34;Making the Complex Simple and Easy to Understand!&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;layout: &amp;#34;about&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;**Put Content Some Content About YourSelf On This Page**
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lastly, we need to create the layout page &lt;code&gt;layouts\page\about.html&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For the content of the about.html, this is where you design how you want the page to look. For my page, here is my design where I have a headshot on the left of the content from the about.md file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- Main Content --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-4&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;headshot img-thumbnail&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{.Site.Params.Headshot }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Content }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can now view your about page by going to /about&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Just like that you now have an about page on your site and it is up to you to add it to your site.&lt;/p&gt;
&lt;h2 id="add-about-to-main-menu"&gt;Add About to Main Menu&lt;/h2&gt;
&lt;p&gt;For my site, the menu is defined in the config.toml file so that the menu is a bit more dynamic and I do not have to update the html every time I want to add/remove a menu item. I am also using Bootstrap v3 for the layout.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Home&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;weight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;About&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/about/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;weight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;menu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Posts&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/posts/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;weight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and then in my header file, I use the Bootstrap navbar and loop through the main menu options from the config.toml&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;navbar navbar-default navbar-custom navbar-fixed-top&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container-fluid&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;navbar-header page-scroll&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;button&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;navbar-toggle&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;data-toggle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;collapse&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;data-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;#bs-example-navbar-collapse-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;sr-only&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Toggle navigation&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;icon-bar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;icon-bar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;icon-bar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;navbar-brand&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Site.Title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;collapse navbar-collapse&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;bs-example-navbar-collapse-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;nav navbar-nav navbar-right&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ range .Site.Menus.main }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .URL }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Name }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{end}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Hugo - Create a Post Series</title><link>https://digitaldrummerj.me/hugo-post-series/</link><pubDate>Sun, 27 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-post-series/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;When writing a blog, sometimes you want to write a multiple part posts that are meant to be read in order and you want to display to the reader that the post is part of a series, where the post is in the series and a link to get to the other posts in the series.&lt;/p&gt;
&lt;p&gt;Out of the box, Hugo does not offer the ability to display the list of posts that are part of the series. Luckily, Hugo has all of the pieces to create one yourself, which we are going to do in this post.&lt;/p&gt;
&lt;p&gt;Here is an example of what we are going to build.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/series/example-series.png" alt="example series"&gt;&lt;/p&gt;
&lt;h2 id="create-series-just-the-code"&gt;Create Series (Just the Code)&lt;/h2&gt;
&lt;p&gt;First, we need to create the partial that will generate the series html.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the file layouts\partial\series.html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following html to the file (We will dig into the html a bit in a moment, but I wanted to give you all of the html that creates the series html you upfront).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ if .Params.series }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h4&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;This article is part of a series.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h4&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ range $num,$post := (index .Site.Taxonomies.series (index .Params.series 0 | urlize)).Pages.ByDate }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ if eq $post.Permalink $.Page.Permalink }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group-item active&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Part {{ add $num 1 }}: This Article
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ else }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group-item&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{$post.Permalink}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Part {{ add $num 1 }}: {{ $post.Params.title}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{end}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{end}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{end}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, to use the layouts\partial\series.html file, we need to add it to our layouts\posts\single.html file. In this case, I am adding it before and after the content of the post.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ partial &amp;#34;series.html&amp;#34; . }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ .Content }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ partial &amp;#34;series.html&amp;#34; . }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To show the series list we need to add the &lt;code&gt;series:&lt;/code&gt; parameter to any posts that are part of a series with the name of the series like we did below with the &lt;code&gt;series: ['ASP.NET Core Code Coverage']&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;categories: [&amp;#34;testing&amp;#34;, &amp;#34;dotnet-core&amp;#34;, &amp;#34;teamcity&amp;#34;, &amp;#34;dotcover&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;published: 2022-02-09T13:00:00Z
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: dotCover - How in TeamCity to create multiple coverage reports
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;series: [&amp;#39;ASP.NET Core Code Coverage&amp;#39;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last thing we need to do is tell Hugo about the series by add it to our config.toml file. If you already have other taxonomies defined, just add the series to the list instead of creating a new list.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[taxonomies]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; series = &amp;#34;series&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="create-series-in-depth"&gt;Create Series (In-Depth)&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s walk through the code snippet above for the layouts\partial\series.html file.&lt;/p&gt;
&lt;h3 id="step-1-only-display-if-series"&gt;Step 1: Only Display If Series&lt;/h3&gt;
&lt;p&gt;We first check if the &lt;code&gt;series:&lt;/code&gt; parameter is present on the post.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ if .Params.series }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{end}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-2-get-the-list-of-posts-in-series"&gt;Step 2: Get the List of Posts in Series&lt;/h3&gt;
&lt;p&gt;Next, we need to grab all of the posts in the series.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ range $num,$post := (index .Site.Taxonomies.series (index .Params.series 0 | urlize)).Pages.ByDate }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{end}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s break down the range statement for you.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Since the series could be an array, we want to get the first name of the series and make it url friendly so that we can ultimately get all of the posts in the series.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(index .Params.series 0 | urlize)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, we want to get all of the posts in the series&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(index .Site.Taxonomies.series (index .Params.series 0 | urlize))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, we want to sort the posts by date (I assume that the series are published in order)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(index .Site.Taxonomies.series (index .Params.series 0 | urlize)).Pages.ByDate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lastly, we want to create a range that we can loop through and store the index and post information as variables that we can reference.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ range $num,$post := (index .Site.Taxonomies.series ((index .Params.series 0 | urlize))).Pages.ByDate }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="step-3-display-series-info-and-link"&gt;Step 3: Display Series Info and Link&lt;/h3&gt;
&lt;p&gt;The last we need to do is display the series information.&lt;/p&gt;
&lt;p&gt;To make it clear to the user where the post they are reading is in the series, we check if the post url matches the current page that is being read. If it does match the url, then display &amp;ldquo;Part X: This Article&amp;rdquo; instead of the title. This makes it super clear where the post they are reading is in the series.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: the index is zero-based, so we add a 1 to it when displaying it so that we start at Part 1 and not Part 0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ if eq $post.Permalink $.Page.Permalink }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group-item active&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Part {{ add $num 1 }}: This Article&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ else }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then if the post is not the current post being read, we create a link to the post and display the part number and title of the post.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group-item&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{$post.Permalink}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Part {{ add $num 1 }}: {{ $post.Params.title}}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{end}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can display a list of posts in a series for the reader.&lt;/p&gt;</description></item><item><title>Hugo - Selectively Add Table of Contents to Post</title><link>https://digitaldrummerj.me/hugo-table-of-contents/</link><pubDate>Sat, 26 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-table-of-contents/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;When you are doing a longer tutorial style post, it is helpful to include a table of contents that will show all of the headings in the post and link to that portion of the post. In this post, we will look at how you can selectively add a table of contents to a post.&lt;/p&gt;
&lt;p&gt;Hugo can generate a table of contents for you by including &lt;code&gt;{{ .TableOfContents }}&lt;/code&gt; in your html templates that are in the layouts folder.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: &lt;code&gt;{{ .TableOfContent }}&lt;/code&gt; does not render when just placed in your markdown file for a post&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The main place that you will want a table of contents is when you are viewing a single post. You can either make it the default for all single pages by placing it in the layouts_defaults\single.html or have it only show up when viewing a post by placing it in layouts\posts\single.html&lt;/p&gt;
&lt;p&gt;I only put a table of contents in my posts. To add the table of contents, you need to create the layouts\posts\single.html file and then add the &lt;code&gt;{{ .TableOfContents }}&lt;/code&gt; where you want it to show.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;layouts\posts\single.html example:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ .Content }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;aside&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ .TableOfContents }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;aside&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above code snippet will always put a table of contents at the bottom of the post.&lt;/p&gt;
&lt;p&gt;Suppose you want only to include a table of contents when you specify a parameter. In that case, you need to add the parameter to your post front matter and add an if statement in your html template to only render the table of contents when the parameter is true.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ .Content }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ if .Params.toc }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;aside&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ .TableOfContents }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;aside&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that you have the &lt;code&gt;{{ if .Params.toc }}&lt;/code&gt; added to your html template, you need to add the &lt;code&gt;toc: true&lt;/code&gt; parameter to the front matter of any post that you want a table of content to be displayed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;toc: true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;categories: [&amp;#34;blogging&amp;#34;, &amp;#34;hugo&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;published: 2022-02-26T13:00:00Z
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: &amp;#34;Hugo - Add Table of Contents to Post&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Just like that, you now have a table of contents that will show only when you want.&lt;/p&gt;
&lt;p&gt;For other posts on Hugo, check out my &lt;a href="/categories/hugo/" &gt;Hugo posts archive&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Hugo - Open External Links in a New Tab</title><link>https://digitaldrummerj.me/hugo-links-to-other-pages/</link><pubDate>Fri, 25 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-links-to-other-pages/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;For Hugo based websites, when a link goes to an external website, I prefer to have them open in a new browser tab.&lt;/p&gt;
&lt;p&gt;To accomplish the goal of opening all external links in a new tab, you need to override the Hugo default behavior for rendering links by creating the file &lt;code&gt;layouts/_default/_markup/render-link.html&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In the render-link.html, we need to look at the prefix of the &lt;code&gt;.Destination&lt;/code&gt; to see if it starts with http or https and if it does then add the `target=&amp;quot;_blank&amp;quot;&lt;/p&gt;
&lt;p&gt;Copy this code snippet into the render-link.html.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;a href=&amp;#34;{{ .Destination | safeURL }}&amp;#34;{{ with .Title}} title=&amp;#34;{{ . }}&amp;#34;{{ end }}{{ if or (strings.HasPrefix .Destination &amp;#34;http&amp;#34;) (strings.HasPrefix .Destination &amp;#34;https&amp;#34;) }} target=&amp;#34;_blank&amp;#34;{{ end }} &amp;gt;{{ .Text | safeHTML }}&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now when any link starts with http or https, it will open in a new tab while links within the website will open in the same tab&lt;/p&gt;</description></item><item><title>Hugo - Create Custom Shortcodes</title><link>https://digitaldrummerj.me/hugo-custom-shortcodes/</link><pubDate>Thu, 24 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-custom-shortcodes/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;In our &lt;a href="/hugo-built-in-shortcodes" &gt;previous post&lt;/a&gt;, we took a look at Hugo shortcodes which are HTML snippets that you can use as a way to extend the html that is generated by Hugo. There are several shortcodes built-in to Hugo but you can also easily create your own custom shortcodes. In this post, we are going to take a look at how to create our own shortcode.&lt;/p&gt;
&lt;p&gt;To create a shortcode you need to create a file in thr layouts\shortcodes folder.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Consider the file name carefully since the shortcode name will mirror be that of the file but without the .html extension. For example, layouts/shortcodes/myshortcode.html will be called with either &lt;code&gt;{{&amp;lt; myshortcode &amp;gt;}}&lt;/code&gt; or &lt;code&gt;{{% myshortcode %}}&lt;/code&gt; depending on the type of parameters you choose.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Shortcodes can also take parameters either as positional or named parameters.&lt;/p&gt;
&lt;h2 id="create-our-custom-shortcode"&gt;Create Our Custom Shortcode&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s build a bootstrap alert shortcode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What We Are Building:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/custom-shortcodes/example-alert.png" alt="example alert shortcode"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create Alert Shortcode:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create layouts\shortcodes\alert.html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following to alert.html&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;div
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; class=“alert alert-{{ with .Get “class” }}{{.}}{{end}}”
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; role=&amp;#34;alert&amp;#34;&amp;gt;{{ .Inner | markdownify }}&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The above shortcode uses named parameters to get the class to use (success, info, warning and danger) &lt;code&gt;{{ with .Get &amp;quot;class&amp;quot; }}{{.}}{{ end }}&lt;/code&gt; and then grabs that inner contents of between the opening and closing alert and converts it to HTML with &lt;code&gt;.Inner | markdownify&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTML Generated:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The above shortcode will generate the following HTML.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alert alert-success&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alert&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alert alert-info&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alert&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alert alert-warning&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alert&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alert alert-danger&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alert&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Using Alert Shortcode:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To use the shortcode, in your post markdown file, add the following lines and replace the class value with success, info, warning or danger and then type your text that you want to highlight as the alert between the opening and closing alert.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{&lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nt"&gt;alert&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;info&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Your .Inner text is here
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt; &lt;span class="nt"&gt;alert&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now anytime you want to call out something you can use the alert shortcode to do it.&lt;/p&gt;
&lt;h2 id="create-single-line-shortcode"&gt;Create Single Line Shortcode&lt;/h2&gt;
&lt;p&gt;You can also create shortcodes that are single line shortcodes such as the one that this blog has for
&lt;code style="background-color: #e3fbc9;"&gt;&lt;span style="color: #e3fbc9; filter: grayscale(1) invert(1) contrast(100);"&gt;colour&lt;/span&gt;&lt;/code&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;layouts\colour.html:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $colour := .Get 0 | safeCSS }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $colour2 := .Get 1 | safeHTML }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;background-color: {{ $colour }};&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;color: {{ $colour }}; filter: grayscale(1) invert(1) contrast(100);&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ $colour2 }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Using Colour ShortCode:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{&amp;lt; colour &amp;#34;#e3fbc9&amp;#34; &amp;#34;colour&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="wrap-up"&gt;Wrap Up&lt;/h2&gt;
&lt;p&gt;Shortcodes can be a really powerful way to create reusable bits of HTML that extends what Hugo is able to directly generate from the markdown.&lt;/p&gt;
&lt;p&gt;You can more in-depth about &lt;a href="https://gohugo.io/templates/shortcode-templates/" target="_blank" &gt;Custom Shortcodes&lt;/a&gt; in the Hugo docs.&lt;/p&gt;
&lt;p&gt;In our &lt;a href="/hugo-links-to-other-pages" &gt;next Hugo post&lt;/a&gt;, we are going to take a look at how to have all links that start with http or https open in a new tab without you having to do anything all at.&lt;/p&gt;</description></item><item><title>Hugo - Use Shortcodes Instead of Embedding HTML in Your Markdown</title><link>https://digitaldrummerj.me/hugo-built-in-shortcodes/</link><pubDate>Wed, 23 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-built-in-shortcodes/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;Welcome back to the &lt;a href="http://localhost:1313/categories/hugo/" target="_blank" &gt;Hugo series&lt;/a&gt;. In our last post, we added an RSS feed to our site. In this post, we are going to look at how to extend the output of the HTML that is generated by Hugo for a post.&lt;/p&gt;
&lt;p&gt;When creating a post, there are times where you really wish Hugo could render something fancier than the basic HTML that markdown to html provides. Maybe you want to add a caption to an image or embed a YouTube video or include a gist. You could embed the HTML within your markdown file, but that makes your markdown file messy, harder to read, and a bit of a pain if you need to update the HTML across all your posts.&lt;/p&gt;
&lt;p&gt;This is where Hugo shortcodes come into play. Shortcodes are simply a way of inserting a snippet of HTML into a page from your markdown.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at how to use the built-in shortcodes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; shortcodes only work in markdown file, not template files. If you need the type of functionality that shortcodes provide but in a template, you want a partial template instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For our walk through, we are going to use the YouTube shortcode to embed a responsive video player for YouTube videos. I am going to use &lt;code&gt;https://www.youtube.com/watch?v=wHtu5NQof_Y&lt;/code&gt; for the video.&lt;/p&gt;
&lt;p&gt;For the shortcode, we just need to pass the YouTube video ID that follows v= in the video’s URL to the youtube shortcode&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{&amp;lt; youtube wHtu5NQof_Y &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, for accessibility, you should provide a title for your YouTube video else it will just say &amp;ldquo;YouTube Video&amp;rdquo; which is not helpful to let someone know what the video is about. You can include a title by providing a title parameter in the shortcode.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can’t mix named and unnamed parameters, so you’ll need to assign video id to the parameter id&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{&amp;lt; youtube
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; id=&amp;#34;wHtu5NQof_Y&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; title=&amp;#34;Level up on Functional Programming by Rebuilding LINQ with Cameron Presley&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The video will be rendered as:&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/wHtu5NQof_Y?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="Level up on Functional Programming by Rebuilding LINQ with Cameron Presley"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Here is the HTML that was generated from the shortcode&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;iframe&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://www.youtube.com/embed/wHtu5NQof_Y&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;allowfullscreen&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Level up on Functional Programming by Rebuilding LINQ with Cameron Presley&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;iframe&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, shortcodes are a powerful and easy way to include additional html to enhance your pages while keeping your markdown clean.&lt;/p&gt;
&lt;p&gt;There are several shortcodes that are included in Hugo.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;figure&lt;/li&gt;
&lt;li&gt;highlight&lt;/li&gt;
&lt;li&gt;YouTube&lt;/li&gt;
&lt;li&gt;gist&lt;/li&gt;
&lt;li&gt;Instagram&lt;/li&gt;
&lt;li&gt;tweet&lt;/li&gt;
&lt;li&gt;Vimeo&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See how to use the built-in shortcodes in the &lt;a href="https://gohugo.io/content-management/shortcodes/#use-hugos-built-in-shortcodes" target="_blank" &gt;Hugo Shortcodes Documentation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In our &lt;a href="/hugo-custom-shortcodes" &gt;next post&lt;/a&gt;, we will take a look at how you can create your own shortcodes.&lt;/p&gt;</description></item><item><title>Hugo - Create RSS Feed for Site</title><link>https://digitaldrummerj.me/hugo-create-rss-feed/</link><pubDate>Tue, 22 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-create-rss-feed/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;If you have a Hugo site, you probably already have a working RSS feed. You just might not know about it!&lt;/p&gt;
&lt;p&gt;Hugo comes with a built-in RSS Template that generates the necessary RSS XML for you. Often, this internal template is good enough, and you just need to include the feed links in the correct places.&lt;/p&gt;
&lt;h2 id="how-rss-works"&gt;How RSS works&lt;/h2&gt;
&lt;p&gt;Before, we go into some Hugo RSS specifics, I think it’s important that you know how RSS actually works.&lt;/p&gt;
&lt;p&gt;RSS stands for Really Simple Syndication, and it’s a way to give a programmatic access to the updates on a website. It uses a standardized format that news aggregators can easily read. The feed is simply an XML file that lists the recent content and the feed consumers periodically poll this file to stay up to date with the changes.&lt;/p&gt;
&lt;p&gt;When someone subscribes to your blog feed with an RSS reader they save the feed URL in their reader application.&lt;/p&gt;
&lt;h2 id="add-rss-to-your-hugo-site"&gt;Add RSS to your Hugo site&lt;/h2&gt;
&lt;p&gt;To include your RSS feed link on your Hugo site, add the following code to the header of the site.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alternate&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;application/rss+xml&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{.Site.BaseURL }}/index.xml&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Site.Title }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now a browser can detect it and let the user add it to their list of feeds. This is really handy for the user since they don’t need to copy and paste the URL around.&lt;/p&gt;
&lt;h2 id="configure-rss-feed"&gt;Configure RSS Feed&lt;/h2&gt;
&lt;p&gt;You also can configure Hugo (config.toml) to show a copyright notice and set the Author information.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;copyright&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;2022 Justin James All rights reserved&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;languageCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;en-us&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Justin James&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;me@example.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next, you need to add the following snippet inside the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; tags of theme’s head partial:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;alternate&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;application/rss+xml&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{.Site.BaseURL }}/feed.xml&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Site.Title }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="showing-all-your-content"&gt;Showing all your content&lt;/h2&gt;
&lt;p&gt;The default template includes only the summary of each blog post. I really like reading entire post in my RSS reader if I can. It removes all the bloat of the original website, and just gives me the text I care about.&lt;/p&gt;
&lt;p&gt;We can override the default rss template by copying it our theme&amp;rsquo;s &lt;code&gt;layouts\_default\rss.xml&lt;/code&gt; and editing it.&lt;/p&gt;
&lt;p&gt;You can find the default rss template that ships with Hugo at &lt;a href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml" target="_blank" &gt;https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To then change from the summary view to the full content view, you’d change this line in the default template&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Summary | html }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;to this&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Content | html }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The default Hugo RSS template, also includes all of the pages in your site and not just your blog post. For my RSS feed, I do not want my about, search, speaking or any other page that is not a blog post included in my RSS feed.&lt;/p&gt;
&lt;p&gt;At the top of the Hugo RSS template, is a &lt;code&gt;$pages&lt;/code&gt; variable. To pull just content located in the &lt;code&gt;content\posts&lt;/code&gt; section, you&amp;rsquo;d update these lines in the default template&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- if or $.IsHome $.IsSection -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- $pages = $pctx.RegularPages -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- else -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- $pages = $pctx.Pages -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- end -}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;to this&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- if or $.IsHome $.IsSection -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- $pages = (where (where $pctx.RegularPages &amp;#34;.Section&amp;#34; &amp;#34;posts&amp;#34;) &amp;#34;Kind&amp;#34; &amp;#34;page&amp;#34;) -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- else -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- $pages = (where (where $pctx.Pages &amp;#34;.Section&amp;#34; &amp;#34;posts&amp;#34;) &amp;#34;Kind&amp;#34; &amp;#34;page&amp;#34;) -}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{- end -}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You now have your RSS configured to only show posts and include the entire post. Your RSS feed is now ready for people to start subscribing to the feed. Your RSS feed can be viewed by going to the &lt;code&gt;index.xml&lt;/code&gt; page on your web site.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You also have RSS feeds for each category by going to &lt;code&gt;/categories/[name]/index.xml&lt;/code&gt; where &lt;code&gt;[name]&lt;/code&gt; is the name of the category.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Hugo - View Posts by Tag Cloud</title><link>https://digitaldrummerj.me/hugo-view-post-tag-cloud/</link><pubDate>Mon, 21 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-view-post-tag-cloud/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;In our &lt;a href="/hugo-view-post-grouped-by-category" &gt;previous post&lt;/a&gt; on Hugo, we created a page to view the post grouped by category and a page to view all posts for a category.&lt;/p&gt;
&lt;p&gt;In this post, we are going to build a similar post list page but this time we are going to look at the list using a tag cloud. A tag cloud is a visual representation of our categories using font size and weight to highlight which categories have more posts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;example tag cloud:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/tag-cloud/example-tag-cloud.png" alt="example tag cloud"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;layouts\pages\archivebycloud.html:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This page is a bit more complicated then the previous archive pages that we have built. As the font size for the categories will increase or decrease based on the number of post in the category, we have a bit of math to do.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Under our theme, create the page layouts\pages\archivebycloud.html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the main definition to the page&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Within the define block, add the following code to add the container div, create a row, column, include a back to top link, and the content of the markdown page that will use this page template&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;top&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ .Content }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now after the &lt;code&gt;{{ .Content }}&lt;/code&gt; but still within the div we want to make sure that there is at least 1 category definition before we try to build the tag cloud. We can make this check by making sure that the site taxonomies categories array has more than 0 categories.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ if not (eq (len $.Site.Taxonomies.categories) 0) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Within the if statement we need to define several variables to examine the number of categories, number of posts within a category, and min/max font sizes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $fontUnit := &amp;#34;rem&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $largestFontSize := 3.0 }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $smallestFontSize := 1.5 }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $fontSpread := sub $largestFontSize $smallestFontSize }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $max := add (len (index $.Site.Taxonomies.categories.ByCount 0).Pages) 1 }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $min := len (index $.Site.Taxonomies.categories.ByCount.Reverse 0).Pages }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $spread := sub $max $min }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $fontStep := div $fontSpread $spread }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we are ready to loop through the categories and increase/decrease the font based on number of page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the variables below, we are doing some math:
&lt;ul&gt;
&lt;li&gt;mul = multiple two numbers&lt;/li&gt;
&lt;li&gt;sub = subtract to numbers&lt;/li&gt;
&lt;li&gt;div = divide two numbers&lt;/li&gt;
&lt;li&gt;math.log = returns the natural logarithm of the given number&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tagcloud03&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;cloud&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ range $.Site.Taxonomies.categories.Alphabetical }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ $currentFontSize := (add $smallestFontSize (mul (sub .Count $min) $fontStep) ) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ $weight := div (sub (math.Log .Count) (math.Log $min)) (sub (math.Log $max) (math.Log $min)) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ $currentFontSize := (add $smallestFontSize (mul (sub $largestFontSize $smallestFontSize) $weight) ) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;#{{ .Page.Title | urlize}}&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;font-size:{{$currentFontSize}}{{$fontUnit}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Page.Title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last thing to decide is how we want to get the posts when you click on the category in the tag cloud.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Option #1: change the category link in the above code snippet to link to &lt;code&gt;/categories/{{ .Page.Title | urlize }}&lt;/code&gt; which is the page that we created in our &lt;a href="/hugo-view-post-grouped-by-category" &gt;previous post&lt;/a&gt; to display a summary list of all posts in a category.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Option #2: add to the page a section for each category that shows the posts for it and have the tag cloud go to that section. If you go with option #2 use the code snippet below to display the list.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ range $.Site.Taxonomies.categories.Alphabetical }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Page.Title | urlize }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Page.Title}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;badge&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Count }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group striped-list&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ range .Pages }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group-item&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .RelPermalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ .Title }} {{ if .Params.subheadline }} ({{ .Params.subheadline }}){{ end }} -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .PublishDate.Format &amp;#34;Jan 02, 2006&amp;#34; }}{{ partial &amp;#34;draft&amp;#34; . }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;#top&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;back to top&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;content\archivebycloud.md:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Now we need to create a page that uses the tag cloud layout that we just created.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the file content\archivebytagcloud.md&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the file to set the url to &lt;code&gt;/post/tagcloudview/&lt;/code&gt;, set the page title, set the layout to be the one we just created and add the links to the other archive pages.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;—
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;permalink: “/posts/tagcloudview/“
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: “Posts by Tag Cloud”
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;url: “/posts/tagcloudview/“
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sidebar: true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;layout: “archivebytagcloud”
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;—
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;**Other Views:** [List](/posts/) | [By Month](/posts/monthview) | [By Category](/categories)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;—
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;static\css\clean-blog.min.css:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The last thing we need to do is add the CSS for our tag cloud.&lt;/p&gt;
&lt;p&gt;Under our theme in the static\css\clean-blog.min.css file add the following CSS to the bottom of the file&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;center&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;cloud&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;cloud&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="kc"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text-overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;ellipsis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kp"&gt;-webkit-&lt;/span&gt;&lt;span class="k"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="kt"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="kt"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#4b56a8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="kc"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#0085A1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our tag cloud page is finished and ready for you to view at &lt;code&gt;/posts/tagcloudview&lt;/code&gt; on your site.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Don&amp;rsquo;t forget to update the other archive pages to also have the link to the tag cloud page&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In our next post on Hugo, we are going to set up our RSS feed for your blog like this blog has at &lt;a href="/feed.xml" &gt;/feed.xml&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Hugo - View Posts Grouped by Category</title><link>https://digitaldrummerj.me/hugo-view-post-grouped-by-category/</link><pubDate>Sun, 20 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-view-post-grouped-by-category/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;In our &lt;a href="/hugo-view-post-grouped-by-month" &gt;previous post&lt;/a&gt;, we create a page to view our posts grouped by month. In this post, we are going to create a page to show post grouped by category.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example of What We Are Building:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/category-list/example-page.png" alt="example of page"&gt;&lt;/p&gt;
&lt;p&gt;For the category list, we are going to take advantage of a feature of Hugo called taxonomies which basically allows to create a group that post can belong to.&lt;/p&gt;
&lt;p&gt;For the category taxonomy, Hugo enables this out of the box for us. If you wanted to create other taxonomies, you would need to add them to our Hugo config.toml&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[taxonomies]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; category = “categories”
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With the category taxonomy created, we now need to generate our page. There are the following options are available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;layouts_default\term.html&lt;/strong&gt; : default page that displays the terms (e.g. categories)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;layouts_default\taxonomy.html&lt;/strong&gt; : default page that display all of the posts for a term&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;layouts\categories\term.html&lt;/strong&gt; : override the default term page just for the categories taxonomy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;layouts\categories\taxonomy.html&lt;/strong&gt; : override the default taxonomy.html for the category&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the purpose of this article, we are just going to create the files in the &lt;code&gt;layouts\categories&lt;/code&gt; folder to have a custom page for display the category that overrides that default display of a term.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create the directory in your theme&amp;rsquo;s layout folder called categories&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a file in the &lt;code&gt;layouts\categories&lt;/code&gt; folder called terms.html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following to the &lt;code&gt;terms.html&lt;/code&gt; file to show the list of categories, have each category link to later in the page, and show the number of post per category&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34;}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- Main Content --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;top&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ if not (eq (len $.Data.Terms) 0) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tagcloud03&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ range .Data.Terms.Alphabetical }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;#{{ .Page.Title | urlize}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Page.Title }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;badge&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Count }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ range .Data.Terms.Alphabetical }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Page.Title | urlize }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Page.Title}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;badge&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Count }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group striped-list&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ range .Pages }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group-item&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .RelPermalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ .Title }} {{ if .Params.subheadline }} ({{ .Params.subheadline }}){{ end }} -
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .PublishDate.Format &amp;#34;Jan 02, 2006&amp;#34; }}{{ partial &amp;#34;draft&amp;#34; . }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;#top&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;back to top&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To create the category list summary page for when a category is directly pulled up, you need to add the a file called &lt;code&gt;layouts\categories\taxonomy.html&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To the &lt;code&gt;taxonomy.html&lt;/code&gt; file, add the following code to the page to display the list of categories&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- Main Content --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ range .Pages }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ .Render &amp;#34;summary&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last thing we need to do is add a bit of CSS to style our list the way we want it to be designed.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the css file &lt;code&gt;static\css\clean-blog.min.css&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add the following code to the bottom of the file&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;center&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;cloud&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;cloud&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;/* border: none !important; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;list-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="kc"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#aaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;white-space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nowrap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text-overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;ellipsis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#333&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kp"&gt;-webkit-&lt;/span&gt;&lt;span class="k"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="kt"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="kt"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;tagcloud03&lt;/span&gt; &lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#4b56a8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt; &lt;span class="kc"&gt;solid&lt;/span&gt; &lt;span class="mh"&gt;#0085A1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you have a custom category list and summary page.&lt;/p&gt;
&lt;p&gt;In our &lt;a href="/hugo-view-post-tag-cloud" &gt;next post&lt;/a&gt; on Hugo, we will create a page to look at posts categories in a word cloud.&lt;/p&gt;</description></item><item><title>Hugo - View Posts Grouped By Month</title><link>https://digitaldrummerj.me/hugo-view-post-grouped-by-month/</link><pubDate>Sat, 19 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-view-post-grouped-by-month/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;In our &lt;a href="/hugo-view-all-post" &gt;previous post&lt;/a&gt;, we looked at how to build an archive page to view all of the posts we have written. In this post, we will build an archive page that shows our posts by month.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example of What We Are Building:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/archive-by-date/page.png" alt="example of page"&gt;&lt;/p&gt;
&lt;p&gt;To create the archive by month page, we need to create a page in the content folder and the accompanying layout in the theme folder.&lt;/p&gt;
&lt;h2 id="create-the-page-to-display-the-posts-by-month"&gt;Create the Page to Display The Posts By Month&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;In the content directory, create a new file called archivebydate.md&lt;/li&gt;
&lt;li&gt;Make the contents of the archivebydate.md, as follows, to set the URL to the page, that the page is published, the layout is archivebydate (we will create this next), the title of the page, and the description of the page&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;permalink: &amp;#34;/posts/monthview/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;url: &amp;#34;/posts/monthview/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;layout: &amp;#34;archivebydate&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;title: &amp;#34;Posts&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;description: &amp;#34;By Date&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**Other Views:**&lt;/span&gt; [&lt;span class="nt"&gt;List&lt;/span&gt;](&lt;span class="na"&gt;/posts/&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="create-the-layout-of-the-monthly-view"&gt;Create the Layout of the Monthly View&lt;/h2&gt;
&lt;p&gt;Now we need to create the layout of the post by monthly page.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file in our theme folder in the layouts\page and name it archivebydate.html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We are going to first define the main section since our baseof.html file uses this section to put the content between the header and footer of the site&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, we need to create the header of the page. The header will consist of the background image, title, and description of the page.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For the header image, we check if the page defined an image in the front matter or if we should use the site&amp;rsquo;s default header image&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;{{ if isset .Params &amp;#34;image&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;intro-header&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;background-image: url(&amp;#39;{{ .Site.BaseURL }}/{{ .Params.image }}&amp;#39;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;{{ else }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;intro-header&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;background-image: url(&amp;#39;{{ .Site.BaseURL }}{{ .Site.Params.defaultHeaderImage }}&amp;#39;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;page-heading&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;small&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;subheading&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Description }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, we are ready to display the actual post grouped by month.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hugo has built-in functions to get all of the pages grouped by the date that we can then make sure only display actual post and not the post list page, and then loops through all of the posts for that month.&lt;/p&gt;
&lt;p&gt;We also allow the page to have content on it that is displayed at the top using the &lt;code&gt;{{ .Content }}&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ .Content }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ range (.Site.RegularPages.GroupByDate &amp;#34;January 2006&amp;#34;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ if (where .Pages &amp;#34;Section&amp;#34; &amp;#34;posts&amp;#34;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Key }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group striped-list&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ range ((where .Pages &amp;#34;Section&amp;#34; &amp;#34;posts&amp;#34;)) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;list-group-item&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .RelPermalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ .PublishDate.Format &amp;#34;Jan 02&amp;#34; }} : {{ .Title }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Our basic page is now built. If you navigate to &lt;a href="/posts/monthview/" &gt;/posts/monthview/&lt;/a&gt; you can see our page of posts grouped by month.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Optionally, you can add strips (color even list items) to the list of blog posts with a bit of CSS&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The above code has a class on the ul element called striped-list. We need to implement the stripping with the CSS below.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In our theme folder, open static\css\clean-blog.min.css and add the following code anywhere in the file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;striped-list&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nd"&gt;nth-of-type&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;odd&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#f3f3f3&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="post-list-update"&gt;Post List Update&lt;/h2&gt;
&lt;p&gt;The last thing we need to do is add links to go between the views that we have for the post list and the post by month list.&lt;/p&gt;
&lt;p&gt;We could directly update the default list.html page, but if we ever have a list that is not a post list, we do not want to have the links to navigate between the post archive pages. Instead, we are going to create a list.html file in the theme\layouts\posts folder that will be used automatically when viewing a list of posts instead of the _default list.html&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In our theme folder, create the file layouts\posts\list.html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the list.html file, add the following code, which mirrors out _default\list.html file, except this code, has the addition of the &amp;ldquo;Other Views:&amp;rdquo; html&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- Page Header --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;intro-header&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;background-image: url(&amp;#39;{{ .Site.BaseURL }}{{ .Site.Params.defaultHeaderImage }}&amp;#39;)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;site-heading&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;small&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;subheading&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Description }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;&amp;lt;!-- Main Content --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Other Views: &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/posts/monthview&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;By Month&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ range .Paginator.Pages }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ .Render &amp;#34;archive&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ template &amp;#34;_internal/pagination.html&amp;#34; . }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Our archive by month page is now completed and ready to deploy. As you can see, there is a lot of power in the functions that Hugo provides us to loop through the post, group posts, run conditional logic, and more.&lt;/p&gt;
&lt;p&gt;In our &lt;a href="/hugo-view-post-grouped-by-category" &gt;next post&lt;/a&gt;, we will build the archive by category page.&lt;/p&gt;</description></item><item><title>Hugo - Create Page to View All Your Posts</title><link>https://digitaldrummerj.me/hugo-view-all-post/</link><pubDate>Fri, 18 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-view-all-post/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;In our post on &lt;a href="/create-blog-with-hugo" &gt;Creating Your Blog Using Hugo&lt;/a&gt;, the theme that I provided you included the archive page that allows you to page through all of the posts you have written. By default, Hugo only shows 10 posts at a time and then you need to have an archive page that pages through the remaining posts. Even though I provided you with the archive page, I want to examine how that page works so you know how you can customize the page to match the look and feel you want.&lt;/p&gt;
&lt;p&gt;The archive page is available in our theme at &lt;code&gt;/posts&lt;/code&gt;. To generate this HTML, we use a combination of files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;layouts_default\baseof.html&lt;/strong&gt; : this file contains the starting point template for all pages. We have a header and footer in our file and then pull in the main block that we will define in the template for the page we are rendering.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;layouts_defaut\list.html&lt;/strong&gt; : for every type of taxonomy in Hugo (post, page, etc) that we want to get a list view of, it will use this file. The main section is defined that the baseof.html file is using.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;layouts\posts\archive.html&lt;/strong&gt; : in our list.html, we tell it to render the archive, which refers to this file for the html to render&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;layouts\partials\meta.html&lt;/strong&gt; : renders the post tag and categories. This file is used both in the post list page and the render of an individual page.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;_internal/pagination.html&lt;/strong&gt; : this is an internal file for Hugo that we can use to page through results.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s break down how our post archive page is generated.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;post list page:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/post-list/post-list-page.png" alt="page list sample"&gt;&lt;/p&gt;
&lt;p&gt;First, we added a header to the page to display the title and description of the page.&lt;/p&gt;
&lt;p&gt;The first thing we do is define the main section to reuse it in our baseof.html page.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ define &amp;#34;main&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- Page Header --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- Main Content --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Within the main section, we have two sections: the page header and the post&amp;rsquo;s content.&lt;/p&gt;
&lt;p&gt;For our page header, we show the purple background image to be consistent with the rest of the site and then the title and description.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- Page Header --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;intro-header&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;background-image: url(&amp;#39;{{ .Site.BaseURL }}{{ .Site.Params.defaultHeaderImage }}&amp;#39;)&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;site-heading&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;small&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;subheading&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Description }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;header:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/post-list/header.png" alt="post list header"&gt;&lt;/p&gt;
&lt;p&gt;To display each post, we use the Hugo range, which loops through the posts to be rendered by using&lt;code&gt;{{ range .Paginator.Pages }}&lt;/code&gt; and then render the archive.html file for each post. Then after the post, we show the list of pages.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- Main Content --&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;container&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;row&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ range .Paginator.Pages }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ .Render &amp;#34;archive&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ template &amp;#34;_internal/pagination.html&amp;#34; . }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;posts:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/post-list/posts.png" alt="post list"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;pager:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/post-list/pager.png" alt="Hugo pager"&gt;&lt;/p&gt;
&lt;p&gt;The last thing that we will look at is the HTML rendered for each of the posts. This code is in the layouts\posts\archive.html.&lt;/p&gt;
&lt;p&gt;This code will display a featured image if we define one in the front matter at the top of the post markdown file.&lt;/p&gt;
&lt;p&gt;Then it will display the post title and link it to the page to go to the full post, followed by the date/categories for the post, then the summary of the post, and then a read more link that will take the viewer to the page to see the full post.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;post-preview&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ if .Params.featured_image }}&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Params.featured_image }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;post-title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Title | markdownify }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;post-meta&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ partial &amp;#34;meta.html&amp;#34; .}}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;post-subtitle&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ .Summary }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Read More&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The last page to look at is the meta.html partial. This code will display the date if the date is defined on the post and in the format that we want, Month Date, Year (e.g., February 18, 2022). Then if there are categories defined on the post, they will be displayed, and if there is more than one category, they will be separated by a /&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ if .Params.Date }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fa fa-calendar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;time&lt;/span&gt; &lt;span class="na"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ dateFormat (.Site.Params.DateForm | default &amp;#34;&lt;/span&gt;&lt;span class="na"&gt;Mon&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;Jan&lt;/span&gt; &lt;span class="na"&gt;2&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="na"&gt;2006&lt;/span&gt;&lt;span class="err"&gt;&amp;#34;)&lt;/span&gt; &lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Date&lt;/span&gt; &lt;span class="err"&gt;}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ dateFormat (.Site.Params.DateForm | default &amp;#34;Mon, Jan 2, 2006&amp;#34;) .Date }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;time&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ if .Params.categories }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ $len := len .Params.categories }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;meta-tags&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fa fa-tags&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ range $index, $category := .Params.categories }}{{ if (gt $index 0) }} / {{ end }}{{.}}{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can now customize the look and feel of the post list. To customize the page, you have two options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Customize the page the theme folder&lt;/li&gt;
&lt;li&gt;If you do not want to modify the theme directly, you can override it by mirroring the folder structure within the layouts folder of your site. Hugo will take care of using your version instead of the theme version. You can even reference the partials like the meta date/category within your override file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I like to modify the theme versus overriding it in the layouts folder since I am typically just adding a minor tweak or two to the format and do not need to override the layout entirely.&lt;/p&gt;
&lt;p&gt;In our next post on Hugo, we will add a page to our blog that will display our post list grouped by month and sorted by date. You can see a &lt;a href="/posts/monthview/" &gt;preview here&lt;/a&gt; of what we will be created.&lt;/p&gt;</description></item><item><title>Hugo - Deploy Staging Test Site with Netlify</title><link>https://digitaldrummerj.me/blog-staging-with-netlify/</link><pubDate>Thu, 17 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blog-staging-with-netlify/</guid><category>blogging</category><category>hugo</category><category>netlify</category><description>&lt;p&gt;In our previous post on &lt;a href="/blog-preview-with-netlify" &gt;Deploying a Preview Release of Site Using Netlify&lt;/a&gt;, we set up Netlify to build and deploy a preview of our site every time we created or updated a pull request that also included all of our draft and future posts.&lt;/p&gt;
&lt;p&gt;In this post, we will look at how to deploy a git branch every time a commit is pushed to the branch to test out what our production site will look like before deploying to production. Unlike the preview deploy in the last post, the staging deployment will not include draft and future posts and mirror the production site configuration.&lt;/p&gt;
&lt;h2 id="adding-netlify-configuration-for-branch"&gt;Adding Netlify Configuration for Branch&lt;/h2&gt;
&lt;p&gt;In our &lt;a href="/blog-preview-with-netlify" &gt;previous post&lt;/a&gt;, we created our Netlify configuration file, netlify.toml, at the root of our site.&lt;/p&gt;
&lt;p&gt;In this post, we will add our staging configuration to the netlify.toml file.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;At the bottom of the netlify.toml file, add the staging configuration. In this case, staging is the branch name. If your branch name is different, please update the &amp;ldquo;staging&amp;rdquo; text&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# This will be your staging build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;staging&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hugo --gc --minify -b $DEPLOY_PRIME_URL&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;staging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.92.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Commit and Push the netlify.toml change to your main branch&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="netlify-setup-for-branch-deploy"&gt;Netlify Setup for Branch Deploy&lt;/h2&gt;
&lt;p&gt;We are now going to set up Netlify to build the staging branch that we will create later in this post.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Log in to Netlify&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the site that you want to add the staging deploy to&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Site Settings&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-step-1.png" alt="site settings button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Build &amp;amp; Deploy in the Sidebar&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-step-2.png" alt="site settings button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down until you see the Branches section and click the Edit settings button.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By default, Netlify only builds your production branch.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-staging/netlify-branch-deploy-step-1.png" alt="branch deploy default settings"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can decide to either build All branches that are deployed or individual branches to deploy in addition to your production branch. In this case, we only want our staging branch to be deployed.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-staging/netlify-branch-deploy-step-3.png" alt="selecting staging branch to be deployed"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, it will show that it will deploy your production and staging branches&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-staging/netlify-branch-deploy-step-4.png" alt="production and staging branches set to deploy"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to GitHub and create your staging branch&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After you have created your staging branch, Netlify will automatically deploy it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="view-staging-deployment"&gt;View Staging Deployment&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In Netlify, you can view the deployment by clicking on the Deploys in the top menu under your site.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-staging/netlify-branch-deploy-step-5.png" alt="top menu deploy option"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the list of deployments, you will see a deployment for the staging branch.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-staging/netlify-branch-deploy-step-7.png" alt="staging branch "&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can view the staging deployment by clicking on the &amp;ldquo;Branch Deploy&amp;rdquo; text in the build summary.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;If you get an error that the build failed, check the build logs for the command used for the build and make sure it was the hugo command that we set up. Most likely the netlify.toml file is missing the branch deploy section that matches the branch name. &lt;img src="/images/hugo/deploy-netlify-staging/netlify-branch-deploy-step-6.png" alt="branch deploy failed"&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now we can test our site on a test server by pushing commits to the staging branch. This is a great way to ensure that everything is working correctly before deploying to production. This is especially helpful when making more changes than just the content of a post.&lt;/p&gt;</description></item><item><title>Hugo - Deploy Preview Release of Site Using Netlify</title><link>https://digitaldrummerj.me/blog-preview-with-netlify/</link><pubDate>Wed, 16 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blog-preview-with-netlify/</guid><category>blogging</category><category>hugo</category><category>netlify</category><description>&lt;p&gt;HUGO_VERSION = &amp;ldquo;0.141.0&amp;quot;site every time we pushed a commit to our GitHub repository. As well, we set up a custom domain for our site.&lt;/p&gt;
&lt;p&gt;In this post, we will look at the preview deploy feature of Netlify to build and deploy to a preview/staging site when we create or update a pull request.&lt;/p&gt;
&lt;p&gt;When you create a new Netlify site, a preview site is set up out of the box to build every time you create or update a pull request. The build configuration will match the configuration of your production site. It is fantastic that Netlify set this up for us. However, to take full advantage of having a preview site, it would be helpful to include future and draft posts to make sure they look right before publishing them to production. It would also be nice to be able to test new versions of Hugo in a preview environment before updating production.&lt;/p&gt;
&lt;h2 id="verify-preview-builds-are-enabled"&gt;Verify Preview Builds Are Enabled&lt;/h2&gt;
&lt;p&gt;Before we dive into how to have different configurations for your preview site, let us verify that you indeed have the preview builds turned on.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Login to Netlify&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the Sites section, click on your site&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under your site, click on the site settings&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-step-1.png" alt="site settings button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the side menu, select Build &amp;amp; Deploy and then Continuous Deployment&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-step-2.png" alt="build &amp;amp; deploy section on sidebar"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down to the &lt;strong&gt;Deploy Previews&lt;/strong&gt; section and click on &lt;strong&gt;Edit settings&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-step-3.png" alt="deploy previews section under build &amp;amp; deploy"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the Deploy Previews settings, you should see that it is set for &amp;ldquo;Any pull request against your production branch / branch deploy branches&amp;rdquo; and the Netlify Drawer is set to Enabled (more on Netlify Drawer later in this post)&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-step-4.png" alt="deploy previews settings"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Save on the Deploy Previews settings&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="netlify-preview-vs-production-configuration"&gt;Netlify Preview vs. Production Configuration&lt;/h2&gt;
&lt;p&gt;Now that we have confirmed that our preview builds are enabled, let&amp;rsquo;s look at how to have different build settings for production vs. preview.&lt;/p&gt;
&lt;p&gt;To have different settings, we will create a file in the root of our site named netlify.toml. The netlify.toml file will set the build command and hugo version for each environment.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First in the netlify.toml file, we need to add the configuration to set the default publish directory, the build command, and the hugo version to use.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# This will be your default build command&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;publish&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;public&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hugo --environment production --gc --minify&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.141.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;HUGO_ENV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;production&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;HUGO_ENABLEGITINFO&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, let&amp;rsquo;s set our preview deployment to have a different build command that will build future posts, draft posts, and update the baseUrl to our preview url so that any links that reference the baseURL continue to work.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# This will be your preview build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deploy-preview&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hugo --environment preview --gc --minify -b $DEPLOY_PRIME_URL&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We can also set different environment variables and override the default ones&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deploy-preview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0.141.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;HUGO_ENV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;preview&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now our production deployment will only include published posts, while our preview deployment will include future and draft posts.&lt;/p&gt;
&lt;h2 id="netlify-drawer"&gt;Netlify Drawer&lt;/h2&gt;
&lt;p&gt;The last thing to look at is the Netlify Drawer option that we saw earlier, which is optional to use. The Netlify Drawer allows a reviewer (you in the case of a free Netlify account but could be a team member if you are paying for a team in Netlify) to give meaningful feedback. It allows integration while reviewing the site with GitHub, GitLab, Jira, Linear, Loom, Shortcut, and Trello. You can take screenshots (and annotate them), record your screen as a video and add a comment.&lt;/p&gt;
&lt;p&gt;When you have the Netlify Drawer turned on and go to your preview site, on the bottom of the screen, you will see the Netlify Drawer bar where you can take your screenshot, record your screen as a video and open up collaboration tools.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-netlify-bar.png" alt="Netlify Drawer bar in preview"&gt;&lt;/p&gt;
&lt;p&gt;If you click on the Deploy Preview on the left side of the bar and then click on the Integrations icon, you can connect your various collaboration tools.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-integrations.png" alt="Netlify sidebar integrations in preview"&gt;&lt;/p&gt;
&lt;p&gt;For my integration, I connected GitHub, which enables me to see all of the activity on the pull request, leave a comment, upload a screen/video, and mark comments as resolved.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-activity.png" alt="Netlify activity section in the sidebar in preview"&gt;&lt;/p&gt;
&lt;p&gt;I can also add a GitHub issue to deal with a problem unrelated to the pull request.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify-preview/netlify-preview-new-issue.png" alt="Netlify new issue section in the sidebar in preview"&gt;&lt;/p&gt;
&lt;p&gt;That was a lap around how to create, configure and test/review a preview deployment for your site. In our &lt;a href="/blog-staging-with-netlify" &gt;next post&lt;/a&gt; on Hugo, we will look at how you can create a staging site that matches the configuration of our production deployment.&lt;/p&gt;</description></item><item><title>Hugo - Deploy Site to Netlify</title><link>https://digitaldrummerj.me/deploy-hugo-netlify/</link><pubDate>Tue, 15 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/deploy-hugo-netlify/</guid><category>blogging</category><category>hugo</category><category>netlify</category><description>&lt;p&gt;In our previous post on &lt;a href="/create-blog-with-hugo/" &gt;Creating a Blog Using Hugo&lt;/a&gt;, we installed Hugo, created a new site, added a theme, and created our first post.&lt;/p&gt;
&lt;p&gt;In this post, we will be deploying the site that we created using a free account at &lt;a href="https://www.netlify.com/" target="_blank" &gt;Netlify&lt;/a&gt;. The site will be re-built and re-deployed whenever we push a new commit to our site&amp;rsquo;s Git repository. We will also look at how to set up our site using our own domain and create an SSL certificate.&lt;/p&gt;
&lt;p&gt;A question that comes up a lot when I talk about deploying static sites like Hugo with Netlify is why not just use GitHub Pages?&lt;/p&gt;
&lt;p&gt;This is a great question. While GitHub Pages allows us to deploy our site with a custom domain and SSL, it does not let us deploy a staging or preview version of the site to test out changes before making them live. Netlify gives us the ability to create a preview site for each pull request which I use to see what my future and draft post will look like on a machine other than my laptop. Netlify also allows us to deploy a branch to a subdomain, allows multiple domains pointing to a single site, unlimited websites, and rollback to a previous build. All of this can be done with the free tier of Netlify.&lt;/p&gt;
&lt;h2 id="create-netlify-account"&gt;Create Netlify Account&lt;/h2&gt;
&lt;p&gt;The first thing we need to do is create a Netlify account if you do not have one already.&lt;/p&gt;
&lt;p&gt;Head over to &lt;a href="https://app.netlify.com/signup" target="_blank" &gt;Netlify sign up&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-signup.png" alt="netlify sign up"&gt;&lt;/p&gt;
&lt;h2 id="import-project"&gt;Import project&lt;/h2&gt;
&lt;p&gt;Now that our Netlify account is created, we are ready to add our site to Netlify.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Once logged into your Netlify account, click on the &amp;ldquo;Add New Site&amp;rdquo; and select &amp;ldquo;Import Project&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-1-add-new-site.png" alt="add new site"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on your Git provider&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-2-import-project.png" alt="import a project into Netlify"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pick your site&amp;rsquo;s repository&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-3-pick-repo.png" alt="netlify pick repository to deploy from Github"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For the build command, use the following to build, clean up resources and minify output&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-4-build-command.png" alt="hugo build command for deploy"&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo --gc --minify
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&amp;ndash;gc cleans up the generated resource to remove anything old&lt;/p&gt;
&lt;p&gt;&amp;ndash;minify will minify things it can like HTML&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Our site is now set up in Netlify and Netlify ran our first deployment for us.&lt;/p&gt;
&lt;h2 id="url-for-site"&gt;Url for Site&lt;/h2&gt;
&lt;p&gt;Netlify will auto-assign a URL to our site for us. We can change the URL to something we can remember or hook up our own domain to the site. We will first look at renaming the Netlify domain and later in this post, we will look at hooking up a custom domain.&lt;/p&gt;
&lt;p&gt;After you finish importing the repository, Netlify will take you to the site details, where you can see the URL that Netlify assigned (it is the netlify.app domain) to the site.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-5-url.png" alt="url netlify assigned"&gt;&lt;/p&gt;
&lt;p&gt;You can also see the Production deploys that have been done for the site. At this point, it will just be the initial deployment.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-6-production-deploys.png" alt="production deployments list"&gt;&lt;/p&gt;
&lt;p&gt;If you want to change the URL that Netlify assigned (will still be netlify.app but can change the subdomain in front of it), you can do so by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Clicking on the domain settings&lt;/li&gt;
&lt;li&gt;Clicking the options next to the domain name&lt;/li&gt;
&lt;li&gt;Clicking the Edit Name&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-7-edit-netlify-app.png" alt="edit netlify app"&gt;&lt;/p&gt;
&lt;p&gt;The last thing we need to do is fix the asset references on our site. Right now, our config.toml file says baseurl is example.org. We need to update this to our Netlify URL.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the config.toml file that is in the root of our site&lt;/li&gt;
&lt;li&gt;Find the baseurl parameter and update it to your Netlify URL&lt;/li&gt;
&lt;li&gt;Commit and push the change to your Git repository&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-7.1-update-config.png" alt="config.toml update"&gt;&lt;/p&gt;
&lt;p&gt;Once you push the change to your repository, Netlify will automatically build and deploy your site.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-11-hugo-baseurl.png" alt="hugo baseurl"&gt;&lt;/p&gt;
&lt;h2 id="add-custom-domain-optional"&gt;Add Custom Domain (optional)&lt;/h2&gt;
&lt;p&gt;You can also set up your own domain in Netlify. Netlify will either set up a brand new domain for you or allow you to point to an existing domain.&lt;/p&gt;
&lt;p&gt;For the steps to set up the DNS for your site, please see the &lt;a href="https://docs.netlify.com/domains-https/custom-domains/" target="_blank" &gt;Netlify Custom Domain Setup Docs&lt;/a&gt;&lt;/p&gt;
&lt;div role="alert" class="alert alert-warning"&gt;Even though Netlify can purchase the domain for you, I would highly suggest that you purchase and manage your domains through something like &lt;a href="https://dnsimple.com/r/7695fdc3a9dc82" target="_blank" &gt;dnsimple&lt;/a&gt;. With Dnsimple, you have complete control and ownership over your domain. Dnsimple also has several one-click setups that do the DNS setup work for you, including one for Netlify.&lt;/div&gt;
&lt;p&gt;Once you have pointed your DNS at Netlify, you are ready to add your custom domain to Netlify.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Under the domain settings, click &amp;ldquo;Add custom domain&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter your domain in and click verify&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-8-custom-domain.png" alt="setup custom domain"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If your domain is already registered with an external DNS provider like mentioned above, you will see the &amp;ldquo;domain already registered&amp;rdquo; warning.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-8-custom-domain-already-registered.png" alt="domain already registered"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click &amp;ldquo;Add domain&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The last thing is to set up your SSL certificate. Netlify users Let&amp;rsquo;s Encrypt and will create your SSL certificate.&lt;/p&gt;
&lt;p&gt;You will see the HTTPS section at the bottom of the domain settings.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Expand the HTTPS section&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-9-ssl.png" alt="SSL setup"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &amp;ldquo;Verify DNS Configuration&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/deploy-netlify/netlify-new-site-step-10-ssl-verified.png" alt="ssl verified"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go back to your config.toml file and update the baseURL to your custom domain and push the change to git.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your site is now deployed with a custom domain that will be re-built and re-deployed with each push to your Git repository.&lt;/p&gt;
&lt;p&gt;In our &lt;a href="/blog-preview-with-netlify" &gt;next post on Hugo&lt;/a&gt;, we will set up a preview site that will be deployed every time a pull request is created or updated. This will allow you to test out changes before they go live in production.&lt;/p&gt;</description></item><item><title>Create a Blog Using Hugo</title><link>https://digitaldrummerj.me/create-blog-with-hugo/</link><pubDate>Mon, 14 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/create-blog-with-hugo/</guid><category>blogging</category><category>hugo</category><description>&lt;p&gt;Are you ready to capturing and sharing your knowledge with others by creating a blog?&lt;/p&gt;
&lt;p&gt;Then this series on &lt;a href="https://gohugo.io/" target="_blank" &gt;Hugo&lt;/a&gt; is what you are looking for. Even if you have never used or heard of Hugo you will be able to create your blog and deploy it.&lt;/p&gt;
&lt;p&gt;In this series of post on Hugo, we are going to build a full blog based on this website. By the end of the series, you will be able to add posts, have a page to view past post by date/category/tag, have an rss feed so people can subscribe to your blog, be able to add pages such as contact, search and about plus we will dig into several useful Hugo features to take some of the work out of creating your blog and post.&lt;/p&gt;
&lt;p&gt;If you have not heard of Hugo before, Hugo is a static site generator. A static site generator renders your content into HTML files and you only have to deploy the HTML to your web server. No database is needed. No fancy hosting service. No Wordpress site. In fact, you can run your site using GitHub pages for free.&lt;/p&gt;
&lt;h2 id="hugo-install"&gt;Hugo Install&lt;/h2&gt;
&lt;p&gt;The first thing we need to do is install Hugo.&lt;/p&gt;
&lt;div role="alert" class="alert alert-warning"&gt;If you are not running Linux or macOS, you can find the install instructions at &lt;a href="https://gohugo.io/getting-started/installing" target="_blank" &gt;https://gohugo.io/getting-started/installing&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;For Windows I prefer to use &lt;a href="https://chocolatey.org/install" target="_blank" &gt;Chocolately&lt;/a&gt; to manage my applications installs and upgrades. For Hugo, Chocolatey does have a package available for us to use.&lt;/p&gt;
&lt;p&gt;By using Chocolately, I can install Hugo and get Hugo available on my environment path with a single command.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you do not have Chocolatey installed already, go to &lt;a href="https://chocolatey.org/install" target="_blank" &gt;https://chocolatey.org/install&lt;/a&gt; and install it first&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up a command prompt as an administrator&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Hugo&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;choco install -y hugo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;When a new version of Hugo comes out you can update your Chocolatey package but running choco upgrade -y hugo&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="create-a-new-site"&gt;Create a New Site&lt;/h2&gt;
&lt;p&gt;Now that we have Hugo install, we need to create a new website.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up a command prompt&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the directory where you store your repositories or code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following to create a new Hugo site in a folder name, MyBlog&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo new site MyBlog
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/hugo/get-started/hugo-new-site-output.png" alt="hugo new site output"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="add-a-theme"&gt;Add a Theme&lt;/h2&gt;
&lt;p&gt;At this point we only have a shell of a website. For the design is the website, Hugo allows us to add themes to control the look and feel.&lt;/p&gt;
&lt;p&gt;There are two ways to install a theme. You can make the theme a git submodule or you can download the theme and unzip it into the themes directory under your website.&lt;/p&gt;
&lt;p&gt;There are pros and cons to both options.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;git submodule pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it is easy to update the theme with any changes the theme author makes.&lt;/li&gt;
&lt;li&gt;fastest way to get started with a theme&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;git submodule cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;changes you make to the theme will not be seen by git&lt;/li&gt;
&lt;li&gt;you have to remember to init the submodule after clone&lt;/li&gt;
&lt;li&gt;you have to remember to update the submodule, git pull for your site does not update the submodule.&lt;/li&gt;
&lt;li&gt;if the theme author decides to delete the theme repository and you don&amp;rsquo;t have a local copy, your theme is lost.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;download theme pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the theme is stored along with your site and any changes you make to it are stored with your site&lt;/li&gt;
&lt;li&gt;you do not have to worry about the theme repository going away&lt;/li&gt;
&lt;li&gt;you have the most control of your theme&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;download themes cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;takes a little bit more work to start using theme since you have to download and unzip it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;you will have to manually merge any changes the theme author makes into your version of the theme.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In my experience, themes rarely get updated after their release&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My preference is to download the theme so that I have full control over it and don&amp;rsquo;t have to worry about the theme repository going away.&lt;/p&gt;
&lt;p&gt;As well for this series on Hugo, we will be starting with a basic starter theme and then adding and modifying the theme so you will want to have the theme installed locally.&lt;/p&gt;
&lt;p&gt;To install the theme:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to &lt;a href="https://github.com/digitaldrummerj/hugo-theme-bootstrap" target="_blank" &gt;https://github.com/digitaldrummerj/hugo-theme-bootstrap&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the green code button and then click the Download ZIP&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/get-started/theme-download.png" alt="download theme from GitHub"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unzip the download to the themes directory&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rename hugo-theme-bootstrap-main to bootstrap&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the config.toml file that is in the root of your site and add the following line to tell Hugo to use the theme. The value of the theme parameter, is the directory name of the theme under Themes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;theme = &amp;#34;bootstrap&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to use a different theme, you can find a good amount of themes available at &lt;a href="https://themes.gohugo.io/" target="_blank" &gt;https://themes.gohugo.io/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="add-some-content"&gt;Add Some Content&lt;/h2&gt;
&lt;p&gt;Now that we have our site and theme setup, we are ready to create our first blog post.&lt;/p&gt;
&lt;p&gt;Post in Hugo use markdown for the content and are stored in the content/post directory.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a command prompt&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to your website folder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the hugo new command to create a new post&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo new posts/my-first-post.md
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="start-web-server"&gt;Start Web Server&lt;/h2&gt;
&lt;p&gt;We are now ready to see what our side looks like and start up the built-in Hugo web server&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;From our command line that we already have open, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo server -D -F
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;the -D argument tells Hugo to include draft post
the -F argument tells Hugo to include future dated post that are published&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open your browser and navigate to &lt;a href="http://localhost:1313" target="_blank" &gt;http://localhost:1313&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your blog is now up and running with your first post. In our &lt;a href="/deploy-hugo-netlify" &gt;next post on Hugo&lt;/a&gt;, we will take a look at how to deploy our site using the free version of Netlify so that every time we push commits to our GitHub repository, our site will update itself.&lt;/p&gt;</description></item><item><title>Hugo - Show ShortCode Markdown in Code Block</title><link>https://digitaldrummerj.me/hugo-show-shortcode-markdown-in-code-block/</link><pubDate>Sun, 13 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-show-shortcode-markdown-in-code-block/</guid><category>hugo</category><category>blogging</category><description>&lt;p&gt;I was trying to add a code block to a post to show how to generate an html figure using the Hugo built-in figure shortcode and it instead of showing the markdown of the figure, it kept rendering the actual figure.&lt;/p&gt;
&lt;p&gt;I could have just took a screenshot of the code but the whole point of having the code block is to make it easier for you the reader to easily copy and paste the code. The key to making this work is knowing how to escape the figure shortcode within the code block so that Hugo renders it as a code block and not a figure.&lt;/p&gt;
&lt;p&gt;I wanted the code block to show the following code&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{&amp;lt; figure src=&amp;#34;/images/aspnet-core-dotcover/teamcity-run-unit-tests.png&amp;#34; caption=&amp;#34;Build Configuration Example&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead though it kept generating the actual figure&lt;/p&gt;
&lt;figure&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-run-unit-tests.png"
alt="Build Configuration Example"&gt;&lt;figcaption&gt;
&lt;p&gt;Build Configuration Example&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The trick to make this work is to escape the figure by adding a &lt;code&gt;/*&lt;/code&gt; after the &lt;code&gt;&amp;lt;&lt;/code&gt; and a &lt;code&gt;*/&lt;/code&gt; before the &lt;code&gt;&amp;gt;&lt;/code&gt; in the figure markdown and then it will render actual markdown&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hugo/shortcode-in-code-block/markdown-to-show-shortcode.png" alt="image of the actual markdown"&gt;&lt;/p&gt;</description></item><item><title>Switch Between Github Accounts on Windows</title><link>https://digitaldrummerj.me/switch-between-github-accounts-on-windows/</link><pubDate>Sat, 12 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/switch-between-github-accounts-on-windows/</guid><category>git</category><category>github</category><description>&lt;p&gt;Recently, I had to create a Github account for work in addition to the one that I have for my personal repos. Not a big deal having two accounts but figuring out how to switch the account to use depending on the repository was difficult to figure out.&lt;/p&gt;
&lt;p&gt;Luckily, the solution is really straight forward to implement.
methods&lt;/p&gt;
&lt;p&gt;The solution is to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Move all your work repositories to a single directory like c:\repos\work&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Move all your personal repositories to a single directory like c:\repos\personal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a .gitconfig-work file in the same directory as your .gitconfig file&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-config" data-lang="config"&gt;[user]
name = Justin James
email = [Work Email Here]
[credential “https://github.com”]
helper = dtkeyring
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a .gitconfig-personal file in the same directory as your .gitconfig file&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-config" data-lang="config"&gt;[user]
name = Justin James
email = [Personal Email Here]
[credential “https://github.com”]
helper = wincred
useHttpPath = true
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;the useHttpPath argument for the credential manager is key as it will will prompt you for credentials for each repository instead of using a single credential for all repositories on a given platform (e.g. GitHub, GitLab, BitBucket, etc)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update your .gitconfig file in your home directory to have an includeif to change which git config pull in based in kn the directory&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-config" data-lang="config"&gt;[includeIf &amp;#34;gitdir:C:/repos/work/&amp;#34;]
path = .gitconfig-work
[includeIf &amp;#34;gitdir:C:/repos/personal/&amp;#34;]
path = .gitconfig-personal
[core]
longpaths = true
[init]
defaultBranch = main
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now your git configuration will change based on the directory your git repository. This ensures that the right account is associated to your commits. As well as it will prompt you for your GitHub credentials per repository url.&lt;/p&gt;</description></item><item><title>Cypress - Migrate from cy.route to cy.intercept</title><link>https://digitaldrummerj.me/cypress-migrate-to-cy-intercept/</link><pubDate>Fri, 11 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-migrate-to-cy-intercept/</guid><category>Cypress</category><description>&lt;p&gt;Starting with Cypress 6.0, the cy.route and cy.server commands were deprecated and replaced with cy.intercept.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cypress is currently at version 9.4&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="comparison-to-cyroute"&gt;Comparison to cy.route()&lt;/h2&gt;
&lt;p&gt;Unlike cy.route(), cy.intercept():&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;can intercept all types of network requests including Fetch API, page loads, XMLHttpRequests, resource loads, etc.&lt;/li&gt;
&lt;li&gt;does not require calling cy.server()&lt;/li&gt;
&lt;li&gt;does not have method set to GET by default, but intercepts all methods&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="using-cyroute"&gt;Using cy.route&lt;/h2&gt;
&lt;p&gt;In my code, we use cy.route that return either response using a file (e.g. fixture) or an object defined in the test code itself.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;My preference is to use a fixture file for any response that is more than just a simple string as it makes your code more readable and allows you to reuse the fixture in other routes/intercepts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is a quick look at the syntax of cy.route.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;cy.route using a fixture file:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fixture:filename&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alias&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;cy.route using a response that is not a fixture file:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alias&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="updated-to-intercept"&gt;Updated to Intercept&lt;/h2&gt;
&lt;p&gt;Updating to use cy.intercept is pretty straight forward.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; make sure for each alias of the same name that you update it to cy.intercept else it will not override itself as you expect it to since cy.route and cy.intercept do not interface with each other.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;cy.intercept using a fixture file:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;filename&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alias&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;cy.intercept using a response that is not a fixture file:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intercept&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;method&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;url&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;response&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;alias&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The last step once all of your cy.routes have been updated to cy.intercept is to remove the call to cy.server as it is no longer needed.&lt;/p&gt;</description></item><item><title>Cypress - Create My Own Cypress Commands</title><link>https://digitaldrummerj.me/cypress-custom-commands/</link><pubDate>Thu, 10 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-custom-commands/</guid><category>Cypress</category><description>&lt;p&gt;As your number of Cypress tests grows you are bound to end up with some duplicate code that would be better to put into a reusable function.&lt;/p&gt;
&lt;p&gt;Luckily, Cypress allows us to create our own custom &lt;code&gt;cy&lt;/code&gt; commands that are available to all of our tests.&lt;/p&gt;
&lt;p&gt;Here are some examples of custom commands that I have created in one of my projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Test grid sorting is working correctly&lt;/li&gt;
&lt;li&gt;Test grid paging is working correctly&lt;/li&gt;
&lt;li&gt;Mock a list of API calls that every page has so we only need to call a single command to get all of the mocks&lt;/li&gt;
&lt;li&gt;Generate a string of a certain length&lt;/li&gt;
&lt;li&gt;Generate a string of a certain length that also contains certain special characters based on a parameter&lt;/li&gt;
&lt;li&gt;Validate input field strips certain special characters on both typing and pasting of text&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These examples are just a few of the ones we have created. The custom commands greatly increased developer productivity, standardized testing of common functionality, and allowed us to remove hundreds of lines of duplicate code.&lt;/p&gt;
&lt;h2 id="syntax"&gt;Syntax&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callbackFn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callbackFn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="usage"&gt;Usage&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`login`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="examples"&gt;Examples&lt;/h2&gt;
&lt;p&gt;Here are a couple of actual custom commands that I have written in my applications.&lt;/p&gt;
&lt;h3 id="angular-material-grid-is-sorted-by-column--direction"&gt;Angular Material Grid Is Sorted By Column &amp;amp; Direction&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Custom Command Code:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;gridSortValidateSortOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;columnName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`mat-header-cell.cdk-column-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;columnName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;have.attr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;aria-sort&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;eq&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Custom Command Usage:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gridSortValidateSortOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;descending&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="generate-random-string-of-a-certain-length"&gt;Generate Random String of a Certain Length&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Custom Command Code:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;generateString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;charactersLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;characters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;charactersLength&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Custom Command Usage:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generateString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txt25&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[data-cy=&amp;#34;accountName&amp;#34;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txt25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="create-your-command"&gt;Create Your Command&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at how and where do you create your custom commands.&lt;/p&gt;
&lt;p&gt;A great place to define commands is in your cypress/support/commands.js file, since it is loaded before any test files.&lt;/p&gt;
&lt;p&gt;However, I prefer to not put my custom command code directly into the commands.js file but instead into their own file in the support directory and import the files into the commands.js file. This way I am able to keep my command.js file manageable and easily find my custom commands based on purpose.&lt;/p&gt;
&lt;p&gt;For the examples above, I created an angular-material.js file and a strings.js file in the cypress/support directory and then in the command.js file imported both files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;cypress/support/angular-material.js&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;gridSortValidateSortOrder&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gridSortValidateSortOrder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;cypress/support/strings.js&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;Cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;generateString&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;generateString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;cypress/support/command.js&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./angular-material&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./strings&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="best-practices"&gt;Best Practices&lt;/h2&gt;
&lt;p&gt;The Cypress Team has done a nice job in their docs of documenting the best practices for creating a Cypress Custom Command. If&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.cypress.io/api/cypress-api/custom-commands#Best-Practices" target="_blank" &gt;Custom Command Best Practices from Cypress Docs&lt;/a&gt;&lt;/p&gt;</description></item><item><title>dotCover - How in TeamCity to create multiple coverage reports</title><link>https://digitaldrummerj.me/teamcity-composite-builds/</link><pubDate>Wed, 09 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/teamcity-composite-builds/</guid><category>testing</category><category>aspnet core</category><category>teamcity</category><category>dotcover</category><description>&lt;p&gt;In our &lt;a href="/dotcover-combine-multiple-results/" &gt;previous post&lt;/a&gt;, we talked about how TeamCity automatically combines multiple dotCover outputs into a single code coverage report but what if we wanted to keep thoose reports seperate?&lt;/p&gt;
&lt;p&gt;You might be thinking why would you want to do this? Don’t you want to see the overall test coverage?&lt;/p&gt;
&lt;p&gt;The overall coverage is nice but when you have both unit tests and integration tests, or multiple test projects serving different purposes, there is a high potential that you are reporting lines of code being covered that were only called but not actually tested.&lt;/p&gt;
&lt;p&gt;In this post, we are going to look at how in TeamCity you can run multiple test builds to generate both individual reports and still have a combined report.&lt;/p&gt;
&lt;h2 id="why-do-we-need-seperate-code-coverage-reports"&gt;Why do we need seperate code coverage reports?&lt;/h2&gt;
&lt;p&gt;Before I dive into the reason the reporting may be higher than reality, lets define the difference between a unit test and integration test.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unit Test:&lt;/strong&gt; testing the business logic and data layers of your application with dependencies mocked out. You are testing the smallest unit of work possible.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Integration Test:&lt;/strong&gt; testing the whole flow of the application starting with what the user interfaces with (UI or Api endpoints) and dependencies are typically not mocked out.&lt;/p&gt;
&lt;p&gt;If we look at an ASP.NET Web Api, the integration test would call the REST endpoint while the unit test would only call a single method in the business logic. This is where the problem comes in!&lt;/p&gt;
&lt;p&gt;With the integration test since it start at the endpoint, that means it calls all of the business logic along the way. So in a combined code coverage report, every line of code that the endpoint calls is considered covered. However, a lot of times the integration tests are looking more at things like security checks on the endpoints or making sure the returned view model is correct or validating how an exception looks coming back to the caller. The integration tests may not actually be looking in depth at the businesss logic like a unit test would. However, if you look at the combined report, it is not clear what lines of business logic code is not actually being tested and can you lead you to believe that you have more test coverage than you actually do.&lt;/p&gt;
&lt;p&gt;This is exactly what is happening on one of my projects as the integration tests are only validating the security setup of the endpoints to ensure that only allows users have data returned and all others get a 403 Forbidden error. For the 403 Forbidden error, the code coverage is spot on as the code stops within the endpoint and returns an error message. However, for authorization success, it call as the way through all the layers of the application and returns data which makes it look like we tested our business logic when is reality we did not run a single tests against that logic in our integration test. So for us, it was very important to have the individual code coverage reports for unit tests and integration tests so that we could easily see where we have gaps in the unit test that are testing our business logic.&lt;/p&gt;
&lt;p&gt;However, we do still like to have the combine report just for reporting purposes to get an overall picture of how we are doing.&lt;/p&gt;
&lt;h2 id="implementation"&gt;Implementation&lt;/h2&gt;
&lt;p&gt;For our TeamCity build, we are going to set up 3 builds in total for this work.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Unit test build to generate the individual report with the snapshot as a build artifact&lt;/li&gt;
&lt;li&gt;Integration test build to generate the individual report with the snapshot as a build artifact&lt;/li&gt;
&lt;li&gt;A build that has a dependency on the unit and integration test builds and takes the artifact for each of those builds to do a combined report.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We could use a composite build in TeamCity to have a build call the individual builds but one of the limitations of a composite build is that it can not have its own build steps, so we could not be able to easily create a combined report.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="create-the-unit-test-build"&gt;Create the Unit Test Build&lt;/h3&gt;
&lt;p&gt;The unit test build will have 4 builds steps, save the dotCover snapshot file as a build artifact and create the code coverage report.&lt;/p&gt;
&lt;h4 id="build-steps"&gt;Build Steps&lt;/h4&gt;
&lt;p&gt;Our unit test build consist of 4 steps to restore our nuget packages, run our tests with code coverage, make a copy of the snapshot file and create our code coverage report.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a build step that is a command line step with the working directory as the Unit Test project folder and the custom script has the command &lt;code&gt;dotnet restore&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet restore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-dotnet-restore.png" alt="dotnet restore build step"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a build step that is a command line type with a script to run our test with code coverage turned on:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet dotcover &lt;span class="nb"&gt;test&lt;/span&gt; --logger &lt;span class="s2"&gt;&amp;#34;console;verbosity=normal&amp;#34;&lt;/span&gt; --dotCoverFilters&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;+:Assembly=Aspnet.Coverage.*;-:Assembly=Aspnet.Coverage.UnitTests;-:Assembly=Aspnet.Coverage.MockData;-:Class=Aspnet.Coverage.Api.Controllers.*;-:Class=Aspnet.Coverage.Api.Startup;-:Class=Aspnet.Coverage.Api.Program;-:Class=Aspnet.Coverage.Api.Migrations.*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-unit-tests-dotcover.png" alt="unit test build run dotcover"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a another build step that is a command line type with a script that will make a copy of our snapshot file created by our previous step&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When TeamCity process the snapshot in the next step, it removes the snapshot file, so the copy is required to be able to make the snapshot a build artifact to use in our combined build that we will create later in this post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;copy UnitTestCodeCoverage.Snapshot.dcvr UnitTestCodeCoverage-Show.Snapshot.dcvr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-unit-tests-copy-snapshot.png" alt="unit test build make copy of dotcover snapshot"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the last build step that is a command line type with a script that will process the dotcover results for the unit tests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[dotNetCoverage dotcover_home=&amp;#39;c:\\dotCover.CommandLineTools.2021.3.3&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[importData type=&amp;#39;dotNetCoverage&amp;#39; tool=&amp;#39;dotCover&amp;#39; path=&amp;#39;Aspnet.Coverage.UnitTests\UnitTestCodeCoverage-Show.Snapshot.dcvr&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-unit-tests-dotcover-results.png" alt="unit test build process dotcover results"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="unit-test-build-artifact-setup"&gt;Unit Test Build Artifact Setup&lt;/h4&gt;
&lt;p&gt;The last thing we need to do for your unit test build is to tell TeamCity to make the snapshot that we copied in the build step above as a build artifact.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On the left menu in TeamCity , click on the General Settings option and then in the Artifact paths section add the snapshot file to the list&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nl"&gt;Aspnet.Coverage.UnitTests\UnitTestCodeCoverage.Snapshot.dcvr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-unit-tests-artifacts.png" alt="unit test build artifact setup"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="create-the-integration-test-build"&gt;Create the Integration Test Build&lt;/h3&gt;
&lt;p&gt;The integration test build will have 4 builds steps, save the dotCover snapshot file as a build artifact and create the code coverage report.&lt;/p&gt;
&lt;h4 id="integration-test-build-steps"&gt;Integration Test Build Steps&lt;/h4&gt;
&lt;p&gt;Our integration test build consist of 4 steps to restore our nuget packages, run our tests with code coverage, make a copy of the snapshot file and create our code coverage report.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a build step that is a command line step with the working directory as the Unit Test project folder and the custom script has the command &lt;code&gt;dotnet restore&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet restore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a build step that is a command line type with a script to run our test with code coverage turned on:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet dotcover &lt;span class="nb"&gt;test&lt;/span&gt; --dotCoverOutput&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;IntegrationTestCodeCoverage.Snapshot.dcvr&amp;#34;&lt;/span&gt; --logger &lt;span class="s2"&gt;&amp;#34;console;verbosity=normal&amp;#34;&lt;/span&gt; --dotCoverFilters&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;+:Assembly=Aspnet.Coverage.*;-:Assembly=Aspnet.Coverage.*Tests;-:Assembly=Aspnet.Coverage.MockData;-:Class=Aspnet.Coverage.Api.Migrations.*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-integration-tests-dotcover.png" alt="integration test build run dotcover"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a another build step that is a command line type with a script that will make a copy of our snapshot file created by our previous step&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When TeamCity process the snapshot in the next step, it removes the snapshot file, so the copy is required to be able to make the snapshot a build artifact to use in our combined build that we will create later in this post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;copy IntegrationTestCodeCoverage.Snapshot.dcvr IntegrationTestCodeCoverage-Show.Snapshot.dcvr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-integration-tests-snapshot-copy.png" alt="integration test build make copy of dotcover snapshot"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the last build step that is a command line type with a script that will process the dotcover results for the unit tests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[dotNetCoverage dotcover_home=&amp;#39;c:\\dotCover.CommandLineTools.2021.3.3&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[importData type=&amp;#39;dotNetCoverage&amp;#39; tool=&amp;#39;dotCover&amp;#39; path=&amp;#39;Aspnet.Coverage.IntegrationTests\IntegrationTestCodeCoverage-Show.Snapshot.dcvr&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-integration-tests-dotcover-results.png" alt="integration test build process dotcover results"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="integration-test-build-artifact-setup"&gt;Integration Test Build Artifact Setup&lt;/h4&gt;
&lt;p&gt;The last thing we need to do for your integration test build is to tell TeamCity to make the snapshot that we copied in the build step above as a build artifact.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On the left menu in TeamCity , click on the General Settings option and then in the Artifact paths section add the snapshot file to the list&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+:Aspnet.Coverage.IntegrationTests\IntegrationTestCodeCoverage.Snapshot.dcvr
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-integration-tests-artifacts.png" alt="integration test build artifact setup"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="create-the-combine-report-build"&gt;Create the Combine Report Build&lt;/h3&gt;
&lt;p&gt;For our combined report build we will be creating a dependency on the unit and integration test builds that we created above and then telling TeamCity about the snapshots so that it creates the report for us.&lt;/p&gt;
&lt;h4 id="combined-build-teamcity-dependencies-setup"&gt;Combined Build TeamCity Dependencies Setup&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a build that is a regular build and hook it up to the repository so that we can get the source code in our dotCover report.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After the build is created, click on Dependencies on the left menu and add the unit test and integration builds as snapshot dependencies&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-snapshot.png" alt="combined build snapshot dependencies"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the dependencies, you also need to setup an artifact dependency for the dotcover snapshots that we made as a build artifact in our unit and integration test builds&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-artifacts.png" alt="combined build artifact dependencies"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="combined-build-steps"&gt;Combined Build Steps&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add a command line build step with the script below to process the unit test build dotCover snapshot&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[dotNetCoverage dotcover_home=&amp;#39;c:\\dotCover.CommandLineTools.2021.3.3&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[importData type=&amp;#39;dotNetCoverage&amp;#39; tool=&amp;#39;dotcover&amp;#39; path=&amp;#39;UnitTestCodeCoverage.Snapshot.dcvr&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-combined-unit-test.png" alt="combined build process unit test coverage"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a command line build step with the script below to process the integration test build dotCover snapshot&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[dotNetCoverage dotcover_home=&amp;#39;c:\\dotCover.CommandLineTools.2021.3.3&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[importData type=&amp;#39;dotNetCoverage&amp;#39; tool=&amp;#39;dotcover&amp;#39; path=&amp;#39;IntegrationTestCodeCoverage.Snapshot.dcvr&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-composite-combined-integration-test.png" alt="combined build process integration test coverage"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now when you run the combined build, it will run the unit test and integration test builds and then it will take the artifacts from those two builds to create the combined dotCover code coverage report.
The last thing we need to do for your integration test build is to tell TeamCity to make the snapshot that we copied in the build step above as a build artifact.&lt;/p&gt;</description></item><item><title>DotCover - Combine Multiple Results into Single Report</title><link>https://digitaldrummerj.me/dotcover-combine-multiple-results/</link><pubDate>Tue, 08 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/dotcover-combine-multiple-results/</guid><category>testing</category><category>aspnet core</category><category>teamcity</category><category>dotcover</category><description>&lt;p&gt;In TeamCity, what if you need to combine the code coverage results say from unit test and integration test projectsthst run as seperate build steps?&lt;/p&gt;
&lt;p&gt;Luckily, TeamCity does this work for you but it is not obvious that it will do it for.&lt;/p&gt;
&lt;p&gt;To get TeamCity to combine the multiple the code coverage results into a single code coverage result, you just need to add the echo command to import the data into the build like we did in our &lt;a href="/aspnet-core-code-coverage-tuning-dotCover/" &gt;previous post&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="unit-test-example"&gt;Unit Test Example&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[dotNetCoverage dotcover_home=&amp;#39;c:\\dotCover.CommandLineTools.2021.3.3&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[importData type=&amp;#39;dotNetCoverage&amp;#39; tool=&amp;#39;dotCover&amp;#39; path=&amp;#39;Aspnet.Coverage.UnitTests\UnitTestCodeCoverage.Snapshot.dcvr&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="integration-test-example"&gt;Integration Test Example&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[dotNetCoverage dotcover_home=&amp;#39;c:\\dotCover.CommandLineTools.2021.3.3&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[importData type=&amp;#39;dotNetCoverage&amp;#39; tool=&amp;#39;dotCover&amp;#39; path=&amp;#39;Aspnet.Coverage.IntegrationTests\IntegrationTestCodeCoverage.Snapshot.dcvr&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When TeamCity run the echo import data commands for both the &lt;code&gt;UnitTestCodeCoverage.Snapshot.dcvr&lt;/code&gt; and &lt;code&gt;IntegrationTestCodeCoverage.Snapshot.dvcr&lt;/code&gt; files, it will combine them together into a single code coverage report.&lt;/p&gt;
&lt;p&gt;In our next post, we are going to go through how to implement a build run each test projects in parallel and keep the reports from combining together.&lt;/p&gt;</description></item><item><title>.NET Core - Code Coverage in TeamCity</title><link>https://digitaldrummerj.me/dotnet-core-code-coverage-teamcity/</link><pubDate>Mon, 07 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/dotnet-core-code-coverage-teamcity/</guid><category>testing</category><category>aspnet core</category><category>teamcity</category><category>dotcover</category><description>&lt;p&gt;In &lt;a href="/aspnet-core-code-coverage" &gt;part 1&lt;/a&gt; and &lt;a href="/aspnet-core-code-coverage-tuning-dotCover/" &gt;part 2&lt;/a&gt; of of this article series, we setup and optimized our code coverage using the free command line version of dotCover.&lt;/p&gt;
&lt;p&gt;In this post, we are going to add our code coverage to our TeamCity builds to run our unit tests with code coverage as part of the automated builds, show us the code coverage metrics summary after the build, be able to view the code coverage report right in TeamCity and add failure metrics for if code coverage percent drops.&lt;/p&gt;
&lt;div role="alert" class="alert alert-warning"&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post assumes that you already have a TeamBuild setup for your project to trigger a build on pull request or manually on pushes to the repository.&lt;/div&gt;
&lt;p&gt;In part 1 and 2 of of this article series, we setup and optimized our code coverage using the free command line version of dotCover.&lt;/p&gt;
&lt;p&gt;In this post, we are going to add our code coverage to our TeamCity builds to run our unit tests with code coverage as part of the automated builds, show us the code coverage metrics summary after the build, be able to view the code coverage report right in TeamCity and add failure metrics for if code coverage percent drops.&lt;/p&gt;
&lt;p&gt;I am a big believer in DevOps and having automated builds and deployments for all of my projects to ensure that the code compiles on a machine other than my development machine, automated tests are executed in a clean environment, and any other work that I need to do to get ready for a deployment is automated. In fact, I have had automated builds and deployments since 2002, long before DevOps become a thing.&lt;/p&gt;
&lt;p&gt;I will be using TeamCity as the automated build platform and am assuming that you already have your Angular build working and are just adding in code coverage to the build.&lt;/p&gt;
&lt;h2 id="step-1-install-dotcover-command-line-on-build-agent"&gt;Step 1: Install dotCover Command Line on Build Agent&lt;/h2&gt;
&lt;p&gt;The first thing that we need to do is install the dotCover command line on our TeamCity build agent. Even though TeamCity has dotCover built-in, unless you are the TeamCity administrator you do not have any ability to update the built-in version of dotCover that TeamCity is using. In my case, we have a team at work that has an on-premise TeamCity instance that they give teams space on but only they have administrative rights to the TeamCity server. We only get to control our build agents.&lt;/p&gt;
&lt;p&gt;Luckily, it is really easy to tell TeamCity to use a locally installed dotCover version instead of the built-in TeamCity one.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Login to Your Build Agent&lt;/li&gt;
&lt;li&gt;Download the &lt;a href="https://www.jetbrains.com/dotcover/download/#section=commandline" target="_blank" &gt;dotCover Command Line&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Unzip it to a location on your TeamCity build agent that account that is running the TeamCity build agent service has access to&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-2-add-builds-step"&gt;Step 2: Add Builds Step&lt;/h2&gt;
&lt;p&gt;We need to add three build steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;dotnet restore&lt;/code&gt; to restore our nuget packages&lt;/li&gt;
&lt;li&gt;Run our unit tests with code coverage&lt;/li&gt;
&lt;li&gt;Tell TeamCity to process the code coverage results&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="21-restore-nuget-packages"&gt;2.1: Restore Nuget Packages&lt;/h3&gt;
&lt;p&gt;You need to create a build step that is a command line step with the working directory as the Unit Test project folder and the custom script has the command &lt;code&gt;dotnet restore&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet restore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-dotnet-restore.png"
alt="Run dotnet restore"&gt;&lt;figcaption&gt;
&lt;p&gt;Run dotnet restore&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="22-run-unit-tests-with-code-coverage"&gt;2.2: Run Unit Tests with Code Coverage&lt;/h3&gt;
&lt;p&gt;We need to add a new build step to our TeamCity build and configure it as follows. For the most part we are going to use the command from our &lt;a href="/aspnet-core-code-coverage-tuning-dotcover/" &gt;previous post&lt;/a&gt; with the except of removing the dcReportType parameter so that a snapshot is created instead.&lt;/p&gt;
&lt;p&gt;The reason to use a snapshot is so that TeamCity will integrate the code coverage summary and generate the report for us instead of us generating the report outside of TeamCity.&lt;/p&gt;
&lt;p&gt;Add a new build step and configure your build step as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Name: Run Unit Tests With Code Coverage&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type: Command Line&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Working Directory: Unit Test Project Folder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: Custom script&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Custom script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet dotcover &lt;span class="nb"&gt;test&lt;/span&gt; --logger &lt;span class="s2"&gt;&amp;#34;console;verbosity=normal&amp;#34;&lt;/span&gt; --dotCoverFilters&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;+:Assembly=Aspnet.Coverage.*;-:Assembly=Aspnet.Coverage.UnitTests;-:Assembly=Aspnet.Coverage.MockData;-:Class=Aspnet.Coverage.Api.Controllers.*;-:Class=Aspnet.Coverage.Api.Startup;-:Class=Aspnet.Coverage.Api.Program;-:Class=Aspnet.Coverage.Api.Migrations.*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Note 1: This command will output a snapshot file named dotCover.Output.dcvr&lt;/p&gt;
&lt;p&gt;Note 2: We use the logger with console and verbosity=normal is so that TeamCity can report the status of each individual test.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-run-unit-tests.png"
alt="Run Unit Test Build Step Configuration"&gt;&lt;figcaption&gt;
&lt;p&gt;Run Unit Test Build Step Configuration&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="23-report-coverage-to-teamcity"&gt;2.3: Report Coverage to TeamCity&lt;/h3&gt;
&lt;p&gt;The next thing we need to do is add a build step to tell TeamCity to process the code coverage snapshot that we generated in the unit tests build step.&lt;/p&gt;
&lt;p&gt;Add a new build step and configure your build step as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Name: Process Code Coverage&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type: Command Line&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: Custom script&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Custom script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[dotNetCoverage dotcover_home=&amp;#39;c:\\dotCover.CommandLineTools.2021.3.3&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c1"&gt;##teamcity[importData type=&amp;#39;dotNetCoverage&amp;#39; tool=&amp;#39;dotCover&amp;#39; path=&amp;#39;Aspnet.Coverage.UnitTests\UnitTestCodeCoverage.Snapshot.dcvr&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;1st line tells TeamCity where to find the local version of dotCover that we can to use. The version number should match the version we setup in our Unit Test project. The dotcover_home should match the location that you unzipped dotcover to.&lt;/p&gt;
&lt;p&gt;2nd line tells TeamCity to import the code coverage data and process it&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-process-code-coverage.png"
alt="Process Code Coverage Build Step Configuration"&gt;&lt;figcaption&gt;
&lt;p&gt;Process Code Coverage Build Step Configuration&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="step-3-add-and-view-code-coverage-metrics"&gt;Step 3: Add and View Code Coverage Metrics&lt;/h2&gt;
&lt;p&gt;There is actually nothing more that we need to do in TeamCity for it to show us the code coverage report. We get two reports automatically:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Summary Report on the Build Overview Tab&lt;/p&gt;
&lt;figure&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-code-coverage-summary.png"
alt="TeamCity Summary Report"&gt;&lt;figcaption&gt;
&lt;p&gt;TeamCity Summary Report&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Code Coverage Tab to view the code coverage full report. The metric that I look at is &lt;code&gt;Statements,%&lt;/code&gt; as that looks at lines of code covered.&lt;/p&gt;
&lt;figure&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-code-coverage-report.png"
alt="TeamCity Code Coverage Tab"&gt;&lt;figcaption&gt;
&lt;p&gt;TeamCity Code Coverage Tab&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can also drill all the way down into each file to see which lines of code were covered.&lt;/p&gt;
&lt;figure&gt;&lt;img src="/images/aspnet-core-dotcover/teamcity-code-coverage-report-single-file.png"
alt="TeamCity Code Coverage Report Code File"&gt;&lt;figcaption&gt;
&lt;p&gt;TeamCity Code Coverage Report Code File&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-4-add-failure-metrics"&gt;Step 4: Add Failure Metrics&lt;/h2&gt;
&lt;p&gt;The last thing that I like to do with my code coverage builds, is to also add failure metrics to the build so that it fails if the code coverage goes down by X percent, the number of skipped tests increases X percent and the number of overall tests goes down by more than a certain percent. For me, the percentage to use for the failure metrics really depends on the number of tests within the project as the more tests you have the lower you want the percent as you will have more tests in the percentage as the number of tests goes up.&lt;/p&gt;
&lt;p&gt;When you are first starting out, I tend to keep it at 5% to 10% and over time I reduce it down to as low as I can go without constantly failing the build. For example, right now we have 800 tests in one of my projects so at 1% that means that we can skip up to 8 tests and remove up to 8 tests. We also have 23,000 lines of code so at 1% it means that we can add up to 230 lines of code that are not called by a test.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that the failure conditions below use 1% but you can change that to the percent that you want to allow&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Find your TeamCity build that you want to add the failure conditions to and edit the settings&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the left sidebar, click on the Failure Conditions&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_sidebar_failure_conditions.png" alt="teamcity build failure conditions sidebar"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Add failure condition button and select &amp;ldquo;Fail build on metric change&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_add_failure_condition.png" alt="teamcity build failure on metric change"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For ignored tests failure: select Fail build if to number of ignored test and set is more by at least 1 percent and click save&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_failure_conditions_number_of_ignored_tests.png" alt="ignored tests failure condition"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For number of tests failure: select Fail build if to number of tests and set is less by at least 1 percent and click save
&lt;img src="/images/cypress-code-coverage-ci/tc_failure_conditions_number_of_ignored_tests.png" alt="ignored tests failure condition"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For code coverage percent failure: select Fail build if to percentage of statement coverage and set is less by at least 1 percent and click save&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_failure_conditions_number_of_ignored_tests.png" alt="ignored tests failure condition"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to download the project that I used for this post, you can do so at &lt;a href="https://github.com/digitaldrummerj/aspnet-core-code-coverage-example" target="_blank" &gt;https://github.com/digitaldrummerj/aspnet-core-code-coverage-example&lt;/a&gt;. It is based on .NET 3.1.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>dotCover - Optimizing Coverage Report to Only Include Our Applications Logic</title><link>https://digitaldrummerj.me/aspnet-core-code-coverage-tuning-dotCover/</link><pubDate>Sun, 06 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/aspnet-core-code-coverage-tuning-dotCover/</guid><category>testing</category><category>aspnet core</category><category>dotcover</category><description>&lt;p&gt;In &lt;a href="/aspnet-core-code-coverage" &gt;part 1&lt;/a&gt; of the ASPNET Code Coverage Using dotCover, we hooked up dotCover to our project to generate our code coverage report. It was pretty easy to hook it up but the number is not the most accurate as it includes every file that is part of the solution including our test project and all of the ASP.NET WebApi code.&lt;/p&gt;
&lt;p&gt;When looking at code coverage, we only want to include files that make sense to test against so that our code coverage number is the most accurate. So that means we should exclude our unit tests project from the report since we are not going to run tests against our test projects. Same thing with the ASP.NET WebApi startup.cs, program.cs and Controllers as we would want to test those using integration tests and not unit tests.&lt;/p&gt;
&lt;p&gt;In the previous article, we used the following command to run generate our coverage report.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet dotCover &lt;span class="nb"&gt;test&lt;/span&gt; --dcReportType&lt;span class="o"&gt;=&lt;/span&gt;HTML --logger &lt;span class="s2"&gt;&amp;#34;console;verbosity=normal&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To add and exclude files we need to add the &lt;code&gt;dotCoverFilters&lt;/code&gt; parameter. The &lt;code&gt;dotCoverFilters&lt;/code&gt; allows us to both add and remove assemblies, classes, modules, and functions. Asterisk (*) wildcard is also supported.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;to add we use start the filter with &lt;code&gt;+:&lt;/code&gt; followed by the type (e.g. Assembly, Class, Module, Function)&lt;/li&gt;
&lt;li&gt;to remove we start the filter with &lt;code&gt;-:&lt;/code&gt; followed by the type (e.g. Assembly, Class, Module, Function&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the example command below we:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add all assemblies in the solution with &lt;code&gt;+:Assembly=&amp;lt;Name&amp;gt;.*&lt;/code&gt; where &lt;code&gt;&amp;lt;Name&amp;gt;&lt;/code&gt; is the full name of the assembly.&lt;/li&gt;
&lt;li&gt;Remove the Unit Test and Mock Data projects with &lt;code&gt;-:Assembly=&amp;lt;Name&amp;gt;.UnitTest;-:Assembly=&amp;lt;Name&amp;gt;.MockData&lt;/code&gt; where &lt;code&gt;&amp;lt;Name&amp;gt;&lt;/code&gt; is the full name of the assembly&lt;/li&gt;
&lt;li&gt;Remove Api Controllers, Startup and Program files, and Entity Framework migrations with &lt;code&gt;-:Class=&amp;lt;Name&amp;gt;.Controllers.*;-:Class=&amp;lt;Name&amp;gt;.Startup;-:Class=&amp;lt;Name&amp;gt;.Program;-:Class=&amp;lt;Name&amp;gt;.*&lt;/code&gt; where &lt;code&gt;&amp;lt;Name&amp;gt;&lt;/code&gt; is the full name of the namespace that hold all of the migration classes.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet dotcover &lt;span class="nb"&gt;test&lt;/span&gt; --dcReportType&lt;span class="o"&gt;=&lt;/span&gt;HTML --logger &lt;span class="s2"&gt;&amp;#34;console;verbosity=normal&amp;#34;&lt;/span&gt; --dotCoverFilters&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;+:Assembly=Aspnet.Coverage.*;-:Assembly=Aspnet.Coverage.UnitTests;-:Assembly=Aspnet.Coverage.MockData;-:Class=Aspnet.Coverage.Api.Controllers.*;-:Class=Aspnet.Coverage.Api.Startup;-:Class=Aspnet.Coverage.Api.Program;-:Class=Aspnet.Coverage.Api.Migrations.*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;The most important thing when adding the &lt;code&gt;dotCoverFilters&lt;/code&gt; is to not abuse it by excluding files that should actually be tested.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/dotcover-run-optimized.gif" alt="dotnet dotcover optimized report"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This command also ran 33% faster as it had less files to examine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The report also now includes just the files that are truly should be under tests (e.g. our business logic) and the amount of code covered went from 11% to 71%.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/dotcover-report-optimized.png" alt="dotcover optimized report"&gt;&lt;/p&gt;
&lt;p&gt;In the next post in the series, we will add our code coverage report as part of our TeamCity build.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to download the project that I used for this post, you can do so at &lt;a href="https://github.com/digitaldrummerj/aspnet-core-code-coverage-example" target="_blank" &gt;https://github.com/digitaldrummerj/aspnet-core-code-coverage-example&lt;/a&gt;. It is based on .NET 3.1.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>ASP.NET Core - Implementing Code Coverage with JetBrains dotCover</title><link>https://digitaldrummerj.me/aspnet-core-code-coverage/</link><pubDate>Sat, 05 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/aspnet-core-code-coverage/</guid><category>testing</category><category>aspnet core</category><category>dotcover</category><description>&lt;p&gt;Having automated tests is a good thing to have to help with your code quality but having those tests without any idea of how much of your code is actually being tested is a really bad thing.&lt;/p&gt;
&lt;p&gt;To figure out how much of our code we are actually testing, we need to create a code coverage report. To generate our code coverage report, we going to use the &lt;a href="https://www.jetbrains.com/dotcover/" target="_blank" &gt;JetBrains dotCover&lt;/a&gt; tool.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This post assumes you already have a .NET Core Project that includes unit tests.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-1-install-dotcover"&gt;Step 1: Install dotCover&lt;/h2&gt;
&lt;p&gt;First, you need to install dotCover.CommandLineTools package from nuget as a command line tool within the project.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: We are using the command line version of dotCover as it is free to use and ultimately we will be adding it to our automated builds, so we do not want the GUI version. If you want the GUI version and Visual Studio integration, they do offer a 30 day trial of &lt;a href="https://www.jetbrains.com/dotcover/download/#section=commandline" target="_blank" &gt;dotCover&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Even though JetBrains says that you can install it from nuget as a package, I have found that if you do that and then try to run &lt;code&gt;dotnet dotcover test&lt;/code&gt; you get the following error message.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/dotcover-not-recognized.png" alt="dotnet dotcover not recognized"&gt;&lt;/p&gt;
&lt;p&gt;To fix this error, you need to add dotCover as a DotNetCliToolReference in the unit tests project file by adding the below &lt;code&gt;&amp;lt;ItemGroup&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;DotNetCliToolReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;JetBrains.dotCover.DotNetCliTool&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;2021.3.3&amp;#34;&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Change the version number above to the latest version available for download at &lt;a href="https://www.jetbrains.com/dotCover/download/" target="_blank" &gt;https://www.jetbrains.com/dotCover/download/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-2-restore-number-packages"&gt;Step 2: Restore Number Packages&lt;/h2&gt;
&lt;p&gt;Now that we have the dotCover cli referenced within the project file as a tool reference, we need to restore the nuget packages by running either &lt;code&gt;dotnet restore&lt;/code&gt; or &lt;code&gt;dotnet build&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open a command line and navigate to your unit test project&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;dotnet restore&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-3-run-test-and-create-test-snapshot"&gt;Step 3: Run Test and Create Test Snapshot&lt;/h2&gt;
&lt;p&gt;Now we are ready to generate our code coverage report.&lt;/p&gt;
&lt;p&gt;From the same command line you already have open run the following command to execute your tests and generate a code coverage report.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet dotcover test --dcReportType=HTML --logger &lt;span class="s2"&gt;&amp;#34;console;verbosity=normal&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/dotcover-run.gif" alt="dotnet dotcover run"&gt;&lt;/p&gt;
&lt;h2 id="step-4-analyze-the-report"&gt;Step 4: Analyze the Report&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;dotCover&lt;/code&gt; command we used in step 3 will generate a report in the same directory that we can it in called dotCover.Output.html&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/aspnet-core-dotcover/dotcover-report.png" alt="dotcover html report"&gt;&lt;/p&gt;
&lt;p&gt;The report will show you which lines of code were covered in your project and you can drill into the source files and see the exact lines that were and were not covered.
&lt;code style="background-color: #e3fbc9;"&gt;&lt;span style="color: #e3fbc9; filter: grayscale(1) invert(1) contrast(100);"&gt;Green&lt;/span&gt;&lt;/code&gt;
text means is was covered and
&lt;code style="background-color: #ffbebe;"&gt;&lt;span style="color: #ffbebe; filter: grayscale(1) invert(1) contrast(100);"&gt;Red&lt;/span&gt;&lt;/code&gt;
text means is was NOT covered.&lt;/p&gt;
&lt;p&gt;The report will allow you to decide if you need to write additional tests or not and to ensure that the tests is actually covering the code that you think it is covering.&lt;/p&gt;
&lt;p&gt;Also, your project team will need to decide on the target code coverage percentage that is your minimum threshold to approve the code to be sent to production.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As a target of 100% code coverage sounds awesome, in a lot of cases this is not a realistic target and the business value to increase the code coverage beyond a certain point does not provide enough additional business value.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In our next post on ASP.NET Code Coverage using dotCover, we will discuss how to filter the report so that it only incudes code that should actually be tested and exclude things like our unit test project, so that our code coverage number is more accurate.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to download the project that I used for this post, you can do so at &lt;a href="https://github.com/digitaldrummerj/aspnet-core-code-coverage-example" target="_blank" &gt;https://github.com/digitaldrummerj/aspnet-core-code-coverage-example&lt;/a&gt;. It is based on .NET 3.1.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Angular - Add Code Coverage to Automated Builds in TeamCity</title><link>https://digitaldrummerj.me/cypress-code-coverage-teamcity/</link><pubDate>Fri, 04 Feb 2022 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-code-coverage-teamcity/</guid><category>Cypress</category><category>testing</category><category>Angular</category><description>&lt;p&gt;Welcome to part two of our two part series on Angular code coverage.&lt;/p&gt;
&lt;p&gt;In the previous article, we set up Cypress code coverage for our Angular project so that we could run it locally on our development machine. In this article, we are going to take it to the next step and add it to our automated build.&lt;/p&gt;
&lt;p&gt;I am a big believer in DevOps and having automated builds and deployments for all of my projects. In fact, I have had automated builds and deployments since 2002, long before DevOps become a thing.&lt;/p&gt;
&lt;p&gt;I will be using TeamCity as the automated build platform and am assuming that you already have your Angular build working and are just adding in code coverage to the build.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This article will be building on the code from the previous article. You can get the code from the previous article at &lt;a href="https://github.com/digitaldrummerj/angular-cypress-code-coverage-example" target="_blank" &gt;https://github.com/digitaldrummerj/angular-cypress-code-coverage-example&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="install-dependencies"&gt;Install Dependencies&lt;/h2&gt;
&lt;p&gt;On our build server, before running any tests, we need to start up the web server and we need to wait for it to respond before kicking off the. Then when the tests are done running we need to stop the web server so that we do not leave it hanging around on our build server.&lt;/p&gt;
&lt;h3 id="install-web-server-dependency"&gt;Install Web Server Dependency&lt;/h3&gt;
&lt;p&gt;To enable us to start up and wait for our web server we are going to install 2 dependencies: &lt;a href="https://www.npmjs.com/package/angular-http-server" target="_blank" &gt;angular-http-server&lt;/a&gt; and &lt;a href="https://www.npmjs.com/package/start-server-and-test" target="_blank" &gt;start-server-and-test&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.npmjs.com/package/angular-http-server" target="_blank" &gt;angular-http-server&lt;/a&gt; allows us to start up the web server in the background so that we can then continue on to run out tests.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.npmjs.com/package/start-server-and-test" target="_blank" &gt;start-server-and-test&lt;/a&gt; allows us to start up the web server and wait for it to respond before running our Cypress tests.&lt;/p&gt;
&lt;p&gt;To install our angular-http-server and start-server-and-test:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a command line and navigate to your Angular project&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following npm command to install them as devDependencies&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --save-dev angular-http-server start-server-and-test
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="install-dependency-to-enable-serialparallel-npm-script-runs"&gt;Install Dependency to Enable Serial/Parallel Npm Script Runs&lt;/h3&gt;
&lt;p&gt;Another really handy npm package for our build scripts is &lt;a href="https://www.npmjs.com/package/npm-run-all" target="_blank" &gt;npm-run-all&lt;/a&gt;. This package allows us to run npm script commands in serial (1 after the other) or parallel (all at the same time) and works cross platform. For build scripts, I run the commands in serial in order to have a clean and easy to read build log that can be read top to bottom to see what is happening in the build. Even though you get slightly quicker builds with running commands in parallel, the build logs end up just being a mess with all of command logs intermingled and virtual impossible to read to figure out what caused a failed build.&lt;/p&gt;
&lt;p&gt;To install npm-run-all:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the command line that you have opened from the previous dependency install run the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --save-dev npm-run-all
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="testing-build-locally"&gt;Testing Build Locally&lt;/h2&gt;
&lt;p&gt;We are now ready to test out our build locally on our development machine. I like to have the ability to run the same build that I would run as part of my automated build locally on my development machine. This also makes it much easier to move to another build provider if needed (yes I have had to do this more than once when a job decide to change their standard platform to use).&lt;/p&gt;
&lt;h3 id="npm-scripts-to-add"&gt;Npm Scripts to Add&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up the package.json file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following scripts to the scripts section of the package.json&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;ci:coverage&amp;#34;: &amp;#34;run-s build:prod:coverage ci:cypress:coverage&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;build:prod:coverage&amp;#34;: &amp;#34;ng build --extra-webpack-config ./cypress/coverage.webpack.js --progress=false&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;ci:cypress:coverage&amp;#34;: &amp;#34;start-server-and-test ci:start-server http://localhost:4200 cy:run:coverage&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;ci:start-server&amp;#34;: &amp;#34;angular-http-server --path ./dist/CypressCodeCoverageDemo -p 4200 --silent&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;cy:run:coverage&amp;#34;: &amp;#34;cypress run --browser chrome --headless --env coverage=true --config video=false&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Description of the scripts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ci:coverage&lt;/strong&gt; - runs all of the npm scripts to run for the CI process&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;build:prod:coverage&lt;/strong&gt; - builds the application with the &amp;ndash;prod flag and with code coverage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ci:cypress:coverage&lt;/strong&gt; - start web server, waits for response, then run tests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ci:start-server&lt;/strong&gt; - runs web server with the Angular prod build&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ci:run:coverage&lt;/strong&gt; - runs the Cypress test with a Chrome headless browser with code coverage turns on and video creation turned off (for performance reasons)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="running-build-locally"&gt;Running Build Locally&lt;/h3&gt;
&lt;p&gt;Now that we have our npm scripts added, we are ready to run our automated build script which is the &lt;code&gt;ci:coverage&lt;/code&gt; script&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm run ci:coverage
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/cypress_run_tests.gif" alt="output of ci:coverage script"&gt;&lt;/p&gt;
&lt;h2 id="teamcity-configuration"&gt;TeamCity Configuration&lt;/h2&gt;
&lt;p&gt;The last thing we need to do is setup TeamCity to run our builds, show us the code coverage metrics, be able to view the code coverage report right in TeamCity and add failure metrics for if code coverage percent drops.&lt;/p&gt;
&lt;h3 id="add-code-coverage-metrics-to-build"&gt;Add Code Coverage Metrics to Build&lt;/h3&gt;
&lt;p&gt;To output the code coverage report and metrics in a format that can be used in TeamCity, we need to add the teamcity reporter to our nyc configuration in the package.json file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;nyc&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;extends&amp;#34;: &amp;#34;@istanbuljs/nyc-config-babel&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;all&amp;#34;: true,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;exclude&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;**/cypress/**&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;**/coverage/**&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;karma.conf.js&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;src/test.ts&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;**/*.spec.ts&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;reporter&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;html&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;teamcity&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By adding the teamcity report, when we run the tests with code coverage enabled, we will see the code coverage summary when viewing the build results.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_build_code_coverage_summary.png" alt="code coverage summary"&gt;&lt;/p&gt;
&lt;h3 id="view-code-coverage-report"&gt;View Code Coverage Report&lt;/h3&gt;
&lt;p&gt;The code coverage summary is great for getting an overall picture of where we are at with out code coverage but it does not allow you to drill into each file to see what was covered or what was not covered. Our build does generate the report but we need to make it visible within TeamCity by adding it as a build report and to the build artifacts.&lt;/p&gt;
&lt;h4 id="adding-build-report"&gt;Adding Build Report&lt;/h4&gt;
&lt;p&gt;Within TeamCity, you need to edit the build settings to add the build report tab.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On the left nav bar, click on the Reports Tabs (&lt;em&gt;Note&lt;/em&gt; if you don&amp;rsquo;t see the Reports Tabs, you might need to go higher in the build hierarchy)
&lt;img src="/images/cypress-code-coverage-ci/tc_report_tabs.png" alt="teamcity build report tab link"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Create new build report tab
&lt;img src="/images/cypress-code-coverage-ci/tc_create_report_buttons.png" alt="teamcity create new build report tab button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the dialog that comes up make the title &amp;ldquo;UI Coverage&amp;rdquo; and the Start page: &amp;ldquo;coverage/index.html&amp;rdquo;
&lt;img src="/images/cypress-code-coverage-ci/tc_build_report_settings.png" alt="teamcity edit report tab settings"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the save button&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now if you view a build, you will see a new tab titled &amp;ldquo;UI Coverage&amp;rdquo; that will contain the code coverage report that we are going to add to the build artifacts in the next section.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_build_tabs.png" alt="teamcity build tabs"&gt;&lt;/p&gt;
&lt;h4 id="adding-code-coverage-report-as-a-build-artifact"&gt;Adding Code Coverage Report as a Build Artifact&lt;/h4&gt;
&lt;p&gt;Now that we have the build tab setup, we need to add the code coverage report as a build artifact, so that the tab is able to display the code coverage report.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Find your TeamCity build that you want to add the code coverage report to and edit the settings&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the general tab, you will see the Artifacts paths&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_artifacts.png" alt="teamcity artifacts configuration"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the artifacts path add the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;coverage =&amp;gt; coverage
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cypress\videos =&amp;gt; cypress\videos
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cypress\screenshots =&amp;gt; cypress\screenshots
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the save button&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="add-failure-metrics"&gt;Add Failure Metrics&lt;/h3&gt;
&lt;p&gt;The last thing that I like to do with my code coverage builds, is to also add failure metrics to the build so that it fails if the code coverage goes down by X percent, the number of skipped tests increases X percent and the number of overall tests goes down by more than a certain percent. For me, the percentage to use for the failure metrics really depends on the number of tests within the project as the more tests you have the lower you want the percent as you will have more tests in the percentage as the number of tests goes up.&lt;/p&gt;
&lt;p&gt;When you are first starting out, I tend to keep it at 5% to 10% and over time I reduce it down to as low as I can go without constantly failing the build. For example, right now we have 800 tests in one of my projects so at 1% that means that we can skip up to 8 tests and remove up to 8 tests. We also have 23,000 lines of code so at 1% it means that we can add up to 230 lines of code that are not called by a test.&lt;/p&gt;
&lt;h4 id="steps-to-add-failure-metrics"&gt;Steps to add failure metrics&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that the failure conditions below use 1% but you can change that to the percent that you want to allow&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Find your TeamCity build that you want to add the failure conditions to and edit the settings&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the left sidebar, click on the Failure Conditions&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_sidebar_failure_conditions.png" alt="teamcity build failure conditions sidebar"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Add failure condition button and select &amp;ldquo;Fail build on metric change&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_add_failure_condition.png" alt="teamcity build failure on metric change"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For ignored tests failure: select Fail build if to number of ignored test and set is more by at least 1 percent and click save
&lt;img src="/images/cypress-code-coverage-ci/tc_failure_conditions_number_of_ignored_tests.png" alt="ignored tests failure condition"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For number of tests failure: select Fail build if to number of tests and set is less by at least 1 percent and click save
&lt;img src="/images/cypress-code-coverage-ci/tc_failure_conditions_number_of_ignored_tests.png" alt="ignored tests failure condition"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For code coverage percent failure: select Fail build if to percentage of statement coverage and set is less by at least 1 percent and click save
&lt;img src="/images/cypress-code-coverage-ci/tc_failure_conditions_number_of_ignored_tests.png" alt="ignored tests failure condition"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="add-build-step"&gt;Add Build Step&lt;/h3&gt;
&lt;p&gt;The last thing we need to do is tell TeamCity to run our cypress tests with code coverage as part of the build.&lt;/p&gt;
&lt;p&gt;Since we create the npm script &lt;code&gt;ci&lt;/code&gt; earlier, it is really quick to add our build step to TeamCity. We just need to create a build step that runs a command line and calls &lt;code&gt;npm run ci&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Warning: if you are planning to take the output of the build step and deploy it to production, you will want to have a production build that does not contain the instrumentation for code coverage as the code coverage will drastically slow down your code as it examines every line of code that is called.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src="/images/cypress-code-coverage-ci/tc_build_step.png" alt="teamcity build step"&gt;&lt;/p&gt;</description></item><item><title>Angular - Add Code Coverage to Your Cypress Tests</title><link>https://digitaldrummerj.me/cypress-code-coverage/</link><pubDate>Thu, 02 Dec 2021 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-code-coverage/</guid><category>Cypress</category><category>testing</category><category>Angular</category><description>&lt;p&gt;Welcome to part one of our two part series on Angular code coverage for Cypress tests.&lt;/p&gt;
&lt;p&gt;As I have implemented more automated tests, one of the must haves for me is code coverage reports. Code coverage allow me to quickly and easily see which lines of the code are not being tested so I can close any critical testing gaps.&lt;/p&gt;
&lt;p&gt;Today, I am going to be talking specifically about how to implement code coverage for an Angular project that was generated from the Angular CLI.&lt;/p&gt;
&lt;h2 id="generate-our-sample-project"&gt;Generate Our Sample Project&lt;/h2&gt;
&lt;p&gt;Before getting started, we need to generate our sample project using the &lt;a href="https://angular.io/cli" target="_blank" &gt;Angular CLI&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ng new CypressCodeCoverageDemo --style scss --routing
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="adding-cypress"&gt;Adding Cypress&lt;/h2&gt;
&lt;p&gt;Now we need to add Cypress to our project&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the CypressCodeCoverageDemo folder that the &lt;code&gt;ng new&lt;/code&gt; command created for us.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; CypressCodeCoverageDemo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we need to install Cypress as a development dependency&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --save-dev cypress
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once Cypress is installed, we need to initialize Cypress by running&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress open
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The Cypress open command will:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create the cypress directory structure&lt;/li&gt;
&lt;li&gt;create a cypress.json configuration file in the root of your project&lt;/li&gt;
&lt;li&gt;create a set of sample tests in the cypress\integration\examples folder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="/images/code-coverage/cypress-open-examples.png" alt="Cypress Open First Time"&gt;&lt;/p&gt;
&lt;p&gt;You can now close the Cypress UI.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also safely delete the cypress\integration\1-getting-started and 2-advanced-examples folders as they are just examples and we won&amp;rsquo;t be using them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="dependencies"&gt;Dependencies&lt;/h2&gt;
&lt;p&gt;We will be using the Cypress code coverage plugin to generate the code coverage reports. Install it from npm as a development dependency by running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --save-dev @cypress/code-coverage
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="instrumenting-your-code"&gt;Instrumenting Your Code&lt;/h2&gt;
&lt;p&gt;In order for you to gather code coverage metrics, the code coverage needs to know which lines of code are being touched by the tests. This is called instrumenting your code.&lt;/p&gt;
&lt;p&gt;Cypress does not instrument your code - you need to do it yourself. We will be using the &lt;a href="https://istanbul.js.org/" target="_blank" &gt;Istanbul&lt;/a&gt; library along with the command-line interface for the &lt;a href="https://istanbul.js.org/" target="_blank" &gt;Istanbul&lt;/a&gt; library, &lt;a href="https://github.com/istanbuljs/nyc" target="_blank" &gt;nyc&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --save-dev @istanbuljs/nyc-config-typescript babel-plugin-istanbul istanbul-lib-coverage nyc source-map-support
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since we are using Angular, we also need to inject the command to instrument our code as part of the Angular build process (&lt;code&gt;ng build&lt;/code&gt;). By default with the Angular CLI there is no way to inject anything extra into the webpack configuration when you build or serve your application but luckily we can use &lt;a href="https://www.npmjs.com/package/ngx-build-plus" target="_blank" &gt;ngx-build-plus&lt;/a&gt; to inject the extra webpack configurations as part of the normal Angular build and serve commands.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WARNING: Never run the build with the code coverage instrumentation as your production build (e.g ng build &amp;ndash;prod). It will cause major performance issues since it tracks each line of code that is called.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To install &lt;a href="https://www.npmjs.com/package/ngx-build-plus" target="_blank" &gt;npx-build-plus&lt;/a&gt; run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ng add ngx-build-plus
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This will install ngx-build-plus and configure angular.json for &lt;code&gt;ng serve&lt;/code&gt; and &lt;code&gt;ng build&lt;/code&gt; to use ngx-build-plus&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="adding-webpack-config"&gt;Adding Webpack Config&lt;/h3&gt;
&lt;p&gt;Once &lt;a href="https://www.npmjs.com/package/ngx-build-plus" target="_blank" &gt;npx-build-plus&lt;/a&gt; is installed, we need to add the configuration that we want webpack to use.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the CypressCodeCoverageDemo folder in your favorite code editor. Personally, I am using &lt;a href="https://code.visualstudio.com/" target="_blank" &gt;Visual Studio Code&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new file in the Cypress folder called &lt;code&gt;coverage.webpack.js&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following code to the &lt;code&gt;coverage.webpack.js&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/\.(ts)$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;babel-loader&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;babel-plugin-istanbul&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;enforce&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;post&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;path&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;..&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;src&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;exclude&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sr"&gt;/node_modules/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sr"&gt;/cypress/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sr"&gt;/(ngfactory|ngstyle)\.js/&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="configuring-the-code-coverage-report"&gt;Configuring the Code Coverage Report&lt;/h2&gt;
&lt;p&gt;Next up we need to configure the &lt;a href="https://github.com/istanbuljs/nyc" target="_blank" &gt;nyc&lt;/a&gt; command line interface for the &lt;a href="https://istanbul.js.org/" target="_blank" &gt;Istanbul&lt;/a&gt; library that determines how our code coverage report is generated.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the package.json file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add a new section at the bottom before the closing &lt;code&gt;}&lt;/code&gt; bracket.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;nyc&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;extends&amp;#34;: &amp;#34;@istanbuljs/nyc-config-babel&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;all&amp;#34;: true,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;exclude&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;**/cypress/**&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;**/coverage/**&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;karma.conf.js&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;src/test.ts&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;**/*.spec.ts&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;reporter&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;html&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;The exclude above just makes the report cleaner by not including directories and files that are related to testing and not the actual production code!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="configure-cypress-code-coverage-plugin"&gt;Configure Cypress Code Coverage Plugin&lt;/h2&gt;
&lt;p&gt;Earlier, we installed the cypress code coverage plugin but we haven&amp;rsquo;t told Cypress about the plugin yet. To use the code coverage plugin, we need to wire it up in the &lt;code&gt;cypress/plugin/index.js&lt;/code&gt; and &lt;code&gt;cypress/support/index.js&lt;/code&gt; files.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the cypress/plugin/index.js file and add the following code inside of the module.exports:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@cypress/code-coverage/task&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;Open the cypress/support/index.js file and add the following code:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;@cypress/code-coverage/support&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="creating-our-first-test"&gt;Creating Our First Test&lt;/h2&gt;
&lt;p&gt;Before we can test our code coverage, we need to create a tests for our Angular application. We are going to create a simple tests that navigates to the home page and makes sure that the H1 tags has the correct text.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the cypress/integration folder, create a new file called &lt;code&gt;homepage.spec.js&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;code&gt;homepage.spec.js&lt;/code&gt; file, add the following text&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Sample Tests for the Home Page&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Does Home Page Load&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;.highlight-card &amp;gt; span&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;contain&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;CypressCodeCoverageDemo app is running!&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="generating-the-code-coverage-report"&gt;Generating the Code Coverage Report&lt;/h2&gt;
&lt;p&gt;We are almost ready to ready to run our code coverage.&lt;/p&gt;
&lt;p&gt;The last thing we need to do before running our tests is to tell Cypress what the url for our Angular application is (e.g. localhost:4200)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;open the cypress.json file in the root of your Angular project&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the baseUrl value instead of the {}&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;baseUrl&amp;#34;: &amp;#34;http://localhost:4200&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we are ready to run our tests.&lt;/p&gt;
&lt;p&gt;First we need to start up the Angular Dev Web Server with ng serve and tell it to include our extra webpack configuration&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Within Visual Studio Code, open the Integrated Terminal (Ctrl+`)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following start the Angular Dev Web Server with the Code Instrumented by running&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ng serve --extra-webpack-config ./cypress/coverage.webpack.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Second, we need to start up the Cypress UI with code coverage enabled.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Within the Integrated Terminal click the + button to create a 2nd terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To run, Cypress with code coverage by running the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress open --env &lt;span class="nv"&gt;coverage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the UI launches, cl;ick on the &amp;ldquo;Run 1 integration spec&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the test runs, it will create a coverage report as well as if you look at the log for the 1 tests that we have, will see 3 messages about the code coverage.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/code-coverage/cypress-code-coverage-in-ui.png" alt="Cypress UI with Coverage Messages"&gt;&lt;/p&gt;
&lt;h2 id="viewing-code-coverage-report"&gt;Viewing Code Coverage Report&lt;/h2&gt;
&lt;p&gt;Once all of your tests are done running, you can find the coverage report in the root of your project in the coverage\index.html file.&lt;/p&gt;
&lt;p&gt;The two numbers that I typically look at are lines and statements. Lines says has the line been touched while statements says has every statement on the line been touched by a test. For example &lt;code&gt;var x = 10;console.log(x)&lt;/code&gt; is 1 line and 2 statements.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/code-coverage/code-coverage-report-ui.png" alt="Code Coverage Report"&gt;&lt;/p&gt;
&lt;p&gt;You can also click on any folder or file and drill down in to see which lines of code were covered and which lines were not covered.&lt;/p&gt;
&lt;h2 id="recap"&gt;Recap&lt;/h2&gt;
&lt;p&gt;Lets do a quick recap of what we accomplished.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Installed the Cypress Code Coverage plugin&lt;/li&gt;
&lt;li&gt;Added instrumentation dependencies of istanbul and nyc&lt;/li&gt;
&lt;li&gt;Added an extra webpack config to call babel-plugin to instrument your code&lt;/li&gt;
&lt;li&gt;Updated the Angular build to use ngx-build-plus&lt;/li&gt;
&lt;li&gt;Configured the Cypress Code Coverage plugin&lt;/li&gt;
&lt;li&gt;Generated the code coverage report&lt;/li&gt;
&lt;li&gt;Reviewed the code coverage report&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;The code for this article is available at &lt;a href="https://github.com/digitaldrummerj/angular-cypress-code-coverage-example" target="_blank" &gt;https://github.com/digitaldrummerj/angular-cypress-code-coverage-example&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In our next article, we will add the running of the code coverage report to TeamCity builds, set up failure metrics, and be able to view the report within TeamCity.&lt;/p&gt;</description></item><item><title>Schedule Post with Hugo and Netlify</title><link>https://digitaldrummerj.me/hugo-scheduled-post/</link><pubDate>Tue, 30 Nov 2021 12:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hugo-scheduled-post/</guid><category>hugo</category><description>&lt;p&gt;Being able to schedule a post is one of the features that I miss when using Hugo for a site.&lt;/p&gt;
&lt;p&gt;Out of the box, Hugo has no way to schedule a post as it is a static site generator which means that when you build the site the html is generated and only updated when you build the site again.&lt;/p&gt;
&lt;p&gt;The manual workaround for the scheduling of post is to create a published post with a future date on it and then on that day, rebuild and redeploy your site. Even though this works, it depends on me to remember to do it vs it being automatic which is what I personally want. I do not want to have to remember to build and deploy the site on the day that I want a post to be published. If it depends on me, then changes are that I am going to forget or get busy and the post won&amp;rsquo;t get published when it is supposed to be.&lt;/p&gt;
&lt;p&gt;Luckily, since I am already using Netlify to build my Hugo site and with the code for the site residing on Github, I can easily create a scheduled Github action that triggers the Netlify build for me.&lt;/p&gt;
&lt;p&gt;The Github action will run on a set schedule. For this site, I decide to have it run every day at 6 am PST. This allows me to publish a post 7 days a week if I needed to. I am using the free tier of both Github and Netlify but since the builds run so quickly (&amp;lt; 60 seconds), I am not worried about running out of build hours.&lt;/p&gt;
&lt;p&gt;Also, the reason that we need to use a Github action instead of just directly scheduling the build in Netlify is that at this time, Netlify does not have the ability to schedule a build. However, Netlify does allow you to trigger a build with a REST POST call. This means thst we can create a Github action that use curl to make this POST call.&lt;/p&gt;
&lt;p&gt;To make all of this work, in Netlify we need to add the build hook that allow us to make the post call, then we need to setup a Github secret with the Netlify token for the build hook, and finally we need to create the Github action that calls the Netlify build hook and gets the token from the Github secrets.&lt;/p&gt;
&lt;h2 id="setup-netlify-build"&gt;Setup Netlify Build&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I am assuming that you already have your Netlify build working to build and deploy your Hugo site.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To enable a Netlify build to be triggered using a REST POST command, we need to add a build hook.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Netlify under your site that you want to trigger the build for go toSite Settings -&amp;gt; Build &amp;amp; Deploy -&amp;gt; Continuous Deployment&lt;/li&gt;
&lt;li&gt;Find the Build Hooks section.&lt;/li&gt;
&lt;li&gt;Click the “Add Build Hook” button&lt;/li&gt;
&lt;li&gt;Give the build a name such as “Github Scheduled Build”&lt;/li&gt;
&lt;li&gt;Pick the Branch to Build (typically main)&lt;/li&gt;
&lt;li&gt;Click the Save button&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After you save the build hook, Netlify will give you a url for the build such as &lt;code&gt;https://api.netlify.com/build_hooks/123456789&lt;/code&gt; where the 123456789 is the Netlify token for the build that we will add into our Github secrets.&lt;/p&gt;
&lt;p&gt;It will also allow you to trigger the build from the command line using &lt;code&gt;curl -X POST -d {} https://api.netlify.com/build_hooks/123456789&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The token should be kept in your build secrets and not stored with your code or Github action.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We are now ready to head over to Github to setup our secret for the Netlify token.&lt;/p&gt;
&lt;h2 id="stores-netlify-token-in-github"&gt;Stores Netlify Token in GitHub&lt;/h2&gt;
&lt;p&gt;In Github, go to the repository for your Hugo site and then:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click on Settings&lt;/li&gt;
&lt;li&gt;Click on secrets&lt;/li&gt;
&lt;li&gt;Click the &amp;ldquo;New repository secret&amp;rdquo; button&lt;/li&gt;
&lt;li&gt;Give it a name. In the example Github action below, I called the secret for the token, NETLIFY_CRON_BUILD_HOOK&lt;/li&gt;
&lt;li&gt;Copy the Netlify token to the value field&lt;/li&gt;
&lt;li&gt;Click the Add Secret button&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you are ready to create the Github action to call the Netlify build hook to trigger the build.&lt;/p&gt;
&lt;h2 id="create-github-action"&gt;Create GitHub Action&lt;/h2&gt;
&lt;p&gt;The Github action is pretty simple. It runs every day at 6 am PST (1 pm UTC) and calls the Netlify build hook.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Github scheduled actions run in UTC time&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To create the Github action follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Under your Hugo site, create a directory named .github/workflows&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the workflows directory, create a file named schedule.yml&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the following code to the schedule.yml file&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Daily Schedule&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;0 13 * * *&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Trigger our build webhook on Netlify&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;curl -s -X POST &amp;#34;https://api.netlify.com/build_hooks/${TOKEN}&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.NETLIFY_CRON_BUILD_HOOK }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Commit and push the .github directory&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now your Hugo site will be updated every day at 1 pm UTC (6 am PST) when the Github action runs.&lt;/p&gt;
&lt;h2 id="create-post-to-be-scheduled"&gt;Create Post To Be Scheduled&lt;/h2&gt;
&lt;p&gt;Now that we have the Github action, to create a scheduled post, all you need to do is set the meta data on the post for published to true and the date of the post to be on the day in the future that you want the post to go live (make sure the time is before 6 am PST).&lt;/p&gt;
&lt;p&gt;Here is an example of the data for this post that will be published on November 24, 2021 at 12 pm&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;—&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;categories&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;“hugo”]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;published&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ld"&gt;2021-11-24T12:00:00Z&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Schedule Post with Hugo and Netlify&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;—&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can schedule your post on your Hugo based site.&lt;/p&gt;</description></item><item><title>Migrating Angular from tslint to to eslint</title><link>https://digitaldrummerj.me/angular-migrate-to-eslint/</link><pubDate>Tue, 23 Nov 2021 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/angular-migrate-to-eslint/</guid><category>Angular</category><description>&lt;p&gt;Since the Angular CLI was released it has included linting using the &lt;code&gt;ng lint&lt;/code&gt; command. With the release of Angular v11 it was announced that tslint which &lt;code&gt;ng lint&lt;/code&gt; used behind the scenes for the linting was being replaced with eslint.&lt;/p&gt;
&lt;p&gt;To make the migration to eslint easier for your existing project they created a couple of tools that automate almost the whole migration process for us.&lt;/p&gt;
&lt;p&gt;I was able to finish the migration start to finish in about 30 minutes.&lt;/p&gt;
&lt;h2 id="installing-dependencies"&gt;Installing Dependencies&lt;/h2&gt;
&lt;p&gt;The first step is to add the &lt;a href="https://github.com/angular-eslint/angular-eslint" target="_blank" &gt;@angular-eslint schematic&lt;/a&gt; to your project. This schematic will handle installing the latest version of all the relevant devDependencies to your package.json so that you can run eslint.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ng add @angular-eslint/schematics
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;This will install the latest package which is compatible with Angular v13 (as of this writing). If you need a version that is compatible with a previous Angular version you can add a @ at the end followed by the major Angular version number. For example for Angular v11 use &lt;code&gt;ng add @angular-eslint/schematics@11&lt;/code&gt; and for Angular v12 use &lt;code&gt;ng add @angular-eslint/schematics@12&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You are now ready to do the actual migration.&lt;/p&gt;
&lt;h2 id="running-the-migration"&gt;Running the Migration&lt;/h2&gt;
&lt;p&gt;To make the migration easier they created another schematic that automates pretty much the whole migration for us.&lt;/p&gt;
&lt;p&gt;If you just have a single project in your workspace you can just run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ng g @angular-eslint/schematics:convert-tslint-to-eslint
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Make sure to install the same major version of this schematic using the same @ trick as above.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you have a projects/directory or similar in your workspace, you will have multiple entries in your projects configuration and you will need to chose which one you want to migrate using the convert-tslint-to-eslint schematic:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ng g @angular-eslint/schematics:convert-tslint-to-eslint &lt;span class="o"&gt;{&lt;/span&gt;PROJECT_NAME&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Make sure to install the same major version of this schematic using the same @ trick as above.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The schematic will do the following for you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Read your project&amp;rsquo;s tslint.json and use it to create a .eslintrc.json at the root of the specific project which extends from the root config (if you do not already have a root config, it will also add one automatically for you).
&lt;ul&gt;
&lt;li&gt;.eslintrc.json will be the closest possible equivalent to your tslint.&lt;/li&gt;
&lt;li&gt;Keep an eye on the terminal output because it will let you know if it couldn&amp;rsquo;t find an appropriate converter for a tslint rule, or if it has installed any additional eslint plugins.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Update the project&amp;rsquo;s angular.json so that lint will use eslint.&lt;/li&gt;
&lt;li&gt;Update any tslint:disable comments that are located within your TypeScript source files to eslint equivalent if possible.&lt;/li&gt;
&lt;li&gt;If you choose YES for the &amp;ndash;remove-tslint-if-no-more-tslint-targets option, it will also automatically remove tslint and codelyzer for you.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You are now ready to test that linting still works.&lt;/p&gt;
&lt;h2 id="test-linting-is-still-working"&gt;Test Linting is Still Working&lt;/h2&gt;
&lt;p&gt;Now that we have updated to eslint we need to make sure that the linting configuration is working.&lt;/p&gt;
&lt;p&gt;To run linting on your project you still use the ng lint command like you did before.&lt;/p&gt;
&lt;p&gt;If you have a single project you can just run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx ng lint
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have multiple projects, you need to tell ng lint which project to lint:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx ng lint &lt;span class="o"&gt;{&lt;/span&gt;PROJECT_NAME&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may still need to manually fix some configurations. The linting output will throw errors for anything that it cannot figure out what to do with that needs manual intervention.&lt;/p&gt;
&lt;p&gt;For my projects, it migrated 99% of the linting configuration. I only had a couple of places in my code where I had an inline tslint:disable rule that the migration was not able to figure what to do with.&lt;/p&gt;
&lt;h2 id="disabling-rules-that-you-do-not-want"&gt;Disabling Rules That You Do Not Want&lt;/h2&gt;
&lt;p&gt;You might also decide that you want to alter a built-in rule after you run &lt;code&gt;ng lint&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The first time I ran &lt;code&gt;ng lint&lt;/code&gt; after the migration, I had a like 100 warnings about empty Angular lifecycle events which meant that I had ngOnInit or other Angular lifecycle methods that had no code within the method.&lt;/p&gt;
&lt;p&gt;For me this rule did not provide any value to use so my project decided to disable the rule by adding it to the .eslintrc.json overrides rules configuration.&lt;/p&gt;
&lt;p&gt;To disable the the Angular empty lifecycle event rule, you need to add it to the overrides rules section in your .eslintrc.json configuration file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;overrides&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;rules&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;@angular-eslint/no-empty-lifecycle-method&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;off&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have any other rules that are flagging that you want to adjust, you can add them to the overrides rules. You might&lt;/p&gt;
&lt;p&gt;Now your project is migrated to eslint and your linting command will continue to run as it always has.&lt;/p&gt;</description></item><item><title>Solved: Angular RxJs Debounce Not Consistently Firing When Testing With Cypress</title><link>https://digitaldrummerj.me/cypress-angular-debounce/</link><pubDate>Thu, 18 Nov 2021 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-angular-debounce/</guid><category>testing</category><category>Cypress</category><category>Angular</category><description>&lt;p&gt;In your Angular application if you are using &lt;a href="https://rxjs.dev/api/operators/debounce" target="_blank" &gt;RxJS Debounce&lt;/a&gt; and running Cypress test you may have run into times that your tests are not consistently getting past the debounce wait time and appear like they are flaky tests.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Debounce is a way to wait X number of milliseconds for something to happen before continuing such waiting for a user to stop typing in a field before making an API call. This way you are not making an API call for each character typed into the field.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In Cypress, you could just use a wait statement to get past the debounce time but adding time based wait statements in Cypress is an anti-pattern.&lt;/p&gt;
&lt;p&gt;Instead in Cypress you should use the &lt;code&gt;cy.clock() and cy.tick()&lt;/code&gt; commands to be able to forward the virtual time and cause debounce to fire. However, I found it was not consistently getting past the debounce. RxJS was acting like we had not waited for the debounce time.&lt;/p&gt;
&lt;p&gt;Luckily, after much troubleshooting the solution ended up being quite simple and only involved test code changes.&lt;/p&gt;
&lt;h2 id="broken-code"&gt;Broken Code&lt;/h2&gt;
&lt;p&gt;In the code below, after the &lt;code&gt;cy.tick(501)&lt;/code&gt; we would find that the call to the API never happened so it would fail on the wait statement. We also noticed that this tended to pass when running locally on our dev machines but would fail on our CI servers. This made it really difficult to replicate and troubleshoot since it appeared to be some kind of a race condition where our dev machines would run slower than our CI servers were.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/api/v1/collections/*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;false&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;nameCheck&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[data-cy=&amp;#34;collectionTitle&amp;#34;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txtMax255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;501&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@nameCheck&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="working-code"&gt;Working Code&lt;/h2&gt;
&lt;p&gt;The fix was pretty easy once we understood what we happening. With &lt;a href="https://docs.cypress.io/api/commands/clock" target="_blank" &gt;cy.clock()&lt;/a&gt; it overrides the native global functions related to time allowing them to be controlled synchronously via &lt;code&gt;cy.tick()&lt;/code&gt;. This includes controlling:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;setTimeout&lt;/li&gt;
&lt;li&gt;clearTimeout&lt;/li&gt;
&lt;li&gt;setInterval&lt;/li&gt;
&lt;li&gt;clearInterval&lt;/li&gt;
&lt;li&gt;Date objects&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Overriding the setTimeout, clearTimeout, setInterval, and clearInterval is where the issue came in since those are key to the way debounce works. So we just needed to set the date and override the Date object.&lt;/p&gt;
&lt;p&gt;In the working code below, the only change that we made from the broken code above was to change &lt;code&gt;cy.clock()&lt;/code&gt; to &lt;code&gt;cy.clock(new Date(), ['Date']);&lt;/code&gt; and then our debounce worked as expected.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Date&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/api/v1/collections/*&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;false&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;as&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;nameCheck&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[data-cy=&amp;#34;collectionTitle&amp;#34;]&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txtMax255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;501&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@nameCheck&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now all of our debounce calls are working as expected in our Cypress tests.&lt;/p&gt;</description></item><item><title>Solved: Use Previous Version of Chrome to Prevent Instant Cypress Crash on Our Build Servers When Using Chrome 95</title><link>https://digitaldrummerj.me/cypress-custom-browser/</link><pubDate>Mon, 15 Nov 2021 13:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-custom-browser/</guid><category>testing</category><category>Cypress</category><description>&lt;p&gt;Recently, on my build servers, the Chrome version update to Chrome 95 and all of a sudden my automated builds stopped being able to communicate between Cypress and Chrome and would instantly cause Cypress to crash on the 1st test when using &lt;code&gt;cypress run&lt;/code&gt;. We could login to the machine and run all of the tests using Chrome as a headed browser but anytime we tried to use Chrome as a headless browser it would instantly fail the build on the 1st tests.&lt;/p&gt;
&lt;p&gt;We have seen other random issues like this show up with Chrome updates in the past and previously the fix was to disable the Chrome auto-update feature and then install a previous version of Chrome. This was a hack though as there is no supported way to downgrade Chrome.&lt;/p&gt;
&lt;p&gt;Thankfully, there is an easier way by using a custom browser when running Cypress. Cypress even maintains a Chromium repository so that you can easily download previous versions of Chrome.&lt;/p&gt;
&lt;p&gt;The first step to running a custom browser with Cypress is to download the previous version of Chromium that we wanted to use for our tests. To download a previous version of Chromium, you need to navigate to &lt;a href="https://chromium.cypress.io/" target="_blank" &gt;Cypress Chromium Site&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; All of the download links on the Cypress Chromium site point directly to Google so you are downloading real versions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On the &lt;a href="https://chromium.cypress.io/" target="_blank" &gt;Cypress Chromium Site&lt;/a&gt; I recommend that you filter the &amp;ldquo;Release Channel&amp;rdquo; by stable so that you only get the versions that have been released to production. You can also filter by the version that you are looking for to make it easier to find the version you want.&lt;/p&gt;
&lt;p&gt;Once you find the version that you want, click on the &amp;ldquo;Get downloads&amp;rdquo; link and then download the zip file.&lt;/p&gt;
&lt;p&gt;Once the download is complete, unzip it to a directory. I like to unzip it into c:\Chromium (I am on Windows) since I can be sure that all of my co-workers and build server will have directory available.&lt;/p&gt;
&lt;p&gt;Now to use the custom browser with &lt;code&gt;cypress run&lt;/code&gt; you to give the location of the custom browser as the browser name in the &lt;code&gt;--browser&lt;/code&gt; argument.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress run --browser C:\Chromium\94.0.4606.81\chrome.exe --headless
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now Cypress will use the browser located at &amp;ldquo;C:\Chromium\94.0.4606.81\chrome.exe&amp;rdquo; when it executed the tests.&lt;/p&gt;
&lt;p&gt;You can also use the custom broswer with &lt;code&gt;cypress open&lt;/code&gt; by using the same &lt;code&gt;--browser&lt;/code&gt; argument that we used with the run command above but without the &lt;code&gt;—headless&lt;/code&gt; argument.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cmd" data-lang="cmd"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress open --browser C:\Chromium\94.0.4606.81\chrome.exe
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Cypress - How to run the same test again and again to confirm it is flake-free</title><link>https://digitaldrummerj.me/cypress-grep-burn-in/</link><pubDate>Thu, 11 Nov 2021 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-grep-burn-in/</guid><category>testing</category><category>Cypress</category><description>&lt;p&gt;So far with the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt; we have looked at how to run tests with certain tags, how to run tests that have no tags, and how to increase the performance of the plugin when running filtered tests.&lt;/p&gt;
&lt;p&gt;In this post, we are going to look at how to run tests multiple times to ensure that they are flake free. I have several times run into issues where we think a tests is working great only to find out if you run it a 2nd time that it fails or causes other tests to fail since Cypress without the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt; will only run each passing tests once.&lt;/p&gt;
&lt;p&gt;In the initial &lt;a href="/cypress-grep" &gt;Cypress Grep&lt;/a&gt; post in this series we installed and setup the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt;. If you do not have the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt; installed and setup, you will need to go through that post before continuing.&lt;/p&gt;
&lt;p&gt;As we saw in the previous post, we can filter our Cypress tests with the Cypress grep plugin like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// &lt;span class="nb"&gt;enable&lt;/span&gt; the tests with tag &lt;span class="s2"&gt;&amp;#34;one&amp;#34;&lt;/span&gt; or &lt;span class="s2"&gt;&amp;#34;two&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress run --env &lt;span class="nv"&gt;grepTags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;one two&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// &lt;span class="nb"&gt;enable&lt;/span&gt; the tests with &lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt; in the title
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress run --env &lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hello
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now in order to tell the Cypress grep plugin to run the tests a certain number of times we need to add the &lt;code&gt;burn&lt;/code&gt; argument and tell it the number of times to run.&lt;/p&gt;
&lt;p&gt;For example, to run each tests 5 times, we would use the argument &lt;code&gt;burn=5&lt;/code&gt; like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// &lt;span class="nb"&gt;enable&lt;/span&gt; the tests with tag &lt;span class="s2"&gt;&amp;#34;one&amp;#34;&lt;/span&gt; or &lt;span class="s2"&gt;&amp;#34;two&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress run --env &lt;span class="nv"&gt;grepTags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;one two&amp;#34;&lt;/span&gt;,burn&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// &lt;span class="nb"&gt;enable&lt;/span&gt; the tests with &lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt; in the title and tag &lt;span class="s2"&gt;&amp;#34;smoke&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress run --env &lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hello,burn&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You might also be asking yourself why not just use the &lt;a href="https://docs.cypress.io/guides/guides/test-retries" target="_blank" &gt;test retries&lt;/a&gt;. Test retries is used when a tests fails and you want to see if a rerun of the tests makes the tests pass but if the tests passes the first time it will not run the tests a second time. With the burn option of Cypress grep, it will run the tests the number of times you tell it to regardless if it passes or not.&lt;/p&gt;</description></item><item><title>Cypress Grep - Faster Test Execution</title><link>https://digitaldrummerj.me/cypress-grep-performance/</link><pubDate>Mon, 08 Nov 2021 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-grep-performance/</guid><category>testing</category><category>Cypress</category><description>&lt;p&gt;By default, for the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt; when using the grep and grepTags all of the specs are executed and then each the filters are applied. This can be very wasteful, if only a few specs contain the grep in the test titles. Thus when doing the positive grep, you can pre-filter specs using the grepFilterSpecs=true parameter.&lt;/p&gt;
&lt;h2 id="examples"&gt;Examples&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt; To filter all specs first and then only run the suite or tests that match the title &amp;ldquo;it loads&amp;rdquo;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress run --env &lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;it loads&amp;#34;&lt;/span&gt;,grepFilterSpecs&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt; To filter all spec files and only run the specs with the tag &amp;ldquo;@smoke&amp;rdquo;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npx cypress run --env &lt;span class="nv"&gt;grepTags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;@smoke,grepFilterSpecs&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="notes"&gt;Notes&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note 1:&lt;/strong&gt; this requires installing this plugin in your project&amp;rsquo;s plugin file like we did in our first &lt;a href="/cypress-grep" &gt;Cypress Grep Plugin&lt;/a&gt; post.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note 2:&lt;/strong&gt; the grepFilterSpecs option is only compatible with the positive grep and grepTags options, not with the negative &amp;ldquo;!&amp;hellip;&amp;rdquo; filter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note 3&lt;/strong&gt; if there are no files remaining after filtering, the plugin prints a warning and leaves all files unchanged to avoid the test runner erroring with &amp;ldquo;No specs found&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="tip"&gt;Tip&lt;/h2&gt;
&lt;p&gt;You can set this environment variable in the cypress.json file to enable it by default and skip using the environment variable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;env&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;grepFilterSpecs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By implement the grepFilterSpecs, you will get much better performance from Cypress grep as your tests suite grows in size.&lt;/p&gt;</description></item><item><title>Cypress Run Tests That Do Not Have Any Tags</title><link>https://digitaldrummerj.me/cypress-grep-no-tags/</link><pubDate>Wed, 03 Nov 2021 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-grep-no-tags/</guid><category>testing</category><category>Cypress</category><description>&lt;p&gt;In the previous post on the &lt;a href="/cypress-grep" &gt;Cypress Grep Plugin&lt;/a&gt; we installed and went through the basics of how to run just tests that have certain tags but what if you want to run all tests that do not have any tags?&lt;/p&gt;
&lt;p&gt;Luckily, it is really easy to run all tests that are not tagged by using the &lt;code&gt;grepUntagged=true&lt;/code&gt; parameter when running Cypress.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; npx cypress run --env &lt;span class="nv"&gt;grepUntagged&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Cypress Run Tests That Have Certain Tags</title><link>https://digitaldrummerj.me/cypress-grep/</link><pubDate>Mon, 01 Nov 2021 09:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/cypress-grep/</guid><category>testing</category><category>Cypress</category><description>&lt;p&gt;One of the features that I wish Cypress had is a way to group feature tests together so that I can run all tests for the feature I am currently coding or testing without having to put them all into the same spec file. Now you can with the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="usage"&gt;Usage&lt;/h2&gt;
&lt;p&gt;With the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt;, you can add a tags config to each tests or describe like below that then you can use to only run tests with those tags.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;works as an array&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;config&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;some-other-tag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;works as a string&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;config&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;block with config tag&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;@smoke&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then when you call Cypress run you can use the &amp;ndash;env parameter to filter the tests like so&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// enable the tests with tag &amp;#34;one&amp;#34; or &amp;#34;two&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;cypress&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="nx"&gt;grepTags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;one two&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// enable the tests with both tags &amp;#34;one&amp;#34; and &amp;#34;two&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;cypress&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="nx"&gt;grepTags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;one+two&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// enable the tests with &amp;#34;hello&amp;#34; in the title and tag &amp;#34;smoke&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;cypress&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;grepTags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;smoke&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// run all tests with &amp;#34;hello world&amp;#34; or &amp;#34;auth user&amp;#34; in their title
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;cypress&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="nx"&gt;grep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hello world; auth user&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="install"&gt;Install&lt;/h2&gt;
&lt;p&gt;Assuming that you have Cypress already installed, you add the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt; as a dev dependency.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --save-dev cypress-grep
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="support-file"&gt;Support File&lt;/h3&gt;
&lt;p&gt;Once the npm install is completed, you need to add it to the support file.&lt;/p&gt;
&lt;p&gt;Open the support/index.js file and add the following line&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// cypress/support/index.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cypress-grep&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)()&lt;/span&gt;&lt;span class="sb"&gt;```
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="plugin-file"&gt;Plugin File&lt;/h3&gt;
&lt;p&gt;Load and register the Cypress grep module in the plugin/index.js file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// cypress/plugins/index.js
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cypress-grep/src/plugin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// make sure to return the config object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// as it might have been modified by the plugin
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By adding the module to the plugin file, when you run tests with Cypress grep it will print out a message on load such as&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ npx cypress run --env &lt;span class="nv"&gt;grep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hello
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cypress-grep: tests with &lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt; in their names
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You are now ready to start using the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In a future post we will look at some of the other features of the &lt;a href="https://github.com/cypress-io/cypress-grep" target="_blank" &gt;Cypress grep plugin&lt;/a&gt; such as how to increase the performance by pre-filtering or run tests multiple times.&lt;/p&gt;</description></item><item><title>Angular - Running SSL Locally</title><link>https://digitaldrummerj.me/angular-local-ssl/</link><pubDate>Thu, 30 Sep 2021 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/angular-local-ssl/</guid><category>angular</category><description>&lt;p&gt;These days all of our websites are running using https and we should be doing our local development work also using SSL. When you create your Angular project, it uses http by default but has the ability to easily run uder SSL as long as your have a certificate for Angular to use.&lt;/p&gt;
&lt;p&gt;Luckily, it is really really easy to generate our own self-signed certificate to use for local development. A self-signed certificate just means that you personally signed the certificate to say it is valid and not one of the trusted authorities on the Internet which is why self-signed certificates only work for your local development.&lt;/p&gt;
&lt;p&gt;In this article we will create your own self-signed certificate, tell Windows to trust our certificate and tell Angular to use our certificate for our local development work.&lt;/p&gt;
&lt;h2 id="creating-the-certificate"&gt;Creating the Certificate&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file called certificate.config with the following content&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[req]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;default_bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;no&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;default_md&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;sha256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;x509_extensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;v3_req&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;distinguished_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;dn&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[dn]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;C&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;US&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;AZ&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;L&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Phoenix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;O&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;My Organization&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;OU&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;My Organization Unit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;emailAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;email@domain.com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;CN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[v3_req]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;subjectAltName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@alt_names&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[alt_names]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;DNS.1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, we can use OpenSSL to create the certificate. I&amp;rsquo;m doing this on Windows using Git Bash and using the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl req -new -x509 -newkey rsa:2048 -sha256 -nodes -keyout localhost.key -days &lt;span class="m"&gt;3560&lt;/span&gt; -out localhost.crt -config certificate.config
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The output of the openssl command should look like&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/angular-ssl/openssl-output.png" alt="openssl output"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="running-angular-cli-using-https"&gt;Running Angular CLI using https&lt;/h2&gt;
&lt;p&gt;Now that we have our certificate files, it is simple to configure Angular CLI to use these certificates to serve over https. Since these a self-signed certificates, I stored the certificates in a certs folder under my Angular project directory.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To run ng serve with ssl using&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ng serve --ssl --ssl-key .&lt;span class="se"&gt;\\&lt;/span&gt;certs&lt;span class="se"&gt;\\&lt;/span&gt;localhost.key --ssl-cert .&lt;span class="se"&gt;\\&lt;/span&gt;certs&lt;span class="se"&gt;\\&lt;/span&gt;localhost.crt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now open your browser and navigate to &lt;a href="https://localhsot:4200" target="_blank" &gt;https://localhost:4200&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When you navigate to the URL in Google Chrome you will be presented with the following warning about the connection not being private.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/angular-ssl/chrome-invalid-cert.png" alt="Chrome your connection is not private"&gt;&lt;/p&gt;
&lt;p&gt;This warning is due to us using a self-signed certificate that is not in the Trusted Root Certification Authorities store. Now you can click the advanced button and then proceed to the site but frankly that is annoying to have to do everytime.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/angular-ssl/chrome-invalid-cert-proceed.png" alt="Chrome proceed to site"&gt;&lt;/p&gt;
&lt;h2 id="trusting-the-certificate-on-windows"&gt;Trusting the certificate on Windows&lt;/h2&gt;
&lt;p&gt;To get past the Chrome warning about the connection not being private we need to the certificate that we created earlier to the Trusted Root Certification Authorities store.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the certs directory under our project and open up the localhost.crt file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Install Certificate button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/angular-ssl/install-certificate.png" alt="install certificate button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the Certificate Import Wizard comes up, select Current User as the Store Location and click Next&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/angular-ssl/install-certificate-2-current-user.png" alt="certificate import wizard current user"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select &amp;ldquo;Place all certificates in the following store&amp;rdquo; then click the Browse button and select &amp;ldquo;Trusted Root Certification Authorities&amp;rdquo; and click Next&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/angular-ssl/install-certificate-3-trusted-root.png" alt="certificate import wizard Trusted Root Certification Authorities"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Finish and you will be presented with a security warning dialog about the self-signed certificate that you need to click Yes to install the certificate&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/angular-ssl/install-certificate-4-security-warning.png" alt="security warning"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Ok to close the Certificate Import Wizard&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Ok to close the Certificate dialog&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to &lt;a href="https://localhost:4200" target="_blank" &gt;https://localhost:4200&lt;/a&gt; and your connection should now be secure with the certificate being trusted.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the certificate is still not trusted, you will need to close all of your Chrome instances and then open back up Chome and go to &lt;a href="https://localhost:4200" target="_blank" &gt;https://localhost:4200&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you can do all of your Angular development using SSL just like you will be using in your production environment which will help ensure that everything will work that same when you go into production.&lt;/p&gt;</description></item><item><title>Git - Remove Local Branches That Are Merged or No Longer Exist</title><link>https://digitaldrummerj.me/git-remove-local-merged-branches/</link><pubDate>Sun, 19 Sep 2021 12:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/git-remove-local-merged-branches/</guid><category>git</category><description>&lt;p&gt;After a while your list of local git branches can get a bit out of control especially if you doing all of your development on a branch, creating a pull request, merging it to main and then deleting the remote git branch when it is merged into main. Once the branch is deleted on the remote repository there is no need to keep it locally anymore.&lt;/p&gt;
&lt;p&gt;Below is the command to delete all local branches that have been merged into the main branch. If you git trunk branch is not main or you want to remove all branches that have been merged into a different branch than main, just change the 2 places in the command that say main to what your branch name is.&lt;/p&gt;
&lt;h2 id="deleting-branches-merged-into-main"&gt;Deleting Branches Merged into Main&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open git bash and navigate to your git repository that you want to clean up&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fetch the latest from the git&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git fetch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;See the list of local git branches&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Delete all local branches that have been merged to main branch&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch --merged main &lt;span class="p"&gt;|&lt;/span&gt; grep -v &lt;span class="s2"&gt;&amp;#34;^\* main&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs -n &lt;span class="m"&gt;1&lt;/span&gt; -r git branch -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;See the list of local git branches that remain&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="deleting-local-branches-that-no-longer-exist-on-the-remote"&gt;Deleting Local Branches That No Longer Exist on the Remote&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open git bash and navigate to your git repository that you want to clean up&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fetch the latest from the git&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git fetch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;See the list of local git branches&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Delete all local branches that have been merged to main branch&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch -vv &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;: gone]&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -v &lt;span class="s1"&gt;&amp;#39;\*&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{ print $1; }&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; xargs -r git branch -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;See the list of local git branches that remain&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git branch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>VSCode: Bracket Pair Colorization Now Native</title><link>https://digitaldrummerj.me/vscode-bracket-pair-colorization/</link><pubDate>Tue, 07 Sep 2021 09:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vscode-bracket-pair-colorization/</guid><category>VSCode</category><media:content medium="image" url="https://digitaldrummerj.me/images/vscode-bracket-pair-colorization/bracket-pair-colorization-on-off.drawio.png"/><description>
&lt;img src="/images/vscode-bracket-pair-colorization/bracket-pair-colorization-on-off.drawio.png" /&gt;&lt;p&gt;One of the extensions for VSCode that I have used for the past couple of years is &lt;a href="https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer-2" target="_blank" &gt;Bracket Pair Colorizer 2&lt;/a&gt; to color each of the bracket pairs a different color so that you can visually see the start/end bracket.&lt;/p&gt;
&lt;p&gt;With the VSCode August 2021 (ver 1.60.0) update, this feature is now built into VSCode. You just need to turn it on.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/vscode-bracket-pair-colorization/bracket-pair-colorization-on-off.drawio.png" alt="Bracket Pair On/Off"&gt;&lt;/p&gt;
&lt;h2 id="update-vscode"&gt;Update VSCode&lt;/h2&gt;
&lt;p&gt;To get the VSCode August 2021 upgrade for VSCode:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open VSCode&lt;/li&gt;
&lt;li&gt;Click on the Help Menu&lt;/li&gt;
&lt;li&gt;Click on Check for Updates or Install Update (if you have a pending a update install)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="turn-on-native-bracket-pair-colorization"&gt;Turn On Native Bracket Pair Colorization&lt;/h2&gt;
&lt;p&gt;Once you have the VSCode August 2021 update installed, you need to turn on the native bracket pair colorizer.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In VSCode, go under the File Menu -&amp;gt; Preferences -&amp;gt; Settings&lt;/li&gt;
&lt;li&gt;Search for the setting: bracketPair&lt;/li&gt;
&lt;li&gt;Click the checkbox to turn on the bracket pair colorization
&lt;img src="/images/vscode-bracket-pair-colorization/bracket-pair-settings.png" alt="Bracket Pair Setting"&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="uninstall-the-bracket-pair-colorization-2-extension"&gt;Uninstall the Bracket Pair Colorization 2 Extension&lt;/h2&gt;
&lt;p&gt;Now that Bracket Pair Colorization is native to VSCode, you can uninstall the Bracket Pair Colorization.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In VSCode, Click on the Extensions Icon in the left navigation bar&lt;/li&gt;
&lt;li&gt;Find Bracket Pair Colorizer 2 in your installed extensions list and click on it&lt;/li&gt;
&lt;li&gt;Click the uninstall button&lt;/li&gt;
&lt;li&gt;If you look back where you found the extension in step #2, you will see there is a Reload Required button that you need to click to restart VSCode for the uninstall to take full effect.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/vscode-bracket-pair-colorization/uninstall-bracket-pair-colorizer2.png" alt="Uninstall Bracket Pair Colorizer 2"&gt;&lt;/p&gt;</description></item><item><title>Solved: Windows 10 Errors When Trying to Watch Video with HEVC Extension Not Found</title><link>https://digitaldrummerj.me/hevc-not-installed/</link><pubDate>Sun, 19 Apr 2020 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/hevc-not-installed/</guid><category>how-to</category><category>windows</category><media:content medium="image" url="https://digitaldrummerj.me/images/hevc/hevc-featured.png"/><description>
&lt;img src="/images/hevc/hevc-featured.png" /&gt;&lt;h2 id="wtf-why-cant-i-play-my-video"&gt;WTF! Why Can&amp;rsquo;t I Play My Video&lt;/h2&gt;
&lt;p&gt;So you tried to watch an mp4 video in Windows 10 and it threw an error at you that &amp;ldquo;To play this video, you need a new codec, HEVC Video Extensions and they want you to pay $0.99 for it. If you are like me, you are wondering what the heck is the HEVC Video Extensions and why all of a sudden do I need it to watch an mp4.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hevc/missing-hevc.png" alt="Missing HEVC Video Extension Dialog"&gt;&lt;/p&gt;
&lt;p&gt;Well, it turns out that at some point Microsoft decided to stop including the HEVC Video Extensions with Windows and wants you to pay for it. Luckily, there is a very easy way to get the HEVC Video Extensions it for free. Microsoft hid another version of the HEVC Video Extension on the Microsoft Store for device manufacturers to install on their desktops and laptops and we can access that version as well.&lt;/p&gt;
&lt;h2 id="get-the-hevc-video-extensions"&gt;Get the HEVC Video Extensions&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to &lt;a href="https://www.microsoft.com/en-us/p/hevc-video-extensions-from-device-manufacturer/9n4wgh0z6vhq" target="_blank" &gt;link&lt;/a&gt; and click the &amp;ldquo;Get&amp;rdquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hevc/get-hevc-extensions.png" alt="HEVC Video Extensions for Device Manufacturer"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then click the Open Microsoft Store button when it prompts you&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &amp;ldquo;Get&amp;rdquo; button to install the HEVC Video Extension for Device Manufacturers&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/hevc/hevc-install.png" alt="Install the HEVC Video Extension for Device Manufacturer"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="happy-video-watching"&gt;Happy Video Watching&lt;/h2&gt;
&lt;p&gt;Now you have the HEVC Extension installed and can watch your videos.&lt;/p&gt;</description></item><item><title>You Need Code Coverage</title><link>https://digitaldrummerj.me/why-code-coverage-is-important/</link><pubDate>Sun, 12 Apr 2020 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/why-code-coverage-is-important/</guid><category>testing</category><media:content medium="image" url="https://digitaldrummerj.me/images/code-coverage/why-code-coverage-featured-image.png"/><description>
&lt;img src="/images/code-coverage/why-code-coverage-featured-image.png" /&gt;&lt;p&gt;Welcome to the start of my series on code coverage. In this article we are going to talk about why you need code coverage reports and then in future articles implement the reports in our Angular based UI and ASP.NET Core based API.&lt;/p&gt;
&lt;p&gt;For years, I pushed back against implementing code coverage on my projects and I am here to say that I was wrong. In the past everytime someone tried to implement code coverage on my projects or suggested it, all they cared about was the percent of code coverage. Code coverage for just the sake of code coverage doesn&amp;rsquo;t create high quality tests or products, it just creates tests to increase code coverage. When code coverage is used to drive the priority of what to test, used to validate that the tests actually covered the lines of code you thought it covered and catching times when you add more code than test code.&lt;/p&gt;
&lt;p&gt;So what drove me to make this change and believe that code coverage is valuable? Recently, I took on the role of leading our Test Automation Team (TAT) as we transition from heavy manual testing to mostly automated testing. Until mid-2019 when we had a management change at the executive level of my organization, automated testing wasn&amp;rsquo;t fully valued and when push came to shove the first thing to drop from the priority list was the automated testing. As a development team we always believed that automated testing was valuable and oushed to make it a priority it but it wasn&amp;rsquo;t a true priority until this year when we decided to kick off a dedicated team.&lt;/p&gt;
&lt;p&gt;Even though automated testing wasn&amp;rsquo;t a priority from a management standpoint in the past, from a development team standpoint we had always valued it and been creating automated tests as much as we could. At the time of the management change in mid-2019 we had 750 API tests and 0 UI tests. Over the last half of 2019, I had many discussions with management about automated testing and the plan moving forward since it was supposedly valued now but we weren&amp;rsquo;t really allocating time to it. If we truly valued testing that we needed to allocate time to it. Time not just to create the tests but time to create an actual strategy that the team could get behind, time to create an end to end testing environment, and time for someone to drive the automated testing strategy.&lt;/p&gt;
&lt;p&gt;So at the start of 2020, it was finally decided to make testing truely a priority and kick off a scrum team dedicated to automated testing consisting of 3 Developers, 1 Quality Assurance Engineer, a Product Owner and a Scrum Master. One of the first questions was how do we measure that we are making progress in our automated testing journey. At the start of 2020, we now had 850 API tests and 250 UI Tests but we were blind to how much of our code was actually being covered. This is where code coverage was very helpful.&lt;/p&gt;
&lt;p&gt;Once you have code coverage implemented, you can get reports generated for you such as:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/code-coverage/cypress-coverage-report-example.png" alt="detailed coveage report"&gt;&lt;/p&gt;
&lt;p&gt;The report shows me a quick summary at the top of the amount of code coverage (33.66% covered) and then I can drill into each file to see what lines are covered and not covered. For the summary section, the important number is the lines (the last one in the list) as this is the totally number of lines of code that is touched by a tests.&lt;/p&gt;
&lt;p&gt;Now that I know where we are at in our test coverage, it allows me as the product owner to make data-driven decisions on which set of tests need to be created. I can drill in and see what portions of our critical features are being tested and seeing where the gaps might be. The development team can also use the coverage report when they add new code to ensure they have good code coverage.&lt;/p&gt;
&lt;p&gt;Again, the ultimate key to making code coverage reports useful like I am using them is to not use them in a draconian way by forcing a random code coverage percent to be hit. The goal is to make the code better, not to hit some random code coverage metric.&lt;/p&gt;
&lt;p&gt;I would encourage you to give code coverage reports a try on your project and use them to make data-driven decision on if you have enough automated tests or not for a given feature.&lt;/p&gt;
&lt;p&gt;In the next article in the series, we will implement the code coverage report for our Angular based UI that is using Cypress to test the UI.&lt;/p&gt;</description></item><item><title>Stop The Zoom Trolls and Prevent Zoombombing</title><link>https://digitaldrummerj.me/stop-zoom-trolls/</link><pubDate>Tue, 07 Apr 2020 06:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/stop-zoom-trolls/</guid><category>zoom</category><media:content medium="image" url="https://digitaldrummerj.me/images/stop-zoom-trolls/stop-zoombombing.png"/><description>
&lt;img src="/images/stop-zoom-trolls/stop-zoombombing.png" /&gt;&lt;p&gt;Are you thinking about running a public meeting using Zoom or are running a public meeting using Zoom? Then you need to prepare for the Zoom Trolls to show up who want nothing more then to force you to end your meeting early. Our goal today is to limit the amount of damage that a troll can do to your meeting to almost nothing while still allow your community to network and grow.&lt;/p&gt;
&lt;p&gt;If you think that a Zoom Troll won&amp;rsquo;t find your meeting, think again. There are already programs available to generate Zoom meeting numbers and auto join those meeting. So it is just a matter of time before they find your meeting. These Zoom Trolls have become such an issue that even the FBI is warning people about. When a Zoom Troll joins your meeting they cause disruptions by sharing porn that is on their screen, draw on the screen using the annotation feature, unmute themselves to talk over the presenter using inappropriate/offensive language and bring inappropriate on camera.&lt;/p&gt;
&lt;p&gt;We can easily minimize the ability of a Zoom Troll to cause any damage to your meeting using the Zoom settings below.&lt;/p&gt;
&lt;h2 id="zoom-account-settings"&gt;Zoom Account Settings&lt;/h2&gt;
&lt;p&gt;Once your are logged into your Zoom account, go under Account and Setting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Turn off Join before host.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/join-before-host.png" alt="join before host"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn on Mute participants upon entry. This set the default to ensure that an attendee doesn&amp;rsquo;t show up and immediately disrupt the meeting.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/mute-participants-upon-entry.png" alt="mute participants upon entry"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn on Allow Host to Add Co-Host. This will allow you to add other organizers or moderators at host of the meeting to have the same meeting controls as you do.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/host-add-co-host.png" alt="co-host setting"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn on Allow host to put attendee on hold.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/allow-host-to-put-attendees-on-hold.png" alt="allow host to put attendee on hold"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set Screen Sharing to Host Only so that participants can’t share&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/screen-sharing.png" alt="screen sharing"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn off Annotations (this is drawing on the screen)&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/annotation.png" alt="annotation"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn Off Whiteboard (this is the sharing of a whiteboard)&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/whiteboard.png" alt="whiteboard"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn On NonVerbal Feedback (this adds options to chat such as Raise Hand, Yes, No, etc)&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/non-verbal.png" alt="non verbal"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn Off Allow removed participants to rejoin. If you kick an attendee, you want them to stay out of the meeting.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/allow-removed-participant-to-rejoin.png" alt="allow removed aprticipants to rejoin"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Turn Off Virtual Background to ensure that they don&amp;rsquo;t put an inappropriate background image behind themselves.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/virtual-backgrounds.png" alt="virtual backgrounds"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="scheduling-a-meeting"&gt;Scheduling a meeting&lt;/h2&gt;
&lt;p&gt;When scheduling a meeting there aren&amp;rsquo;t many options you can set but there are a few important ones.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generate the Meeting ID and DON’T use your personal meeting ID&lt;/li&gt;
&lt;li&gt;Require meeting password. In the Zoom link you can include the password but it will at least stop people from entering the room that didn&amp;rsquo;t get the official link.&lt;/li&gt;
&lt;li&gt;Don’t Allow Join Before Host. First person in is the host so you don&amp;rsquo;t want a participant to be a host.&lt;/li&gt;
&lt;li&gt;Mute Participants on Entry. This ensure that anyone joining the meeting is automatically muted.&lt;/li&gt;
&lt;li&gt;Set to Auto Record the meeting so that you don’t forget and record to the cloud.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/schedule-meeting.png" alt="schedule meeting"&gt;&lt;/p&gt;
&lt;h2 id="running-the-meeting"&gt;Running the Meeting&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;In the Participants panel, at the start during networking time allow participants to unmute themselves. Open the Participants panel and click the more button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/manage-participant-options-unmute-self.png" alt="allow participants to unmute themselves"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;During the presentation, mute all participants and don’t allow them to unmute themselves. Use the nonverbal feedback so they can signal that they have a question so you can unmute them. This stops a troll from being able to just randomly unmute themselves.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open the Participants panel, click on mute all and click the more button to uncheck the allow participants to unmute themselves.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/manage-participant-mute-unmute.png" alt="mute all participants"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/stop-zoom-trolls/manage-participant-options.png" alt="dont allow participants to unmute themselves"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the participants panel, under the more option uncheck Allow Participants to Rename Themselves&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the participants panel, as the host you need to promote all of your other organizers and the presenter to co-host so they can share their desktop and moderate the meeting. Right click on each participant that you want to promote to co-host and select the co-host option.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these settings in place it is much harder for a troll to disrupt and destroy your meeting. While at the same time still allowing you to build and grow your community.&lt;/p&gt;
&lt;p&gt;The only trolling that they will be able to do is to be inappropriate on camera. If this happen, in the participants panel, right-click on the person and turn off their camera.&lt;/p&gt;
&lt;p&gt;Happy Zooming!&lt;/p&gt;</description></item><item><title>Git: Clone Branch to New Repo without History</title><link>https://digitaldrummerj.me/git-clone-branch/</link><pubDate>Mon, 06 May 2019 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/git-clone-branch/</guid><category>git</category><description>&lt;p&gt;For my Angular workshop &lt;a href="https://github.com/digitaldrummerj/angular-tutorial-code" target="_blank" &gt;repository&lt;/a&gt; I wanted to clone the final branch without any history to a new repository so that I could try out some different technologies but I didn&amp;rsquo;t want to polute the workshop repository.&lt;/p&gt;
&lt;p&gt;Luckily, we can do this using the git clone command.&lt;/p&gt;
&lt;h2 id="command"&gt;Command&lt;/h2&gt;
&lt;p&gt;Here is the basic command. Replace &amp;ldquo;Branch Name&amp;rdquo; with the name of your branch, &amp;ldquo;Git Repo&amp;rdquo; with the url to your Git repository, and &amp;ldquo;Folder Name&amp;rdquo; to the directory that you want to clone the branch into.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone --depth &lt;span class="m"&gt;1&lt;/span&gt; --branch &lt;span class="s2"&gt;&amp;#34;Branch Name&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Git Repo&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Folder Name&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="example"&gt;Example&lt;/h2&gt;
&lt;p&gt;Here is the actual command that I used to clone the chapter-ui-tests branch from &lt;a href="https://github.com/angular-tutorial-code" target="_blank" &gt;https://github.com/angular-tutorial-code&lt;/a&gt; repository to the directory percy-cypress-example&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone --depth &lt;span class="m"&gt;1&lt;/span&gt; --branch chapter-ui-tests https://github.com/angular-tutoral-code percy-cypress-example
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have a new repository that is a clone of our branch with only the last commit in the history.&lt;/p&gt;</description></item><item><title>Which RxJS Operators to use in your NgRx Effects</title><link>https://digitaldrummerj.me/ngrx-effects-operators/</link><pubDate>Tue, 30 Apr 2019 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ngrx-effects-operators/</guid><category>angular</category><category>ngrx</category><description>&lt;p&gt;When creating NgRx effects you need to decide which RxJS operators to use. There are a lot of RxJS operators but the ones that we are going to use are: mergeMap, concatMap, exhaustMap, and switchMap. Each of these have recommended use cases in order to avoid race conditions.&lt;/p&gt;
&lt;h3 id="operator-explanation"&gt;Operator Explanation&lt;/h3&gt;
&lt;p&gt;Before we look at when to use each of the operators, lets look at what each of the operators does.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;mergeMap&lt;/strong&gt;: subscribe immediately, never cancel or discard&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;concatMap&lt;/strong&gt;: subscribe after the last one finishes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;exhaustMap&lt;/strong&gt;: discard until the last one finishes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;switchMap&lt;/strong&gt;: cancel the last one if it has not completed&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="operator-usage"&gt;Operator Usage&lt;/h3&gt;
&lt;p&gt;Now lets look at when to use each of the operators&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;mergeMap&lt;/strong&gt;: deleting items&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;concatMap&lt;/strong&gt;: updating or creating items&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;exhaustMap&lt;/strong&gt;: non-parameterized queries&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;switchMap&lt;/strong&gt;: parameterized queries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By following these recommendations on usage, you will avoid race conditions within your effects.&lt;/p&gt;</description></item><item><title>Download .gitignore with a .NET CLI Global Tool</title><link>https://digitaldrummerj.me/dotnet-global-ignore/</link><pubDate>Tue, 06 Nov 2018 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/dotnet-global-ignore/</guid><category>aspnet core</category><description>&lt;p&gt;If you are using Git as your version control system, you need a .gitignore file to keep all of those user specific files out of Git like the bin/obj directories. You could manually create and configure the .gitignore file but why do it yourself when others have already done it for you. A quick search and you will run across the &lt;a href="https://github.com/github/gitignore" target="_blank" &gt;gitignore repo&lt;/a&gt; where you could download a premade file but what about if you had a tool to do this for you?&lt;/p&gt;
&lt;p&gt;This is where .NET CLI Global Tool &lt;a href="https://github.com/Arasz/dotnet-ignore" target="_blank" &gt;dotnet-ignore&lt;/a&gt; comes into play. This tool allow you to easily see a list of preconfigured .gitignore files from this repo and then download the one you need.&lt;/p&gt;
&lt;p&gt;The first thing you need to do is install the dotnet-ignore tool:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet tool install -g dotnet-ignore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that it is installed you can see the available commands by viewing the help:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet-ignore -h
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will notice that there are two commands available: get and list.&lt;/p&gt;
&lt;p&gt;When you run the &lt;code&gt;list&lt;/code&gt; command it will show you all of the available .gitignore file&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet-ignore list
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you find the one that you need in the list, you will run the &lt;code&gt;get&lt;/code&gt; command to download it by using the &lt;code&gt;-n&lt;/code&gt; parameter to specify the name.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet-ignore get -n &lt;span class="s2"&gt;&amp;#34;Git Ignore Name&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To download the Visual Studio .gitignore file run&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet-ignore get -n visualstudio.gitignore
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default the .gitignore file is placed into the directory that the command is run from. If you want it in a different directory use the &lt;code&gt;-d&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;Just like that you have your .gitignore file. Way better than manually searching for it and downloading it.&lt;/p&gt;</description></item><item><title>Dotnet CLI Global Tools Are Awesome!</title><link>https://digitaldrummerj.me/dotnet-global-tools/</link><pubDate>Mon, 05 Nov 2018 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/dotnet-global-tools/</guid><category>aspnet core</category><description>&lt;p&gt;With the release of .NET Core 2.1 the .NET Core CLI includes a feature called Global Tools that provides a simple way to create and share cross-platform console tools.&lt;/p&gt;
&lt;p&gt;When you install a global tool, the CLI will download a special NuGet package that contains a console application and make your console tool available as a new command from the command line.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You will need to download .NET Core 2.1 to use this to try this on your own.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To install a global tool you execute “dotnet tool install”. For example to install the &amp;ldquo;amazing-tool&amp;rdquo; you would run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dotnet tool install -g amazing-tool
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you have the &amp;ldquo;amazing-tool&amp;rdquo; installed, now you can run it from the command line. The &lt;code&gt;-h&lt;/code&gt; used below is the standard for seeing the help.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;amazing-tool -h
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Unfortunately, one of the features that is missing though is the ability to search a registery of all of the available tools out there. I did find a really nice list of tools from Nate McMaster at
&lt;a href="https://github.com/natemcmaster/dotnet-tools" target="_blank" &gt;https://github.com/natemcmaster/dotnet-tools&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In our next article, we will start to take a look at a few of the global tools that I am using.&lt;/p&gt;</description></item><item><title>How to Download and Extract a Zip File with Node</title><link>https://digitaldrummerj.me/node-download-zip-and-extract/</link><pubDate>Wed, 07 Feb 2018 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/node-download-zip-and-extract/</guid><category>node</category><description>&lt;p&gt;Downloading and extracting a zip file using Node seemed like a pretty easy task but alas it took some time to figure out.&lt;/p&gt;
&lt;p&gt;While researching how to do this, I didn&amp;rsquo;t find a library that had all of the requirements within it but I did find a few that allowed me to meet the requirements&lt;/p&gt;
&lt;h2 id="requirements"&gt;Requirements&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Download zip file from a url&lt;/li&gt;
&lt;li&gt;Extract zip file to a directory location&lt;/li&gt;
&lt;li&gt;Extract a single directory and all of its sub-directories within the zip file&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="downloading-the-zip-file"&gt;Downloading the Zip File&lt;/h2&gt;
&lt;p&gt;Step 1 was to get the zip file downloaded using Node and make sure that I could manually open it. To download the zip file, I am using the superagent package and piping the download to a file using fs.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install superagent package&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --save superagent
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using superagent along with fs we can download a zip file and save the zip to a local file. In the code below, I am downloading the zip for the master branch of the Github repo located at &lt;a href="https://github.com/digitaldrummerj/node-zip-download-sample" target="_blank" &gt;https://github.com/digitaldrummerj/node-zip-download-sample&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are &amp;ldquo;TODO&amp;rdquo; statements in the code below for the things that you would need to change to use this code for your own zip file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt;&amp;#39;use strict&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Import
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;superagent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;fs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// TODO: change to where your zip file is located
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repoName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;node-zip-download-sample&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`https://github.com/digitaldrummerj/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;/archive`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zipFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;master.zip&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;zipFile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// TODO: change to the directory instead of the zip that you want to extract
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extractEntryTo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;-master/`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// TODO: change to the directory where you want to extract to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`./&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;-master/`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;error&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createWriteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;zipFile&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;finish&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// add code below to here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that the zip file is downloaded we can unzip it.&lt;/p&gt;
&lt;h2 id="unzipping-the-zip-file"&gt;Unzipping the Zip File&lt;/h2&gt;
&lt;p&gt;For unzipping, I didn&amp;rsquo;t find anything built into Node but I did find the &lt;a href="https://www.npmjs.com/package/adm-zip" target="_blank" &gt;Adm-Zip&lt;/a&gt; package on NPM.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.npmjs.com/package/adm-zip" target="_blank" &gt;Adm-Zip&lt;/a&gt; has functions for dealing with the entire zip file or a single file/directory within the zip file. In my case all of my files were contained within a directory in the zip file and I wanted to extract the files contained within that directory.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://www.npmjs.com/package/adm-zip" target="_blank" &gt;Adm-Zip&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;npm install --save adm-zip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add Adm-Zip to the list of requires&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;admZip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;adm-zip&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Within the on finish statement from the previous section, add the code below to use Adm-Zip to extract a directory from the downloaded zip file and extract it to a directory&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;finished downloading&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;zip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;admZip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;zipFile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;start unzip&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extractEntryTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;extractEntryTo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;outputDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;finished unzip&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Just like that we had our zip file downloaded and extracted to a directory.&lt;/p&gt;
&lt;p&gt;You can find a working sample at &lt;a href="https://github.com/digitaldrummerj/node-zip-download-sample" target="_blank" &gt;https://github.com/digitaldrummerj/node-zip-download-sample&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Leave a comment below if you end up using this solution or are doing it another way.&lt;/p&gt;</description></item><item><title>How I Record My Conference Talks</title><link>https://digitaldrummerj.me/recording-my-conference-talks/</link><pubDate>Thu, 01 Feb 2018 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/recording-my-conference-talks/</guid><category>speaking</category><description>&lt;p&gt;When I got started as a conference speaker I would see &lt;a href="http://www.jeremybytes.com/" target="_blank" &gt;Jeremy Clark&lt;/a&gt; recording his talks and I thought I should record my talks too. It would help me improve as a speaker by seeing how I actually was on stage versus how I think I was. As well, it gives attendees the ability to watch a replay of the talk in case they missed something or weren&amp;rsquo;t able to see it. This is assuming though that the recordings are posted somewhere and advertised to the attendees, which I used to be really bad about. It was difficult for a long time for me to watch myself giving a talk.&lt;/p&gt;
&lt;p&gt;So I started recording my talks. Unfortunately, in 2015 and 2016 I didn&amp;rsquo;t process most of those videos. However, at the start of 2017 I decided that if I was going to take the time to record a talk I would process it and post it. A side goal was to also see if I could post the video while at the conference so that I could ensure that I actually posted it. For 2017, I posted 9 talks publicly and 15 talks as unlisted (paid workshops) to my &lt;a href="https://www.youtube.com/channel/UCDMvOL1XSKclxwplUT0fzLA" target="_blank" &gt;YouTube channel&lt;/a&gt;. I even managed to post all of the public talks while at the conference. Don&amp;rsquo;t forget to subscribe to my channel to get notified when my talks are posted.&lt;/p&gt;
&lt;h2 id="general-recording-requirements"&gt;General Recording Requirements&lt;/h2&gt;
&lt;p&gt;Before we get into the specifics, I have some basic recording requirements that guided me as I figured out this process and what equipment to use.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First and foremost be able to record my conference talks as painlessly as possible with good audio and HD video&lt;/li&gt;
&lt;li&gt;Can be setup in less than 5 minutes&lt;/li&gt;
&lt;li&gt;Be able to capture both my screen and me presenting&lt;/li&gt;
&lt;li&gt;Be able to upload the recordings to YouTube&lt;/li&gt;
&lt;li&gt;Be able to use a similar setup to create training videos outside of my conference talks&lt;/li&gt;
&lt;li&gt;Have the equipment be lightweight enough that I don&amp;rsquo;t have to bring an extra bag with me just for equipment&lt;/li&gt;
&lt;li&gt;Keep the budget as low as possible (e.g. don&amp;rsquo;t need to spend $1000&amp;rsquo;s on professional grade equipment)&lt;/li&gt;
&lt;li&gt;Be able to process the videos fast enough that I can upload them while at the conference&lt;/li&gt;
&lt;li&gt;Be able to reuse a majority of the equipment to record meetups that I attend but am not presenting at&lt;/li&gt;
&lt;/ol&gt;
&lt;div role="alert" class="alert alert-warning"&gt;Note: Many of the product links below are Amazon affiliate links. I won&amp;rsquo;t get rich from them but every little bit helps me be able to continue to speak and record my talks.&lt;/div&gt;
&lt;h2 id="history"&gt;History&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;If you don&amp;rsquo;t care about the equipment that I tried out before settling on my current equipment, you skip this section.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before we get into my current setup, lets take a quick look at some of the other equipment that I have used before settling in on my current setup.&lt;/p&gt;
&lt;p&gt;For the camera, I started out with a GoPro Hero 3 Silver since I already had this camera. The camera was small, easy to use, and recorded decent video but battery life was an issue as well as it started to become really unreliable and where would just randomly power off. Next, I used my iPhone 6 (64 gig) mounted on a tripod along with the &lt;a href="https://itunes.apple.com/us/app/moviepro-video-recorder/id547101144?mt=8" target="_blank" &gt;MoviePro&lt;/a&gt; app. Using my phone worked great since I always had my phone with me and the video quality was good. However, disk space was an issue as the phone only had 64 gigs and an hour of footage would take ~12 gigs. Then it would take over an hour to get the file off the phone and it was very flaky when copying it over which totally sucked. As well several times I had talks close enough together that I wasn&amp;rsquo;t able to transfer the recording to the computer in time and had to rush to clear off enough space for 2 hours worth of footage. I was also starting to do more workshops of 4-8 hours in length and the phone didn&amp;rsquo;t have enough space or battery life for those recordings.&lt;/p&gt;
&lt;p&gt;For audio, since I wanted good audio, I knew from the start that I couldn&amp;rsquo;t use the cameras built-in microsoft. So I started with a Zoom H2 mounted on a small table top tripod since I already had it. Even though Zoom is known for their audio quality it still was just recording the audio in the room so it was hit and miss plus moving around the stage greatly effected the quality of the audio. Next, I went with a Zoom H1 with an external lapel mic which gave even better audio and I didn&amp;rsquo;t lose quality as I moved around. The downside to both Zoom devices was that it was a 3rd device that I had to worry about getting setup and then syncing up the footage in editing.&lt;/p&gt;
&lt;p&gt;I also tried a live steaming type setup with a 2nd laptop plus video capture card so that I wouldn&amp;rsquo;t have to do any editing. This setup was a total bust. It took too long to get setup. Carrying a 2nd laptop was annoying. I had to get the camera, capture card and microphone synced since there is a slight delay with capture cards which manufacturers don&amp;rsquo;t publish and If I screwed this up then the whole video was ruined as there was no way to post edit the video to fix it.&lt;/p&gt;
&lt;h2 id="current-equipment"&gt;Current Equipment&lt;/h2&gt;
&lt;p&gt;For my current setup, I have used it to record over 30 talks and it hasn&amp;rsquo;t let me down yet. Yes that is more talks than is on my &lt;a href="https://www.youtube.com/channel/UCDMvOL1XSKclxwplUT0fzLA" target="_blank" &gt;YouTube channel&lt;/a&gt; as some of the talks are internal talks at work.&lt;/p&gt;
&lt;h3 id="microphone"&gt;Microphone&lt;/h3&gt;
&lt;p&gt;A key piece to a great video is great audio. You can get away with an ok video but if you have crappy audio no one will watch your video.&lt;/p&gt;
&lt;h4 id="audio-requirements"&gt;Audio Requirements&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Needs to record directly on the computer&lt;/li&gt;
&lt;li&gt;Needs to be wireless so that I can move around the stage&lt;/li&gt;
&lt;li&gt;Needs to be a lapel microphone (just in case the event gives me a headset mic for the room I can still record)&lt;/li&gt;
&lt;li&gt;Battery in transmitter uses either AA or 9V batteries&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Meeting requirement #2 and 3 was hard as most wireless lapel microphones are XLR connections which means that I would need to carry around an audio interface to go from XLR to USB. I am already carrying enough equipment and didn&amp;rsquo;t want more equipment. Also, XLR Wireless microphones are pretty expensive as they start around $299 plus the audio interface. Since I already had an audio interface, I did try out a few of the cheaper XLR wireless microphones but the quality was terrible and they were quickly returned.&lt;/p&gt;
&lt;p&gt;The microphone I am currently using is the &lt;a href="http://amzn.to/2Gtj9EX" target="_blank" &gt;Samson Stage XPD1&lt;/a&gt; which is a wireless usb lapel microphone that cost &amp;lt; $100 and provides a good compromise between quality and cost.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/Samson-SWXPD1BLM5-Stage-XPD1-Presentation/dp/B014803CMK/ref=as_li_ss_il?_encoding=UTF8&amp;pd_rd_i=B014803CMK&amp;pd_rd_r=NX45M2C8EGX6139BM0S7&amp;pd_rd_w=RjkWv&amp;pd_rd_wg=3YlJn&amp;psc=1&amp;refRID=NX45M2C8EGX6139BM0S7&amp;linkCode=li3&amp;tag=digitaldrumme-20&amp;linkId=513d044a7dce457517f945ed03936d40" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B014803CMK&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li3&amp;o=1&amp;a=B014803CMK" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PROS:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sound quality is good&lt;/li&gt;
&lt;li&gt;The range on the wireless is good. Never had it cut out as I was moving around&lt;/li&gt;
&lt;li&gt;It just worked when I plugged it in&lt;/li&gt;
&lt;li&gt;Since it is USB it records directly into the computer without an extra device&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of the videos on my &lt;a href="https://www.youtube.com/channel/UCDMvOL1XSKclxwplUT0fzLA" target="_blank" &gt;YouTube channel&lt;/a&gt; are using this microphone. Don&amp;rsquo;t forget to subscribe to my channel to get notified when my talks are posted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CONS:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is no battery indicator. Since I am paranoid about batteries dying in the middle of a recording, I have no clue how long they really last since I switch them out every couple of hours. This in turn meant that at one point I had a huge bag of used but still good batteries that I didn&amp;rsquo;t trust for anything important.&lt;/li&gt;
&lt;li&gt;The case is made of plastic&lt;/li&gt;
&lt;li&gt;The clip is plastic&lt;/li&gt;
&lt;li&gt;The USB dongle is quite wide and tends to cover more than 1 port&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To solve the battery problem, I purchased a set of &lt;a href="http://amzn.to/2EhYi6L" target="_blank" &gt;Rechargeable batteries&lt;/a&gt;. I needed a charger since I didn&amp;rsquo;t have one and AAA batteries are used for my &lt;a href="http://amzn.to/2DQwUjD" target="_blank" &gt;Logitech R400 Slide Advancer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/gp/product/B01LWCW3WY/ref=as_li_ss_il?ie=UTF8&amp;psc=1&amp;linkCode=li3&amp;tag=digitaldrumme-20&amp;linkId=f41bbb078e144400d38990ee758d1e40" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B01LWCW3WY&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li3&amp;o=1&amp;a=B01LWCW3WY" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Pro Tip: If the event is also giving you a lapel mic for the room, put their microphone above yours. The audience in the room deserves the best sound possible. Yes, I am also aware that you will be wearing 2 lapel microphones and 2 transmitter packs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote class="warning"&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; If you are using a room microphone, you most likely will hear a slight amount of reverb in the recording and you can&amp;rsquo;t fix it without getting an audio feed directly from the room mic. Getting a direct feed for the room mic is not typically possible at most events. To see an example of what I am talking about, check out my &lt;a href="https://www.youtube.com/watch?v=IV5i0znk9Os&amp;amp;t=12s" target="_blank" &gt;Angular Mix Talk&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="software"&gt;Software&lt;/h3&gt;
&lt;p&gt;The two pieces of software that I needed were screen capture and video editing. My main goal was to find something that was easy to use and had a very small learning curve. After all, I am not an expert video editor nor do I want to become one.&lt;/p&gt;
&lt;h4 id="screen-capture-requirements"&gt;Screen Capture Requirements&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Needed to be able to record my screen with the microphone and optionally include the system sounds&lt;/li&gt;
&lt;li&gt;Be able to capture the full screen or a portion of the screen&lt;/li&gt;
&lt;li&gt;Be able to select which audio device to use&lt;/li&gt;
&lt;li&gt;Be able to capture system sounds if needed&lt;/li&gt;
&lt;li&gt;Have no time limit on how long the screen capture can be&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="video-editing-requirements"&gt;Video Editing Requirements&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;First and foremost, it needed to be really easy to do the simple edits that I was doing&lt;/li&gt;
&lt;li&gt;Needed to be to combine multiple sources into the final video (camera, screen, intro, outro, music, etc)&lt;/li&gt;
&lt;li&gt;Needed to be able to show both the screen and presenter at the same time in the final video&lt;/li&gt;
&lt;li&gt;Needed to be able to separate the audio from the video of a clip so I can get rid of the bad audio from the camera&lt;/li&gt;
&lt;li&gt;Needed to be able to slice up the footage to remove unwanted portions such as the time before the talk actually starts&lt;/li&gt;
&lt;li&gt;Needed to be able to add transitions between scenes&lt;/li&gt;
&lt;li&gt;Needed to be able to do basic audio editing (add gain, noise filter or silence bad audio, fade in/out, etc)&lt;/li&gt;
&lt;li&gt;Needed to be able to work with MOV videos (the camera below records in this format)&lt;/li&gt;
&lt;li&gt;Needed to be able to output in HD (1920x1080). If you are doing any kind of showing of code, anything below 720p is typically hard to read.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I went with &lt;a href="https://www.techsmith.com/video-editor.html" target="_blank" &gt;Camtasia&lt;/a&gt; for both the screen capture and video editing as it meet all of my requirements in one piece of software.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.techsmith.com/video-editor.html" target="_blank" &gt;&lt;img src="/images/recording-conference/camtasia-logo.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PROS:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Was easy to figure&lt;/li&gt;
&lt;li&gt;There are lots of free and easy to follow tutorials available from Techsmith as well as the community&lt;/li&gt;
&lt;li&gt;Comes with a good amount of ready to use assets such as intros, backgrounds, and music.&lt;/li&gt;
&lt;li&gt;Is very easy to create your own assets&lt;/li&gt;
&lt;li&gt;Full featured and only cost $200&lt;/li&gt;
&lt;li&gt;Can batch produce videos&lt;/li&gt;
&lt;li&gt;Only takes ~1 gig of space for a 1 hour screen capture&lt;/li&gt;
&lt;li&gt;Price was low for the functionality provide&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;CONS:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: These are more nitpicks than cons.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Can only open 1 project at a time. Not a huge deal but sometimes I have something I want to copy from another project.&lt;/li&gt;
&lt;li&gt;File paths in the project file are absolute so if you move the raw footage around, you have to manually update the paths in the project file. Wish they were relative to the project file since normally the videos are within the same directory as the project file.&lt;/li&gt;
&lt;li&gt;Only the main display is recorded if you have multiple monitors and are extending the screen instead of just duplicate the screen. To not have to worry about this limitation, when I present I have the laptop and projector duplicated. The downside is that I do not get to use any notes but I don&amp;rsquo;t like using notes anyway so I can live with this downside.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="camera"&gt;Camera&lt;/h3&gt;
&lt;p&gt;The camera is technically optional but since one of my goals is to use the videos to improve my speaking, I need to be able to see myself.&lt;/p&gt;
&lt;h4 id="camera-requirements"&gt;Camera Requirements&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Can record in HD (1920x1080)&lt;/li&gt;
&lt;li&gt;Has a built-in microphone (you need this to be able to sync up the camera and screen capture)&lt;/li&gt;
&lt;li&gt;Can fit into my laptop bag&lt;/li&gt;
&lt;li&gt;Has a metal tripod screw hole (some cameras are plastic and they are not durable)&lt;/li&gt;
&lt;li&gt;Can record at least 2 hours without running out of battery or can record using an external power source&lt;/li&gt;
&lt;li&gt;Can be run off an external power battery. External battery is very important since there is not normally power available out in the audience at a conference.&lt;/li&gt;
&lt;li&gt;Not related to conference talks, but I wanted to be able record my drums or concerts in a point and shoot fashion which meant I needed a good microphone built into the camera. This was the deciding factor in the camera the I went with. If I didn&amp;rsquo;t have this requirement I most likely would have went with a cheaper camera.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The camera I use is the &lt;a href="http://amzn.to/2Ehsnn0" target="_blank" &gt;Zoom Q4n&lt;/a&gt;. Make sure it is the Q4&lt;strong&gt;n&lt;/strong&gt; as they also make a Q4. Since this is the 3rd product from Zoom in this post and 4th product that I own from them, I really like their equipment. I can get great audio and good video for a reasonable price.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/Zoom-Portable-Handy-Screen-Recorder/dp/B01BHDB0ZC/ref=as_li_ss_il?s=electronics&amp;ie=UTF8&amp;qid=1517199880&amp;sr=1-4&amp;keywords=zoom+q4n&amp;linkCode=li2&amp;tag=digitaldrumme-20&amp;linkId=d7c1f03ee688f962311bf2be232f3366" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B01BHDB0ZC&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li2&amp;o=1&amp;a=B01BHDB0ZC" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PROS:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is basically point and shoot&lt;/li&gt;
&lt;li&gt;It has auto gain so I do not have to worry much about it overloading the audio or having the audio too quiet&lt;/li&gt;
&lt;li&gt;It has a flip around screen so I can see what is being recorded. This was more important when I was going to use it as a Webcam (see cons to why I don&amp;rsquo;t)&lt;/li&gt;
&lt;li&gt;It is lightweight&lt;/li&gt;
&lt;li&gt;The microphone can either be set as X/Y or A/B patterns. X/Y is great for musical performances or video blogging while A/B is ideal for recording the sound of people taking in different parts of a room.&lt;/li&gt;
&lt;li&gt;Runs off 5v 1A which means I can use my phone charger or any external usb batteries that I have&lt;/li&gt;
&lt;li&gt;Comes with a wind screen and lens hood (not that I have used either of them yet)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;CONS:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It advertises that it can be used as a 720p webcam but it looks horrible. Looks more like a 240 resolution. My &lt;a href="http://amzn.to/2GADeJD" target="_blank" &gt;Logitech C922x&lt;/a&gt; is far superior 720p as a webcam&lt;/li&gt;
&lt;li&gt;The casing is plastic so it does feel a bit cheap for the price&lt;/li&gt;
&lt;li&gt;The memory card slot is on the bottom which means you have to take it off the tripod to change the memory card&lt;/li&gt;
&lt;li&gt;You can change the battery without taking it completely off the tripod.&lt;/li&gt;
&lt;li&gt;If you forget to put the screen out before mounting it on the tripod, it is difficult to get the screen to open but not impossible&lt;/li&gt;
&lt;li&gt;Doesn&amp;rsquo;t come with a wall charger&lt;/li&gt;
&lt;li&gt;Doesn&amp;rsquo;t come with a case&lt;/li&gt;
&lt;li&gt;Uses a proprietary battery&lt;/li&gt;
&lt;li&gt;About every 3.5 GB, you will get a new video file that you will have to splice together in editing. It is easy to slice together but it does add an extra step.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="accessories"&gt;Accessories&lt;/h3&gt;
&lt;p&gt;There are lots of little things that you don&amp;rsquo;t think about such as travel tripods, memory cards, and cases to protect it all in travel that I also purchased.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tripod:&lt;/strong&gt; I have to say that it was difficult to find a tripod that was full size, lightweight, sturdy enough that I would trust my camera on it, and most important it would fit in carry on luggage. I went with the &lt;a href="http://amzn.to/2DJQF83" target="_blank" &gt;Neewer Carbon Fiber Portable Mini Tripod&lt;/a&gt;. It extends to 56 inches and folds down to 13 inches. It is also really light but still very sturdy.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/gp/product/B01MZDDWYK/ref=as_li_ss_il?ie=UTF8&amp;psc=1&amp;linkCode=li2&amp;tag=digitaldrumme-20&amp;linkId=81c4e6821d9b654a1d1aafce27b49acd" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B01MZDDWYK&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li2&amp;o=1&amp;a=B01MZDDWYK" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Camera memory card 64 GB:&lt;/strong&gt; I went with two &lt;a href="http://amzn.to/2E4p4SA" target="_blank" &gt;Sandisk 64 GB Class 10 SD Card&lt;/a&gt; for no other reason than I have other cameras that can use the same card, so now I can do a multiple camera setup which I have done before with some drum videos. Space wise, at 1920x1080, I am able to get about 5.5 hours of footage.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/Sandisk-Ultra-Micro-UHS-I-Adapter/dp/B073JYVKNX/ref=as_li_ss_il?s=electronics&amp;ie=UTF8&amp;qid=1517203688&amp;sr=1-3&amp;keywords=sdxc+64gb+memory+card+class+10&amp;linkCode=li2&amp;tag=digitaldrumme-20&amp;linkId=bac654abc3b5b5a3b64361e7d5d19458" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B073JYVKNX&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li2&amp;o=1&amp;a=B073JYVKNX" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;External Battery for Camera:&lt;/strong&gt; Since the camera can run off 5V 1A, I can use the iPhone portable charger that I already have. I am using the &lt;a href="http://amzn.to/2EkgHzQ" target="_blank" &gt;UNU Superpak 10000 mAH&lt;/a&gt; and can get around 4 full phone charges out it. It also has a really nice battery charge light so I know if I need to charge it or not. Don&amp;rsquo;t discount the value of having the status lights.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/gp/product/B00LXL6L9E/ref=as_li_ss_il?ie=UTF8&amp;psc=1&amp;linkCode=li2&amp;tag=digitaldrumme-20&amp;linkId=930a00452221343b8cae003d1469c933" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B00LXL6L9E&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li2&amp;o=1&amp;a=B00LXL6L9E" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;iPhone Wall Charger:&lt;/strong&gt; Since the camera does not come with the wall charger, I got a [cheap pack of 5V 1A wall chargers][http://amzn.to/2BHRr3O]. I am always misplacing them so having extra on hand never hurts.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/Charger-FREEDOMTECH-Adapter-Travel-Samsung/dp/B013F1WCFQ/ref=as_li_ss_il?_encoding=UTF8&amp;pd_rd_i=B013F1WCFQ&amp;pd_rd_r=352HKA1Y55KK1XB0CDD6&amp;pd_rd_w=j80I1&amp;pd_rd_wg=Nea0w&amp;psc=1&amp;refRID=352HKA1Y55KK1XB0CDD6&amp;linkCode=li2&amp;tag=digitaldrumme-20&amp;linkId=bba764f1576e3688cc4b438a67d7836b" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B013F1WCFQ&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li2&amp;o=1&amp;a=B013F1WCFQ" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;USB Extender Cable:&lt;/strong&gt; - Unfortunately the USB cable that is included with the camera does not reach to the floor when the tripod is fully extended. I went with an &lt;a href="http://amzn.to/2Gpo49U" target="_blank" &gt;Amazon Basics 6 foot USB Extender cable&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/AmazonBasics-Extension-Cable-Male-Female/dp/B00NH134L6/ref=as_li_ss_il?s=electronics&amp;ie=UTF8&amp;qid=1517199618&amp;sr=1-2-spons&amp;keywords=usb+extender&amp;psc=1&amp;linkCode=li2&amp;tag=digitaldrumme-20&amp;linkId=f7ad6e941d0f24473acf1288dcc9b6b1" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B00NH134L6&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li2&amp;o=1&amp;a=B00NH134L6" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Case for Camera, Microphone, Extender Cable, Batteries, and Memory Cards:&lt;/strong&gt; I didn&amp;rsquo;t want all of the equipment just hanging out in my laptop bag especially since the casings are plastic. Since this equipment is meant to be travelled with, I went with the &lt;a href="http://amzn.to/2nmmI74" target="_blank" &gt;CaseMatrix Hard Case with Diced Foam&lt;/a&gt;. This case is big enough to hold all of the equipment except the tripod and still fits into my laptop bag.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/gp/product/B00RMGXZTS/ref=as_li_ss_il?ie=UTF8&amp;psc=1&amp;linkCode=li2&amp;tag=digitaldrumme-20&amp;linkId=9e76e6e97b05e14350f9ca295c25a9d2" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B00RMGXZTS&amp;Format=_SL160_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li2&amp;o=1&amp;a=B00RMGXZTS" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: I just purchased a &lt;a href="http://amzn.to/2FyF8Jw" target="_blank" &gt;camera backpack with a laptop compartment&lt;/a&gt; that I am going to try instead of the case and the &lt;a href="http://www.ogio.com/backpacks/gambit-laptop-backpack/spr4704908.html" target="_blank" &gt;OGIO Gambit&lt;/a&gt; laptop backpack.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Travel Power Strip:&lt;/strong&gt; To make sure that I get everything charged up in the hotel room and don&amp;rsquo;t have to worry about running out of power outlets in hotel rooms, I got a &lt;a href="http://amzn.to/2DLDu6K" target="_blank" &gt;Belkin travel surge protector with 2 usb ports&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.amazon.com/Belkin-3-Outlet-SurgePlus-Protector-BST300/dp/B00ATZJ5YS/ref=as_li_ss_il?s=electronics&amp;ie=UTF8&amp;qid=1517285862&amp;sr=1-3&amp;keywords=belkin+travel+surge+protector&amp;linkCode=li3&amp;tag=digitaldrumme-20&amp;linkId=e270ae3e78bd89d89faba15471a76c17" target="_blank"&gt;&lt;img border="0" src="//ws-na.amazon-adsystem.com/widgets/q?_encoding=UTF8&amp;ASIN=B00ATZJ5YS&amp;Format=_SL250_&amp;ID=AsinImage&amp;MarketPlace=US&amp;ServiceVersion=20070822&amp;WS=1&amp;tag=digitaldrumme-20" &gt;&lt;/a&gt;&lt;img src="https://ir-na.amazon-adsystem.com/e/ir?t=digitaldrumme-20&amp;l=li3&amp;o=1&amp;a=B00ATZJ5YS" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /&gt;&lt;/p&gt;
&lt;h2 id="syncing-audio"&gt;Syncing Audio&lt;/h2&gt;
&lt;p&gt;With this setup, we will have 2 audio tracks: camera and screen capture. We will need to sync the audio in editing and then remove the camera audio. This sounds like a pain to do but I am going to give you the best trick that I ever learned that will enable you to sync the audio in 30 seconds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recording The Audio&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Turn on the microphone&lt;/li&gt;
&lt;li&gt;Start recording in camtasia&lt;/li&gt;
&lt;li&gt;Start the camera recording&lt;/li&gt;
&lt;li&gt;While standing in front of the camera either loudly snap your fingers or clap your hands (this makes everyone in the room stare at you, so I normally snap my fingers)&lt;/li&gt;
&lt;li&gt;Now give your talk&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What we did was to create a big spike in the audio that you will be able to see in the audio wav form in Camtasia when you zoom into the timeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Syncing the Audio&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the Camtastia screen capture footage&lt;/li&gt;
&lt;li&gt;Find the spot in the screen capture that you snapped your fingers and add a marker to it&lt;/li&gt;
&lt;li&gt;Import the camera footage&lt;/li&gt;
&lt;li&gt;Add the 1st video from the camera to the timeline. You will be moving it around on the timeline so you only want the 1st one at this point&lt;/li&gt;
&lt;li&gt;Turn off the screen capture footage so that you can hear the camera audio&lt;/li&gt;
&lt;li&gt;Find the spot in the camera video that you snapped your fingers and add a marker to it&lt;/li&gt;
&lt;li&gt;Enable the screen capture footage&lt;/li&gt;
&lt;li&gt;Roughly align the markers for the footages&lt;/li&gt;
&lt;li&gt;Make the footage for the screen and camera taller so that you can easily see the audio wav form. It sometimes takes a few minutes for Camtasia to show the wav&lt;/li&gt;
&lt;li&gt;Zoom all the way into the timeline and fine tune the alignment&lt;/li&gt;
&lt;li&gt;Listen to the audio to make sure you do not have an echo. If you have an echo, keep making adjustments until it goes away.&lt;/li&gt;
&lt;li&gt;Add the rest of the camera footage&lt;/li&gt;
&lt;li&gt;Right-click on the camera footage that is in the timeline and select the separate audio and video option&lt;/li&gt;
&lt;li&gt;Now either delete the camera audio or turn it off. Note if you turn it off and splice up the video the disable audio doesn&amp;rsquo;t get touched and will be out of sync so you have to remember to turn back on the audio before removing any portion of the video.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After a few times of doing this you will get really good at syncing multiple audio sources and will even be able to sync the audio just by looking at the way forms.&lt;/p&gt;
&lt;h2 id="adding-an-intro-and-outro"&gt;Adding an Intro and Outro&lt;/h2&gt;
&lt;p&gt;I like to add an intro and outro clip onto my videos. I made both of these clips once and then added them to the asset library. Now for the intro all I have to do is add it onto the timeline and change the talk title and event name. For the outro, I don&amp;rsquo;t have to make any changes and just put it at the end after all of the editing is done. It nows takes me all of 2 minutes to add the intro and outro. This has been a huge time saver for me and made it possible to get the video editing done while at the conference.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To add the clips to the Camtasia asset library, you highlighted the set of elements on the timeline that make up the click, right-click on them and select save to library. Then give the new asset a name and you are ready to use it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you are ready to record your talks. Make sure to do some practice runs with the equipment to work out any kinks. Leave a comment below and let me know where your recordings are and how the process worked out for you.&lt;/p&gt;
&lt;p&gt;Also, remember that the most important thing is that you can give the talk, not that you can record the talk so if the recording isn&amp;rsquo;t working for whatever reason, don&amp;rsquo;t delay your talk just for the recording. There will be times where the equipment will just not work for whatever reason and you won&amp;rsquo;t be able to capture that talk. It sucks when it happens but honestly the recording of the talk is an added bonus.&lt;/p&gt;</description></item><item><title>AngularJS - Why is there an ! in my url now?</title><link>https://digitaldrummerj.me/angularjs-url-prefix-update/</link><pubDate>Thu, 25 Jan 2018 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/angularjs-url-prefix-update/</guid><category>angularjs</category><description>&lt;blockquote&gt;
&lt;p&gt;Note: This post applies to AngularJS. The 1.x version of Angular. AngularJS has been end of life at of 12/31/2021.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Recently, I upgraded one of my apps to AngularJS 1.6 along with a bunch of other changes and a bunch of my routes broke. Unfortunately, I didn&amp;rsquo;t catch the routing issue before making a bunch of other changes. The one thing I noticed for all of the broken routes is the urls now had an #! (&lt;code&gt;https://myapp.com/#!/&lt;/code&gt;) in them instead of just the # (&lt;code&gt;https://myapp.com/#/&lt;/code&gt;). I thought maybe I had done something in one of my changes. Low and behold though, it was not something I did but was a breaking change in AngularJS 1.6 per the &lt;a href="https://docs.angularjs.org/guide/migration#commit-aa077e8" target="_blank" &gt;AngularJS version migration guide&lt;/a&gt;. Thankfully the fix to revert the functionality to the previous version of AngularJS was really easy.&lt;/p&gt;
&lt;p&gt;In my AngularJS module config I needed to inject the &lt;code&gt;$locationProvider&lt;/code&gt; and set to the hashPrefix to an empty string like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;appModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;$locationProvider&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$locationProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;$locationProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hashPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now my routes work again they did before.&lt;/p&gt;</description></item><item><title>Automatically Tweet New Blog Post</title><link>https://digitaldrummerj.me/jekyll-automatically-tweeting-new-post/</link><pubDate>Tue, 16 Jan 2018 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/jekyll-automatically-tweeting-new-post/</guid><category>blogging</category><description>&lt;p&gt;After each blog post is published, we need to let people know that a new post is published. We can&amp;rsquo;t expect people to only find out about the new post through the RSS feed. For this blog, I tweet out that I have a new blog post. However, I don&amp;rsquo;t want to have remember to send out the tweet because I will forget or get busy with something else. Instead, sending out a tweet should be done for me.&lt;/p&gt;
&lt;p&gt;To automate the process we are going to use Zapier to monitor the RSS feed for new post and then add it to &lt;a href="https://buffer.com" target="_blank" &gt;Buffer&lt;/a&gt;. I use &lt;a href="https://buffer.com" target="_blank" &gt;Buffer&lt;/a&gt; as a way to schedule tweets to go out at specific times throughout the day.&lt;/p&gt;
&lt;p&gt;I could have also used &lt;a href="http://ifttt.com" target="_blank" &gt;IFTTT&lt;/a&gt; but they do not have the ability to submit to the top of the &lt;a href="https://buffer.com" target="_blank" &gt;Buffer&lt;/a&gt; list, so instead I would either have to have my post wait in the buffer list along with everything else I am retweeting or would have to immediately send it to Twitter. This wasn&amp;rsquo;t the desired functionality that I wanted.&lt;/p&gt;
&lt;p&gt;If you haven&amp;rsquo;t heard of either of these systems before they both work pretty much the same way. They monitor something and then take action when it changes. They both have hundreds of task that you can automate against a variety of different systems like Trello, Github, Google Sheet, etc.&lt;/p&gt;
&lt;p&gt;You will need an account in order to use these systems. Both of them are free but Zapier limits the number of automated task to 5 and the number of times it can run to 100 times per month. IFTTT does not appear to have any limits.&lt;/p&gt;
&lt;p&gt;Both systems work extremely well it is just a matter of which system has the ability to interact with the system you trying to automate.&lt;/p&gt;
&lt;h2 id="setting-up-zapier"&gt;Setting up Zapier&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Sign up for a free Zapier account at &lt;a href="https://zapier.com/sign-up" target="_blank" &gt;https://zapier.com/sign-up&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &lt;img src="/images/tweet-new-post/make-a-zap.png" alt="Make a Zap"&gt; button. &lt;strong&gt;Note:&lt;/strong&gt; Zaps are their name for the automated task&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search for RSS as the App Name and select RSS by Zapier from the results.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/search-rss.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select New Item In Feed&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/new-item-in-feed.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;img src="/images/tweet-new-post/save-continue.png" alt="save and continue"&gt; button&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Input your RSS feed url&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/feed-url.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Leave everything else with the defaults and click the &lt;img src="/images/tweet-new-post/continue.png" alt="continue"&gt; button&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;img src="/images/tweet-new-post/fetch-continue.png" alt="fetch and continue"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If everything with the feed setup is correct you will get a successfully message&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/fetch-continue-test-successful.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;img src="/images/tweet-new-post/continue.png" alt="continue"&gt; button&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search for buffer as the App Name and select Buffer from the results.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/search-buffer.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select the Add to Buffer option&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/add-to-buffer.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;img src="/images/tweet-new-post/save-continue.png" alt="save and continue"&gt; button&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select your Buffer Account or click the &lt;img src="/images/tweet-new-post/connect-buffer-account.png" alt="connect to account"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For the profile drop down select your Twitter profile&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/twitter-profile.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For the text field, input what you want your new post tweet to say. The important piece to add into the text field is the link to the new blog post.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To add the link, click on the &lt;img src="/images/tweet-new-post/plus-button.png" alt="plus"&gt; and select the link option&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/blog-link.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For my text field, it looks like&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/text-field.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For top? field select yes to put your new blog post tweet at the top of your Buffer queue.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/top-field.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;img src="/images/tweet-new-post/continue.png" alt="continue"&gt; button&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;img src="/images/tweet-new-post/send-to-buffer.png" alt="send test to buffer"&gt; button&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If everything is working, you should see a test successful message&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/send-to-buffer-successful.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Also, go to buffer and verify that a new post has been added to the buffer queue. You will also want to delete the new post from your buffer queue&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/tweet-new-post/new-post-test.png" alt=""&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;img src="/images/tweet-new-post/finish.png" alt="finish"&gt; button&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Zapier is now setup to put a tweet at the top of your Buffer queue whenever you publish a new blog post into your RSS feed.&lt;/p&gt;</description></item><item><title>Angular - No Test Found</title><link>https://digitaldrummerj.me/ng2-karma-not-finding-tests/</link><pubDate>Fri, 06 Jan 2017 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ng2-karma-not-finding-tests/</guid><category>angular</category><description>&lt;blockquote&gt;
&lt;p&gt;Note: This post applies to Angular. The 2+ version of Angular.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Are you trying to run your Angular 2 unit test and the Karma test runner is not finding any tests to execute? This is exactly what happened to me when I tried to run the unit tests that are included as part of the project that the Angular CLI generates.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ng2-karma-not-finding-test/no-test-found.png" alt="Karma 0 test found"&gt;&lt;/p&gt;
&lt;p&gt;The test runner should have found 3 tests to execute but as your could see above it didn&amp;rsquo;t find any test to execute. Never having used Karma before, I was unsure what the issue was or where to start troubleshooting. However, when the Karma test runner executes it started up Chrome and the UI had a Debug button on it so I figured that would be a good first step.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ng2-karma-not-finding-test/karma-browser.png" alt="Karma Browser"&gt;&lt;/p&gt;
&lt;p&gt;I was slightly disappointed when I clicked on the Debug button as it just opened up a new tab blank tab. After a bit of reading, it turns out that you are supposed to open the Chrome Developer Tools on that new blank tab that the Debug button opened. Once I opened the Chrome Developer Tools, I noticed that the console had the following error.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ng2-karma-not-finding-test/karma-debug.png" alt="Karma Error"&gt;&lt;/p&gt;
&lt;p&gt;Chrome should have run the test.ts file with no problems and it is obviously not a video file. After a bit of searching, I ran across &lt;a href="https://github.com/angular/angular-cli/issues/2125" target="_blank" &gt;Angular CLI Issue 2125&lt;/a&gt; that had a potential workaround of adding a mime type to the Karma configuration. Below is the configuration that you need to add to the karma.conf.js file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;mime:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;&amp;#39;text/x-typescript&amp;#39;:&lt;/span&gt; &lt;span class="err"&gt;[&amp;#39;ts&amp;#39;,&lt;/span&gt; &lt;span class="err"&gt;&amp;#39;tsx&amp;#39;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you update the configuration you need to stop Karma and re-run it with the &lt;code&gt;npm run test&lt;/code&gt; command. It should now find and execute 3 tests.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ng2-karma-not-finding-test/test-found.png" alt="Karma Running"&gt;&lt;/p&gt;
&lt;p&gt;Now you are ready to go test your Angular application. If you are looking for a good testing reference, check out the &lt;a href="https://angular.io/docs/ts/latest/guide/testing.html" target="_blank" &gt;Angular Testing Guide&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Angular - Adding Bootstrap Library</title><link>https://digitaldrummerj.me/ng2-add-bootstrap/</link><pubDate>Thu, 05 Jan 2017 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ng2-add-bootstrap/</guid><category>angular</category><description>&lt;blockquote&gt;
&lt;p&gt;Note: This post applies to Angular. The 2+ version of Angular.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Welcome to the continuing series on Getting Started with Angular 2. In the [previous post][], we created our project using the Angular CLI. In this post, we will be adding the &lt;a href="https://getbootstrap.com/" target="_blank" &gt;Bootstrap&lt;/a&gt; library to the project to make it easier to style our application.&lt;/p&gt;
&lt;p&gt;To make &lt;a href="https://getbootstrap.com/" target="_blank" &gt;Bootstrap&lt;/a&gt; play nice with Angular we are going to use the &lt;a href="https://valor-software.com/ng2-bootstrap/" target="_blank" &gt;ng2-bootstrap&lt;/a&gt; library which rewrites the &lt;a href="https://getbootstrap.com/" target="_blank" &gt;Bootstrap&lt;/a&gt; components to be powered by Angular instead of JQuery. The &lt;a href="https://valor-software.com/ng2-bootstrap/" target="_blank" &gt;ng2-bootstrap&lt;/a&gt; library also works with both Bootstrap &lt;a href="https://getbootstrap.com" target="_blank" &gt;v3&lt;/a&gt; and &lt;a href="http://v4-alpha.getbootstrap.com/" target="_blank" &gt;v4&lt;/a&gt; which means when &lt;a href="http://v4-alpha.getbootstrap.com/" target="_blank" &gt;v4&lt;/a&gt; is finally released to production you will only have to change the Bootstrap css reference and fix any breaking changes listed in the &lt;a href="http://v4-alpha.getbootstrap.com/migration/" target="_blank" &gt;v4 migration guide&lt;/a&gt;. &lt;a href="http://v4-alpha.getbootstrap.com/" target="_blank" &gt;Bootstrap v4&lt;/a&gt; is not yet recommend for production. The rest of this article is going to focus on using &lt;a href="https://getbootstrap.com/" target="_blank" &gt;Bootstrap v3&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="install-libraries"&gt;Install Libraries&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;If you need the code from the previous post, you can get it from this &lt;a href="https://github.com/digitaldrummerj/angular2-getting-started" target="_blank" &gt;Github&lt;/a&gt; repository in the 1-CreateProject branch.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first thing that we need to do is install the ng2-bootstrap and bootstrap-sass libraries as dependencies for the project. Since we generated our project with the &amp;ndash;style=scss flag, we need to use the bootstrap-sass module to get the sass versions of bootstrap instead of the css versions that are contained in the bootstrap module.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a command prompt and navigate to your project directory&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the npm install command below to install ng2-bootstrap and bootstrap-sass&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; npm install bootstrap-sass ng2-bootstrap --save
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="updating-the-angular-cli-configurations"&gt;Updating the Angular CLI Configurations&lt;/h3&gt;
&lt;p&gt;Now we need to tell the Angular CLI about the &lt;a href="https://getbootstrap.com/" target="_blank" &gt;Bootstrap&lt;/a&gt; scss file.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In your code editor, open up the angular-cli.json file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Search for the styles node and add the bootstrap file&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;quot;styles&amp;quot;: [
&amp;quot;styles.scss&amp;quot;,
&amp;quot;../node_modules/bootstrap-sass/assets/stylesheets/_bootstrap.scss&amp;quot;
],
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="using-bootstrap"&gt;Using Bootstrap&lt;/h3&gt;
&lt;p&gt;Now that we have told the Angular CLI to include the bootstrap library, we are ready to use it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In your code editor, open up the src\app\app.component.html file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replace the contents of the file with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;page-header&amp;quot;&amp;gt;
&amp;lt;h1&amp;gt;
{{ title }}
&amp;lt;/h1&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We are now ready run &lt;code&gt;ng serve&lt;/code&gt; and view our home page at &lt;a href="http://localhost:4200" target="_blank" &gt;http://localhost:4200&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: If you already have ng serve running, you will need to stop it using ctrl+c and run ng serve again. Anytime you make changes to the angular-cli.json file, they will not take effect until ng serve is restarted.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The page should now look like&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/angular2-add-bootstrap/view-page.png" alt="Page with Bootstrap"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;At this point you have your Angular 2 project with the &lt;a href="https://getbootstrap.com/" target="_blank" &gt;Bootstrap&lt;/a&gt; library added for styling. The code for this post is available on &lt;a href="https://github.com/digitaldrummerj/angular2-getting-started/tree/2-AddBootstrap" target="_blank" &gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the next post, we will create a header and footer component that will be used on each page.&lt;/p&gt;</description></item><item><title>Angular - Your First Project</title><link>https://digitaldrummerj.me/ng2-your-first-project/</link><pubDate>Tue, 03 Jan 2017 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ng2-your-first-project/</guid><category>angular</category><description>&lt;blockquote&gt;
&lt;p&gt;Note: This post applies to Angular. The 2+ version of Angular.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Welcome to the series on Getting Started with Angular. Angular 2 was released in September 2016 and and so far I have been enjoying working with it. I have been using the TypeScript version of Angular 2. It is has been pretty easy for me so far to pick it up but there have been a few things that have made me scratch me head. In this series I am going to walk you through creating a simple Angular 2 project that has a header/footer, routing to components/modules, show how to add new components/services, create multiple modules, lock down routes, changing UI configurations based on the environment parameter and adding in the Bootstrap library. When you are done with the series, you will have a good structure for any project that you want to start.&lt;/p&gt;
&lt;p&gt;In this post we will get everything setup on your machine to do Angular development and then you will create your Angular project that the rest of the series will build on. We will be using the new Angular CLI to generate the project and several of the features such as components, services, modules, and pipes.&lt;/p&gt;
&lt;h2 id="installing-the-angular-cli"&gt;Installing The Angular CLI&lt;/h2&gt;
&lt;p&gt;The Angular CLI is a node based utility which means that we need to first install Node before we can install the npm package angular-cli. The minimum version of Node that is required for the the Angular CLI requires is Node 4.x but I would suggest installing the latest LTS version from &lt;a href="https://nodejs.org" target="_blank" &gt;https://nodejs.org&lt;/a&gt; which at the time of this writing is 6.9.1. To verify the version of node that you have installed, launch a command prompt and run &lt;code&gt;node -v&lt;/code&gt;. If you need to install Node, download the LTS installer from &lt;a href="https://nodejs.org" target="_blank" &gt;https://nodejs.org&lt;/a&gt; and accept any prompts that the installer has.&lt;/p&gt;
&lt;p&gt;After you have Node installed, we need to globally install the Angular CLI by running (it will take several minutes):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install -g @angular/cli
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;On Linux and Mac, you may need to run the command with sudo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can verify that the Angular CLI installed and the version by running the command below. As of this writing, the Angular CLI version is 1.0.0-beta.25.5&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ng --version
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;If it says that ng is unknown, close the command prompt, re-open it and run ng &amp;ndash;version again. If you are still having issues, leave a comment below and I will try to help you out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="generating-your-angular-2-project"&gt;Generating Your Angular 2 Project&lt;/h2&gt;
&lt;p&gt;After the Angular CLI is installed you can create a new project with the ng new command.&lt;/p&gt;
&lt;p&gt;The ng new command does several things for you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creates a directory named after the project name.&lt;/li&gt;
&lt;li&gt;Installs the npm dependencies&lt;/li&gt;
&lt;li&gt;Configures Webpack&lt;/li&gt;
&lt;li&gt;Adds a development and production environment configuration file&lt;/li&gt;
&lt;li&gt;Adds a placeholder file for the stylesheet. By default it is css based but with the &amp;ndash;style=scss parameter that we are using below, it will make it scss based instead.&lt;/li&gt;
&lt;li&gt;Adds a routing file for the app module and import it into the App Module with the &amp;ndash;routing parameter&lt;/li&gt;
&lt;li&gt;Adds unit testing spec files&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="generate-project-steps"&gt;Generate Project Steps&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a command prompt&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to where you want to store your project&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On Windows, I store mine at c:\projects and on Mac I stored it at ~/projects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generate the new project called angular2-getting-started by running the command below. It will take a few minutes to create and install the dependencies. I like to use scss instead of css for my styles which the style parameter enables. The routing parameter sets up the routing that we will use later on when we have more than 1 page to navigate to.&lt;/p&gt;
&lt;p&gt;ng new angular2-getting-started &amp;ndash;style=scss &amp;ndash;routing&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ng2-getting-started/ng-new-output.png" alt="ng new output"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can view the full documentation for the Angular CLI at &lt;a href="https://github.com/angular/angular-cli" target="_blank" &gt;https://github.com/angular/angular-cli&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="running-your-angular-2-project"&gt;Running Your Angular 2 Project&lt;/h2&gt;
&lt;p&gt;Now that we have our project generated, we can view it in a web browser by running &lt;code&gt;ng serve&lt;/code&gt; to build it and start up a web server with live reload. Once &lt;code&gt;ng serve&lt;/code&gt; has completed, open a web browser and navigate to &lt;a href="http://localhost:4200" target="_blank" &gt;http://localhost:4200/&lt;/a&gt;. You should be looking a page that says:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ng2-getting-started/app-works.png" alt="App Works"&gt;&lt;/p&gt;
&lt;h2 id="project-layout"&gt;Project layout&lt;/h2&gt;
&lt;p&gt;If you look at the project directory and file layout there are some key pieces of the project that I have highlighted below.&lt;/p&gt;
&lt;div class="row"&gt;
&lt;div class="medium-12 large-6 columns"&gt;
&lt;p class="solidborder"&gt;&lt;img src="/images/ng2-getting-started/ng-project-layout.png" alt="Project Layout"&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="medium-12 large-6 columns"&gt;
&lt;ul&gt;
&lt;li&gt;src
&lt;ul&gt;
&lt;li&gt;app -&gt; Your Application Code&lt;/li&gt;
&lt;li&gt;asset -&gt; Static Assets (/images, non-npm libaries, custom javascript that you created)&lt;/li&gt;
&lt;li&gt;environments -&gt; Environment specific files. Will will look at these in a later post.&lt;/li&gt;
&lt;li&gt;styles.scss -&gt; Application wide css. Any CSS that is not component specific.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;angular-cli.json -&gt; Configuration for the Angular CLI and Webpack build&lt;/li&gt;
&lt;li&gt;package.json -&gt; Standard npm configuration file for dependencies, scripts, project info&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;At this point you have your Angular 2 project created and are ready to start coding. In the next post, we will add bootstrap for our styling.&lt;/p&gt;</description></item><item><title>Angular - WTF Module Won't Route</title><link>https://digitaldrummerj.me/ng2-wtf-my-module-wont-route/</link><pubDate>Thu, 29 Dec 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ng2-wtf-my-module-wont-route/</guid><category>angular</category><description>&lt;blockquote&gt;
&lt;p&gt;Note: This post applies to Angular. The 2+ version of Angular.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have been really enjoying working with Angular 2 over the last few months but the other day I spent well over an hour cursing Angular wondering why my new module would not route. I didn&amp;rsquo;t have this much trouble when I created my other modules a few weeks before. However, this time when I navigated to my new module route it kept going to my catch all route.&lt;/p&gt;
&lt;p&gt;I verified that I had spelled everything correctly in the routing configuration and in the browser url. I verified that I had imported the new module in the app module. I swore everything was setup correctly. WTF. What was going on? What did I miss.&lt;/p&gt;
&lt;p&gt;Turns out that order is important when importing modules in the app.module.ts file especially when you have configured a route to catch any unknown routes and redirect them to a 404 page. In this scenario, you have to import the AppRoutingModule last. This was so obvious once I figured it out but never thought about it before then. I assumed that it would automatically added the child routes into the routing configuations before the catch all route. Nope it doesn&amp;rsquo;t work that way. It adds the routes as they are imported. Below shows the broken code and then the fixed code.&lt;/p&gt;
&lt;div role="alert" class="alert alert-info"&gt;Original Broken Code: app.module.ts&lt;/div&gt;
&lt;p&gt;Notice that the MyNewModule is listed in the @NgModule imports section after the AppRoutingModule. Note that the order of import statements at the top of the code does not matter.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;import { BrowserModule } from &amp;#39;@angular/platform-browser&amp;#39;;
import { NgModule } from &amp;#39;@angular/core&amp;#39;;
import { FormsModule } from &amp;#39;@angular/forms&amp;#39;;
import { HttpModule } from &amp;#39;@angular/http&amp;#39;;
import { MyNewModule } from &amp;#39;./my-new-module/my-new-module.module&amp;#39;;
import { AppRoutingModule } from &amp;#39;./app-routing.module&amp;#39;;
import { AppComponent } from &amp;#39;./app.component&amp;#39;;
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
AppRoutingModule,
MyNewModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
&lt;/code&gt;&lt;/pre&gt;&lt;div role="alert" class="alert alert-success"&gt;Fixed Code: app.module.ts&lt;/div&gt;
&lt;p&gt;Notice that now the MyNewModule is listed in the @NgModule imports section before the AppRoutingModule. Note that the order of import statements at the top of the code does not matter.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;import { BrowserModule } from &amp;#39;@angular/platform-browser&amp;#39;;
import { NgModule } from &amp;#39;@angular/core&amp;#39;;
import { FormsModule } from &amp;#39;@angular/forms&amp;#39;;
import { HttpModule } from &amp;#39;@angular/http&amp;#39;;
import { MyNewModule } from &amp;#39;./my-new-module/my-new-module.module&amp;#39;;
import { AppRoutingModule } from &amp;#39;./app-routing.module&amp;#39;;
import { AppComponent } from &amp;#39;./app.component&amp;#39;;
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
MyNewModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Such a simple solution to a problem that will have you cursing at Angular. Hopefully in the future they can change this within Angular so that import order doesn&amp;rsquo;t affect routing like this.&lt;/p&gt;
&lt;p&gt;Leave a comment below, if you have run into other issues with Angular 2 that have been making you scratch your head and curse at the framework.&lt;/p&gt;</description></item><item><title>Ionic v2 - How to setup on Windows</title><link>https://digitaldrummerj.me/ionic-v2-setup-windows/</link><pubDate>Tue, 01 Nov 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ionic-v2-setup-windows/</guid><category>ionic</category><description>&lt;p&gt;In order to work with the Ionic framework version 2 there is a bit of software installs and configuration that needs to happen in order to deploy to devices. However, many of the guides out there leave out a number of steps that tripped me up when I first started using Ionic.&lt;/p&gt;
&lt;p&gt;This guide will go through all of the steps needed for deploying to an Android device using a Windows machine. Note that deploying to an iOS device requires a Mac.&lt;/p&gt;
&lt;p&gt;Since I love to automate setup work so that I can easily repeat it, we will be using &lt;a href="http://www.chocolatey.org" target="_blank" &gt;Chocolatey&lt;/a&gt; and &lt;a href="http://www.boxstarter.org" target="_blank" &gt;Boxstarter&lt;/a&gt; for all of the installs and configurations.&lt;/p&gt;
&lt;h2 id="software-to-be-installed"&gt;Software to be installed&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/nodejs" target="_blank" &gt;NodeJS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/git" target="_blank" &gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://chocolatey.org/packages/gradle" target="_blank" &gt;Gradle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/jdk8" target="_blank" &gt;JDK8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/android-sdk" target="_blank" &gt;Android SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/AndroidStudio" target="_blank" &gt;Android Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/GoogleChrome" target="_blank" &gt;Google Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Npm Modules:
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/cordova" target="_blank" &gt;cordova&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/ionic" target="_blank" &gt;ionic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/" target="_blank" &gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.visualstudio.com/en-us/features/msft-android-emulator-vs.aspx" target="_blank" &gt;Visual Studio Android Emulator (Hyper-V Based)&lt;/a&gt; or &lt;a href="https://www.genymotion.com/fun-zone/" target="_blank" &gt;Genymotion (Virtualbox Based)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="installing-software"&gt;Installing Software&lt;/h2&gt;
&lt;p&gt;Most of the software installs are automated using Chocolatey which is an awesome Software Package Manager for Windows. Chocolatey packages take care of downloading, installing, and configuring the software for you so that you do not have to worry about to do it.&lt;/p&gt;
&lt;p&gt;Once you install Chocolatey we will be using a Chocolatey package called Boxstarter to take care of orchestrating the multiple installs with a single command.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To install &lt;a href="http://www.chocolatey.org" target="_blank" &gt;Chocolatey&lt;/a&gt; you need to open an administrative command prompt.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go under the start menu&lt;/li&gt;
&lt;li&gt;Type cmd&lt;/li&gt;
&lt;li&gt;Find the command prompt result and ctrl + shift + click on it&lt;/li&gt;
&lt;li&gt;If prompted, accept the User Access Control request.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command in the command prompt you just opened&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; @powershell -NoProfile -ExecutionPolicy unrestricted -Command &amp;quot;iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))&amp;quot; &amp;amp;&amp;amp; SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After installing Chocolatey, in the same command prompt run the following command to install &lt;a href="http://boxstarter.org" target="_blank" &gt;BoxStarter&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; choco install -y BoxStarter
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can now close the administrative command prompt&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that we have the infrastructure components installed we are ready to use Boxstarter to run our installation script.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To run the script we need to run the Boxstarter Shell by clicking on the icon on your desktop&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the icon is not on the desktop, then open up a command prompt and type BoxStarterShell.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Boxstarter Shell run the following command&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; Install-BoxStarterPackage -PackageName https://gist.githubusercontent.com/digitaldrummerj/3fe2eb057004b6742b89/raw/021eb3bb7e48745c68507904cecde1625ed0eac1/ionic2 -DisableReboots
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can view the actual script in the your browser at &lt;a href="https://gist.githubusercontent.com/digitaldrummerj/3fe2eb057004b6742b89/raw/021eb3bb7e48745c68507904cecde1625ed0eac1/ionic2" target="_blank" &gt;link&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Before we test if ionic is working or ot, we have one last bit of software to install which is an Android Emulator. Unless you plan on always deploying to a physical Android device during your development you will need an Android Emulator.&lt;/p&gt;
&lt;p&gt;There are 2 options for the Android emulator:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.visualstudio.com/vs/msft-android-emulator/" target="_blank" &gt;Visual Studio Android Emulator (Hyper-V Based)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.genymotion.com/fun-zone/" target="_blank" &gt;Genymotion (Virtualbox Based)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;If you are using virtualization software on your machine other than Hyper-V be aware that VMWare and Virtualbox does not work when Hyper-V is turned on. It requires a reboot of Windows to turn Hyper-V off.
{:.warning}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Both of the emulator works well but my preference is the &lt;a href="https://www.visualstudio.com/vs/msft-android-emulator/" target="_blank" &gt;Visual Studio Android Emulator&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you install one of the emulators, you will want to download at least 1 device. Both emulator platforms have Android machines from 4.4.0 to the current release.&lt;/p&gt;
&lt;h2 id="verify-that-everything-works"&gt;Verify that everything works&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a command prompt&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate the directory where you store you development projects (I use c:\projects)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From c:\projects create a new project based on the Tabs template by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic start todo tabs --v2
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into c:\projects\todo (directory was created by the ionic start command)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first test that we are going to run is to make sure that we can test the todo app that we generated in the web browser by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic serve --lab
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;This will start up a node based web server and the &amp;ndash;lab will tell it to launch a page that shows what the app would look like on an iOS, Android and Windows phone. Granted the node based serve is about 80% accurate but good enough to do a majority of our testing. Ultimately you should test on a device before releasing into the app stores.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next we are going to test our Android device setup but first we need to tell ionic that we want to add the Android platform to our todo app by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic platform add android
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now that the Android platform has been added we can build our application for the platform by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic build android
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last thing we need to verify is that we can deploy the todo app to the Visual Studio Android Emulator or a Physical device.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For the emulator you need to the emulated device before proceeding&lt;/li&gt;
&lt;li&gt;For a physical device, you need to make sure that Windows sees that device and the USB debugging is turned on.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To run on either the emulator or physical device run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic run android
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="congratulations"&gt;Congratulations&lt;/h2&gt;
&lt;p&gt;Congratulations, you made it through the guide and have everything setup to create your ionic applications for Android devices. Unfortunately, if you want to develop for iOS devices you have to do it on a Mac since XCode only runs on a Mac.&lt;/p&gt;</description></item><item><title>Ionic v2 - Setup on OSx</title><link>https://digitaldrummerj.me/ionic-v2-setup-osx/</link><pubDate>Tue, 01 Nov 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ionic-v2-setup-osx/</guid><category>ionic</category><description>&lt;p&gt;In order to work with the Ionic framework there is a bit of software installs and configuration that needs to happen in order to deploy to devices. However, many of the guides out there leave out a number of steps that tripped me up when I first started using Ionic.&lt;/p&gt;
&lt;p&gt;This guide will go through all of the steps needed for deploying to an Android and iOS device using a Mac.&lt;/p&gt;
&lt;h2 id="general-install-steps"&gt;General Install Steps&lt;/h2&gt;
&lt;p&gt;For the first part of this tutorial, we will be installing everything to do Ionic development and test your application in Google Chrome. This will be how you will do about 80% of your development since deploying to a device or emulator can be time consuming.&lt;/p&gt;
&lt;h3 id="node"&gt;Node&lt;/h3&gt;
&lt;blockquote class="warning"&gt;
&lt;p&gt;If you already have Node installed, you can skip this section.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download the LTS version of node from &lt;a href="https://www.nodejs.org" target="_blank" &gt;https://www.nodejs.org&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you are using Chrome to download, it may tell you that a file with a pkg extension could be harmful to your computer and ask if you want to keep the file. Make sure to click on the keep button.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/node-0.png" alt="NodeJs LTS download link"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After the download completes, double-click on the pkg file to run it&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Continue&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/node-1.png" alt="NodeJs Install Screen 1"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Continue&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/node-2.png" alt="NodeJs Install Screen 2"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Agree&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/node-3.png" alt="NodeJs Install Screen 3"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Install&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/node-4.png" alt="NodeJs Install Screen 4"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill in your login user name and password and click &amp;ldquo;Install Software&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/node-5.png" alt="NodeJs Install Screen 5"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It will take a few minutes for NodeJs to install. Once it does, click the Close button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/node-6.png" alt="NodeJs Install Screen 6"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and run the following to verify that node and npm was installed and is available from the command line&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; node -v
npm -v
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;As of the writing of this post, you should have gotten node v6.9.1 and npm 3.10.8&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="npm-packages"&gt;Npm Packages&lt;/h3&gt;
&lt;p&gt;Next we need to install 3 npm packages for cordova, gulp and ionic. At the time of this publication, ionic is on release candidate 0.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and run the following commands to install the Global NPM packages that we need:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; sudo npm install -g cordova ionic
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the terminal run the following to verify that Cordova install correctly. As of the writing of this post, you should get Cordova version 6.4.0&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; cordova -v
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;When prompted with &amp;ldquo;May Cordova anonymously report usage statistics to improve the tool over time?&amp;rdquo;, answer Yes or No depending on your preference.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the terminal run the following to verify that Ionic install correctly. As of the writing of this post, you should get Ionic version 2.1.4&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic -v
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="google-chrome"&gt;Google Chrome&lt;/h3&gt;
&lt;blockquote class="warning"&gt;
&lt;p&gt;If you already have Google Chrome installed, you can skip this section.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download from &lt;a href="https://www.google.com/chrome/browser/desktop/" target="_blank" &gt;https://www.google.com/chrome/browser/desktop/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Double click the dmg file to run it&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Drag the Google Chrome icon to the Application folder&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/chrome-1.png" alt="Google Chrome drag to Application folder"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="visual-studio-code"&gt;Visual Studio Code&lt;/h3&gt;
&lt;blockquote class="warning"&gt;
&lt;p&gt;If you already have Visual Studio Code installed, you can skip to step 5.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download from &lt;a href="https://code.visualstudio.com" target="_blank" &gt;https://code.visualstudio.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/vscode-download.png" alt="VS Code Download"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Double-click on the downloaded archive to expand the contents.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Drag Visual Studio Code.app to the Applications folder, making it available in the Launchpad.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add VS Code to your Dock by right-clicking on the icon and choosing Options, Keep in Dock.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open Visual Studio Code&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the Command Palette (⇧⌘P) and type shell command to find the Shell Command: Install &amp;lsquo;code&amp;rsquo; command in PATH command and double-click on it.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/vscode-cli.png" alt="VSCode install code shell command"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the Command Palette (⇧⌘P) and type ext command to find the Extensions: Install Extensions and double-click on it.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/vscode-ext.png" alt="VSCode Open Extensions: Install Extensions"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Enter Cordova into the search box, find the Cordova Tools from Microsoft, and click the Install button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/vscode-ext-cordova.png" alt="VSCode Install Cordova Extensions"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the install is completed, the Enable button will be available and will restart Visual Studio Code to make the extension after.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/vscode-ext-enable.png" alt="VSCode Install Cordova Tools Enable"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Ok on the confirmation prompt&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/vscode-ext-enable-confirm.png" alt="VSCode Extension Enable Confirmation"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Visual Studio Code will restart and the extensions will be enabled&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="validating-general-install-steps"&gt;Validating General Install Steps&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate the directory where you store you development projects (I use ~/projects)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to generate an ionic v2 project based on the tabs template&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic start todo tabs --v2
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into todo (directory was created by the ionic start command)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We are going to run is to make sure that we can run the todo app in the web browser by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic serve --lab
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This will start up a node based web server and the &amp;ndash;lab will tell it to launch a page that shows what the app would look like on an iOS, Android and Windows phone. Granted the node based serve is about 80% accurate but good enough to do a majority of our testing. Ultimately you should test on a device before releasing into the app stores.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, we can create ionic projects and test them in a web browser. However, you have some more steps to complete in order to deploy to Android and iOS devices.&lt;/p&gt;
&lt;h2 id="android-setup-steps"&gt;Android Setup Steps&lt;/h2&gt;
&lt;p&gt;In this section, we will be installing everything that is needed in order to deploy your application to an Android device or emulator. We will be installing JDK8, Android Studio, Android SDK, Gradle, and Genymotion.&lt;/p&gt;
&lt;h3 id="jdk-8"&gt;JDK 8&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a web brower and navigate to &lt;a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_blank" &gt;http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download the Java SE Development Kit 8u101 for Mac OSx&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the dmg file after it downloads&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Double Click on the icon&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/jdk8-1.png" alt="JDK8 Install Screen 1"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click continue&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/jdk8-2.png" alt="JDK8 Install Screen 2"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Install&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/jdk8-3.png" alt="JDK8 Install Screen 3"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill in your login user name and password and click &amp;ldquo;Install Software&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/jdk8-4.png" alt="JDK8 Install Screen 4"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the install completes, click the Close button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/jdk8-6.png" alt="JDK8 Install Screen 6"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and run the following to verify that you can run java from the command line. As of this writing it should return javac 1.8.0_101&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; javac -version
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open up the vi editor and edit your bash profile. We need to add in the JAVA_HOME environment variable&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; vi ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To edit in vi hit &lt;code&gt;i&lt;/code&gt; to enter edit mode and add the text below to the .bash_profile&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press the &lt;code&gt;esc&lt;/code&gt; key to exit edit mode&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press &lt;code&gt;:&lt;/code&gt; (colon key) to enter command mode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;type &lt;code&gt;wq&lt;/code&gt; and press enter to save and exit vi&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following to make the change active in your existing session&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; source ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test that the $JAVA_HOME variable was set. If should return /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; echo $JAVA_HOME
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="android-sdk"&gt;Android SDK&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;If you are using Safari to download files you need to change the preference to &amp;ldquo;open safe files after downloading&amp;rdquo; else Safari will automatically unzip the downloaded file into the Download folder and delete the zip file. You can change this option by going into the preferences and unchecking the &amp;ldquo;Open safe files after downloading&amp;rdquo; box&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/safari-2-uncheck.png" alt="uncheck open safe files after downloading"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download from &lt;a href="https://developer.android.com/studio/index.html#downloads" target="_blank" &gt;https://developer.android.com/studio/index.html#downloads&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Version 24.4.1 as of this writing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-sdk-1.png" alt="android sdk download"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When prompt for the license agreement, check the &amp;ldquo;I have read and agree withthe above terms and conditions&amp;rdquo; box and then click on the &amp;ldquo;Download Android-SDK_R24.4.1-MACOSX.zip&amp;rdquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-sdk-2.png" alt="android sdk license agreement"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can close the next dialog as it just tells you there is nothing else to do. Close it by clicking on the X in the upper right of the dialog&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-sdk-3.png" alt="nothing else to do dialog"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following to unzip the android sdk to the Development directory that is under your user home directory&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; mkdir ~/Library/Android &amp;amp;&amp;amp; unzip ~/Downloads/android-sdk_r24.4.1-macosx.zip -d ~/Library/Android/sdk &amp;amp;&amp;amp; mv ~/Library/Android/sdk/android-sdk-macosx/* ~/Library/Android/sdk &amp;amp;&amp;amp; rmdir ~/Library/Android/sdk/android-sdk-macosx
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open up the vi editor and edit your bash profile. We need to add in the JAVA_HOME environment variable&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; vi ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To edit in vi hit &lt;code&gt;i&lt;/code&gt; to enter edit mode and add the text below to the .bash_profile&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; export PATH=${PATH}:~/Library/Android/sdk/tools:~/Library/Android/sdk/platform-tools
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press the &lt;code&gt;esc&lt;/code&gt; key to exit edit mode&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press &lt;code&gt;:&lt;/code&gt; (colon key) to enter command mode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;type &lt;code&gt;wq&lt;/code&gt; and press enter to save and exit vi&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following to make the change active in your existing session&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; source ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test that the $PATH variable was updated. The command below will return the $PATH variable and the end of the output should include the android-sdk-macosx/tools and android-sdk-macosx/platforms-tools directories.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; echo $PATH
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now that the Android SDK manager is installed we need to install the Android SDKs that we will be using&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; echo 'y' | android update sdk --filter tools,platform-tools,build-tools-24.0.3,android-19,android-20,android-21,android-22,android-23,android-24,source-24 --all --no-ui
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote class="warning"&gt;
&lt;p&gt;&lt;pre&gt;&lt;code&gt;Keep an eye on the output from this command. I had it error downloading one of the packages a time or two and had to run the command a 2nd time.
&lt;/code&gt;&lt;/pre&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="android-studio"&gt;Android Studio&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download from &lt;a href="https://developer.android.com/studio/index.html#downloads" target="_blank" &gt;https://developer.android.com/studio/index.html#downloads&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Version 2.2.2.0 as of this writing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-studio-download.png" alt="Android Studio Download"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Double-click on the dmg file after it downloads&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Drag the Android Studio Icon to the Applications folder&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-studio-1.png" alt="Android Studio icon drag to Applications"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Applications folder, launch the Android Studio Application&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-studio-2.png" alt="Android Studio icon in Applications folder"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the Complete installation dialog, select &amp;ldquo;I do not have a previous version of Studio or I do not want to import my settings&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-studio-3.png" alt="Complete Installation selection"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Next&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-studio-4.png" alt="Android Studio Setup Wizard Step 1"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select Standard for the setup type&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-studio-5.png" alt="Android Setup type. select Standard"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Most of the Android SDK items were installed as part of the Android SDK isntall but Android Studio has a couple of additional ones that it wants to install. Click the Finish button to install them.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-studio-6.png" alt="Android Studio Verify Settings"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It will take a few minutes to download the components&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-studio-7.png" alt="Android Studio download components"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When prompted for the HAXM installation, enter your user name and password then click Ok&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/android-studio-8.png" alt="HAXM installation user credential prompt"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the install is completed, click the Finish button&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="virtualbox"&gt;Virtualbox&lt;/h3&gt;
&lt;p&gt;In order to run the Genymotion emulator, we need to install Virtualbox.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to &lt;a href="https://www.virtualbox.org/wiki/Downloads" target="_blank" &gt;https://www.virtualbox.org/wiki/Downloads&lt;/a&gt; and click on the install link for OSx and the extension package. Version 5.1.4 as of this writing.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-download.png" alt="Virtualbox Download Page"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Virtualbox dmg file after it downloads&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Double-click on the Virtualbox Icon&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-1.png" alt="Virtualbox Icon"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the Continue button to determine if it can install Virtualbox&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-3.png" alt="Virtualbox Install dialog to see if you can proceed with install"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Continue&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-4.png" alt="Virtualbox Install Intro"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Install&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-5.png" alt="Virtualbox Install Space Needed"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill in your login user name and password and click &amp;ldquo;Install Software&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-6.png" alt="Virtualbox Install Username and Password Dialog"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click Close&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-8.png" alt="Virtualbox Install Completed"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that Virtualbox is installed, we need to install the extension pack.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Double click on the extension pack file that you downloaded earlier.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once Virtualbox is launched, it will ask you if you want to install the extension pack. Click Install&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-ext-1.png" alt="vritualbox extension pack"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When the license agreement comes up, once you get to the bottom of it the &amp;ldquo;I Agree&amp;rdquo; button is enable for you to click it.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-ext-2.png" alt="virtualbox license agreement"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When prompted input your username and password then click Ok&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-ext-3.png" alt="username and password prompt"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the install is completed, click the Ok button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/virtualbox-ext-4.png" alt="virtualbox extension installed"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="genymotion"&gt;Genymotion&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download Genymotion from &lt;a href="https://www.genymotion.com/fun-zone/" target="_blank" &gt;https://www.genymotion.com/fun-zone/&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Version 2.7.2 as of this writing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &amp;ldquo;Download Genymotion Personal Edition&amp;rdquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-download.png" alt="Genymotion Download Genymotion package button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Sign in to your Genymotion Account if you have one. If not, then click on the Create Account button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-download-signin.png" alt="Genymotion Account signin"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After creating the account and logging in, click on the &amp;ldquo;Download the Mac OSX&amp;rdquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-download-osx.png" alt="Genymotion download osx"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once Genymotion is downloaded, double click the genymotion-2.8.0.dmg file to launch the installer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Drag Genymotion and Genymotion Shell to the Applications folder&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-install-1.png" alt="Genymotion copy to Applications folder"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After Genymotion is installed, open up the Applicaton folder and launch the Genymotion UI&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-app-icon.png" alt="Genymotion Application Icon"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the Usage notice dialog click Accept&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-app-license-agree.png" alt="Genymotion Usage Agreement Dialog"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Yes button on the &amp;ldquo;You don&amp;rsquo;t have any devices dialog&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-app-add-new.png" alt="Genymotion Yes to add a new device"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the Sign in button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-app-sign-in.png" alt="Genymotion Signin button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Input your account information that you create as part of the Genymotion download.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-app-add-new-signin.png" alt="Genymotion add new device accoun sign in"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After you are logged in, from the Android Version drop down select 6.0.0&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-app-android-version.png" alt="Android Version Dropdown"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then select a device from the available list and click next. In this case I selected the &amp;ldquo;Custom Phone - 6.0.0 - 768x1280&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-app-download-device.png" alt="Genymotion Available Virtual devices"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It will take several minutes to download the virtual device. When the download is done, click the Finish button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/genymotion-app-add-new-finished.png" alt="Genymotion Virtual device install finished"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="gradle"&gt;Gradle&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;If you are using Safari to download files and you didn&amp;rsquo;t change the preference to &amp;ldquo;open safe files after downloading&amp;rdquo; when you downloaded the Android SDK, you need to change it before downloading Gradle. Go into the Safari preferences and uncheck the &amp;ldquo;Open safe files after downloading&amp;rdquo; box. Without doing this, Safari will automatically unzip the downloaded file into the Download folder and delete the zip file.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/safari-2-uncheck.png" alt="uncheck open safe files after downloading"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Download the Gradle Binary Only Distribution from &lt;a href="https://gradle.org/gradle-download/" target="_blank" &gt;https://gradle.org/gradle-download/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/gradle-1-download.png" alt="gradle download"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following to unzip the android sdk to the Development directory that is under your user home directory&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; unzip ~/Downloads/gradle-3.1-bin.zip -d ~/Development
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open up the vi editor and edit your bash profile. We need to add in the GRADLE_HOME environment variable&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; vi ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To edit in vi hit &lt;code&gt;i&lt;/code&gt; to enter edit mode and add the text below to the .bash_profile&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; export GRADLE_HOME=~/Development/gradle-3.1/bin
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press the &lt;code&gt;esc&lt;/code&gt; key to exit edit mode&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Press &lt;code&gt;:&lt;/code&gt; (colon key) to enter command mode.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;type &lt;code&gt;wq&lt;/code&gt; and press enter to save and exit vi&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following to make the change active in your existing session&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; source ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test it by running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; echo $GRADLE_HOME
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="verify-android-setup"&gt;Verify Android Setup&lt;/h3&gt;
&lt;p&gt;Next we are going to test our Android device setup. The first thing we need to do is tell ionic that we want to add the Android platform to our todo application that we created earlier.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and navigate to ~/projects/todo&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the android platform&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic platform add android
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we need to validate that we can build for Android, run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic build android
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last thing we need to verify is that we can deploy the todo app to the Genymotion Emulator. Before we can deploy the application, we need to start up Genymotion Device that we want to deploy to.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Once the Genymotion device is started, you can deploy to it by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic run android
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are now ready to go create your ionic applications for the Android platform. Continue with the guide to setup for developing for the iOS platform.&lt;/p&gt;
&lt;h2 id="ios-setup-steps"&gt;iOS Setup Steps&lt;/h2&gt;
&lt;h3 id="xcode"&gt;XCode&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Install Xcode from app store. This will take awhile since it is ~2 gigs in size.&lt;/li&gt;
&lt;li&gt;Once install is completed, open xcode and accept the license
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If prompted to install additional required components, click Install&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/xcode-1.png" alt="xcode additional components prompt"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When prompted for credentials, enter username and password then click Ok&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/xcode-2.png" alt="credentials for xcode install"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It will take a few minutes to complete the install&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic2-osx/xcode-3.png" alt="installing xcode additional components"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="ios-simulator"&gt;iOS Simulator&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install the iOS Simulator that Ionic will use.&lt;/p&gt;
&lt;p&gt;npm install -g ios-sim&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You may need to start the npm install command with sudo depending on your node setup.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="ios-deploy-package"&gt;iOS Deploy Package&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install the iOS Deploy npm package&lt;/p&gt;
&lt;p&gt;npm install -g ios-deploy&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You may need to start the npm install command with sudo depending on your node setup.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="verifying-ios-setup"&gt;Verifying iOS Setup&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate the directory where you store you development projects (I use ~/projects)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to generate an ionic v2 project based on the tabs template&lt;/p&gt;
&lt;p&gt;ionic start todo tabs &amp;ndash;v2&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into todo (directory was created by the ionic start command)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first test that we are going to run is to make sure that we can test the todo app that we generated in the web browser by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic serve --lab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will start up a node based web server and the &amp;ndash;lab will tell it to launch a page that shows what the app would look like on an iOS, Android and Windows phone. Granted the node based serve is about 80% accurate but good enough to do a majority of our testing. Ultimately you should test on a device before releasing into the app stores.&lt;/p&gt;
&lt;p&gt;Next we are going to test our iOS device setup. The first thing we need to do is tell ionic that we want to add the iOS platform to our todo app by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic platform add ios
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This sets up the todo app to be able to be build and deployed to an iOS device. To validate that we can build for iOS, run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic build ios
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last thing we need to verify is that we can deploy the todo app to the iOS Simulator.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;For the iOS Simulator, run the following:&lt;/p&gt;
&lt;p&gt;ionic run ios&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="congratulations"&gt;Congratulations&lt;/h2&gt;
&lt;p&gt;Congratulations, you made it through the guide and have everything setup to create your ionic applications for both Android and Ionic.&lt;/p&gt;</description></item><item><title>ASP.NET Web Api - Setup Generic Response Handler</title><link>https://digitaldrummerj.me/webapi-standard-response/</link><pubDate>Thu, 29 Sep 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/webapi-standard-response/</guid><category>dotnet</category><category>aspnet</category><description>&lt;p&gt;Welcome to the continuing series on getting started with ASP.NET Web Api. In the previous post, we created our ASP.NET Web Api project, created our 1st controller, enabled Windows authentication and configured JSON to be camel cased for our returned C# class. In this article we will learn how to setup a generic response handler for all of Api calls. This will allow us to consolidate the logic needed to create a proper response as well as it will allow us to consolidate the exception handling logic.&lt;/p&gt;
&lt;p&gt;Before we get started, if you have not read the previous post, I would suggest that you do so before continuing with this artcle so that you are at the same starting point as I am.&lt;/p&gt;
&lt;p&gt;Our generic response handler will inherit from IHttpActionResult which basically defines an HttpResponseMessage factory.&lt;/p&gt;
&lt;p&gt;Some of the advantages of using IHttpActionResult are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simplifies unit testing your controllers&lt;/li&gt;
&lt;li&gt;Moves common logic for creating HTTP responses into separate classes.&lt;/li&gt;
&lt;li&gt;Makes the intent of the controller action clearer, by hiding the low-level details of constructing the response.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IHttpActionResult contains a single method, ExecuteAsync, which asynchronously creates an HttpResponseMessage instanc that returns back an Http response message.&lt;/p&gt;
&lt;h2 id="create-the-response-handler"&gt;Create the Response Handler&lt;/h2&gt;
&lt;p&gt;Lets go ahead and start creating our generic reponse handler.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the solution in Visual Studio and within the Api project create a new directory called Helpers.&lt;/li&gt;
&lt;li&gt;Within the Helpers directory, create a class called WrapResponseResult.cs.&lt;/li&gt;
&lt;li&gt;To the WrapResponseResult class definition add &lt;code&gt;&amp;lt;T&amp;gt;&lt;/code&gt; to the class name, inherit the class from IHttpActionResult, and add the ExecuteAsync method.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-C#" data-lang="C#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WrapResponseResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHttpActionResult&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Not Implemented Yet.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have the structure for the WrapResponseResult, we need to add some functionality to it. The first thing we are going to do within the WrapResponseResult class is to add a variable to hold the value for our function in Func&lt;T&gt; and a 2nd variable to hold the HttpRequestMessage. Then we will set the values in the constructor.&lt;/p&gt;
&lt;p&gt;The Func&lt;T&gt; is not something that you see everyday but it is very useful. Basically it is a placeholder for a function and returns back whatever object we tell it to. This is very useful when creating generic handlers like we are doing. The value that we will set the Func&lt;T&gt; variable to when we call our wrapper is the function that will give us the results for the response.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_func&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HttpRequestMessage&lt;/span&gt; &lt;span class="n"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;WrapResponseResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpRequestMessage&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_func&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we are going to create a method within the WrapResponseResult class to create our HttpResponseMessage. This method will take in two parameters: Func&lt;T&gt; and HttpRequestMessage. Then it will execute the Func&lt;T&gt; to get the result to add to the response. If the execution is successful it will return back a status message of 200. If it is not successful, it will check if it is an HttpResponseException and if it is will just rethrow the error since it is already in the format needed. If it is not an HttpResponseException it will return an HttpResponseMessage with a status code of 500 Internal Server Error.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt; &lt;span class="n"&gt;CreateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpRequestMessage&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpResponseException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InternalServerError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The last thing to do is update the ExecuteAsync method to call the CreateResponse method and pass in our _func and _request variables.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="completed-wrapresponseresult"&gt;Completed WrapResponseResult&lt;/h2&gt;
&lt;p&gt;The completed WrapResponseResult should look like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WrapResponseResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IHttpActionResult&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_func&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HttpRequestMessage&lt;/span&gt; &lt;span class="n"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;WrapResponseResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpRequestMessage&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;_func&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FromResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CreateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt; &lt;span class="n"&gt;CreateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpRequestMessage&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpResponseException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InternalServerError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="updating-api-method-to-use-generic-response-handler"&gt;Updating Api Method to Use Generic Response Handler&lt;/h2&gt;
&lt;p&gt;Now that we have our WrapResponseResult class created, we need to update our FirstController to return a WrapResponseResult&lt;UserModel&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up the Controllers\FirstController.cs file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change the return type of the Get method to WrapResponseResult&lt;UserModel&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; public WrapResponseResult&amp;lt;UserModel&amp;gt; Get()
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replace the contents of the Get method with the code below. When the WrapResponseResult ExecuteAsync method is called it will run the code in the () =&amp;gt; { } function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; return new WrapResponseResult&amp;lt;UserModel&amp;gt;(() =&amp;gt;
{
string userName = RequestContext.Principal.Identity.Name;
return new UserModel { UserName = userName };
}, this.Request);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you open a web browser and do a get against the api/First endpoint, you won&amp;rsquo;t see any difference from what is returned back compared to just returning UserModel. However, the benefit is that you now have a single generic method that will execute the function to get the results, format out the results, and check for errors. This greatly simplifies the logic and amount of code that you will need to write for all of your Web Api methods. As well since all of the methods will be using the same response handler, if you ever had to make a change to how the response is generated, all of the logic is contained within one class.&lt;/p&gt;</description></item><item><title>ASP.NET Web Api - Setup JSON Camel Cased Fields</title><link>https://digitaldrummerj.me/webapi-json-setup/</link><pubDate>Wed, 31 Aug 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/webapi-json-setup/</guid><category>dotnet</category><category>aspnet</category><description>&lt;p&gt;Welcome to the continuing series on getting started with ASP.NET Web Api. In the last post, we created our ASP.NET Web Api project, created our 1st controller and enabled Windows authentication. In this article we will learn how to set the JSON response to convert the .NET pascal cased properties into camel cased properties.&lt;/p&gt;
&lt;p&gt;The naming convention between .NET and JSON is different but we should present our Api users the naming convention that they expect without having to write all kinds of conversion code. Luckily this is very easy to accomplish with a few lines of code in ASP.NET Web Api.&lt;/p&gt;
&lt;p&gt;For the naming convention, .NET uses pascal case which means that properties start each word in the property name with a captial letter (e.g. UserName). JSON uses camel case which means that the property names start with a lowercase letter and then each word after that starts with a captial letter (e.g. userName). Notice the lower case u in userName and then the uppercase N for the next work.&lt;/p&gt;
&lt;p&gt;Before we get started, if you have not read the first post, I would suggest that you do so before continuing with this artcle so that you are at the same starting point as I am. It will take you about 10 minutes to complete the 1st article.&lt;/p&gt;
&lt;p&gt;The first thing that we are going to do is create a C# class to hold the information about the logged in user. Then we will update the FirstController that we created in the previous post to return the C# class that we created.&lt;/p&gt;
&lt;h3 id="creating-a-class-to-return-from-web-api-method"&gt;Creating A Class to Return from Web Api Method&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Open up the solution that we created in the previous article in Visual Studio&lt;/li&gt;
&lt;li&gt;In the solution explorer, right-click on the Models directory&lt;/li&gt;
&lt;li&gt;Select Add&lt;/li&gt;
&lt;li&gt;Select Class&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/webapi-add-new-model.png" alt="Add New Class to Model Folder in Visual Studio"&gt;&lt;/p&gt;
&lt;p&gt;In the &amp;ldquo;Add New Item&amp;rdquo; dialog that comes up, name the file UserModel.cs and click ok&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/webapi-add-new-model-filename.png" alt="Set new model file name to UserModel.cs"&gt;&lt;/p&gt;
&lt;p&gt;In the UserModel.cs replace the contents with the following code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Linq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Web&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Web&lt;/span&gt; &lt;span class="n"&gt;Api_Demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Models&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserModel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Next we need to update the FirstController Get method to return a UserModel with the UserName set to the logged in user.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the Controllers\FirstController.cs file&lt;/li&gt;
&lt;li&gt;Change the return type of the Get method to &lt;code&gt;UserModel&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Change the return statement to create a new UserModel with the UserName set to RequestContext.Principal.Identity.Name&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;UserModel&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;UserModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;UserName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Principal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You will also need to add the following using statement so that is knows how to find the UserModel class we created.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Web&lt;/span&gt; &lt;span class="n"&gt;Api_Demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="testing-default-behavior"&gt;Testing Default Behavior&lt;/h2&gt;
&lt;p&gt;We are now ready to test our changes. However, before we do, we need to make 1 configuration change. Previously we were able to test in our browser since we didn&amp;rsquo;t care if XML was returned. This time we do care about the return type as we want it to be JSON. By default XML is returned when hitting the url directly from the browser since it sends application/xml as the content-type from the browser. There are two options to fix this: 1.) Use the Google Chrome Extension Postman to do our testing and tell it the type is application/json 2.) Turn off XML as a format option.&lt;/p&gt;
&lt;p&gt;Since we do not need XML to be returned from our Web Api we are going to go with option #2.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the file App_Start\Web ApiConfig.cs&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;Register&lt;/code&gt; method, add the following code after the call to config.Routes.MapHttpRoute&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Formatters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Formatters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;XmlFormatter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Press F5 to compile our code and start a debug session. Go to the api/first endpoint and you will notice that the output casing currently matches that of our C# class.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;UserName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[My User Name]&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have our Api updated to return the UserModel C# class that we created, we need to configure the JSON formatter to convert the C# class properties to camel case.&lt;/p&gt;
&lt;h2 id="updating-web-api-configuration"&gt;Updating Web Api Configuration&lt;/h2&gt;
&lt;p&gt;To fix the casing, we will use the CamelCasePropertyNamesContractResolver that is part of the JSON.NET library that is included with Web Api.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the file App_Start\Web ApiConfig.cs&lt;/li&gt;
&lt;li&gt;In the &lt;code&gt;Register&lt;/code&gt; method, add the following code after the call to config.Routes.MapHttpRoute&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GlobalConfiguration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Formatters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JsonFormatter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SerializerSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContractResolver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CamelCasePropertyNamesContractResolver&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We also need to add the using statement to tell .NET where to find the CamelCasePropertyNamesContractResolver&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Newtonsoft.Json.Serialization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now anytime we return back a serialized .NET class, all of the properties will be converted to the standard JSON camel case instead of using the standard .NET Pascal case.&lt;/p&gt;
&lt;p&gt;If you press F5 to compile our code and start a debug session and go to the api/first endpoint, you will notice that the output casing is now camel cased&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;userName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;[My User Name]&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclussion"&gt;Conclussion&lt;/h2&gt;
&lt;p&gt;In this guide we learned how to convert the JSON output response to be camel cased instead of the .NET pascal case without having to modify all of our .NET classes. In the next post, we will look at creating a common response format to all of the endpoint return values.&lt;/p&gt;</description></item><item><title>ASP.NET Web Api - Getting Started</title><link>https://digitaldrummerj.me/webapi-getting-started/</link><pubDate>Tue, 23 Aug 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/webapi-getting-started/</guid><category>dotnet</category><category>aspnet</category><description>&lt;p&gt;Welcome to the series on getting started with ASP.NET Web Api. In this article we will create a basic C# Web Api with Windows Integrated Authentication and create our first Web Api endpoint.&lt;/p&gt;
&lt;p&gt;ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is a great platform for building RESTful applications using the .NET Framework.&lt;/p&gt;
&lt;p&gt;In this series we will learn how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a basic C# Web Api with Windows Integrated Authentication&lt;/li&gt;
&lt;li&gt;Setup camel-cased json properties for the response&lt;/li&gt;
&lt;li&gt;Setup A Standard Response&lt;/li&gt;
&lt;li&gt;Solving CORS Issues When Using Credentials&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="create-a-new-web-api-project"&gt;Create a new Web Api Project&lt;/h2&gt;
&lt;p&gt;To make a C# application with Visual Studio:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Open Visual Studio 2015. Any edition will work. I am using Community Edition.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click File -&amp;gt; New -&amp;gt; &lt;em&gt;Project&amp;hellip;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/vs-start-project.png" alt="New Project"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Find and select &lt;em&gt;ASP.NET Web Application&lt;/em&gt;, give your application a name and select &lt;em&gt;ok&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;On the left side under Installed -&amp;gt; Templates, select Web&lt;/li&gt;
&lt;li&gt;Select &amp;ldquo;ASP.NET Web Application&amp;rdquo;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Note:&lt;/strong&gt; Your list of templates may differ but as long as your have the ASP.NET Web Application template listed we are good to go.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Give the project a name (Web Api-Demo in this case)&lt;/li&gt;
&lt;li&gt;Select a location to store the project (c:\projects in this case)&lt;/li&gt;
&lt;li&gt;Uncheck the &amp;ldquo;Application Insights&amp;rdquo; box since we are not going to be using Application Insights&lt;/li&gt;
&lt;li&gt;Click the Ok button&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/vs-new-web-app.png" alt="New Web Application"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the next screen, we need to select the New ASP.NET Project Options&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Select Web Api for the template&lt;/li&gt;
&lt;li&gt;Click on the &amp;ldquo;Change Authentication&amp;rdquo; button
&lt;ul&gt;
&lt;li&gt;Select &amp;ldquo;Windows Authentication&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Click Ok&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Uncheck &amp;ldquo;Host in the cloud&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Click Ok to generate the project&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/vs-new-web-app-options.png" alt="Web App Options"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You project has now been generated and you should see a screen similar to the follow in Visual Studio&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/vs-new-web-app-finished.png" alt="Generated Project"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you hit F5 your default browser will launch with the Web Api Start Page.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/chrome-initial-start-page.png" alt="Initial Start Page in Chrome"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Web Api project comes with a couple of Web Api Endpoints and a very useful Api documentation page that shows all of the available Api endpoints. Click on the API link in the top nav bar to view the documentation page.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/chrome-api-doc-page.png" alt="Api Doc Page in Chrome"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="creating-our-first-controller"&gt;Creating Our First Controller&lt;/h2&gt;
&lt;p&gt;Now that we have a working Web Api project, lets add some functionality to it. The first thing we are going to do is to create a controller that will return back the logged in Windows user using Windows Integrated Authentication.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You should only uses Windows Integrated Authentication within a business and not for a public Api.
{:.warning}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;In the Solution Explorer, right-click on the Controllers&lt;/li&gt;
&lt;li&gt;Select Add from the menu the comes up&lt;/li&gt;
&lt;li&gt;Select &amp;ldquo;Controller&amp;hellip;&amp;rdquo; from the list of templates&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/webapi-new-controller.png" alt="Web Api New Controller"&gt;&lt;/p&gt;
&lt;p&gt;This will open up the Controller type options.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Select &amp;ldquo;Web API 2 Controller - Empty&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the Add button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/webapi-new-controller-template.png" alt="Web Api Controller Template Selection"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next you will need to input the file name.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Change the file name to FirstController&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the Add button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/web-api-getting-started/webapi-new-controller-filename.png" alt="Web Api Controller File Name"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We now have a blank Web Api Controller that is ready for us to create methods within.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Collections.Generic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Linq&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Web.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Web&lt;/span&gt; &lt;span class="n"&gt;Api_Demo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Controllers&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FirstController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ApiController&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We are now going to add a GET method that will return back the logged in user.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c#" data-lang="c#"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Principal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before we test our new controller, we need to make sure that Windows Authentication is enabled and Anonymous Authentication is disabled.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Solution Explorer, select the Solution and press F4 to open the Properties window&lt;/li&gt;
&lt;li&gt;Set &amp;ldquo;Anonymous Authentication&amp;rdquo; to disabled&lt;/li&gt;
&lt;li&gt;Set &amp;ldquo;Windows Integrated&amp;rdquo; to enabled&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="%7B%7B%22iisexpress-windows-auth.png" alt="VS Solution Properties Set Dev Server Authentication Options"&gt;&lt;/p&gt;
&lt;p&gt;Now we are ready to test our Api. In Visual Studio, press F5 to start up a debugging session. This will launch your default web browser. Once the initial page for Web Api has loaded navigate to the /api/first page (e.g. http://localhost:58842/api/First). Your port number will be different than mine.&lt;/p&gt;
&lt;p&gt;The response you get back will be an xml document that contains a string with your domain and user name that you are logged in with. In this case it is [Your Domain]/[Your User Name]. It will look similar to below.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;string&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;http://schemas.microsoft.com/2003/10/Serialization/&amp;#34;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;[Your Domain]/[Your User Name]&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this guide we learned how to create a basic C# Web Api project that uses Windows Integrated Authentication. In the next guide, we will learn how to convert the JSON responses to be camel cased instead of following the .NET pascal case convention without having to update all of our .NET class.&lt;/p&gt;</description></item><item><title>Running Multiple Version of Node On Windows</title><link>https://digitaldrummerj.me/windows-running-multiple-versions-of-node/</link><pubDate>Wed, 20 Jul 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/windows-running-multiple-versions-of-node/</guid><category>node</category><description>&lt;p&gt;I am sure that many of you are in the same situation that I am in with needing a different version of node for different projects and you don&amp;rsquo;t want to have to create a new virtual machine for each project just because of node. Luckily with &lt;a href="https://github.com/coreybutler/nvm-windows/" target="_blank" &gt;nvm&lt;/a&gt; you can install multiple versions of Node on the same machine and switch between them with a simple command line call.&lt;/p&gt;
&lt;p&gt;The one downside to having multiple versions of Node installed is that you have to install the global packages for each version of node that you want them available to. There is no ability to share packages between versions. This means that it will take a bit more disk space but most node packages are fairly small so this should be a none issue.&lt;/p&gt;
&lt;h2 id="installing-nvm"&gt;Installing NVM&lt;/h2&gt;
&lt;p&gt;The first thing that we need to do is install NVM.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Uninstall existing version of node since we won&amp;rsquo;t be using it anymore&lt;/li&gt;
&lt;li&gt;Delete any existing nodejs installation directories. e.g. &amp;ldquo;C:\Program Files\nodejs&amp;rdquo;) that might remain. NVM&amp;rsquo;s generated symlink will not overwrite an existing (even empty) installation directory.&lt;/li&gt;
&lt;li&gt;Delete the npm install directory at C:\Users[Your User]\AppData\Roaming\npm&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We are now ready to install nvm. Download the installer from &lt;a href="https://github.com/coreybutler/nvm/releases" target="_blank" &gt;https://github.com/coreybutler/nvm/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To upgrade&lt;/strong&gt;, run the new installer. It will safely overwrite the files it needs to update without touching your node.js installations. Make sure you use the same installation and symlink folder. If you originally installed to the default locations, you just need to click &amp;ldquo;next&amp;rdquo; on each window until it finishes.&lt;/p&gt;
&lt;h2 id="installing-and-picking-a-node-version"&gt;Installing and Picking a node version&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get a the list so you can see what is available&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ nvm list available
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Pick the one you want to install. Below we are installing 4.4.5 and 5.10.1. You can pick any version that you want.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ nvm install 4.4.5
$ nvm install 5.10.1
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select the node version to use. Note that only 1 node version can be activate at a time.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ nvm use 4.4.5
$ nvm use 5.10.1
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Reminder!&lt;/strong&gt; Any global npm modules you may have installed are not shared between the various versions of node.js you have installed. Additionally, some npm modules may not be supported in the version of node you&amp;rsquo;re using, so be aware of your environment as you work.&lt;/p&gt;
&lt;h2 id="installing-packages"&gt;Installing Packages&lt;/h2&gt;
&lt;p&gt;Installing Node packages is the same as you are used to. Nvm just switches out which version is referenced in your path variables.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;npm install [package name] [--save or --save-dev]
npm install -g [package name]
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="other-commands"&gt;Other Commands&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;View Default Node Architecture Being Used&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm arch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Change Default Node Architecture&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm arch 32
$ nvm arch 64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Install Node Version&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm install 4.4.5
$ nvm install 4.4.5 64
$ nvm install 4.4.5 32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Get Installed Versions&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Get Available Versions to Install&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm list available
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Turn On nvm&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm on
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Turn Off nvm&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm off
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Set Proxy&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm proxy [url]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Remove Proxy&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm proxy none
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;View Proxy Setting&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm proxy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Uninstall Node Version&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm uninstall [version number]
$ nvm uninstall 4.4.5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Select Node Version to Use&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The [arch] parameter below is optional&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$ nvm use [version] [arch]
$ nvm use 4.4.5 64
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Get the Nvm Version&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nvm version
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can easily switch between Node versions and don&amp;rsquo;t have to worry about compatibility with your code not working on your installed Node version.&lt;/p&gt;</description></item><item><title>Docker - Running Container As a Service</title><link>https://digitaldrummerj.me/docker-running-container-as-service/</link><pubDate>Wed, 15 Jun 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/docker-running-container-as-service/</guid><category>docker</category><description>&lt;p&gt;In the &lt;a href="../docker-windows-mounting-directories/" &gt;previous tutorial&lt;/a&gt; we learned how to mount additional directories within the Docker containers. In this tutorial we are going to learn how to run a Docker container as a service a.k.a daemon for nginx and mysql.&lt;/p&gt;
&lt;p&gt;To run a Docker container as a daemon, we run it with the -d flag. This will tell Docker to start up the container in the background and return back to the command prompt.&lt;/p&gt;
&lt;h3 id="nginx"&gt;nginx&lt;/h3&gt;
&lt;p&gt;The Docker Hub image for run an nginx server, is called nginx. If you do not already have the nginx image the run command will download it from the Docker hub. To start the nginx container run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker run -d -p 8000:80 nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;-d: runs container in the background&lt;/li&gt;
&lt;li&gt;-p: set the port to forward to.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the nginx container is up and running, we can verify it is running by executing&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;To see all of the containers even if they are not running execute add a &lt;code&gt;-a&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To connect to the nginx web page, we need to know the ip address of the docker machine.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker-machine ip
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;In my case it is 192.168.99.100. To navigate to the web page, open up a browser and navigate to http://192.168.99.100:8000/.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you need to to attach to a shell within the running container, run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker attach [container id]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;If docker attach never connects, run docker exec -i -t [container id] /bin/bash
{:.warning}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you need to stop the container, run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker stop [container id]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify it stopped by running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker ps -a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are done with the container and ready to delete it, run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker rm [container id]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="mysql-sample"&gt;MySQL Sample&lt;/h3&gt;
&lt;p&gt;This example will download the mysql image, create a mysql database and expose it to your local machine to interact with.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ docker run --name some-mysql -e &lt;span class="nv"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-secret-pw -e &lt;span class="nv"&gt;MYSQL_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql_user -e &lt;span class="nv"&gt;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql_user1 -e &lt;span class="nv"&gt;MYSQL_DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql_test -p 3306:3306 -d mysql
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&amp;ndash;name: is the name to give the container&lt;/li&gt;
&lt;li&gt;-e: sets environment variables&lt;/li&gt;
&lt;li&gt;-d: runs container in the background&lt;/li&gt;
&lt;li&gt;-p: set the port to forward to.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the MySql container is up and running we can see that it is running with&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;To see all of the containers even if they are not running execute add a &lt;code&gt;-a&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To connect to the MySql database, we need to know the ip address of the docker machine&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker-machine ip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we need to install MySql Workbench from &lt;a href="https://www.mysql.com/products/workbench/" target="_blank" &gt;https://www.mysql.com/products/workbench/&lt;/a&gt; to connect to the database to interact with the database.&lt;/p&gt;
&lt;p&gt;If you need to to attach to a shell within the running container, run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker attach [container id]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;If docker attach never connects, run docker exec -i -t [container id] /bin/bash
{:.warning}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you need to stop the container, run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker stop [container id]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can verify it stopped by running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker ps -a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you are done with the container and ready to delete it, run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker rm [container id]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;WARNING: This will delete any data that you added to the database
{:.warning}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You have learn how to run 2 different types of Docker containers as background containers. Any Docker container can be run as a background container by using the -d when starting up the container for the first time.&lt;/p&gt;</description></item><item><title>Docker - Mounting Windows Directories in Containers</title><link>https://digitaldrummerj.me/docker-windows-mounting-directories/</link><pubDate>Fri, 03 Jun 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/docker-windows-mounting-directories/</guid><category>docker</category><description>&lt;p&gt;In the &lt;a href="../docker-on-windows-getting-started/" &gt;previous tutorial&lt;/a&gt; we learned how to install Docker and get our first container running. In this tutorial we are going to learn how to mount additional directories within our Docker container that are outside of the c:\Users directory. By default, Docker only mounts the c:\Users directory inside the docker machine and containers. For myself, I have all of my project files two places: c:\projects and c:\personal. I didn&amp;rsquo;t want to change my standard configuration just for Docker. Luckily, it is really easy to mount additional directories.&lt;/p&gt;
&lt;p&gt;To mount additional directories, you need to add the directory as a shared folder within Virtualbox and then enable long file paths and symlinks. Once the Virtualbox shared folders are setup, you need to mount the directories within the docker machine so that they are available to the containers.&lt;/p&gt;
&lt;h2 id="step-1-adding-shared-folders"&gt;Step 1: Adding Shared Folders&lt;/h2&gt;
&lt;p&gt;The first step is to add the directories as Virtualbox shared folders by using the VBoxManage.exe utility that comes with Virtualbox. VBoxManage.exe is located in your Virtualbox install directory, which by default is C:\Program Files\Oracle\VirtualBox.&lt;/p&gt;
&lt;blockquote class="warning"&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; To make symlinks works when you start up the docker-machine, you need to run the Docker Quickstart Terminal or Command Line as an administrator. This is a security limitation of Windows for symlinks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before adding the shared folders, we need to make sure that no docker machines are running. We are going to check for running docker containers and machines as both a non-admin and admin.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Non-Admin Checking For Running Machines&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Launch the Windows Command Prompt and run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker-machine ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If any machines comes back with the state of running, you will need to stop the machine.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Before stopping the machine you will want to make sure that your containers are stopped.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For any containers that are returned you can stop them by running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker stop [Container ID]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once all of the containers are stop, you can stop the docker machine. Replace the &amp;ldquo;[machine name]&amp;rdquo; with you machine name that we returned from the docker-machine ls command. Typically you will only have 1 machine and it will be named default&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker-machine stop [machine name]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Admin Checking For Running Machines&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the Windows Command Prompt as an administrator.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start Menu&lt;/li&gt;
&lt;li&gt;Search for command prompt&lt;/li&gt;
&lt;li&gt;Right-click on the Command Prompt and select Run as Administrator&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To check if any docker machines are running, run the command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker-machine ls
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If any machines comes back with the state running, you will need to stop the machine.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Before stopping the machine you will want to make sure that your containers are stopped.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For any containers that are returned you can stop them by running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker stop [Container ID]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once all of the containers are stop, you can stop the docker machine. Replace the &amp;ldquo;[machine name]&amp;rdquo; with you machine name that we returned from the docker-machine ls command. Typically you will only have 1 machine and it will be named default&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker-machine stop [machine name]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We are now ready to add in our shared folders.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the Virtualbox directory.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ cd &amp;quot;c:\Program Files\Oracle\Virtualbox&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to add the shared folders. For your shared folders, replace the projects or personal name in the name and hostpath options.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ VBoxManage.exe sharedfolder add default --name &amp;quot;c/projects&amp;quot; --hostpath &amp;quot;\\?\c:\projects&amp;quot; --automount
$ VBoxManage.exe sharedfolder add default --name &amp;quot;c/personal&amp;quot; --hostpath &amp;quot;\\?\c:\personal&amp;quot; --automount
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote class="warning"&gt;
&lt;p&gt;The \?\ in the hostpath tells Windows to enable long file paths.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-2-allow-long-paths-and-symlinks"&gt;Step 2: Allow Long Paths and Symlinks&lt;/h2&gt;
&lt;p&gt;If you are using node many of the modules will create symlinks which are supported under Virtualbox but you need to make a configuration change to enable them.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ VBoxManage.exe setextradata default VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next you need to enable symlinks for each of the shared folders. Replace SharedFolderName with the &amp;ndash;name value that you used when creating the sharedfolder.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ VBoxManage.exe setextradata default VBoxInternal2/SharedFoldersEnableSymlinksCreate/c/personal 1
$ VBoxManage.exe setextradata default VBoxInternal2/SharedFoldersEnableSymlinksCreate/c/projects 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="step-3-mounting-shared-folders-in-docker"&gt;Step 3: Mounting Shared Folders in Docker&lt;/h2&gt;
&lt;blockquote class="warning"&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; To make symlinks works when you start up the docker-machine, you need to run the Docker Quickstart Terminal as an administrator. This is a security limitation of Windows for symlinks. Right-click on the Docker Quickstart Terminal and select Run As Administrator.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Unfortunately even with auto-mount Docker will only mount the c/Users folder in the docker-machine. If you want the folders to auto-mount you will need to manually mount them each time you start up the default docker machine.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the Docker Quickstart Terminal as an administrator&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start Menu&lt;/li&gt;
&lt;li&gt;Search for Docker Quickstart Terminal&lt;/li&gt;
&lt;li&gt;Right-click on the Docker Quickstart Terminal and select &amp;ldquo;Run as Administrator&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In order to mount the directories we need to ssh into the docker machine&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker-machine ssh default
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once you have ssh&amp;rsquo;ed into the docker machine run the following commands to mount the shared folders we created.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ sudo mkdir --parents /c/projects
$ sudo mount -t vboxsf c/projects /c/projects/
$ sudo mkdir --parents /c/personal
$ sudo mount -t vboxsf c/personal /c/personal/
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Within the docker machine you should now be able to see the files and directories in /c/projects and c/personal&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ ls /c/projects
$ ls /c/personal
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;exit the ssh session&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You are now ready to start up or create your containers&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="step-4-using-the-shared-folders"&gt;Step 4: Using the Shared Folders&lt;/h2&gt;
&lt;p&gt;Now that the shared folders are mounted in the docker machine to use them from a container -v argument. The command below will mount the local directory c:\projects (in unix form /c/projects) to /projects within the container. The -it say run interactively, ubuntu is the image name and bash is the type of interactive shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker run -v /c/projects:/projects -it ubuntu bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the container, you can look at the projects directory by running&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ ls /projects
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You are now ready to start using the container for your development work. In the next tutorial we will look at running docker containers in the background for processes like mysql and postgres.&lt;/p&gt;</description></item><item><title>Docker - Getting Started On Windows</title><link>https://digitaldrummerj.me/docker-on-windows-getting-started/</link><pubDate>Tue, 31 May 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/docker-on-windows-getting-started/</guid><category>docker</category><description>&lt;p&gt;After seeing a Docker presentation recently I decided to finally figure out how to get Docker working correctly on Windows. Luckily it worked out of the box fairly well but I did run into issues with Windows file path lengths and proxy issues. This series of article will documented how I got Docker working and overcame those issues.&lt;/p&gt;
&lt;p&gt;To get started, you will need the docker toolkit. I followed the instructions on the Docker website to get the Docker Toolkit with Virtualbox installed. The instructions for Windows are at &lt;a href="https://docs.docker.com/windows/" target="_blank" &gt;https://docs.docker.com/windows/&lt;/a&gt;. The instructions also have links to the Linux and Mac instructions. As I am a Windows user, I can only verify that this tutorial all worked under Windows.&lt;/p&gt;
&lt;p&gt;Once you get the Docker toolkit installed you are probably wondering now what do I do. On the desktop, it installed a shortcut to the &amp;ldquo;Docker Quickstart Terminal&amp;rdquo;. This terminal will ensure that you have the base image that Docker uses for Virtualbox on Windows and you can run all of the Docker commands from this terminal.&lt;/p&gt;
&lt;p&gt;The reason that we need Virtualbox is because Windows does not yet natively support Docker containers. All of the containers run within a Linux virtual machine that Docker is using.&lt;/p&gt;
&lt;p&gt;Below are a few examples to help get you started with usage of Docker.&lt;/p&gt;
&lt;h2 id="example-1-basic-hello-world"&gt;Example 1: Basic Hello World&lt;/h2&gt;
&lt;p&gt;This will download the hello-world image if it is not already one your machine. Then is will create a container, run hello-world and finally remove the container (&amp;ndash;rm).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker run --rm hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="example-2-nginx"&gt;Example 2: nginx&lt;/h2&gt;
&lt;p&gt;This example will start up an nginx web server.&lt;/p&gt;
&lt;p&gt;When you run the docker run command, it will pull down the nginx image if you do not already have it, the -d will leave the container running in the background, and the -p will set port forwarding of port 8000 to port 80 in the container.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker run -d -p 8000:80 nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the nginx container is up and running we can see that it is running with&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To connect to the nginx web page, we need to know the ip address of the docker machine.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker-machine ip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my case it is 192.168.99.100. To navigate to the web page, open up a browser and navigate to http://192.168.99.100:8000/&lt;/p&gt;
&lt;p&gt;Since the container is running behind the scenes, to stop the container you need to issues the&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker stop command.
docker stop [Container ID from docker ps command]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="example-3-ubuntu-bash-shell"&gt;Example 3: Ubuntu Bash Shell&lt;/h2&gt;
&lt;p&gt;This example will download the ubuntu docker image and start up an interactive bash shell that will allow you to make changes to the image.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; docker run -it ubuntu bash
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to interact with files on the host system you can do this as long as they are in the c:\Users directory. Docker auto mounts this directory for you. In a future post will cover the ins and outs of mounting other directories.
{:.warning}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Also, in a future post, we will cover how to save the change made to a container to a new image that you can use as the base for future containers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="finding-additional-docker-images"&gt;Finding Additional Docker Images&lt;/h2&gt;
&lt;p&gt;So far we have used a few basic images but there are many more Docker images available in the Docker Hub at &lt;a href="http://hub.docker.com" target="_blank" &gt;http://hub.docker.com&lt;/a&gt;. The Docker Hub host a number of common containers that you might use for your application such as node, mysql, postgres, couchbase, plus many more.&lt;/p&gt;
&lt;p&gt;Once you find an image that you want to get run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker pull [Image Name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want to pull only a specific version of any image which is common with the nodejs so that you can get the version of node that you need versus the latest version of node, run the following&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker pull [image name]:[tag or version]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="common-docker-commands"&gt;Common Docker Commands&lt;/h2&gt;
&lt;p&gt;Below is a list of common commands that you will get you started using Docker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;List Docker Images&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker images
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Get Docker image from Docker Hub&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker pull [image name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Get Docker Image from at specific version&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker pull [image name]:[tag or version]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Update an existing image&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker pull [image name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;list of running containers&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Seeing Existing Containers&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker ps -a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;See last nth Container Created&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker ps -n=1
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Will pull last container created. -n says how many back to pull&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Connect to Existing Container&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker start -i [Container ID]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;-i connects to the standard in/out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Delete image&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker rmi [image id]
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;It will warn you if you have a container based on the image
{:.warning}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Delete container&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ docker rm [container id]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="wrap-up"&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;Docker is a powerful tool to have in your arsenal. Once you get it working on Windows, you will never install services that can run within Docker locally again. With Docker you can run light-weight containers for services such as mysql, postgres, mongodb, redis, node, plus many more.&lt;/p&gt;
&lt;p&gt;This is the first of several post on using Docker. In future post we will cover mounting additional directories, saving changes to a container into an image, saving our image to the Docker Hub and creating our own Docker image from scratch.&lt;/p&gt;</description></item><item><title>Jekyll Tip: Showing Liquid Code in Code Snippets</title><link>https://digitaldrummerj.me/jekyll-show-liquid-in-code-snippet/</link><pubDate>Thu, 26 May 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/jekyll-show-liquid-in-code-snippet/</guid><category>jekyll</category><category>blogging</category><description>&lt;p&gt;When blogging with Jekyll there are times when you want to be able to output a code snippet that contains what Jekyll thinks is liquid code. This especially happens when you are doing Angular tutorials since using the double brackets ({{ }}) for data binding. Since the code snippets are enclosed in a pre tag, you are not able to html encode the brackets.&lt;/p&gt;
&lt;p&gt;Instead, to include liquid markup in the code snippet you need to surround the code snippet with the raw and endraw tags like so&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-liquid" data-lang="liquid"&gt;{% raw %}
{{ Notice the double brackets will be in the output }}
{% endraw %}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will output:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-liquid" data-lang="liquid"&gt;{{ Notice the double brackets are output }}&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Jekyll Tip: Adding Styling To Html Output</title><link>https://digitaldrummerj.me/styling-jekyll-markdown/</link><pubDate>Tue, 24 May 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/styling-jekyll-markdown/</guid><category>jekyll</category><category>blogging</category><description>&lt;p&gt;As I was writing some tutorials recently I wanted to be able to style the html elements that Jekyll outputs with different css classes without having to write the actually html in the markdown.&lt;/p&gt;
&lt;p&gt;For example I wanted to use a blockquote for items to be aware of that has a blue highlight as well as warnings to watch out for that has a red highlight. Here is the output of the blockquote with the different styles.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a normal blockquote. Without doing anything extra in markdown this is my default blockquote.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a warning blockquote.
{:.warning}&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With the kramdown markdown parser that Jekyll uses you can easily add these css classes without having to write out the html code.&lt;/p&gt;
&lt;h2 id="basics"&gt;Basics&lt;/h2&gt;
&lt;p&gt;In order to specify additional attributes to output in the html on the element, you start it with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{: }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To specify a class it is&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{:.MyClass}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To specify a title attribute it is&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{:title=&amp;#34;My Title&amp;#34;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Below are some specific examples.&lt;/p&gt;
&lt;h2 id="paragraph"&gt;Paragraph&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;markdown&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{:.fake-h2}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;This will be styled as a p tag with the css class fake-2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;html output&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;fake-h2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;This will be styled as a p tag with the css class fake-2&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="list"&gt;List&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;markdown&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;* {:.done} done - Completed this
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;* Not done yet&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;html output&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;done&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;done - Completed this&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Not done yet&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="blockquotes"&gt;blockquotes&lt;/h2&gt;
&lt;p&gt;With the blockquote you can also optional elements such as a title and id tag.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;markdown&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; The npm install command will take several minutes to run depending on internet speed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{:.warning}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{:title=&amp;#34;my title&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{: #myid }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;html output&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;warning&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;myid&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;my title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; The npm install command will take several minutes to run depending on internet speed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;blockquote&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="inline"&gt;inline&lt;/h2&gt;
&lt;p&gt;In the previous examples, we were adding classes to the whole element. However, there are times where you just want to highlight something within an element or sentence. For these times, you write the styling the same way as above but have to put it inline with the text.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;markdown&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;This *is*{:.underline} some `code`{:#id}{:.class}.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;A [link](google.com){:rel=&amp;#39;something&amp;#39;} and some **tools**{:.tools}.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;this *is italic*{::}*marked*{:.special} text&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;html output&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;This &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;em&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;underline&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;is&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; some &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;class highlighter-rouge&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; code&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;code&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;A &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;google.com&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;something&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;link&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; and some &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;tools&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;tools&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;this &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;is italic&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;em&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;special&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;marked&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;em&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; text&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With these new tricks you can make you markdown output look even better without having to resort to writing a lot of html within your markdown.&lt;/p&gt;</description></item><item><title>Add Table of Contents to Markdown</title><link>https://digitaldrummerj.me/adding-toc-to-markdown/</link><pubDate>Tue, 10 May 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/adding-toc-to-markdown/</guid><category>markdown</category><category>blogging</category><description>&lt;p&gt;When you are writing tutorials that are broken up by sections it is nice to have a table of contents at the top to help the users navigate. However, maintaining this by hand is a no go. Luckily there is a great npm package called doctoc that will look at the headings in your markdown file and generated a table of contents for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Table of Contents Sample Using This Post&lt;/strong&gt;&lt;/p&gt;
&lt;!-- START doctoc generated TOC please keep comment here to allow auto update --&gt;
&lt;!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#installing" &gt;Installing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#adding-to-all-files" &gt;Adding to All Files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#add-to-single-file" &gt;Add to Single File&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#examples" &gt;examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#skipping-files" &gt;Skipping Files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#specifying-location-of-the-table-of-contents" &gt;Specifying Location of the Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#changing-the-title-to-the-table-of-contents" &gt;Changing the Title to the Table of Contents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#changing-max-level-of-headings" &gt;Changing Max Level of Headings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#site-compatibility" &gt;Site Compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#using-with-jekyll" &gt;Using with Jekyll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#running-doctoc-as-a-git-pre-commit" &gt;Running doctoc as a Git pre-commit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#more-info" &gt;More Info&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- END doctoc generated TOC please keep comment here to allow auto update --&gt;
&lt;h2 id="installing"&gt;Installing&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;npm install -g doctoc
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="adding-to-all-files"&gt;Adding to All Files&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;doctoc .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="add-to-single-file"&gt;Add to Single File&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;doctoc /path/to/file [...]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="examples"&gt;examples&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;doctoc README.md
doctoc CONTRIBUTING.MD LICENSE.MD
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="skipping-files"&gt;Skipping Files&lt;/h2&gt;
&lt;p&gt;There is no built-in mechanism out of the box for doctoc to skip certain files from having a table of contents. However you can add a comment to each file and then use a little bit of command line logic to exclude those files. Below are examples using Ack and out-of-the-box Windows commands.&lt;/p&gt;
&lt;p&gt;The first step is to add this line of text to the markdown files to skip of:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- DOCTOC SKIP --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using &lt;a href="http://beyondgrep.com/" target="_blank" &gt;Ack&lt;/a&gt; which requires Perl&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ack -L 'DOCTOC SKIP' | xargs doctoc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is an example using built-in Windows command line options. If you are not running it in the directory that contains your markdown files that you want a table of contents on then make sure to update the source variable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@echo off
setlocal
set source=.
set extension=*.md
set string=&amp;quot;DOCTOC SKIP&amp;quot;
for /f &amp;quot;tokens=*&amp;quot; %%G in ('dir &amp;quot;%source%\%extension%&amp;quot; /a:-d /b') do (
find /c /i %string% &amp;quot;%%G&amp;quot; &amp;gt; NUL || (
echo &amp;quot;Add TOC to %%G&amp;quot;
doctoc --github --title &amp;quot;**Table of Contents**&amp;quot; &amp;quot;%%G&amp;quot;
)
)
pause
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="specifying-location-of-the-table-of-contents"&gt;Specifying Location of the Table of Contents&lt;/h2&gt;
&lt;p&gt;By default doctoc will add the table of contents at the top of the file. You can however indicate where you would like to have it placed with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- START doctoc generated TOC please keep comment here to allow auto update --&amp;gt;
&amp;lt;!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --&amp;gt;
&amp;lt;!-- END doctoc generated TOC please keep comment here to allow auto update --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="changing-the-title-to-the-table-of-contents"&gt;Changing the Title to the Table of Contents&lt;/h2&gt;
&lt;p&gt;Pass in the &amp;ndash;title option&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;doctoc --title '**Contents**' .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="changing-max-level-of-headings"&gt;Changing Max Level of Headings&lt;/h2&gt;
&lt;p&gt;By default it will process 4 levels. You can change this with the maxlevel option&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;doctoc --maxlevel 3 .
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="site-compatibility"&gt;Site Compatibility&lt;/h2&gt;
&lt;p&gt;By default doctoc generated github formatted links. To change use the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--bitbucket bitbucket.org
--nodejs nodejs.org
--github github.com
--gitlab gitlab.com
--ghost ghost.org
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;doctoc README.md --bitbucket
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="using-with-jekyll"&gt;Using with Jekyll&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;UPDATED 2016-05-10: For Jekyll the recommendation is now to use the kramdown table of contents built-in generator&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With the kramdown parser that Jekyll uses by default you can easily add in a table of contents. The one limitation that had stopped me from using it for the pages that I wanted a table of contents is that it gets all headers and not just the ones after he table of contents. Granted most of the time that table of contents is at the top of the page but I had an objectives section above my table of contents that was being added in the table of contents which I did not want. However, I was able to work around this issue by using html and CSS instead of markdown for the headers I wanted to exclude. I would style the div tag just like it was an H1-H6 tag without it actually being a header.&lt;/p&gt;
&lt;p&gt;To use add a table of contents using kramdown on your Jekyll blog, add the follow to your file where you are want your table of contents.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;* TOC
{:toc}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Jekyll Kramdown TOC Sample Showing All Headers&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TOC
{:toc}&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This will cause the * TOC to be replaced with the actual table of contents when the jekyll build/serve is run.&lt;/p&gt;
&lt;h2 id="running-doctoc-as-a-git-pre-commit"&gt;Running doctoc as a Git pre-commit&lt;/h2&gt;
&lt;p&gt;To remember to always update the Table of Contents before committing to you can use a git hook to run your doctoc call before committing any files to Github for your repo.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go into the .git\hooks directory&lt;/li&gt;
&lt;li&gt;Copy the pre-commit.sample to pre-commit with no file extension&lt;/li&gt;
&lt;li&gt;Add your doctoc command to the file and save&lt;/li&gt;
&lt;li&gt;Now the next time you do a git commit the table of contents will automatically updated&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="more-info"&gt;More Info&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/thlorenz/doctoc" target="_blank" &gt;Github Repo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.npmjs.com/package/doctoc" target="_blank" &gt;npm package&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Factory Reset Dell Venue 7 x86 Android Tablet</title><link>https://digitaldrummerj.me/reset-dell-tablet/</link><pubDate>Wed, 13 Apr 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/reset-dell-tablet/</guid><category>android</category><description>&lt;p&gt;I rarely use my Dell tablet and when I went to use it again I couldn&amp;rsquo;t remember the lock pattern. No problem I thought I will just reset it but I had since changed or my router and the tablet wasn&amp;rsquo;t able to connect to the Internet.&lt;/p&gt;
&lt;p&gt;So my only option was to figure out to do a factory reset of the device. This device does not have a reset button on it. So off to Google I went.&lt;/p&gt;
&lt;p&gt;So long story short. After much searching I figured it out.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hold power button down to shutdown the device&lt;/li&gt;
&lt;li&gt;Power on device and immediately hold volume down button.&lt;/li&gt;
&lt;li&gt;This will bring you to a screen where you can selected DruidBoot or Recovery. Use could button to navigate to Recovery option and then press the power button to select it.&lt;/li&gt;
&lt;li&gt;You will then get the Android logo will an exclamation point over it. This is Androids way of saying you don&amp;rsquo;t have a custom recovery. Hold down the power and press the volume up to get a mini menu.&lt;/li&gt;
&lt;li&gt;Select factory reset from the options. Use volume buttons to navigate up and down. Press power once you have factory reset selected.&lt;/li&gt;
&lt;li&gt;On the next screen is a menu that ask are you sure you want to reset. Use the volume buttons to navigate to the I am sure option.&lt;/li&gt;
&lt;li&gt;It will take several minutes to reset the device and deleted all of the data. When done it will return you to the initial menu.&lt;/li&gt;
&lt;li&gt;Select the reboot option and when it completes the start up it will walk you through the initial setup agasetup Android.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Figuring out these instructions was a pain. I was ready to give up on the tablet being able to be used again and just put it in a drawer to collect dust. Now I can use the tablet again and it only took a few minutes once I figured out the instructions.&lt;/p&gt;</description></item><item><title>Jekyll Part 14: How To Validate Links and Images</title><link>https://digitaldrummerj.me/jekyll-validating-links-and-images/</link><pubDate>Mon, 01 Feb 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/jekyll-validating-links-and-images/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we are going to go through how you can validate your link and image references.&lt;/p&gt;
&lt;p&gt;As your blog grows and you get more posts, it becomes harder to validate images and links are still valid on older post. On new post it is pretty easy since you only have one last to look for. However, this is a process that can be fully automated so got don&amp;rsquo;t even have to worry about it anymore.&lt;/p&gt;
&lt;p&gt;Since Jekyll is Ruby based we are going to use a ruby gem called &lt;a href="https://github.com/gjtorikian/html-proofer/" target="_blank" &gt;html-proofer&lt;/a&gt;. Html-proofer is a command line utility that will run a set of tests to validate your HTML output. These tests check if your image references are legitimate, if they have alt tags, if your internal links are working, and so on. It&amp;rsquo;s intended to be an all-in-one checker for your output.&lt;/p&gt;
&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;
&lt;p&gt;You can either install the gem as part of your Gemfile or as another ruby gem.&lt;/p&gt;
&lt;p&gt;Add this line to your Gemfile&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gem 'html-proofer'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then execute&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bundle
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or install it yourself, just like any other ruby gems&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gem install html-proofer
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="generating-report"&gt;Generating Report&lt;/h2&gt;
&lt;p&gt;To generate a report open up the command line and run the command the corresponds to how you install html-proofer.&lt;/p&gt;
&lt;h3 id="gemfile"&gt;Gemfile&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;bundle exec htmlproofer ./_site/ --only-4xx
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="ruby-gem"&gt;Ruby Gem&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;htmlproofer ./_site/ --only-4xx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &amp;ndash;only4xx parameter above tell it to only reports errors for links that fall within the 4xx status code range. This would capture not found (404) and not authorized (401) errors but would ignore 500 internal server errors. The reason to ignore 500 errors is that we don&amp;rsquo;t want validation to fail if their web server is throwing an error since we are only testing that the link went some place valid.&lt;/p&gt;
&lt;p&gt;You will now get a report of any broken links or image tags. Since html-proofer is evaluating the Jekyll output, you may need to look at some of the include or layout file to fix links.&lt;/p&gt;
&lt;p&gt;Once you get the initial set of issues fixed, you will mainly have to worry about the new updates to your blog.&lt;/p&gt;
&lt;p&gt;Overall, this will help improve the quality of your blog by ensuring that you fix broken links and images before your users spot them.&lt;/p&gt;</description></item><item><title>AngularJS - Calling Filters in Your Angular Controller</title><link>https://digitaldrummerj.me/angular-calling-filters-in-your-controller/</link><pubDate>Thu, 28 Jan 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/angular-calling-filters-in-your-controller/</guid><category>angularjs</category><description>&lt;blockquote&gt;
&lt;p&gt;Note: This post applies to AngularJS. The 1.x version of Angular. AngularJS has been end of life at of 12/31/2021.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is a quick tip for how to call a filter from within your Angular controller. This example assumes that you already know what a filter is and have one created.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Inject $filter into your controller&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;angular&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;sample&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;SampleController&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SampleController&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* @ngInject */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;SampleController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Call your filter by calling:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;View&lt;/strong&gt; call your filter called &amp;ldquo;myFilter&amp;rdquo; on myDateVariable with arguments arg1 and arg2, you would use:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{myDateVariable | myfilter : arg1 : &amp;#39;arg2&amp;#39; }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Controller&lt;/strong&gt; call the same filter from within your controller:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;SampleController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$filter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;$filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;myFilter&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;myDateVariable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arg2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>AngularJS - Communicating Between Parent And Child Scopes</title><link>https://digitaldrummerj.me/angular-communication-between-parent-and-child-scopes/</link><pubDate>Mon, 25 Jan 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/angular-communication-between-parent-and-child-scopes/</guid><category>angularjs</category><description>&lt;blockquote&gt;
&lt;p&gt;Note: This post applies to AngularJS. The 1.x version of Angular. AngularJS has been end of life at of 12/31/2021.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is a quick tip in Angular on how to communicate between parent and child scopes.&lt;/p&gt;
&lt;p&gt;If you have a need to send notification of an event from a parent scope to a child scope, you use $scope.$broadcast to send the event.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$scope.$broadcast(&amp;quot;parent event name&amp;quot;, dataTo Send);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you then need to send notification of an event from the child scope back to the parent scope you use $scope.$emit&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$scope.$emit(&amp;quot;child event name&amp;quot;, dataTo Send);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To listen to the events regardless of if it sent from the parent or child scope, you use $scope.$on.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$scope.$on(&amp;quot;parent event name&amp;quot;, function(){
});
$scope.$on(&amp;quot;child event name&amp;quot;, function(){
});
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>AngularJS In Action Book Review</title><link>https://digitaldrummerj.me/angular-in-action-book-review/</link><pubDate>Thu, 21 Jan 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/angular-in-action-book-review/</guid><category>angularjs</category><description>&lt;blockquote&gt;
&lt;p&gt;Note: This post applies to AngularJS. The 1.x version of Angular. AngularJS has been end of life at of 12/31/2021.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img style="float: left;" src="/images/bookreviews/angular_in_action_cover.jpg"&gt; At the &lt;a href="http://holidayjs.com" target="_blank" &gt;HolidayJs&lt;/a&gt; event I won a book and I picked a copy of &lt;a href="https://www.manning.com/books/angularjs-in-action" target="_blank" &gt;Angular In Action&lt;/a&gt; by &lt;a href="http://onehungrymind.com/" target="_blank" &gt;Lukas Ruebbelke&lt;/a&gt;. I had already been using Angular for a few projects and wasn&amp;rsquo;t expecting to get much out of the book. Honestly I got it to use as more of a give away at one of my talks but figured I would at least glance through a few chapters of it first. Needless to say I ended up reading the whole book and plan to keep it.&lt;/p&gt;
&lt;p&gt;This book is one of the best introductions to Angular that I have read. Lukas does a fantastic jobs of explaining how it all works with lots of good code examples in a way that is very easy to understand. The book walks you through building a Trello clone with a pre-built backend so that you can stay focused on Angular and not worry about the data storage. After reading this book and going through the code examples you will be well on your way to being able to create your own Angular application.&lt;/p&gt;
&lt;p&gt;The book starts off explaining how all of the parts of Angular play together: configurations, routes, views, directives, controllers, services, and $scope. It is one of the best explanation that I had read or seen. Before this book I had understood how it played together enough to be able to code with it but couldn&amp;rsquo;t explain it well to others. Not anymore. If nothing else the opening chapter was more than worth it.&lt;/p&gt;
&lt;p&gt;Before diving into the details of each component, Lukas tasks about how to structure your Angular applications. He covers the basics and some common best practices. For detailed style guides he refers you to John Papa and Todd Moto&amp;rsquo;s style guides. I very much appreciate that he didn&amp;rsquo;t rehash their style guides.&lt;/p&gt;
&lt;p&gt;Next the book dives into the details of each of the components. He starts with views and controllers, then discusses services and finishes off with directives. He spends a while chapter on each component and the explanation of each is extremely easy to understand. He walks through each of the sections as he is building the sample app which really help to reinforce the concepts.&lt;/p&gt;
&lt;p&gt;Then we take a trip into polishing the UI with animations. At first I was thinking this is a throw away chapter but it turned out to be a really fun chapter. Lukas did a good job of explaining how to quickly and easily create both CSS and Javascript animations. All of the code was very easy to understand and could easily be applied to any application. I may just have to start thinking about how to use animation in my application.&lt;/p&gt;
&lt;p&gt;After animations, he jumps into how to navigate to the different views using routing with ngRoute. Like the rest of the book, it was a well explained chapter and a good overview for how routing actually works. I do wish that he had used UI router instead of ng-route since many of the projects that I have been working are using UI router but that is just me being greedy. ngRoute is more than enough for the coding examples. The loading spinner control was cool to see since this is something that every application should have to indicate something is happening to users.&lt;/p&gt;
&lt;p&gt;Next up is a quick trip into forms and forms validation. This chapter cover the basics of form validation for textboxes using required and minimum length. He covered a nice bit of the basic form validation code that you would need to know. I do wish that he would have cover more of the Html controls such as drop downs, multiple select, checkboxes, etc.&lt;/p&gt;
&lt;p&gt;One more cool thing that Lukas does is include a testing with Karma section in each of the chapters. Coming from an Agile background where I have done a good amount of automated unit tests for my projects, it was nice to see Lukas cover this topic. Lukas covers just enough to get you started on automated testing and shows you how easy it is to get started with it.&lt;/p&gt;
&lt;p&gt;Overall, I would give this book 5 out of 5 stars. If you are already an Angular developer or just getting into Angular this is a must have book. Wish I would have had it a long time ago. Great job Lukas.&lt;/p&gt;
&lt;p&gt;Link to book &lt;a href="https://www.manning.com/books/angularjs-in-action" target="_blank" &gt;https://www.manning.com/books/angularjs-in-action&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Ionic - Stop Icons from Overlapping in Ion List</title><link>https://digitaldrummerj.me/ionic-ion-item-multiple-icon-inline/</link><pubDate>Tue, 19 Jan 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ionic-ion-item-multiple-icon-inline/</guid><category>ionic</category><description>&lt;p&gt;Today I ran into an issue using Ionic, where I was trying to put two icons on the right side of an ion list item. There is really easy to do with the item-icon-right css class. Unfortunately, when you have multiple icons, it overlaps instead of showing them side by side. I was not expecting this as I had only used 1 icon before and assumed that item-icon-right would just put them side by side.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ionic-ion-list-inline-icons/icons-overlapped.png" alt="Overlapped Icons"&gt;&lt;/p&gt;
&lt;p&gt;Here is the code that causes the overlapped icons.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ion-list&amp;gt;
&amp;lt;ion-item class=&amp;quot;item item-icon-right&amp;quot;&amp;gt;
Task 1
&amp;lt;i class=&amp;quot;icon ion-ios-circle-outline&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;i class=&amp;quot;icon ion-close icon-accessory&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/ion-item&amp;gt;
&amp;lt;ion-item class=&amp;quot;item item-icon-right&amp;quot;&amp;gt;
Task 2
&amp;lt;i class=&amp;quot;icon ion-checkmark-circled&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;i class=&amp;quot;icon ion-close icon-accessory&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;
&amp;lt;/ion-item&amp;gt;
&amp;lt;/ion-list&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Luckily, there is an easy fix for this using some built-in Ionic css classes. We need to convert the icons into buttons, surround them with a div that has the class &amp;ldquo;buttons&amp;rdquo; on it, and change the ion-item class from item-icon-right to item-button-right.&lt;/p&gt;
&lt;p&gt;Also, to make the buttons still look like icons and not give them a border, we can use the button-icon class.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ion-list&amp;gt;
&amp;lt;ion-item class=&amp;quot;item item-button-right&amp;quot;&amp;gt;
Task 1
&amp;lt;div class=&amp;quot;buttons&amp;quot;&amp;gt;
&amp;lt;button class=&amp;quot;button button-icon ion-ios-circle-outline&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;button class=&amp;quot;button button-icon ion-close icon-accessory&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/ion-item&amp;gt;
&amp;lt;ion-item class=&amp;quot;item item-button-right&amp;quot;&amp;gt;
Task 2
&amp;lt;div class=&amp;quot;buttons&amp;quot;&amp;gt;
&amp;lt;button class=&amp;quot;button button-icon ion-ios-circle-outline&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;button class=&amp;quot;button button-icon ion-close icon-accessory&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/ion-item&amp;gt;
&amp;lt;/ion-list&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/images/ionic-ion-list-inline-icons/icons-not-overlapped.png" alt="Not Overlapped Icons"&gt;&lt;/p&gt;</description></item><item><title>Preparing A Conference Talk</title><link>https://digitaldrummerj.me/preparing-a-presentation/</link><pubDate>Thu, 14 Jan 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/preparing-a-presentation/</guid><category>speaking</category><description>&lt;p&gt;I have done a bunch of conference talks and brown bag sessions this year (~45 in the past 2 years) and here is how I prepare for those talks. Sorry that the reply is a little long but I wanted to make sure you had a process to get started with. Remember that this is my process and you have to find what works best for you.&lt;/p&gt;
&lt;p&gt;There is no set time for how long each phase takes. It all depends on the presentation and the length of it. I have had talks where I spent several weeks working on it while other talks were done in less than a day.&lt;/p&gt;
&lt;p&gt;You can see a good number of my presentations at &lt;a href="http://slides.com/digitaldrummerj" title="http://slides.com/digitaldrummerj" target="_blank" &gt;Presentations by Justin&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="phase-1-brainstorming"&gt;Phase 1: Brainstorming&lt;/h2&gt;
&lt;p&gt;In this phrase you are brainstorm the flow and content of the talk. I like using Post-In notes for this since it is very easy to move them around and add/remove them and I don&amp;rsquo;t get into the rut of worrying about the content/format in powerpoint. I have also used mindmapping software for this but I like the low tech post-it notes better.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Jot down the goals for the talk. Essentially what do I want attendees to walk away with and what am I solving for them. Do I want attendees to be excited about a new technology? Is it having code samples that they can immediately implement into their projects? Is it helping them solve a common problem that I think they will run into?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Brainstorm the main categories/themes that the talk with be split into. I try not to have more more than 3 categories/theme. Don&amp;rsquo;t worry about wording here as long you know what the idea is.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Brainstorm the content under each category/theme. Since we are using post-in notes, it is meant to be just a few words and not the whole slide of content. I don&amp;rsquo;t have a limit on post-in notes per category. Each post-it equals 1 slide.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Arrange the categories and content post-in notes for each category in the order that I think it would go in.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, the brainstorming for the overall content is done and I start drafting in powerpoint.&lt;/p&gt;
&lt;p&gt;Also, a couple of times I have review these post-it notes with other people to get their feedback before I start drafting actual slides so that I can make sure that the content is something that makes sense and that I didn&amp;rsquo;t forget anything that they would want to see.&lt;/p&gt;
&lt;h2 id="phase-2-drafting"&gt;Phase 2: Drafting&lt;/h2&gt;
&lt;p&gt;Typically I move to powerpoint during this phrase but I have also times where I have done a full outline using just OneNote before moving into drafting in powerpoint.&lt;/p&gt;
&lt;p&gt;Using the post-it note order from phrase 1, I create a slides for each post-in note. As I create each slide I populate the content of it. I find many times as I am drafting that what I thought would be slides either go away or change, so that is why I create the content as I got.&lt;/p&gt;
&lt;p&gt;I do have a few rules I try to follow during this phrase to make sure that I don&amp;rsquo;t get bogged down in the details.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&amp;rsquo;t look for pictures for the slides. Focus on the content first. If I want a picture I make a note on the slide of what kind of picture I am looking for and once I finalize the content I go look for the pictures.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t worry about the powerpoint template. Focus on the content first. If it is an Intel presentation, I normally start with the Intel template. If I am doing the presentation outside of Intel and it is not an Intel sponsored presentation, I don&amp;rsquo;t worry about the template until later.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t worry about the number of bullet points per slide yet. That will come during the editing phase.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t worry about transitions or only showing 1 bullet point at a time. Those get added during the finalizing phase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this point, you should have a good start to the talks content and slide deck. Now we need to focus the content and edit the slide deck.&lt;/p&gt;
&lt;p&gt;Also, I normally don&amp;rsquo;t show other people the powerpoint during this phase since I have found that they get hung up the stuff you will be doing in the editing phase instead of review the overall structure and message.&lt;/p&gt;
&lt;h2 id="phase-3-editing"&gt;Phase 3: Editing&lt;/h2&gt;
&lt;p&gt;In this phrase, we need to focus and tighten up the content of the presentation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I edit each slide to have 3-5 single line bullet points max.&lt;/li&gt;
&lt;li&gt;I remove excess words to make each bullet point short and sweet.&lt;/li&gt;
&lt;li&gt;I make sure that I keep the font size between 24-28 pt for each bullet. This makes it so that people in the back of the room can read the bullets with ease and makes you focus each bullet point.&lt;/li&gt;
&lt;li&gt;I make sure that the slide is a summary of what I want to say and not everything that I will be saying. People don&amp;rsquo;t want to listen to you read the slide to them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the content editing is done, then I look for images and powerpoint templates.&lt;/p&gt;
&lt;p&gt;Now onto the last phase where the presentation is finalized and you are ready to give it.&lt;/p&gt;
&lt;p&gt;This is also the point that I would send out the presentation for people to review since it is essentially done.&lt;/p&gt;
&lt;h2 id="phase-4-finalizing"&gt;Phase 4: Finalizing&lt;/h2&gt;
&lt;p&gt;Now it is time to put the finishing touches on the presentation.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add transitions between slides. Nothing crazy, just a basic fade. I have gotten feedback from multiple presenters and attendees that having nothing makes it difficult for their eyes and brain to move to the next slide.&lt;/li&gt;
&lt;li&gt;Have bullets come in 1 at a time (appear animation in powerpoint terms). Having all of the bullets show at once normally has the effect of having people reading the slide instead of listening to you.&lt;/li&gt;
&lt;li&gt;Double check that the font is big enough&lt;/li&gt;
&lt;li&gt;Double check that the font colors have enough contrast to be readable&lt;/li&gt;
&lt;li&gt;Run though the slide in presentation mode and make sure everything works as expected (transitions, bullet animations, etc). Try to stand back 5-10 feet from the monitor and make sure that you can read the presentation.&lt;/li&gt;
&lt;li&gt;If you are going to be giving it on a projector or TV, hook it up to one and make sure that it looks rights (fonts, font size, colors, etc)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;##Phase 5: Practice&lt;/p&gt;
&lt;p&gt;Practice you talk out loud as much as you can. By practicing the talk out loud you will find any spots where you might stumble on words or phrases or that something doesn&amp;rsquo;t make sense said out loud.&lt;/p&gt;
&lt;p&gt;If you are going to use a slide advancer, make sure to practice with it. I strongly recommend a slide advancer if you don&amp;rsquo;t have one. Not having to run back to the laptop to go to the next slide or hunting for the page down makes your presentation smoother. I have the Logitech R400 slide advancer, &lt;a href="http://www.amazon.com/Logitech-910-001354-Wireless-Presenter-R400/dp/B002GHBUTK/ref=sr_1_1?s=pc&amp;amp;ie=UTF8&amp;amp;qid=1449186602&amp;amp;sr=1-1&amp;amp;keywords=logitech&amp;#43;slide&amp;#43;advancer" title="http://www.amazon.com/Logitech-910-001354-Wireless-Presenter-R400/dp/B002GHBUTK/ref=sr_1_1?s=pc&amp;amp;ie=UTF8&amp;amp;qid=1449186602&amp;amp;sr=1-1&amp;amp;keywords=logitech&amp;#43;slide&amp;#43;advancer" target="_blank" &gt;http://www.amazon.com/Logitech-910-001354-Wireless-Presenter-R400/dp/B002GHBUTK/ref=sr_1_1?s=pc&amp;amp;ie=UTF8&amp;amp;qid=1449186602&amp;amp;s…&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If giving the talk outside of an Intel facility, plan for slow network connections or no internet at all unless you tether to your phone. If you plan to tether to your phone make sure to test it out more than once to make sure it works. If you are going to be downloading anything, make sure to have an already downloaded copy in case you do have no internet.&lt;/p&gt;
&lt;p&gt;If you are doing any kind of demos, have a one click reset button.&lt;/p&gt;
&lt;p&gt;If giving the talk outside of Intel, make sure to have backup copies of your slides, demos, etc on either a usb drive just in case something happens with your computer and you are able to find a someone that can loan you one that you might have a chance to still give the talk that you would have from your laptop.&lt;/p&gt;
&lt;h2 id="phase-6-giving-the-talk"&gt;Phase 6: Giving the Talk&lt;/h2&gt;
&lt;p&gt;Now is the fun part, giving the actual talk. Remember to breath and that everyone in the audience is rooting for you to do well. If you have done all of the prep work then you will be able to work through anything that comes up.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the content is something that you can give out, have your material available online for people to have as soon as you are done with your talk.&lt;/li&gt;
&lt;li&gt;If you are asked a question that you don&amp;rsquo;t know the answer to, don&amp;rsquo;t be afraid to say you have not look what they are asking or that you are not sure and can get back to them later with an answer.&lt;/li&gt;
&lt;li&gt;Make sure to have a water bottle with you. Beside your throat getting horse, it is a great way to add a pause into the talk without it feeling awkward to you.&lt;/li&gt;
&lt;li&gt;Make sure to pace yourself and not rush. It is extremely easy to rush through a talk and finish in half the time.&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>AngularJS - Calling Service Methods from Console</title><link>https://digitaldrummerj.me/angular-running-service-in-console/</link><pubDate>Mon, 11 Jan 2016 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/angular-running-service-in-console/</guid><category>angularjs</category><description>&lt;blockquote&gt;
&lt;p&gt;Note: This post applies to AngularJS. The 1.x version of Angular. AngularJS has been end of life at of 12/31/2021.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here is a quick tip for how you can run your Angular service and factory methods within the Chrome Dev Tools console. No longer will you have to go through the process of navigating through the UI to trigger a Service/Factory method to run. Now you can just load up the web site and do all of your debugging through the Chrome console.&lt;/p&gt;
&lt;p&gt;If your service/factory was called &amp;ldquo;YourFactory&amp;rdquo; and the ng-app attribute is on the body tag, you can get a reference to the &amp;ldquo;YourFactory&amp;rdquo; with the following line in the console.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var t = angular.element(document.querySelector('body')).injector().get('YourFactory');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After you have the reference, then you can call any method that is exposed by the &amp;ldquo;YourFactory&amp;rdquo; service such calling the method &amp;ldquo;myServiceMethod&amp;rdquo; and using the returned promise from the method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;t.myServiceMethod().then(function(response) { console.log(response); });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you can quickly debug your service/factory.&lt;/p&gt;</description></item><item><title>2015 Year In Review</title><link>https://digitaldrummerj.me/year-in-review/</link><pubDate>Thu, 31 Dec 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/year-in-review/</guid><category>year in review</category><description>&lt;p&gt;2015 was a great year with lots of new adventures. I did a bunch of travelling, started a new position at work, competed in my first hackathon (and win), and started a couple of meetups. At the beginning of 2015 I set a few goals for myself:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Speak at more events&lt;/li&gt;
&lt;li&gt;Start blogging again&lt;/li&gt;
&lt;li&gt;Be more active in my local dev community&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="speaking"&gt;Speaking&lt;/h2&gt;
&lt;p&gt;I spoke at a good amount of events this year considering this was my first year putting myself out there. I found that I really love presenting and helping people.&lt;/p&gt;
&lt;p&gt;I presented 32 talks between 20 events. I found that I also don&amp;rsquo;t mind presenting a bunch of talks at a single event. In fact I like presenting more than 1 talk events.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;10 User Groups / Meetups&lt;/li&gt;
&lt;li&gt;5 Conferences&lt;/li&gt;
&lt;li&gt;4 Code Camps&lt;/li&gt;
&lt;li&gt;1 podcast&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It was amazing meeting all of the different people. Many of whom I admired as speakers and developers. It was hard to believe that I was presenting at the same event that they were.&lt;/p&gt;
&lt;p&gt;Thank you to those that attended my talks. I couldn&amp;rsquo;t do it without you.&lt;/p&gt;
&lt;p&gt;Thank you as well to those that provided me feedback and helped me become a better speaker.&lt;/p&gt;
&lt;p&gt;I even got to fulfill a long time dream of being on the &lt;a href="http://dotnetrocks.com" target="_blank" &gt;.Net Rocks podcast&lt;/a&gt;. I never imagined I would be on the podcast and it was a total out of the blue thing. I got introduced to Carl and Richard at &lt;a href="http://nebraskacode.com" target="_blank" &gt;Nebraska Code&lt;/a&gt;. Then I saw Richard again at &lt;a href="http://anglebrackets.org" target="_blank" &gt;Angle Brackets&lt;/a&gt; where I was an attendee and I mentioned to him that one of my goals is to speak at the conference. As we got to talking about potential talks, he said my Vagrant talk would be a great podcast episode. Luckily for me, we were both going to be at NDC Oslo and he was just starting planning the NDC episodes. You can listen to me on &lt;a href="https://www.dotnetrocks.com/?show=1158" target="_blank" &gt;episode 1158 &lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As well you can see all my presentations at &lt;a href="http://digitaldrummerj.me/speaking" target="_blank" &gt;http://digitaldrummerj.me/speaking/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="speaking-goals-for-2016"&gt;Speaking Goals for 2016&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Submit to more conferences. Want to put speak at more but since it is CFP for them the selection part is out of my control.&lt;/li&gt;
&lt;li&gt;Complete my Pluralsight audition and hopefully a course&lt;/li&gt;
&lt;li&gt;Come up with at least 1 new talk&lt;/li&gt;
&lt;li&gt;Turn my net promoter feedback system into an actual application.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="blogging"&gt;Blogging&lt;/h2&gt;
&lt;p&gt;I had tried blogging in the past and it never lasted more than a few post. Well, not this time. In January, I created &lt;a href="http://digitaldrummerj.me" target="_blank" &gt;http://digitaldrummerj.me &lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This year I wrote 50 posts with 54,000 page views. For my first year blogging, overall I consider it a success.&lt;/p&gt;
&lt;p&gt;I used Jekyll for the blog engine and hosted it on Github Page. It was a lot of fun using Jekyll and creating the layout and theme for my blog. I could have easily went with WordPress or one of the canned solutions but I took it as an opportunity to sharpen my html and css skills as well as learn some new things like using Jekyll, and integrating Google Analytics and Disqus.&lt;/p&gt;
&lt;p&gt;I even redid the theme for the blog in the middle of the year which was an adventure in itself. However, I don&amp;rsquo;t plan on doing this very often as it took a lot of time away from writing blog post. I am much happier though with the new theme, so the time was at least worth it.&lt;/p&gt;
&lt;p&gt;I also figured out how to use Zapier to create a scheduling engine for the blog using Google Calendar and Github Pull Request. Since Jekyll is a static site generator it does not have admin functions like scheduling. It was one of the things that I did miss compared to other blogging engines. Since using Zapier for this, I have used it as well as &lt;a href="http://ifttt.com" target="_blank" &gt;IFTTT&lt;/a&gt; to automate a few things.&lt;/p&gt;
&lt;p&gt;It was also fun figuring out how in Jekyll to easily create a series post and get a listing of all of the post for the series in order. Originally I did this by hand but that got old quickly. Now I just have to add a series tag to the front matter and it does it for me. This was important toe since several of the topics I was writing on were too big for a single blog post.&lt;/p&gt;
&lt;p&gt;I have a whole series of blog post detailing out setting up Jekyll, integrating Disqus and Google Analytics, creating series, plus several other ones. View the series at &lt;a href="http://digitaldrummerj.me/blogging-on-github-part-1-Getting-Started/" target="_blank" &gt;http://digitaldrummerj.me/blogging-on-github-part-1-Getting-Started/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="blogging-goals-for-2016"&gt;Blogging Goals for 2016&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Double the number of post&lt;/li&gt;
&lt;li&gt;Stick to the schedule of post in Trello&lt;/li&gt;
&lt;li&gt;Write every day even if it is just 10 minutes&lt;/li&gt;
&lt;li&gt;Submit to be a guest blogger on at least one popular blog&lt;/li&gt;
&lt;li&gt;Get ahead in my completed post so everything is not last minute. Be nice to have at least 2 post scheduled at all times.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="dev-community"&gt;Dev Community&lt;/h2&gt;
&lt;p&gt;I have been part of various user groups over the years but was never part of the organizing committee nor did I give back as much as I wanted to.&lt;/p&gt;
&lt;p&gt;This year I decided to change that. I am now running 2 meetups, mentored at some events, and am on the planning comittee for a couple events.&lt;/p&gt;
&lt;h3 id="phoenix-version-control-meetup"&gt;Phoenix Version Control Meetup&lt;/h3&gt;
&lt;p&gt;Right after I started using Jekyll and Github Pages, what is now the Phoenix Version Control meetup was started and I was asked if I wanted to be a co-organizer. Originally it started as a Github Pages group but quickly changed to more about version control with a focus on Github.&lt;/p&gt;
&lt;p&gt;This was my first experience organizing a meetup and it had been a great learning experience. This group has struggled a bit to get going but we have a monthly meeting with about 10 people attending regularly. Not sure what 2016 will bring for this group.&lt;/p&gt;
&lt;h3 id="ionic-arizona-meetup"&gt;Ionic Arizona Meetup&lt;/h3&gt;
&lt;p&gt;Then around August of this year I saw on meetup that Michael Iglesias was starting an Ionic Arizona meetup. I had also been thinking about starting one as well, so I sent a message to Michael offering to present at one of the meetings and asking if he needed a co-organizer. He graciously accepted both offers.&lt;/p&gt;
&lt;p&gt;After some searching for a venue, we held our first meeting at the end of September with about 30 people attending. Since then we have had 3 more meetings with 20-25 people at each meeting.&lt;/p&gt;
&lt;p&gt;For 2016, we already have a great set of talks for the first half of the year scheduled. We are doing &amp;ldquo;Releasing Your to the App Stores&amp;rdquo; in January, &amp;ldquo;IoT with Octoblu and Ionic&amp;rdquo; in February, &amp;ldquo;Database as a Service with Backand&amp;rdquo; in March, &amp;ldquo;ionic 2 and angular 2&amp;rdquo; in April and &amp;ldquo;An Ionic Showcase of thing members have built with Ionic&amp;rdquo; in May. If you would like to speak at one of the meetings, please let me know.&lt;/p&gt;
&lt;p&gt;In December, I also registered &lt;a href="http://ionic-az.org" target="_blank" &gt;ionic-az.org&lt;/a&gt;, created a Twitter account (&lt;a href="http://twitter.com/ionic_az" target="_blank" &gt;@ionic_az&lt;/a&gt;), created email accounts using &lt;a href="http://zoho.com" target="_blank" &gt;Zoho&lt;/a&gt;, and created a newsletter for the group.&lt;/p&gt;
&lt;p&gt;The reason for the newsletter is to be able to share all of the great Ionic articles that I run across without spamming the group with a ton of messages. The first newsletter went out the week of Christmas and the next one is ready and will be going out next week. The newsletter will be bi-weekly. Look for a blog post soon on how I created the newsletter using a combination of a bunch of tools to automate most of the work.&lt;/p&gt;
&lt;p&gt;We have also been work hard to get sponsorship for the group. So far we have a venue/food sponsor in &lt;a href="http://www.integrate.com" target="_blank" &gt;Integrate&lt;/a&gt; and &lt;a href="http://jetbrains.com" target="_blank" &gt;JetBrains&lt;/a&gt; has been gracious enough to give a free license each month for an idea of the winners choice.&lt;/p&gt;
&lt;p&gt;The one not so great part of the Ionic Az group is that my co-leader, Michael, moved out of Phoenix and back to the East Coast. We worked well together and it was a pleasure running the group with him. I will be continuing the group and am looking for a co-leader.&lt;/p&gt;
&lt;h3 id="conference-planning"&gt;Conference Planning&lt;/h3&gt;
&lt;p&gt;I have also joined the planning comittee for an Agile Conference in Portland that is scheduled for April and I will be helping with the planning for Desert Code Camp in Phoenix.&lt;/p&gt;
&lt;h3 id="mentoring"&gt;Mentoring&lt;/h3&gt;
&lt;p&gt;I also helped out as a mentor at the Chandler Startup weekend. Was also suppose to mentor at She Hacks AZ but that event got cancelled. It was fun doing the mentoring and being on that side of the process.&lt;/p&gt;
&lt;h3 id="community-goals"&gt;Community Goals&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Continue to grow Ionic AZ attendance.&lt;/li&gt;
&lt;li&gt;Grow Ionic AZ to where I do not have to present a topic at each meeting&lt;/li&gt;
&lt;li&gt;Continue to build my community network.&lt;/li&gt;
&lt;li&gt;Figuring out to organize more workshops locally on various topics.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Well this post turn out way longer than I thought it would be. Guess I did a lot more in 2015 than I realized. Every time I thought I was done I remembered something else. 2015 was a great year and I am looking forward to 2016.&lt;/p&gt;
&lt;p&gt;To start 2016 I am running an &lt;a href="https://www.eventbrite.com/e/use-your-existing-web-skills-to-create-native-mobile-applications-tickets-19830200664" target="_blank" &gt;Ionic Workshop&lt;/a&gt; on January 16th at Gangplank Chandler, will be speaking at the Javascript Summit Virtual Conference in February and in March will be running an &lt;a href="http://conferences.oreilly.com/fluent/javascript-html-us/public/schedule/speaker/219565" target="_blank" &gt;Ionic workshop&lt;/a&gt; at O&amp;rsquo;Reilly Fluent Conf.&lt;/p&gt;
&lt;p&gt;If you see me out at any events, come up and say hi.&lt;/p&gt;
&lt;p&gt;Looking forward to seeing everyone in 2016.&lt;/p&gt;</description></item><item><title>Ionic - Using Local Notifications</title><link>https://digitaldrummerj.me/ionic-local-notification/</link><pubDate>Tue, 01 Dec 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ionic-local-notification/</guid><category>ionic</category><description>&lt;p&gt;When you are creating a mobile applications there are times where you need to notify users about something such as an upcoming appointment. If the application is running and the user is using it in the foreground, this is easy to accomplish. However, if the application is running in the background this can be a challenging task. You could do push notifications but that takes a decent amount of work to setup with both iOS and Android app stores. If all you need to do is alert them on their local device you can just use the cordova local notification plugin and skip the headache of setting up push notifications.&lt;/p&gt;
&lt;p&gt;In this post we will walk you through creating an ionic application that uses the &lt;a href="http://ngcordova.com/docs/plugins/localNotification/" target="_blank" &gt;ngCordova local notification plugin&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="environment-setup"&gt;Environment Setup&lt;/h2&gt;
&lt;p&gt;Before we get started, we need to make sure that you have your development environment configured with either an emulator or physical device as the local notification only works on a device and not a web browser. If you have already configured the Ionic Framework and an emulator/physical device, you can skip this section.&lt;/p&gt;
&lt;p&gt;Make sure that you meet these requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Android environment configured -
&lt;ul&gt;
&lt;li&gt;Java 7+&lt;/li&gt;
&lt;li&gt;Android SDK&lt;/li&gt;
&lt;li&gt;Android Studio&lt;/li&gt;
&lt;li&gt;Either emulator, genymotion, or physical device&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;iOS environment configured (if on a MAC)
&lt;ul&gt;
&lt;li&gt;XCode&lt;/li&gt;
&lt;li&gt;Either iOS Simulator or Device&lt;/li&gt;
&lt;li&gt;XCode Command Line Tools&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;nodejs&lt;/li&gt;
&lt;li&gt;ionic&lt;/li&gt;
&lt;li&gt;cordova&lt;/li&gt;
&lt;li&gt;gulp&lt;/li&gt;
&lt;li&gt;bower&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can refer to the following articles to configure your environment:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="/images/IonicLocalNotifications/Ionic-Setup-Windows" &gt;Setup Ionic on Windows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/images/IonicLocalNotifications/ionic-setup-osx/" &gt;Setup Ionic on Mac&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="creating-project"&gt;Creating Project&lt;/h2&gt;
&lt;p&gt;The first thing we need to do is create a new ionic project that we will use to test the local notifications.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ionic start ionic-local-notifications blank
cd ionic-local-notifications
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to add the platforms to the ionic application since we need to deploy to a device in order for the local notifications to work. It will not work correctly in the browser.&lt;/p&gt;
&lt;p&gt;To add the android platform:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ionic platform add android
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To add the iOS platform (for Mac users):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ionic platform add ios
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember, if you’re not using a Mac, you cannot build for the iOS platform.&lt;/p&gt;
&lt;h2 id="install-ngcordova"&gt;Install ngCordova&lt;/h2&gt;
&lt;p&gt;ngCordova is a collection of 70+ AngularJS extensions on top of the Cordova API that make it easy to build, test, and deploy Cordova mobile apps with AngularJS. These extensions allow us to interact with device features such as the camera, battery status, and geolocation.&lt;/p&gt;
&lt;p&gt;To install ngCordova run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bower install ngCordova --save
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Include ng-cordova.js or ng-cordova.min.js in your www\index.html file before cordova.js and after your AngularJS / Ionic file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script src=&amp;quot;lib/ionic/js/ionic.bundle.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&amp;quot;lib/ngCordova/dist/ng-cordova.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&amp;quot;cordova.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="install-the-local-notification-plugin"&gt;Install the local notification plugin&lt;/h2&gt;
&lt;p&gt;To install the plugin run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ionic plugin add https://github.com/katzer/cordova-plugin-local-notifications.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you open up the package.json file, you will see the local notification plugin add along with the other cordova plugins.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;cordovaPlugins&amp;quot;: [
{
&amp;quot;locator&amp;quot;: &amp;quot;https://github.com/katzer/cordova-plugin-local-notifications.git&amp;quot;,
&amp;quot;id&amp;quot;: &amp;quot;de.appplant.cordova.plugin.local-notification&amp;quot;
}
]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="add-ngcordova-as-a-dependency"&gt;Add ngCordova as a dependency&lt;/h2&gt;
&lt;p&gt;Open up the www/js/app.js file and inject ngCordova into the module&lt;/p&gt;
&lt;p&gt;angular.module(&amp;lsquo;starter&amp;rsquo;, [&amp;lsquo;ionic&amp;rsquo;, &amp;rsquo;ngCordova&amp;rsquo;])&lt;/p&gt;
&lt;h2 id="creating-a-new-controller"&gt;Creating a New Controller&lt;/h2&gt;
&lt;p&gt;We need to create an Angular controller that the UI will interact with. At the bottom of the www/js/app.js file add the following&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.controller('SampleController', function($scope, $cordovaLocalNotification) {
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to tell the UI what the controller is. Open up the www/index.html page and add the controller to the ion-content.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ion-pane&amp;gt;
&amp;lt;ion-header-bar class=&amp;quot;bar-stable&amp;quot;&amp;gt;
&amp;lt;h1 class=&amp;quot;title&amp;quot;&amp;gt;Local Notification Sample&amp;lt;/h1&amp;gt;
&amp;lt;/ion-header-bar&amp;gt;
&amp;lt;ion-content ng-controller=&amp;quot;SampleController&amp;quot;&amp;gt;
&amp;lt;/ion-content&amp;gt;
&amp;lt;/ion-pane&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="adding-functions-to-create-notifications"&gt;Adding Functions to Create Notifications&lt;/h2&gt;
&lt;p&gt;Within the SampleController, we need to add all of the functions to do the scheduling of the notifications.&lt;/p&gt;
&lt;p&gt;All of the local notification functions should be contained within an ionic platform ready function and inside of a check that makes sure we are running in a WebView on a device. This will at least allow us to see the UI in the browser without error, it just won&amp;rsquo;t run any of the local notification function unless on a WebView.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ionicPlatform.ready(function () {
if (ionic.Platform.isWebView()) {
}
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure to inject $ionicPlatform into the SampleController&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.controller('SampleController', function($scope, $cordovaLocalNotification, $ionicPlatform) {
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="instant-notification"&gt;Instant Notification&lt;/h3&gt;
&lt;p&gt;To schedule an immediate notification, add the following function within the ionicPlatform.ready function&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$scope.scheduleInstantNotification = function () {
$cordovaLocalNotification.schedule({
id: 1,
text: 'Instant Notification',
title: 'Instant'
}).then(function () {
alert(&amp;quot;Instant Notification set&amp;quot;);
});
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the www/index.html page within the ion-content, add a button and set the ng-click to call the scheduleInstantNotification function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button
class=&amp;quot;button&amp;quot;
ng-click=&amp;quot;scheduleInstantNotification()&amp;quot;&amp;gt;
Instant
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="notification-x-seconds-from-now"&gt;Notification X Seconds from Now&lt;/h3&gt;
&lt;p&gt;To schedule a notification 5 seconds from now, add the following function within the ionicPlatform.ready function.&lt;/p&gt;
&lt;p&gt;The difference between this function and the immediate notification is the date property is being set.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$scope.scheduleNotificationFiveSecondsFromNow = function () {
var now = new Date().getTime();
var _5SecondsFromNow = new Date(now + 5000);
$cordovaLocalNotification.schedule({
id: 2,
published: _5SecondsFromNow,
text: 'Notification After 5 Seconds Has Been Triggered',
title: 'After 5 Seconds'
}).then(function () {
alert(&amp;quot;Notification After 5 seconds set&amp;quot;);
});
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the www/index.html page within the ion-content, add a button and set the ng-click to call the scheduleNotificationFiveSecondsFromNow function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button
class=&amp;quot;button&amp;quot;
ng-click=&amp;quot;scheduleNotificationFiveSecondsFromNow()&amp;quot;&amp;gt;
In 5 Sec
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="notification-every-minute"&gt;Notification Every Minute&lt;/h3&gt;
&lt;p&gt;To schedule a notification every minute, add the following function within the ionicPlatform.ready function&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$scope.scheduleEveryMinuteNotification = function () {
$cordovaLocalNotification.schedule({
id: 3,
title: 'Every Minute',
text: 'Give a real message',
every: 'minute'
}).then(function (result) {
console.log('Every Minute Notification Set');
});
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: The every options possible values are second, minute, hour, day, week, month, or year.&lt;/p&gt;
&lt;p&gt;In the www/index.html page within the ion-content, add a button and set the ng-click to call the scheduleEveryMinuteNotification function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button
class=&amp;quot;button&amp;quot;
ng-click=&amp;quot;scheduleEveryMinuteNotification()&amp;quot;&amp;gt;
Every Minute
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="update-notification-text"&gt;Update Notification Text&lt;/h3&gt;
&lt;p&gt;To update a the every minute notification, add the following function within the ionicPlatform.ready function. You also need to check that the notification is scheduled before trying to update it. This functions requires that you clicked on the Every Minute button to schedule the every minute notification.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$scope.updateNotificationText = function () {
$cordovaLocalNotification.isPresent(3).then(function (present) {
if (present) {
$cordovaLocalNotification.update({
id: 3,
title: 'Notificaton Update',
text: 'Notification Update Details'
}).then(function (result) {
console.log('Updated Notification Text');
});
} else {
alert(&amp;quot;Must Schedule Every Minute First&amp;quot;);
}
});
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the www/index.html page within the ion-content, add a button and set the ng-click to call the updateNotification function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button
class=&amp;quot;button&amp;quot;
ng-click=&amp;quot;updateNotificationText()&amp;quot;&amp;gt;
Update Text for Every Minute
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="update-notification-interval"&gt;Update Notification Interval&lt;/h3&gt;
&lt;p&gt;To update a the every minute notification to be scheduled every second, add the following function within the ionicPlatform.ready function. You also need to check that the notification is scheduled before trying to update it. This functions requires that you clicked on the Every Minute button to schedule the every minute notification.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$scope.updateNotificationEvery = function () {
$cordovaLocalNotification.isPresent(3).then(function (present) {
if (present) {
$cordovaLocalNotification.update({
id: 3,
title: 'Notification Update',
text: 'Every Minute change to second',
every: 'second'
}).then(function (result) {
console.log('Updated Notification Every');
});
} else {
alert(&amp;quot;Must Schedule Every Minute First&amp;quot;);
}
});
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the www/index.html page within the ion-content, add a button and set the ng-click to call the updateNotification function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button
class=&amp;quot;button button-block button-positive&amp;quot;
ng-click=&amp;quot;updateNotificationEvery()&amp;quot;&amp;gt;
Update Every Min to Second
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="cancel-notification"&gt;Cancel Notification&lt;/h3&gt;
&lt;p&gt;To cancel a notification, add the following function within the ionicPlatform.ready function. You also need to check that the notification is scheduled before trying to cancel it. This functions requires that you clicked on the Every Minute button to schedule the every minute notification.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$scope.cancelNotification = function () {
$cordovaLocalNotification.isPresent(3).then(function (present) {
if (present) {
$cordovaLocalNotification.cancel(3).then(function (result) {
console.log('Notification EveryMinute Cancelled');
alert('Cancelled Every Minute');
});
} else {
alert(&amp;quot;Must Schedule Every Minute First&amp;quot;);
}
});
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the www/index.html page within the ion-content, add a button and set the ng-click to call the cancelNotification function.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button
class=&amp;quot;button&amp;quot;
ng-click=&amp;quot;cancelNotification()&amp;quot;&amp;gt;
Cancel Every Minute
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="deploy-to-device"&gt;Deploy to Device&lt;/h2&gt;
&lt;p&gt;In order for the local notifications to work, you need to deploy the application to a device or an emulator. It will not work correctly in the browser.&lt;/p&gt;
&lt;p&gt;Note: You can only compile and deploy ios application on a Mac.&lt;/p&gt;
&lt;p&gt;Android:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ionic run android
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;iOS:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ionic run ios
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: You may need to pass in &amp;ndash;device to the command to get it to run on a device vs an emulator.&lt;/p&gt;
&lt;h2 id="wrap-up"&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;Local notifications are a great option for being able to alert a user to something in your application. As you saw it only took a few lines of code to enable them. This article touched on the basic but there is more that you can do with local notifications. You can interact with multiple notifications at once for scheduling, updating, and cancelling. You can pass additional data into each notification that you can then use when the notification is trigger. On the rootscope you can listen for notifications to be scheduled, tiggered, updated or cancelled.&lt;/p&gt;
&lt;p&gt;Source code for this article is available at &lt;a href="https://github.com/digitaldrummerj-ionic/ionic-local-notifications-sample" target="_blank" &gt;https://github.com/digitaldrummerj-ionic/ionic-local-notifications-sample&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Running Gulp Task in Visual Studio</title><link>https://digitaldrummerj.me/running-gulp-task-in-visual-studio/</link><pubDate>Sun, 15 Nov 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/running-gulp-task-in-visual-studio/</guid><category>visual studio</category><category>gulp</category><description>&lt;p&gt;When I am working in an code editor such as Visual Studio, I do not want to have to have to leave the editor to run command line programs such as Gulp task. I want to be able to run the gulp task right from instead the editor. Starting with Visual Studio 2013, you could do this with the &lt;a href="https://visualstudiogallery.msdn.microsoft.com/8e1b4368-4afb-467a-bc13-9650572db708" target="_blank" &gt;Task Runner Explorer extension&lt;/a&gt;. Microsoft then integrated the Task Runner Explorer into Visual Studio 2015 so you no longer have to install an extension.&lt;/p&gt;
&lt;p&gt;In this post, we will look at how to run your Gulp task within Visual Studio and then how to integrate them into the Visual Studio build process.&lt;/p&gt;
&lt;h2 id="code-for-this-tutorial"&gt;Code for this tutorial&lt;/h2&gt;
&lt;p&gt;The sample code for this tutorial is available at &lt;a href="https://github.com/digitaldrummerj/pluralsight-audition" target="_blank" &gt;https://github.com/digitaldrummerj/pluralsight-audition&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="pulling-up-the-task-runner-explorer"&gt;Pulling up the Task Runner Explorer&lt;/h2&gt;
&lt;p&gt;You can get to the task runner explorer, by going under the View Menu, selecting Other Windows, and then selecting the Task Runner Explorer.
&lt;img src="/images/GulpInVisualStudio/TaskRunnerExplorer_FindInMenu.png" alt="Open Task Runner Explorer"&gt;&lt;/p&gt;
&lt;p&gt;When the Task Runner Explorer opens it will pull up all of the task from gulpfile.js that is in the root directory of the project.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/GulpInVisualStudio/TaskRunnerExplorer.png" alt="Task Runner Explorer Initial View"&gt;&lt;/p&gt;
&lt;p&gt;In this case it shows the 5 task that are available in the gulpfile.js&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;default&lt;/li&gt;
&lt;li&gt;clean&lt;/li&gt;
&lt;li&gt;inject&lt;/li&gt;
&lt;li&gt;release&lt;/li&gt;
&lt;li&gt;watch&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="running-a-task"&gt;Running a Task&lt;/h2&gt;
&lt;p&gt;After the task is opened up, to run a task, right-click on it and select the run option.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/GulpInVisualStudio/TaskRunnerExplorer_TaskRun_RightClickMenu.png" alt="Running a Task"&gt;&lt;/p&gt;
&lt;p&gt;When a task it run, it will open up a tab in the Task Runner explorer and show the results of the gulp task&lt;/p&gt;
&lt;p&gt;For the demo, we have right-clicked on the default task and selected the run option.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/GulpInVisualStudio/TaskRunnerExplorer_TaskRun.png" alt="Results of Task Run"&gt;&lt;/p&gt;
&lt;h2 id="integrating-into-visual-studio-build-process"&gt;Integrating into Visual Studio Build Process&lt;/h2&gt;
&lt;p&gt;Manually running a task is nice but it is even better if you can integrate it into the Visual Studio build process. Luckily the Task Runner Explorer, give you just that option. If you right-click on a task and select Bindings, you can see the options.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/GulpInVisualStudio/TaskRunnerExplorer_BuildBindings.png" alt="Task Build Options"&gt;&lt;/p&gt;
&lt;p&gt;There are 4 bindings options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Before Build - task will run before the Visual Studio build.&lt;/li&gt;
&lt;li&gt;After Build - task will run after the Visual Studio Build.&lt;/li&gt;
&lt;li&gt;Clean - task will run when the project is cleaned.&lt;/li&gt;
&lt;li&gt;Project Open - task will run when the project is opened.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this example, we are going to set the &amp;ldquo;Before Build&amp;rdquo; binding for the default task. Right-click on the default task, select Bindings, and then Before Build.&lt;/p&gt;
&lt;p&gt;When you set the binding, 2 things happened in the Task Runner Explorer:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The default task now shows up in the Bindings tab under the Before Build secton.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you right-click on the default task and select binding there will be a checkmark next to the &amp;ldquo;Before Build&amp;rdquo; to indicate that it is set for this task.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/GulpInVisualStudio/TaskRunnerExplorer_BindingSetForBeforeBuild.png" alt="Before Build Binding"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Also, if you open up the gulpfile.js, you will notice that the first line now has a comment with the bindings in it.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/GulpInVisualStudio/TaskRunnerExplorer_GulpfileWithBeforeBuildSet.png" alt="Gulpfile with BeforeBuild set"&gt;&lt;/p&gt;
&lt;h2 id="wrap-up"&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;Now as a Visual Studio user, you kow how to run your gulp task without leaving Visual Studio and how to integrate it into the Visual Studio build process.&lt;/p&gt;</description></item><item><title>Strongloop - Fixing Security When Extending User Model</title><link>https://digitaldrummerj.me/strongloop-extending-user-model-security/</link><pubDate>Fri, 30 Oct 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/strongloop-extending-user-model-security/</guid><category>ionic</category><category>strongloop</category><description>&lt;p&gt;After following Raymond Camden&amp;rsquo;s &lt;a href="http://www.raymondcamden.com/tags/strongloop/" target="_blank" &gt;Strongloop Introduction&lt;/a&gt;, I was ready to update the &lt;a href="https://github.com/Ionic-AZ/Todo-Lab1-LocalStorage" target="_blank" &gt;todo demo application&lt;/a&gt; that I have been using for the &lt;a href="http://meetup.com/ionic-az" target="_blank" &gt;Ionic Arizona Meetup&lt;/a&gt;. So I created a models for projects and app users. The app users model base class was User. Then within the project model I associated a project to an app users with a belongsTo and in the app users model I associated multiple projects to a single user with a hasMany. At this point, when I examined the API explorer I could see that it can me a rest endpoint to get the projects associated to a user. However, I ran into an issue with getting 401 Unauthorized errors when I tried to query any of the rest endpoints to get the projects associated to the user.&lt;/p&gt;
&lt;p&gt;I could not find anything in the Strongloop docs that told me how to get around the 401 errors or what was causing them except that the ACL security was causing the issue and that I should be able to set the ACL security for appuser. However, after much research, it turns out that the built-in user model has security (ACL) on it to restrict everyone from being able to query the user endpoints that do not have an explictly defined security setup. This is great except for the fact that even if you add additional ACLs in your extended user model the default security has already denied the user before it gets to the security you setup.&lt;/p&gt;
&lt;p&gt;You can view the default security by navigating to your strongloop project directory and looking in the node_modules\loopback\common\models\user.json file. You can see looking at the ACL list that the first item in the list is to DENY $everyone&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$everyone&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;DENY&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The way Strongloop orders the ACLs is that it starts at the bottom of the ACL list and reads it to the top. Since we are extending the user class, the DENY $everyone will come before our defined ACL. Strongloop strongly suggest that you do not modify the built-in user class, so then how do we allow users to query our additional endpoints?&lt;/p&gt;
&lt;p&gt;When you generate a model there is an accompanying file for each model in the common\models directory, that has a .js extension to write custom code to add additional functionality to the model. For example if your model is called appuser, you will see two files: appuser.json and appuser.js.&lt;/p&gt;
&lt;p&gt;Go ahead and open up the .js extension for your model. You will the following code where Appuser is your model&amp;rsquo;s name.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module.exports = function(Appuser) {
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing to do is clear out the existing security for the model&lt;/p&gt;
&lt;p&gt;Appuser.settings.acls.length = 0;&lt;/p&gt;
&lt;p&gt;Next we need to create a file in the models directory to hold the ACL for the model. My file naming convention is to add &amp;ldquo;acl&amp;rdquo; after the model name like so [Your Model]acl.json, ex: appuseracl.json&lt;/p&gt;
&lt;p&gt;To ensure that you do not lose any of the default security, copy the ACLs json from the node_modules\loopback\common\model\user.json file&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;acls&amp;quot;: [
{
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principal&amp;quot;: &amp;quot;$everyone&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;DENY&amp;quot;
},
{
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$everyone&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;ALLOW&amp;quot;,
&amp;quot;property&amp;quot;: &amp;quot;create&amp;quot;
},
{
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$owner&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;ALLOW&amp;quot;,
&amp;quot;property&amp;quot;: &amp;quot;deleteById&amp;quot;
},
{
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$everyone&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;ALLOW&amp;quot;,
&amp;quot;property&amp;quot;: &amp;quot;login&amp;quot;
},
{
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$everyone&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;ALLOW&amp;quot;,
&amp;quot;property&amp;quot;: &amp;quot;logout&amp;quot;
},
{
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$owner&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;ALLOW&amp;quot;,
&amp;quot;property&amp;quot;: &amp;quot;findById&amp;quot;
},
{
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$owner&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;ALLOW&amp;quot;,
&amp;quot;property&amp;quot;: &amp;quot;updateAttributes&amp;quot;
},
{
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$everyone&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;ALLOW&amp;quot;,
&amp;quot;property&amp;quot;: &amp;quot;confirm&amp;quot;
},
{
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$everyone&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;ALLOW&amp;quot;,
&amp;quot;property&amp;quot;: &amp;quot;resetPassword&amp;quot;,
&amp;quot;accessType&amp;quot;: &amp;quot;EXECUTE&amp;quot;
}
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that the ACL configuration is setup, you need to tell Strongloop to load it by add the following line to the [Your Model].js file&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Appuser.settings.acls = require('./appuseracl.json');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you test your rest endpoints, at this point you will have the security as your started with.&lt;/p&gt;
&lt;p&gt;To add the security for your added endpoints, add them before the DENY everyone configuration that is at the top of the ACL list.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$everyone&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;DENY&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example here is the ACL for my app users model to get the list of projects associated to that user.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {
&amp;quot;principalType&amp;quot;: &amp;quot;ROLE&amp;quot;,
&amp;quot;principalId&amp;quot;: &amp;quot;$owner&amp;quot;,
&amp;quot;permission&amp;quot;: &amp;quot;ALLOW&amp;quot;,
&amp;quot;property&amp;quot;: &amp;quot;__get__projects&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you test your rest endpoints again, you will be able to successfully call the endpoints that were added when you associated projects to a user in your user model.&lt;/p&gt;</description></item><item><title>Installing Gulp 4.x</title><link>https://digitaldrummerj.me/installing-gulp-4/</link><pubDate>Thu, 29 Oct 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/installing-gulp-4/</guid><category>gulp</category><description>&lt;p&gt;Gulp 4 is not released yet but if you have a need to install it here is how to do it. I will cover how to globally install it as well as how to update your package.json for your projects.&lt;/p&gt;
&lt;h2 id="globally-installing-gulp"&gt;Globally Installing Gulp&lt;/h2&gt;
&lt;p&gt;The first thing we need to do is install the Gulp command line to be able to run the gulp task.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a Command Prompt (Windows) or Terminal (Mac or Linux)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check if you have Gulp CLI &amp;lt; 1.2.1 installed&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ gulp -v
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If a Gulp version is &amp;lt; 1.2.1, you will need to upgrade by running the following commands&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ npm install -g gulp-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify Gulp CLI 1.2.1 installed correctly&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ gulp -v
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="updating-your-projects-packageson"&gt;Updating Your Projects package.son&lt;/h2&gt;
&lt;p&gt;Now we need to tell our project to use Gulp 4.0 when it runs the gulp task.&lt;/p&gt;
&lt;p&gt;If you using a previous version of Gulp in your package.json file, you can upgrade it if you would like or continue to use Gulp 3.x. I have not had any issue with leaving my local repository at 3.9 while having Gulp 4 installed globally.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a Command Prompt (Windows) or Terminal (Mac or Linux)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the directory that contains your package.json&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Uninstall gulp. &lt;strong&gt;Note:&lt;/strong&gt; If your package.json has gulp listed as a dev dependency use &amp;ndash;save-dev. If gulp is listed as a dependency use &amp;ndash;save.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ npm uninstall gulp --save-dev
$ npm install git+https://git@github.com/gulpjs/gulp.git#4.0 --save-dev
OR
$ npm uninstall gulp --save
$ npm install git+https://git@github.com/gulpjs/gulp.git#4.0 --save
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="wrap-up"&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;You can keep up to date on Gulp 4 at &lt;a href="https://github.com/gulpjs/gulp/tree/4.0" target="_blank" &gt;https://github.com/gulpjs/gulp/tree/4.0&lt;/a&gt; and &lt;a href="https://twitter.com/gulpjs?lang=en" target="_blank" &gt;https://twitter.com/gulpjs?lang=en&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Sync your Git Fork to the Original Repo</title><link>https://digitaldrummerj.me/git-sync-fork-to-master/</link><pubDate>Tue, 27 Oct 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/git-sync-fork-to-master/</guid><category>git</category><category>github</category><description>&lt;p&gt;Syncing your forked repository to the original repository is an important step before submitting any pull request to the original repository for the changes in your forked repository. Even if you are not going to submit a pull request to the original repository, there are times that you want the additional features and/or bug fixes that have been done since you forked the original repository.&lt;/p&gt;
&lt;p&gt;You could do a pull request but this adds an additional commit into your forked repository instead of making your forked repository match the original repository. In order to sync the forked repository without adding any additional commits as part of the process you need to configure the original repository as an upstream remote, merge in the changes from the original repository and then push the merged version back to Github.&lt;/p&gt;
&lt;h2 id="adding-original-repo-as-an-upstream-repo"&gt;Adding Original Repo As an Upstream Repo&lt;/h2&gt;
&lt;p&gt;In order to pull the changes from the original repository into your forked version, you need to add the original git repo as an upstream repository.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a Command Prompt (Windows) or Terminal (Mac or Linux)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the directory that contains your forked repository&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to list the currently configured remote repositories&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git remote -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; origin https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Your UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Your Fork&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; origin https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Your UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Your Fork&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;push&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the original repository as an upstream repository&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git remote add upstream https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Original Owner Username&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Original Repository&lt;span class="o"&gt;]&lt;/span&gt;.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you run the git remote command again, you will now see both origin and upstream are configured&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git remote -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;origin https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Your UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Your Fork&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;origin https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Your UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Your Fork&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;push&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;upstream https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Original Owner UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Original Repository&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;upstream https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Original Owner UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Original Repository&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;push&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are now ready to pull the changes from the original repository to the your forked repository.&lt;/p&gt;
&lt;h2 id="merging-original-repo-into-your-fork"&gt;Merging Original Repo Into Your Fork&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a Command Prompt (Windows) or Terminal (Mac or Linux)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the directory that contains your forked repository that you configured with the upstream repository&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first thing is to fetch all of the changes from the original repository. Note that commits to the original repository will be stored in a local branch called, upstream/master&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ git fetch upstream
remote: Counting objects: 75, done.
remote: Compressing objects: 100% (53/53), done.
remote: Total 62 (delta 27), reused 44 (delta 9)
Unpacking objects: 100% (62/62), done.
From https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY
* [new branch] master -&amp;gt; upstream/master
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure that you are on your fork&amp;rsquo;s master branch&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ git checkout master
Switched to branch 'master'
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Merge the changes from the upstream/master into your local master branch. This will bring your fork&amp;rsquo;s master branch into sync with the upstream repository without losing your local changes. If you have made any changes that create a conflict, you will obviously have to resolve those before you can complete the merge.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ git merge upstream/master
Updating a422352..5fdff0f
Fast-forward
....
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At this point your local branch is synced to the original repositories master branch. If you want to update the Github repository, you need to push your changes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ git push origin master
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="wrap-up"&gt;Wrap-Up&lt;/h2&gt;
&lt;p&gt;To summarize, with the 5 commands below you can sync your forked repository with the original repository and push the changes to your Github repository.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git remote add upstream https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Original Owner Username&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Original Repository&lt;span class="o"&gt;]&lt;/span&gt;.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git fetch upstream
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git checkout master
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git merge upstream/master
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git push
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Do Not Swallow The Exceptions</title><link>https://digitaldrummerj.me/dont-swallow-the-exceptions/</link><pubDate>Mon, 05 Oct 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/dont-swallow-the-exceptions/</guid><category>dotnet</category><description>&lt;p&gt;Throwing away exceptions in your code is just a bad practice and makes it harder to support your application. It may make it easier for you as a developer to get something working but in the long run it cost way more money to do the maintenance and troubleshooting then if you had just put in proper exception handling to start with.&lt;/p&gt;
&lt;p&gt;I have worked on several codebases recently where methods returned false if either a business rule failed or an unexpected exception occurred with no logging of the error anywhere. This made it extremely difficult to figure out what the issue was. In several cases, even when you hooked up a debugger there was no way to get at the exception details since the exception was not passed into the catch block.&lt;/p&gt;
&lt;h2 id="examples-of-issue"&gt;Examples of Issue&lt;/h2&gt;
&lt;p&gt;Here are a couple of examples of what I am talking about when I say swallowing the exception.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example 1: Totally Throwing Away Exception&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this example, the code does nothing with the exception and does not even tell the caller that something failed. I have spent many hours troubleshooting projects with this exact try/catch block and wondering why something that I thought should have worked was not, just to discover that deep down in the call stack it was swallowing the exception. Since the catch block is empty, there is no way to put a breakpoint in the catch block but even if you could, there is no way to get at the exception in the catch block since it was not passed into it. You would need to make a temp code change to add an exception parameter to the catch block and a bogus line in the catch block to be able to get at the exception.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void SomeMethod()
{
try
{
//Some Code That Errors
}
catch
{
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example 2: At Least Tells You Something Went Wrong&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this example, the code at least returns false back from the method so you can assume that something didn&amp;rsquo;t go right but you don&amp;rsquo;t know if it was an exception or if it was business logic that failed. This example is also not logging any information about the exception and there is no way in a debugger to get the exception details. You can at least put a breakpoint in the catch block, but you would need to make a code change to add pass the exception into the catch block.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public bool SomeMethod()
{
try
{
//Some code
if (&amp;quot;Business Logic Failed&amp;quot;)
{
return false;
}
//everything passed
return true;
}
catch
{
return false;
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="fixing-this-issue"&gt;Fixing This Issue&lt;/h2&gt;
&lt;p&gt;Now that you understand the issue, lets examine how to fix it. The fix is really simple. There are two options that I use:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pass the exception variable into the catch block and log the information somewhere.&lt;/li&gt;
&lt;li&gt;Let the exception bubble up the call stack and handle it at a higher level.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Fix Example #1: Logging The Exception&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In this example, the exception along with all of the inner exceptions are logged and the business rules are checked. You can also put a breakpoint inside the catch block to get details on the exception. This code still returns a false though if something went wrong which doesn&amp;rsquo;t make it obvious if it was an exception or business logic that didn&amp;rsquo;t pass without looking at the logs.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public bool SomeMethod()
{
try
{
//Some code
if (&amp;quot;Business Logic Failed&amp;quot;)
{
throw new ApplicationException(&amp;quot;Business Logic Failed...Give details on what failed&amp;quot;);
}
return true;
}
catch (Exception ex)
{
//Logs StackTrace, Message, and all InnerException Message/StackTrace
LogMessage(ex);
return false;
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Fix Example #2: Bubbling Exception Up Call Stack&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This example lets the full exception go up the call stack with a custom exception so that the calling method can figure out how it wants to handle the exception. You should handle this exception at some point. It is bad practice to let it become an unhandled exception and crash your application.&lt;/p&gt;
&lt;p&gt;The reason for using a custom exception is to be able to catch your business logic errors versus an unexpected exception.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void SomeMethod()
{
//Some Code
if (&amp;quot;Business Logic Failed&amp;quot;)
{
//Let Exception Bubble Up the Call Stack with a custom exception
//Can also use ApplicationException but harder to catch specific exceptions without examining the exception message
throw new MyCustomException(&amp;quot;Business Logic Failed...Give details on what failed&amp;quot;);
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Fix Example #3: Ability to Put in a Breakpoint&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes I know I said there is 2 ways to fix it but you can also do it this way. However this is my least favorite way to not swallow exception as you are still technically swallowing the error but you can at least hook up the debugger, put a breakpoint on the return statement, and get at the exception details. This only is of value in your development machine but it is at least better than nothing.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public void SomeMethod()
{
try
{
//Some Code
}
catch (Exception ex)
{
return;
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="wrap-up"&gt;Wrap-Up&lt;/h2&gt;
&lt;p&gt;As you can see it does not take much more work to be able to do something with the exception. It will save you hours of troubleshooting work just by handling the exception and not throwing it away. Don&amp;rsquo;t take the easy way out by swallowing exceptions. Be nice to your fellow developers and don&amp;rsquo;t throw away the exceptions.&lt;/p&gt;</description></item><item><title>Vagrant - Fixing Opentable Basebox looking for Windows Plugin</title><link>https://digitaldrummerj.me/vagrant-fixing-opentable-basebox/</link><pubDate>Sun, 04 Oct 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vagrant-fixing-opentable-basebox/</guid><category>vagrant</category><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;As part of my &lt;a href="https://github.com/digitaldrummerj/VagrantTalk/tree/master/ExampleVagrantFiles/WindowsWithChocolatey" target="_blank" &gt;demo&lt;/a&gt; during my Vagrant talk, I use the &lt;a href="https://atlas.hashicorp.com/opentable/boxes/win-8.1-enterprise-amd64-nocm" target="_blank" &gt;opentable/win-8.1-enterprise-amd64-nocm&lt;/a&gt; vagrant base box with the virtualbox provider. This vagrant base box unfortunately has an issue with the vagrantfile that is included with it looking for the old/unneeded vagrant windows plugin to be installed and trying to port forward the WinRM and RDP ports without detecting if the port is already in use.&lt;/p&gt;
&lt;p&gt;Luckily, it is really easy to fix the included vagrantfile so that you can create vagrant machines but you have to do some prework before running a vagrant up using this base box.&lt;/p&gt;
&lt;h2 id="downloading-the-box"&gt;Downloading the Box&lt;/h2&gt;
&lt;p&gt;Normally with vagrant you do not need to download the vagrant box before running vagrant up but in this case you do need to download the &lt;a href="https://atlas.hashicorp.com/opentable/boxes/win-8.1-enterprise-amd64-nocm" target="_blank" &gt;opentable/win-8.1-enterprise-amd64-nocm&lt;/a&gt; box first. We can download the box by running the following command from the command line.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ vagrant box add opentable/win-8.1-enterprise-amd64-nocm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will take a bit to download the vagrant box as it is several gigs in size.&lt;/p&gt;
&lt;h2 id="fix-for-unneeded-check-for-vagrant-windows-plugin"&gt;Fix for Unneeded Check for Vagrant Windows Plugin&lt;/h2&gt;
&lt;p&gt;Once the box is downloaded, you need to go to the .vagrant.d directory that contains the box you just downloaded. On Windows this directory is located at %userprofile%.vagrant.d\boxes\opentable-VAGRANTSLASH-win-8.1-enterprise-amd64-nocm\1.0.0\virtualbox&lt;/p&gt;
&lt;p&gt;This directory contained the Vagrantfile that we need to update. You can open this file in any text editor.&lt;/p&gt;
&lt;p&gt;When you open up the file you will see this section of code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if !Vagrant.has_plugin?('vagrant-windows')
puts &amp;quot;vagrant-windows missing, please install the vagrant-windows plugin!&amp;quot;
puts &amp;quot;Run this command in your terminal:&amp;quot;
puts &amp;quot;vagrant plugin install vagrant-windows&amp;quot;
exit 1
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This section is no longer need and can be deleted. The vagrant-windows plugins is how Vagrant used to supported the Windows OS before it was supported out of the box.&lt;/p&gt;
&lt;h2 id="fix-for-port-forwarding-auto-correct"&gt;Fix for Port Forwarding Auto Correct&lt;/h2&gt;
&lt;p&gt;We are going to edit the same Vagrantfile as the previous section. Again this file is located at %userprofile%.vagrant.d\boxes\opentable-VAGRANTSLASH-win-8.1-enterprise-amd64-nocm\1.0.0\virtualbox&lt;/p&gt;
&lt;p&gt;Open up the Vagrantfile in any text editor.&lt;/p&gt;
&lt;p&gt;When you open up the file you will see this section of code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config.vm.network :forwarded_port, guest: 3389, host: 3389
config.vm.network :forwarded_port, guest: 5985, host: 5985
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem with this section is that if ports 3389 or 5985 are already in use on your host machine, then the command will fail.&lt;/p&gt;
&lt;p&gt;To correct this we need to add the auto_correct parameter to each of the port forwarding commands so that Vagrant will automatically pick an unused port if it detects either 3389 or 5985 are in use on your host machine.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;config.vm.network :forwarded_port, guest: 3389, host: 3389, auto_correct: true
config.vm.network :forwarded_port, guest: 5985, host: 5985, auto_correct: true
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="wrap-up"&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;Now you can use the &lt;a href="https://atlas.hashicorp.com/opentable/boxes/win-8.1-enterprise-amd64-nocm" target="_blank" &gt;opentable/win-8.1-enterprise-amd64-nocm&lt;/a&gt; vagrant base box with the vagrant up command to create a new virtual machine.&lt;/p&gt;
&lt;p&gt;Not all vagrant base boxes have this issue but if you do run across one you know how to fix it.&lt;/p&gt;</description></item><item><title>Sync your Git Fork to the Original Repo</title><link>https://digitaldrummerj.me/git-syncing-fork-with-original-repo/</link><pubDate>Mon, 28 Sep 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/git-syncing-fork-with-original-repo/</guid><category>git</category><category>github</category><description>&lt;p&gt;Syncing your forked repository to the original repository is an important step before submitting any pull request to the original repository for the changes in your forked repository. Even if you are not going to submit a pull request to the original repository, there are times that you want the additional features and/or bug fixes that have been done since you forked the original repository.&lt;/p&gt;
&lt;p&gt;You could do a pull request but this adds an additional commit into your forked repository instead of making your forked repository match the original repository. In order to sync the forked repository without adding any additional commits as part of the process you need to configure the original repository as an upstream remote, merge in the changes from the original repository and then push the merged version back to Github.&lt;/p&gt;
&lt;h2 id="adding-original-repo-as-an-upstream-repo"&gt;Adding Original Repo As an Upstream Repo&lt;/h2&gt;
&lt;p&gt;In order to pull the changes from the original repository into your forked version, you need to add the original git repo as an upstream repository.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a Command Prompt (Windows) or Terminal (Mac or Linux)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the directory that contains your forked repository&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to list the currently configured remote repositories&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git remote -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; origin https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Your UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Your Fork&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; origin https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Your UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Your Fork&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;push&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the original repository as an upstream repository&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git remote add upstream https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Original Owner Username&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Original Repository&lt;span class="o"&gt;]&lt;/span&gt;.git
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you run the git remote command again, you will now see both origin and upstream are configured&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git remote -v
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;origin https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Your UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Your Fork&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;origin https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Your UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Your Fork&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;push&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;upstream https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Original Owner UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Original Repository&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;fetch&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;upstream https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Original Owner UserName&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Original Repository&lt;span class="o"&gt;]&lt;/span&gt;.git &lt;span class="o"&gt;(&lt;/span&gt;push&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are now ready to pull the changes from the original repository to the your forked repository.&lt;/p&gt;
&lt;h2 id="merging-original-repo-into-your-fork"&gt;Merging Original Repo Into Your Fork&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a Command Prompt (Windows) or Terminal (Mac or Linux)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the directory that contains your forked repository that you configured with the upstream repository&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first thing is to fetch all of the changes from the original repository. Note that commits to the original repository will be stored in a local branch called, upstream/master&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ git fetch upstream
remote: Counting objects: 75, done.
remote: Compressing objects: 100% (53/53), done.
remote: Total 62 (delta 27), reused 44 (delta 9)
Unpacking objects: 100% (62/62), done.
From https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY
* [new branch] master -&amp;gt; upstream/master
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure that you are on your fork&amp;rsquo;s master branch&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ git checkout master
Switched to branch 'master'
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Merge the changes from the upstream/master into your local master branch. This will bring your fork&amp;rsquo;s master branch into sync with the upstream repository without losing your local changes. If you have made any changes that create a conflict, you will obviously have to resolve those before you can complete the merge.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ git merge upstream/master
Updating a422352..5fdff0f
Fast-forward
....
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At this point your local branch is synced to the original repositories master branch. If you want to update the Github repository, you need to push your changes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ git push origin master
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="wrap-up"&gt;Wrap-Up&lt;/h2&gt;
&lt;p&gt;To summarize, with the 5 commands below you can sync your forked repository with the original repository and push the changes to your Github repository.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git remote add upstream https://github.com/&lt;span class="o"&gt;[&lt;/span&gt;Original Owner Username&lt;span class="o"&gt;]&lt;/span&gt;/&lt;span class="o"&gt;[&lt;/span&gt;Original Repository&lt;span class="o"&gt;]&lt;/span&gt;.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git fetch upstream
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git checkout master
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git merge upstream/master
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git push
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Getting Visual Studio Cordova Tooling Working with the Ionic Framework</title><link>https://digitaldrummerj.me/visual-studio-2015-cordova-tools-and-ionic-framework/</link><pubDate>Thu, 17 Sep 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/visual-studio-2015-cordova-tools-and-ionic-framework/</guid><category>ionic</category><category>visual studio</category><description>&lt;p&gt;I am doing an &lt;a href="http://www.ionicframework.com" target="_blank" &gt;Ionic Framework&lt;/a&gt; presentation and I wanted to use the Visual Studio 2015 Cordova Tooling.&lt;/p&gt;
&lt;p&gt;I have done this presentation twice in the past couple of months using my Intel Nuc machine with the Visual Studio 2015 RTM Cordova tooling but it is kind of pain to do this since the Nuc is a desktop machine and doesn&amp;rsquo;t have a monitor so I have to look at the projector screen or being a laptop to use as well. Setting up 2 machines in 15 minutes as well is a pain and hoping that the remoting from the laptop to the Nuc is stable just asking for trouble even with a travel router that I have.&lt;/p&gt;
&lt;p&gt;So I decided I would just get my laptop working with my demos since I already had Visual Studio 2015 installed and had everything I needed for Ionic already working.&lt;/p&gt;
&lt;p&gt;I thought this shouldn&amp;rsquo;t take that long but unfortunately I ran into a bunch of issues. Luckily I have managed to fix all of the issue and can move on with the prep work for my presentation.&lt;/p&gt;
&lt;h3 id="software-installed-on-laptop"&gt;Software Installed on Laptop&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Windows 8.1 Enterprise&lt;/li&gt;
&lt;li&gt;Visual Studio 2015 with Cordova Tooling Update 2&lt;/li&gt;
&lt;li&gt;Node 4.1.0 tried both 32bit and 64bit versions. Note that the Intel NUC machine had 0.12.4 when I last presented.&lt;/li&gt;
&lt;li&gt;Npm 2.14.3 (version that comes with Node 4.1.0)&lt;/li&gt;
&lt;li&gt;Npm Global Modules: gulp, bower, Ionic, Cordova, and pjup&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="steps-to-create-visual-studio-ionic-project-that-caused-issue"&gt;Steps to Create Visual Studio Ionic Project that caused issue&lt;/h3&gt;
&lt;p&gt;To create the project I first created the Ionic project using the Ionic CLI and then created a Cordova project in Visual Studio based on the Ionic CLI project that was created.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Command Line&lt;/li&gt;
&lt;li&gt;Navigate to c:\projects&lt;/li&gt;
&lt;li&gt;Run ionic start Demo blank&lt;/li&gt;
&lt;li&gt;cd into c:\projects\Demo&lt;/li&gt;
&lt;li&gt;open up the package.json file and removed the gulp-sass line. This version doesn&amp;rsquo;t work with Node 4.1.0.&lt;/li&gt;
&lt;li&gt;Run npm install gulp-sass &amp;ndash;save&lt;/li&gt;
&lt;li&gt;Run npm install&lt;/li&gt;
&lt;li&gt;Open Visual Studio&lt;/li&gt;
&lt;li&gt;Go File -&amp;gt; New -&amp;gt; Project From Existing Code&lt;/li&gt;
&lt;li&gt;Select Cordova as the project type and click Next&lt;/li&gt;
&lt;li&gt;Click the browse button, navigate to the c:\projects\Demo folder, and click the Select Folder button&lt;/li&gt;
&lt;li&gt;Name the project Demo&lt;/li&gt;
&lt;li&gt;Click the Finish button&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now the fun begins. Below I have documented the different issues that I ran into.&lt;/p&gt;
&lt;h3 id="issue-1-opening-cordova-project-took-100-cpu"&gt;Issue 1: Opening Cordova project took 100% CPU&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Error&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When I would open up a Cordova project it would try to parse the Npm global packages and it would fail everytime on the cordova dependency, graceful-fs. When I opened up Task Manager, I would see Visual Studio and Node taking all of the CPU. I would also see anywhere between 2-15 node processes that Visual Studio had started.&lt;/p&gt;
&lt;p&gt;I wondered what node processes were doing so I used Process Explorer and I found that they were all running npm config ls -g.&lt;/p&gt;
&lt;p&gt;My only guess is that Visual Studio didn&amp;rsquo;t like something about my npm global packages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since Visual Studio and Node were taking all of the CPU, I had to open up the Task Manager and kill all of the node processes in order to get Visual Studio to close so that the CPU went back to a normal level.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Visual Studio without a project open&lt;/li&gt;
&lt;li&gt;Under the Tools Menu -&amp;gt; Options -&amp;gt; Projects and Solutions -&amp;gt; External Web Tools, I reset it to the defaults which has the Visual Studio path higher than the System Path.&lt;/li&gt;
&lt;li&gt;Open the Demo project from c:\projects\Demo&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The CPU this time should stay at a normal level&lt;/p&gt;
&lt;h3 id="issue-2-node-sass-would-fail-due-to-not-having-the-32-bit-version-installed"&gt;Issue 2: node-sass would fail due to not having the 32 bit version installed&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Error&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This was caused by running npm install from the command line outside of Visual Studio and also not having a version of gulp-sass in the package.json that worked with Node 4.1.0.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2 steps to get around this issue:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;From the command line in c:\projects\Demo, run the pjup command to update the npm package vesion in package.json to the latest versions.
&lt;ul&gt;
&lt;li&gt;Warning: Make sure that you have the loglevel for npm set to the default of warn. You can reset your custom loglevel with npm config delete loglevel.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Delete the node_modules directory from your project directory and do the package restore from within Visual Studio. You may need to do a rm -rf node_modules from the command line.&lt;/li&gt;
&lt;li&gt;Open the Demo project from c:\projects\Demo in Visual Studio&lt;/li&gt;
&lt;li&gt;In the Solution Explorer, right-click on the Dependencies folder and select Restore Packages&lt;/li&gt;
&lt;li&gt;After a few minutes the package restore should be completed.
&lt;ul&gt;
&lt;li&gt;Now the Task Runner Explorer should also be working&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="issue-3-typescript-error-on-visual-studio-build"&gt;Issue 3: Typescript error on Visual Studio build&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Error&lt;/strong&gt;
This issue actually had nothing to do with Visual Studio itself but with the angular bower packages.&lt;/p&gt;
&lt;p&gt;It turns out that the angular-ui-router bower package that is installed as part of the ionic install when you do a bower install, has the typescript api file included in the package but the other angular packages do not have their typescript files included, so typescript doesn&amp;rsquo;t know what some of the types are that are referenced in the angular-ui-router typescript api file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I just deleted the angular-ui-router\api folder.&lt;/p&gt;
&lt;h3 id="issue-4-cordova-error-on-visual-studio-build"&gt;Issue 4: Cordova error on Visual Studio build&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Error&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Cordova CLI is set to 5.1.1 when you create the project.&lt;/p&gt;
&lt;p&gt;When you build the project in Visual Studio, it generates the following error due to being set to Cordova 5.1.1. I had also seen this issue on my Intel NUC machine and had to drop the npm global Cordova version back to 5.0.0 but they have since fixed this issue with Cordova.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; 1&amp;gt; ------ Adding platform: android
1&amp;gt; No version supplied. Retrieving version from config.xml...
1&amp;gt; _http_client.js:51
1&amp;gt; throw new TypeError('Request path contains unescaped characters.');
1&amp;gt; ^
1&amp;gt;
1&amp;gt; TypeError: Request path contains unescaped characters.
1&amp;gt; at new ClientRequest (_http_client.js:51:11)
1&amp;gt; at TunnelingAgent.exports.request (http.js:31:10)
1&amp;gt; at TunnelingAgent.createSocket (C:\Users\jpjames\AppData\Roaming\npm\node_modules\vs-tac\node_modules\cordova\5.1.1\node_modules\cordova\node_modules\cordova-lib\node_modules\npm\node_modules\request\node_modules\tunnel-agent\index.js:117:25)
1&amp;gt; at TunnelingAgent.createSecureSocket [as createSocket] (C:\Users\jpjames\AppData\Roaming\npm\node_modules\vs-tac\node_modules\cordova\5.1.1\node_modules\cordova\node_modules\cordova-lib\node_modules\npm\node_modules\request\node_modules\tunnel-agent\index.js:184:41)
1&amp;gt; at TunnelingAgent.addRequest (C:\Users\jpjames\AppData\Roaming\npm\node_modules\vs-tac\node_modules\cordova\5.1.1\node_modules\cordova\node_modules\cordova-lib\node_modules\npm\node_modules\request\node_modules\tunnel-agent\index.js:80:8)
1&amp;gt; at new ClientRequest (_http_client.js:133:16)
1&amp;gt; at Object.exports.request (http.js:31:10)
1&amp;gt; at Object.exports.request (https.js:163:15)
1&amp;gt; at Request.start (C:\Users\[UserName]\AppData\Roaming\npm\node_modules\vs-tac\node_modules\cordova\5.1.1\node_modules\cordova\node_modules\cordova-lib\node_modules\npm\node_modules\request\index.js:594:30)
1&amp;gt;MSBUILD : cordova-build error BLD104: Error : BLD00104 : There was an error installing a component from NPM, most likely because this device is behind a proxy or firewall. Please see the following link for possible solutions: http://go.microsoft.com/fwlink/?LinkID=623434
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I believe that this error has nothing to do with the firewall since I can restore npm and bower packages without an issue and I have the http_proxy and https_proxy set as system environment variables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the taco.json file&lt;/li&gt;
&lt;li&gt;Change the Cordova version to 5.3.1&lt;/li&gt;
&lt;li&gt;Rebuild the solution and the build should be complete successfully&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="issue-5-visual-studio-seems-to-randomly-decide-to-use-my-node-410-install"&gt;Issue 5: Visual Studio seems to randomly decide to use my node 4.1.0 install&lt;/h3&gt;
&lt;p&gt;It appear that Visual Studio sometimes wants to use its version of node and other times uses my 4.1.0 install.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Visual Studio Version&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;npm package restore
&lt;ul&gt;
&lt;li&gt;I see lines like this in the output window: npm WARN engine &lt;a href="mailto:get-stdin@5.0.0" &gt;get-stdin@5.0.0&lt;/a&gt;: wanted: {&amp;ldquo;node&amp;rdquo;:&amp;quot;&amp;gt;=0.12.0&amp;quot;} (current: {&amp;ldquo;node&amp;rdquo;:&amp;ldquo;v0.10.31&amp;rdquo;,&amp;ldquo;npm&amp;rdquo;:&amp;ldquo;1.4.9&amp;rdquo;})&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;task runner explorer
&lt;ul&gt;
&lt;li&gt;I assume on this one since it complained about the gulp-sass package in Visual Studio but worked fine from the command line with node 4.1.0.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4.1.0 Install&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cordova build
&lt;ul&gt;
&lt;li&gt;I see that at the start of the output windows for the build: Your environment has been set up for using Node.js 4.1.0 (ia32) and npm.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="issue-6-visual-studio-seem-to-have-a-bunch-of-updates-that-were-needed-as-well"&gt;Issue 6: Visual Studio seem to have a bunch of updates that were needed as well&lt;/h3&gt;
&lt;p&gt;Here is a list of different installed/re-installs/uninstalls that I did:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cordova tooling from RTM to update 1 (update 2 wasn&amp;rsquo;t out yet)&lt;/li&gt;
&lt;li&gt;Due to the CPU issues with #1, I uninstall Visual Studio Cordova tooling and re-installed it, selecting update 1&lt;/li&gt;
&lt;li&gt;Updated Cordova tooling from update 1 to update 2. It came out during all of this troubleshooting&lt;/li&gt;
&lt;li&gt;Tried out npm 3.x. Didn&amp;rsquo;t see any changes.&lt;/li&gt;
&lt;li&gt;Tried install the npm taco package to see if it would help and it didn&amp;rsquo;t. Uninstall it from the node 4.1.0 npm global.&lt;/li&gt;
&lt;li&gt;At once point I even managed to break the Cordova tools and I uninstalled and re-installed them with update 2.&lt;/li&gt;
&lt;li&gt;Uninstalled Node 4.1.0 64 bit and installed the 32 bit version&lt;/li&gt;
&lt;li&gt;Tried out the NodeJSTools for Visual Studio latest RC release. Didn&amp;rsquo;t see any changes so uninstalled it&lt;/li&gt;
&lt;li&gt;Uninstall all of the NodeJsTools for Visual Studio&lt;/li&gt;
&lt;li&gt;Upgraded TypeScript for Visual Studio since the Visual Studio installer said there was a new version out&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="working-steps"&gt;Working Steps&lt;/h3&gt;
&lt;p&gt;To create the project I first created the Ionic project using the Ionic CLI and then created a Cordova project in Visual Studio based on the Ionic CLI project that was created.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Command Line&lt;/li&gt;
&lt;li&gt;Navigate to c:\projects&lt;/li&gt;
&lt;li&gt;Run ionic start Demo blank&lt;/li&gt;
&lt;li&gt;cd into c:\projects\Demo&lt;/li&gt;
&lt;li&gt;run npm config delete loglevel&lt;/li&gt;
&lt;li&gt;run pjup to update all of the npm package. Say Y to everything except the run npm install&lt;/li&gt;
&lt;li&gt;run ionic setup sass&lt;/li&gt;
&lt;li&gt;run rm -rf node_modules&lt;/li&gt;
&lt;li&gt;Open Visual Studio&lt;/li&gt;
&lt;li&gt;Go File -&amp;gt; New -&amp;gt; Project From Existing Code&lt;/li&gt;
&lt;li&gt;Select Cordova as the project type and click Next&lt;/li&gt;
&lt;li&gt;Click the browse button, navigate to the c:\projects\Demo folder, and click the Select Folder button&lt;/li&gt;
&lt;li&gt;Name the project Demo&lt;/li&gt;
&lt;li&gt;Click the Finish button&lt;/li&gt;
&lt;li&gt;Let Visual Studio do an npm package restore&lt;/li&gt;
&lt;li&gt;Ignore the gulp-sass error popup as this appears to be task runner explorer related and will go away after the npm package restore&lt;/li&gt;
&lt;li&gt;Run the gulp default task to generate the sass file using the Task Runner Explorer&lt;/li&gt;
&lt;li&gt;Open the taco.json file and update the version to 5.3.1&lt;/li&gt;
&lt;li&gt;Delete the www\lib\angular-ui-router\api directory&lt;/li&gt;
&lt;li&gt;Build the Visual Studio project&lt;/li&gt;
&lt;li&gt;Deploy it to a Device/Emulator&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;After all of this Visual Studio is now performing like I expected it to and like it does on my Intel NUC machine. The CPU is operating at normal levels. I am able to build everything in Visual Studio and deploy to either the Visual Studio Android Emulator or an actual Android device.&lt;/p&gt;
&lt;p&gt;With all of the fixes in place, I have been able to run through creating my Ionic project, then creating a Visual Studio project from it, and finally deploying it to the Emulator/Device.&lt;/p&gt;
&lt;p&gt;I am very happy I got this all working since I am a huge fan of Visual Studio and wanted to show people all of the goodness in Visual Studio like the package restore, task runner explorer, and debugging.&lt;/p&gt;</description></item><item><title>Jekyll Part 13: Creating an Article Series</title><link>https://digitaldrummerj.me/blogging-on-github-part-13-creating-an-article-series/</link><pubDate>Tue, 15 Sep 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-13-creating-an-article-series/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we are going to go through how to create the series listing like you see for this blog series.&lt;/p&gt;
&lt;h3 id="overview"&gt;Overview&lt;/h3&gt;
&lt;p&gt;Sometimes a blog post gets too big and you need to split it into multiple articles or you want to do a series like this one that builds upon each other but you don&amp;rsquo;t want to have to manage a listing of all of the post in the series. Instead you can easily create a liquid template that does all of the work for you.&lt;/p&gt;
&lt;h3 id="section-1-creating-the-template"&gt;Section 1: Creating the Template&lt;/h3&gt;
&lt;p&gt;In this section we are going to create the html template that will get the series listings for you.&lt;/p&gt;
&lt;p&gt;Note: I assume that you have alrady cloned your jekyll repo to your machine. This tutorial is based off the jekyll repo at &lt;a href="https://github.com/digitaldrummerj/jekyllforblogseries" target="_blank" &gt;https://github.com/digitaldrummerj/jekyllforblogseries&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the _includes directory create a new file called series.html&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first thing we are going to do is add an if statement to make sure that the it is a series before trying to output the listing. Without this, it would output every page if the post didnt have a series and you included the series.html&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {% if page.series %}
{% endif %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All of the code for the rest of this section will go in between the if and endif statements&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next we are going to figure out how many post are part of the series and which article number in the series this post is.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Total count will store in the count variable&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The article number for this post in the idx variable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We will loop through the post and increment the count if the Front Matter series tag match the current page&amp;rsquo;s series tag.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {% assign count = '0' %}
{% assign idx = '0' %}
{% for post in site.posts reversed %}
{% if post.series == page.series %}
{% capture count %}{{ count | plus: '1' }}{% endcapture %}
{% if post.url == page.url %}
{% capture idx %}{{count}}{% endcapture %}
{% endif %}
{% endif %}
{% endfor %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we need to output the actual html code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Note: We are using the panel css from bootstrap. If you have bootstrap you are good, if not we will add just the panel css in the next section.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;First, we output a header for the series that says which part # this post is and how many total parts there are for the series.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Second, we loop through the post and increment the count if the Front Matter series tag match the current page&amp;rsquo;s series tag so that we can append Part # onto each post title. If the url for the post in the series matches the current page&amp;rsquo;s url, then it outputs &amp;ldquo;This Article&amp;rdquo; instead of the actual title.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;div class=&amp;quot;panel seriesNote&amp;quot;&amp;gt;
&amp;lt;p&amp;gt;
This article is &amp;lt;strong&amp;gt;Part {{ idx }}&amp;lt;/strong&amp;gt; in a &amp;lt;strong&amp;gt;{{ count }}-Part&amp;lt;/strong&amp;gt; Series.
&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
{% assign count = '0' %}
{% for post in site.posts reversed %}
{% if post.series == page.series %}
{% capture count %}{{ count | plus: '1' }}{% endcapture %}
&amp;lt;li&amp;gt;Part {{ count }} -
{% if page.url == post.url %}
This Article
{% else %}
&amp;lt;a href=&amp;quot;{{post.url}}&amp;quot;&amp;gt;{{post.title}}&amp;lt;/a&amp;gt;
{% endif %}
&amp;lt;/li&amp;gt;
{% endif %}
{% endfor %}
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the next section, we will add the series listing onto a couple of post so you can see them in action&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="section-2-add-series-tag-to-post"&gt;Section 2: Add Series Tag to Post&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The first thing we need to do is create 2 new blog post article so that we can add the series to them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In both articles, add a front matter tag called series and make the value &amp;ldquo;Intro to Series&amp;rdquo;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ---
layout: post
title: You're up and running!
series: &amp;quot;Intro to Series&amp;quot;
---
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In both article, include the series.html at the point in the html that you want the series listing to show.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {% include series.html %}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now run jekyll serve and view one of the new blog post.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you do not have the bootstrap css include in your blog, then you will notice that the series listing does not stand out at all.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/ScreenshotOfSeriesWithNoCss.png" alt="Series Listing with No Css"&gt;&lt;/p&gt;
&lt;p&gt;In the next section we will add the missing panel css to make it look more like this.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/ScreenshotOfSeriesWithCss.png" alt="Series Listing with Css"&gt;&lt;/p&gt;
&lt;h3 id="section-3-adding-css"&gt;Section 3: Adding CSS&lt;/h3&gt;
&lt;p&gt;In this section we will add in the css to make the series listing stand out and look like it is a section that goes together instead of just some text on the page.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the css\style.scss file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the bottom of the file paste in the following CSS. This css is directly from the bootstrap css. I didn&amp;rsquo;t want to include all of the bootstrap framework just for the panel css.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; .panel {
padding: 15px;
margin-bottom: 20px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 4px;
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.05);
box-shadow: 0 1px 1px rgba(0,0,0,0.05)
}
.panel-heading {
padding: 10px 15px;
margin: -15px -15px 15px;
background-color: #f5f5f5;
border-bottom: 1px solid #ddd;
border-top-right-radius: 3px;
border-top-left-radius: 3px
}
.panel-title {
margin-top: 0;
margin-bottom: 0;
font-size: 17.5px;
font-weight: 500
}
.panel-footer {
padding: 10px 15px;
margin: 15px -15px -15px;
background-color: #f5f5f5;
border-top: 1px solid #ddd;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 3px
}
.panel-primary {
border-color: #428bca
}
.panel-primary .panel-heading {
color: #fff;
background-color: #428bca;
border-color: #428bca
}
.panel-success {
border-color: #d6e9c6
}
.panel-success .panel-heading {
color: #468847;
background-color: #dff0d8;
border-color: #d6e9c6
}
.panel-warning {
border-color: #fbeed5
}
.panel-warning .panel-heading {
color: #c09853;
background-color: #fcf8e3;
border-color: #fbeed5
}
.panel-danger {
border-color: #eed3d7
}
.panel-danger .panel-heading {
color: #b94a48;
background-color: #f2dede;
border-color: #eed3d7
}
.panel-info {
border-color: #bce8f1
}
.panel-info .panel-heading {
color: #3a87ad;
background-color: #d9edf7;
border-color: #bce8f1
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Save the file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run jekyll serve and view one of the new blog post. If should look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/ScreenshotOfSeriesWithCss.png" alt="Series Listing with Css"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="conclusion"&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Now if you do an article series, you can easily have a professional looking series listing and it requires very little to get it working.&lt;/p&gt;
&lt;p&gt;In the next lesson, I will show you how to get the code highlighting working when your code includes liquid syntax such as we did in the code examples in this article.&lt;/p&gt;</description></item><item><title>Jekyll Part 12: Editing Locally</title><link>https://digitaldrummerj.me/blogging-on-github-part-12-editing-locally/</link><pubDate>Fri, 11 Sep 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-12-editing-locally/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we are going to set it up so that we can do draft posts that we can check into our repo but not have them show up on the production site.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;In the last couple of articles, we installed jekyll locally but we didn&amp;rsquo;t talk about editing existing blog post or adding in new ones. There will be times where you will start a blog post but not have time to finish it all in one sitting. You could just create all of the files in the post directory and set the publish flag to false but over time it will become harder and harder to tell which articles have actually been published.&lt;/p&gt;
&lt;p&gt;Thankfully, jekyll supports having draft posts that by default don&amp;rsquo;t show even if the publish flag is set to true unless you tell jekyll to run with drafts. On Github, jekyll runs without the drafts flag so you don&amp;rsquo;t have to worry about drafts accidentally showing up.&lt;/p&gt;
&lt;h2 id="section-1-creating-a-draft"&gt;Section 1: Creating a draft&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In your blog repo, create a folder called _drafts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new blog post in there called DraftsTest.md&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following to the DraftsTest.md file&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ---
layout: post
title: 'Drafts Test'
categories: ['How-To']
published: 2015-09-11 06:00
---
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you run the jekyll serve command, you will not see this post showing up.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; jekyll serve --config _config.yml,_configdev.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-2-running-with-drafts"&gt;Section 2: Running with Drafts&lt;/h2&gt;
&lt;p&gt;To run jekyll with drafts, you just need to pass in the &amp;ndash;drafts argument to the serve command&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jekyll serve --config _config.yml,_configdev.yml --drafts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now if you view your site in your web browser, you will see your draft post.&lt;/p&gt;
&lt;p&gt;With the drafts argument, it does respect the publish front matter, so if you don&amp;rsquo;t want a draft to show up even with the &amp;ndash;drafts argument, just set the published to false for that article.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you can do all of your editing locally and check the drafts into your git repo without having to fear they will accidentally get published before they are ready.&lt;/p&gt;
&lt;p&gt;In our next lesson, I will show you how to create the series listing like you see below and how to be able to easily create a blog article series&lt;/p&gt;</description></item><item><title>Jekyll Part 11: Installing Jekyll On OSx</title><link>https://digitaldrummerj.me/blogging-on-github-part-11-installing-jekyll-osx/</link><pubDate>Wed, 09 Sep 2015 03:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-11-installing-jekyll-osx/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we are going to setup your MAC (OSx) computer to be able to edit your blog on your computer.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Up to this point we have been using the Github web site to edit all of our files but the downside to this is that any chances you want to make show up live in your blog before you have had a chance to test them.&lt;/p&gt;
&lt;p&gt;Instead, it is better if you can test out all of your changes and review your blog post before letting the world see them. It will also let you have draft post where you can see them locally but on github they will not be visible.&lt;/p&gt;
&lt;h2 id="section-1-installing-software"&gt;Section 1: Installing Software&lt;/h2&gt;
&lt;p&gt;We need to install XCode command line tools, nodejs and python pip.&lt;/p&gt;
&lt;h3 id="section-11-installing-nodejs"&gt;Section 1.1: Installing NodeJs&lt;/h3&gt;
&lt;p&gt;Head over to &lt;a href="https://nodejs.org" target="_blank" &gt;nodejs.org&lt;/a&gt; and download the NodeJs Installer and run it. Take all of the defaults.&lt;/p&gt;
&lt;h3 id="section-12-installing-xcode-command-line-tools"&gt;Section 1.2: Installing XCode Command Line Tools&lt;/h3&gt;
&lt;p&gt;Unfortunately to get the XCode command line tools, you first need to install XCode.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Go the App Store&lt;/li&gt;
&lt;li&gt;Search for XCode&lt;/li&gt;
&lt;li&gt;Hit the Install Button&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once XCode is installed, open up a terminal windows (Application -&amp;gt; Other -&amp;gt; Terminal) and run the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;xcode-select --install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will bring up a windows asking you to install the command line tools package that we need and just click the Install button.&lt;/p&gt;
&lt;h3 id="section-13-installing-pygments-code-highlighter"&gt;Section 1.3: Installing Pygments Code Highlighter&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pip install Pygments
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="section-14-install-ruby-gems"&gt;Section 1.4: Install Ruby Gems&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo gem update --system
sudo gem install bundler
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="section-2-getting-your-blog-onto-your-computer"&gt;Section 2: Getting your Blog onto your computer&lt;/h2&gt;
&lt;p&gt;In this section, you will clone the blog repo from github and install jekyll.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the directory ~/projects&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; mkdir ~/projects
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into c:\projects&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; cd ~/projects
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clone your github blog repo to your local machine with the &amp;ldquo;git clone [Repo Name]&amp;rdquo; command. Below is the example if you were to clone the jekyll repo for this blog series.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; git clone https://github.com/digitaldrummerj/jekyllforblogseries.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into the repo that you just cloned&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; cd jekyllforblogseries
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure that you have a GemFile with no file extension in the root of your repo with the following contents. Warning that Github Pages supports very few jekyll plugins. The jekyll-redirect-from is one of them.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; source 'https://rubygems.org'
gem 'github-pages'
gem 'jekyll-redirect-from'
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From the jekyllforblogseries directory or your blogs directory, run the following command to install gems listed in the Gemfile.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we have jekyll installed. Time to test it out&lt;/p&gt;
&lt;h2 id="section-3-testing-your-blog-works-on-your-computer"&gt;Section 3: Testing Your Blog Works on Your Computer&lt;/h2&gt;
&lt;p&gt;Now that we have everything installed for jekyll it is time to test it out.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;From a command prompt in your blog repo directory run the following command to tell jekyll to build and run the web site locally&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; jekyll serve
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If it build successfully you will see something like this&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/jekyllserve.png" alt="Jekyll Serve Success"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now if you open up your browser and navigate to http://localhost:4000 you will see you blog.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, by default your _config.yml file will be set for production which will cause any place that you have referenced the site.url to not working on your local machine. You don&amp;rsquo;t want to change your _config.yml file though for development since you will accidentally check it in at some point and break your blog. Instead we can tell jekyll to use multiple configuration files. When you load multiple files it will load them in order and then override any settings from a previously loaded config.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new file in the root of your repo called _configdev.yml&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the _configdev.yml add the following lines to set the url, turn off disqus/google analytics and google search.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; # then add this to the url as well &amp;quot;/repository-name&amp;quot;
url: http://localhost:4000
disqus:
disquscommentcount:
google_analytics:
google_search:
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If your jekyll serve is still running do a ctrl+c to stop it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now run the following command to tell jekyll the config yml files to load&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; jekyll serve --config _config.yml,_configdev.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If it build successfully you will see something like this&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/jekyllserve_multipleconfigs.png" alt="Jekyll Serve Success"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now if you open up your browser and navigate to http://localhost:4000 you will see you blog and any place that reference site.url will be working.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you are ready to do all of your editing locally and test it out before the world gets to see it.&lt;/p&gt;
&lt;p&gt;In our next lesson, I will show you how to create draft blog post that will only show on your local machine so that you don&amp;rsquo;t have to either clutter up your post directory with drafts or worry about accidentally publishing an unfinished article.&lt;/p&gt;</description></item><item><title>Jekyll Part 10: Installing Jekyll On Linux</title><link>https://digitaldrummerj.me/blogging-on-github-part-10-installing-jekyll-on-linux/</link><pubDate>Wed, 09 Sep 2015 02:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-10-installing-jekyll-on-linux/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we are going to setup your Ubuntu Linux computer to be able to edit your blog on your computer.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Up to this point we have been using the Github web site to edit all of our files but the downside to this is that any chances you want to make show up live in your blog before you have had a chance to test them.&lt;/p&gt;
&lt;p&gt;Instead, it is better if you can test out all of your changes and review your blog post before letting the world see them. It will also let you have draft post where you can see them locally but on github they will not be visible.&lt;/p&gt;
&lt;h2 id="section-1-installing-software"&gt;Section 1: installing Software&lt;/h2&gt;
&lt;p&gt;We need to install nodejs, ruby 2.x, python pip, and git.&lt;/p&gt;
&lt;h3 id="section-11-installing-nodejs"&gt;Section 1.1: Installing NodeJs&lt;/h3&gt;
&lt;p&gt;First we are going to install NodeJS using the command below&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Note the new setup script name for Node.js v0.12
curl -sL https://deb.nodesource.com/setup_0.12 | sudo bash -
# Then install with:
sudo apt-get install -y nodejs
node -v
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="section-12-installing-ruby"&gt;Section 1.2: Installing Ruby&lt;/h3&gt;
&lt;p&gt;Ubuntu Trusty 14.04 unfortunately comes with Ruby 1.9.x and we need 2.x. There is also a bug in the ubuntu packages where the Ruby 2.0 install is actually the 1.9.3 branch.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;We are going to use &lt;a href="https://github.com/postmodern/ruby-install" target="_blank" &gt;ruby-install&lt;/a&gt; to get the latest version of Ruby installed&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; wget -O ruby-install-0.5.0.tar.gz https://github.com/postmodern/ruby-install/archive/v0.5.0.tar.gz
tar -xzvf ruby-install-0.5.0.tar.gz
cd ruby-install-0.5.0/
sudo make install
sudo ruby-install --system ruby
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We need to install chruby to change the ruby version to the one that we just installed&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; wget -O chruby-0.3.9.tar.gz https://github.com/postmodern/chruby/archive/v0.3.9.tar.gz
tar -xzvf chruby-0.3.9.tar.gz
cd chruby-0.3.9/
sudo make install
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To make chruby auto run we need to add the following lines to our ~/.bashrc or ~/.bash_profile script&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; source /usr/local/share/chruby/chruby.sh
source /usr/local/share/chruby/auto.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In order to it to take effect run replace .bashrc with .bash_profile if you are using that instead&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify your ruby version changed with&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ruby -v
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure that the Ruby Gems are updated and then install the bundler GEM&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; sudo gem update --system
sudo gem install bundler
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="section-13-python"&gt;Section 1.3: Python&lt;/h3&gt;
&lt;p&gt;In order to use the Pygments code syntax highlighter, we need to install python pip&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install python-pip -y
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="section-14-installing-git"&gt;Section 1.4: Installing Git&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install git -y
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="section-2-getting-your-blog-onto-your-computer"&gt;Section 2: Getting your Blog onto your computer&lt;/h2&gt;
&lt;p&gt;In this section, you will clone the blog repo from github and install jekyll.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the directory ~/projects&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; mkdir ~/projects
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into c:\projects&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; cd ~/projects
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clone your github blog repo to your local machine with the &amp;ldquo;git clone [Repo Name]&amp;rdquo; command. Below is the example if you were to clone the jekyll repo for this blog series.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; git clone https://github.com/digitaldrummerj/jekyllforblogseries.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into the repo that you just cloned&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; cd jekyllforblogseries
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure that you have a GemFile with no file extension in the root of your repo with the following contents&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; source 'https://rubygems.org'
gem 'github-pages'
gem 'jekyll-redirect-from'
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the command to install the github-pages gem which has all of the required modules to make jekyll work and the jekyll redirect from plugin. It is one of the few plugins that Github pages supports and allows you to move pages around and have them automatically redirect so that people with bookmarks can still find a moved page.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we have jekyll installed. Time to test it out&lt;/p&gt;
&lt;h2 id="section-3-testing-your-blog-works-on-your-computer"&gt;Section 3: Testing Your Blog Works on Your Computer&lt;/h2&gt;
&lt;p&gt;Now that we have everything installed for jekyll it is time to test it out.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;From a command prompt in your blog repo directory run the following command to tell jekyll to build and run the web site locally&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; jekyll serve
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If it build successfully you will see something like this&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/jekyllserve.png" alt="Jekyll Serve Success"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now if you open up your browser and navigate to http://localhost:4000 you will see you blog.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;However, by default your _config.yml file will be set for production which will cause any place that you have referenced the site.url to not working on your local machine. You don&amp;rsquo;t want to change your _config.yml file though for development since you will accidentally check it in at some point and break your blog. Instead we can tell jekyll to use multiple configuration files. When you load multiple files it will load them in order and then override any settings from a previously loaded config.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new file in the root of your repo called _configdev.yml&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the _configdev.yml add the following lines to set the url, turn off disqus/google analytics and google search.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; # then add this to the url as well &amp;quot;/repository-name&amp;quot;
url: http://localhost:4000
disqus:
disquscommentcount:
google_analytics:
google_search:
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If your jekyll serve is still running do a ctrl+c to stop it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now run the following command to tell jekyll the config yml files to load&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; jekyll serve --config _config.yml,_configdev.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If it build successfully you will see something like this&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/jekyllserve_multipleconfigs.png" alt="Jekyll Serve Success"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now if you open up your browser and navigate to http://localhost:4000 you will see you blog and any place that reference site.url will be working.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you are ready to do all of your editing locally and test it out before the world gets to see it.&lt;/p&gt;
&lt;p&gt;In our next lesson, I will show you how to install jekyll on OSx. Then you will learn how to create draft blog post that will only show on your local machine so that you don&amp;rsquo;t have to either clutter up your post directory with drafts or worry about accidentally publishing an unfinished article.&lt;/p&gt;</description></item><item><title>Jekyll Part 09: Installing Jekyll On Windows</title><link>https://digitaldrummerj.me/blogging-on-github-part-9-installing-jekyll-on-windows/</link><pubDate>Wed, 09 Sep 2015 01:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-9-installing-jekyll-on-windows/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we are going to setup your Windows computer to be able to edit your blog on your computer.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Up to this point we have been using the Github web site to edit all of our files but the downside to this is that any chances you want to make show up live in your blog before you have had a chance to test them.&lt;/p&gt;
&lt;p&gt;Instead, it is better if you can test out all of your changes and review your blog post before letting the world see them. It will also let you have draft post where you can see them locally but on github they will not be visible.&lt;/p&gt;
&lt;p&gt;Note that Jekyll is not officially supported on Windows but it does work and I have not had any issues with it.&lt;/p&gt;
&lt;h2 id="section-1-installing-software"&gt;Section 1: installing Software&lt;/h2&gt;
&lt;p&gt;I am a big fan of Chocolatey and luckily a good majority of the software that we need had a chocolatey package so I wrote a gist file that we will install using Boxstarter.&lt;/p&gt;
&lt;h3 id="section-11-installing-chocolatey"&gt;Section 1.1: Installing Chocolatey&lt;/h3&gt;
&lt;p&gt;If you are not familiar with Chocolatey, check it out at &lt;a href="http://chocolatey.org" target="_blank" &gt;http://chocolatey.org&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a command prompt&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to install Chocolatey&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; @powershell -NoProfile -ExecutionPolicy Bypass -Command &amp;quot;iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))&amp;quot; &amp;amp;&amp;amp; SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Close the command prompt and re-open it so that we can get the Chocolatey environment variables&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Next we are going to install Boxstarter.&lt;/p&gt;
&lt;h3 id="section-12-installing-boxstarer"&gt;Section 1.2: Installing Boxstarer&lt;/h3&gt;
&lt;p&gt;Boxstarter gives you the ability to bulk install Chocolatey packages plus several helper functions for Windows configuration options.&lt;/p&gt;
&lt;p&gt;When you bulk install using Boxstarter, it will detect any reboots that are triggered by MSI installers, reboot the machine and then run the Boxstarter script again.&lt;/p&gt;
&lt;p&gt;The Windows configuration helper functions that Boxstarter provides to enable or disable Windows features include items such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remote desktop.&lt;/li&gt;
&lt;li&gt;Microsoft update.&lt;/li&gt;
&lt;li&gt;User access control (UAC).&lt;/li&gt;
&lt;li&gt;Set taskbar options like size, postion, and lock the size.&lt;/li&gt;
&lt;li&gt;Set Windows explorer options like showing hidden files, protected OS files, and file extensions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Run the following commands to install Boxstarter&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chocolatey feature enable -n=allowGlobalConfirmation
choco install BoxStarter
chocolatey feature disable -n=allowGlobalConfirmation
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="section-13-installing-the-rest-of-the-software"&gt;Section 1.3: Installing the rest of the software&lt;/h3&gt;
&lt;p&gt;We are going to be installing Ruby, Ruby DevKit, and Python using Chocolatey and Boxstarter.&lt;/p&gt;
&lt;p&gt;Now that you have Boxstarter installed, you will notice on your desktop a new icon called Boxstarter Shell.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/BoxStarterShellIcon.png" alt="Boxstarter Shell Icon"&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run the Boxstarter Shell&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to install the gist file commands. You can view the contents of the gist file &lt;a href="https://gist.githubusercontent.com/digitaldrummerj/f290a11d16e98beabd8b/raw/de3d6d551a0f881e0e66cf6c8ec2cc49c35525e0/jekyll" target="_blank" &gt;here&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; Install-BoxStarterPackage -PackageName https://gist.githubusercontent.com/digitaldrummerj/f290a11d16e98beabd8b/raw/de3d6d551a0f881e0e66cf6c8ec2cc49c35525e0/jekyll
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If there were no errors, you are now ready to install jekyll.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-2-getting-your-blog-onto-your-computer"&gt;Section 2: Getting your Blog onto your computer&lt;/h2&gt;
&lt;p&gt;In this section, you will clone the blog repo from github and install jekyll.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a command prompt&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the directory c:\projects&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; c:
mkdir \projects
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into c:\projects&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; c:
cd \projects
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clone your github blog repo to your local machine with the &amp;ldquo;git clone [Repo Name]&amp;rdquo; command. Below is the example if you were to clone the jekyll repo for this blog series.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; git clone https://github.com/digitaldrummerj/jekyllforblogseries.git
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into the repo that you just cloned&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; cd jekyllforblogseries
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure that you have a GemFile with no file extension in the root of your repo with the following contents&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; source 'https://rubygems.org'
gem 'github-pages'
gem 'jekyll-redirect-from'
gem 'wdm', '~&amp;gt; 0.1.0' if Gem.win_platform?
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the command to install the github-pages gem which has all of the required modules to make jekyll work and the jekyll redirect from plugin. It is one of the few plugins that Github pages supports and allows you to move pages around and have them automatically redirect so that people with bookmarks can still find a moved page.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; bundle install
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last thing we need to do is update the github-pages dependencies to be able to use the latest pygments.rb gem as the version included with the github-pages gem is not compatible with Windows&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; bundle update github-pages
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now we have jekyll installed. Time to test it out&lt;/p&gt;
&lt;h2 id="section-3-testing-your-blog-works-on-your-computer"&gt;Section 3: Testing Your Blog Works on Your Computer&lt;/h2&gt;
&lt;p&gt;Now that we have everything installed for jekyll it is time to test it out.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;From a command prompt in your blog repo directory run the following command to tell jekyll to build and run the web site locally&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; jekyll serve
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If it build successfully you will see something like this&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/jekyllserve.png" alt="Jekyll Serve Success"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now if you open up your browser and navigate to http://localhost:4000 you will see you blog.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-31-setting-up-dev-_configyml"&gt;Section 3.1: Setting Up Dev _config.yml&lt;/h2&gt;
&lt;p&gt;However, by default your _config.yml file will be set for production which will cause any place that you have referenced the site.url to not working on your local machine. You don&amp;rsquo;t want to change your _config.yml file though for development since you will accidentally check it in at some point and break your blog. Instead we can tell jekyll to use multiple configuration files. When you load multiple files it will load them in order and then override any settings from a previously loaded config.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new file in the root of your repo called _configdev.yml&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the _configdev.yml add the following lines to set the url, turn off disqus/google analytics and google search.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; # then add this to the url as well &amp;quot;/repository-name&amp;quot;
url: http://localhost:4000
disqus:
disquscommentcount:
google_analytics:
google_search:
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If your jekyll serve is still running do a ctrl+c to stop it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now run the following command to tell jekyll the config yml files to load&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; jekyll serve --config _config.yml,_configdev.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If it build successfully you will see something like this&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/jekyllserve_multipleconfigs.png" alt="Jekyll Serve Success"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now if you open up your browser and navigate to http://localhost:4000 you will see you blog and any place that reference site.url will be working.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now you are ready to do all of your editing locally and test it out before the world gets to see it.&lt;/p&gt;
&lt;p&gt;In our next lesson, I will show you how to install jekyll on OSx and Linux. Then you will learn how to create draft blog post that will only show on your local machine so that you don&amp;rsquo;t have to either clutter up your post directory with drafts or worry about accidentally publishing an unfinished article.&lt;/p&gt;</description></item><item><title>Camtasia 8.5 - How to Record to Mono</title><link>https://digitaldrummerj.me/camtasia-8.5-record-to-mono/</link><pubDate>Tue, 25 Aug 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/camtasia-8.5-record-to-mono/</guid><category>camtasia</category><description>&lt;p&gt;Today I upgraded from Camtasia 8.3 to Camtasia 8.5 and ran into an issue with my audio settings only recording to the left speaker. I know that my microphone only records in mono so this was not surprising.&lt;/p&gt;
&lt;p&gt;What was surprising is that the audio settings for the Camtasia Recorder were greyed out when using the TREC format.&lt;/p&gt;
&lt;p&gt;In Camtasia 8.3 I could tell the Camtasia Recorder to record in mono so that it would be in both left and right speakers.&lt;/p&gt;
&lt;p&gt;After a little bit of searching I came across this &lt;a href="https://feedback.techsmith.com/techsmith/topics/audio_settings_greyed_out_disabled" target="_blank" &gt;forum post&lt;/a&gt; that had the answer at the bottom.&lt;/p&gt;
&lt;p&gt;When you are in Camtasia Studio, click on your clip, select audio options and check the mix to mono option.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/Camtasia8_5_MixToMono.png" alt="Camtasia Mix To Mono"&gt;&lt;/p&gt;
&lt;p&gt;Note: You may have to separate the audio from the video by right clicking on the clip and selecting the separate video and audio. Then click on the audio track and follow the steps above.&lt;/p&gt;
&lt;p&gt;Problem solved and I can go back to finishing my video.&lt;/p&gt;</description></item><item><title>Visual Studio 2015 - External Web Tools</title><link>https://digitaldrummerj.me/visual-studio-2015-external-web-tools/</link><pubDate>Thu, 20 Aug 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/visual-studio-2015-external-web-tools/</guid><category>dotnet</category><category>npm</category><category>node</category><category>git</category><description>&lt;p&gt;I ran into an issue with an npm package mis-behaving in Visual Studio 2015 but working just fine from the command line.&lt;/p&gt;
&lt;p&gt;After scratching my head for awhile trying to figure out what was going on, I discovered that Visual Studio was pointing to its own version of npm and node and not that ones that were available in my path that the command line was using. Visual Studio 2015 ships with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node&lt;/li&gt;
&lt;li&gt;npm&lt;/li&gt;
&lt;li&gt;git&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these tools are installed as part of Visual Studio but they are installed into the Visual Studio install directory and are not used by the command line.&lt;/p&gt;
&lt;p&gt;If you have manually installed any of these tools like I had, then most likely you have a difference in versions between what the command line is using versus Visual Studio.&lt;/p&gt;
&lt;p&gt;Luckily you can easily tell Visual Studio which versions to use.&lt;/p&gt;
&lt;p&gt;The settings in Visual Studio are under Tools -&amp;gt; Options -&amp;gt; Projects and Solutions -&amp;gt; External Web Tools&lt;/p&gt;
&lt;p&gt;By default the system path is set to be looked at after the Visual Studio versions ($(DevEnvDir)\Extensions\Microsoft\Web Tools\External).&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/VisualStudio/VisualStudio2015-ExternalWebTools.png" alt="Visual Studio External Web Tools Options"&gt;&lt;/p&gt;
&lt;p&gt;To use the system PATH environment variable instead, click on the $(PATH) and use the arrows at the top-right to move it up.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/VisualStudio/VisualStudio2015-ExternalWebTools_PathHigher.png" alt="Visual Studio External Web Tools Path Moved Higher"&gt;&lt;/p&gt;
&lt;p&gt;This will ensure that the version of the tools used matches what the command line is using.&lt;/p&gt;</description></item><item><title>Visual Studio Code Snippets</title><link>https://digitaldrummerj.me/visual-studio-code-snippets/</link><pubDate>Tue, 18 Aug 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/visual-studio-code-snippets/</guid><category>productivity</category><category>visual studio</category><description>&lt;p&gt;There are a bunch of built-in Visual Studio code snippets that will generate code for you with a short keyword and then a tab key press. These shortcuts will make you more efficient when writing code such as creating properties, loops, exceptions, etc.&lt;/p&gt;
&lt;p&gt;Below I have listed the code snippets that I most frequently use and what the output from them looks like.&lt;/p&gt;
&lt;p&gt;To use these snippets type they keyword and then press the tab key.&lt;/p&gt;
&lt;h2 id="loops"&gt;Loops&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;do&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; do
{
} while (b);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;while&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; while (true)
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;for&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; for (int i = 0; i &amp;lt; UPPER; i++)
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;foreach&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; foreach (var VARIABLE in COLLECTION)
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conditionals"&gt;Conditionals&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;if&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; if (b)
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;else&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; else
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;switch&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; switch (@enum)
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="error-trapping"&gt;Error Trapping&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;try&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; try
{
}
catch (Exception)
{
throw;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;tryf&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; try
{
}
finally
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;exception&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; [Serializable]
public class MyException : Exception
{
//
// For guidelines regarding the creation of new exception types, see
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
// and
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp
public MyException()
{
}
public MyException(string message) : base(message)
{
}
public MyException(string message, Exception inner) : base(message, inner)
{
}
protected MyException(
SerializationInfo info,
StreamingContext context) : base(info, context)
{
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="properties"&gt;Properties&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;prop&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; public TYPE Type { get; set; }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;propfull&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; private int myVar;
public int MyProperty
{
get { return myVar; }
set { myVar = value; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;propg&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; public int I { get; private set; }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="misc"&gt;Misc&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ctor&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; public class Misc
{
public Misc()
{
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;enum&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; enum MyEnum
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;struct&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; struct MyStruct
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;#region&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; #region MyRegion
#endregion
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are several more code snippets that are available at &lt;a href="http://tinyurl.com/vscodesnippets" target="_blank" &gt;http://tinyurl.com/vscodesnippets&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With these few code snippets you will be amazed at how much less code you end up writing by hand. Ever little bit helps to make you more productive and efficient as a developer.&lt;/p&gt;
&lt;p&gt;Let me know what you favorite snippets are or ones that you have created.&lt;/p&gt;</description></item><item><title>Favorite Visual Studio Shortcuts</title><link>https://digitaldrummerj.me/visual-studio-shortcuts/</link><pubDate>Thu, 13 Aug 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/visual-studio-shortcuts/</guid><category>productivity</category><category>visual studio</category><description>&lt;p&gt;Here is a list of Visual Studio Community Edition shortcuts that I use.&lt;/p&gt;
&lt;h2 id="building-solution"&gt;Building Solution&lt;/h2&gt;
&lt;table class="exampleTable"&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Solution&lt;/td&gt;
&lt;td&gt;Ctrl+Shift+B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Run Code Analysis on Solution&lt;/td&gt;
&lt;td&gt;Alt+F11&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h2 id="building-current-project"&gt;Building Current Project&lt;/h2&gt;
&lt;p&gt;There is no default shortcut for building the current project or running code analysis on it. You can go under the Build menu and select to build just the current project but this is a manual step that requires you to navigate through the menus.&lt;/p&gt;
&lt;p&gt;To accomplish this, I set the keyboard shortcut Ctrl + \ .&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tools -&amp;gt; Options -&amp;gt; Keyboard or Ctrl + Q, type Keyboard, and hit Enter&lt;/li&gt;
&lt;li&gt;Set shortcut for Build.BuildSelection.&lt;/li&gt;
&lt;li&gt;Open a file and Ctrl+\ should now build that project&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Run Code Analysis on Current Project&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can also run code analysis on the current project like you can on the whole solution.&lt;/p&gt;
&lt;p&gt;To accomplish this, I set the keyboard shortcut Ctrl+Alt+\&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tools -&amp;gt; Options -&amp;gt; Keyboard or Ctrl + Q, type Keyboard, and hit Enter&lt;/li&gt;
&lt;li&gt;Set shortcut for Build.RunCodeAnalysisonSelection.&lt;/li&gt;
&lt;li&gt;Open a file and Ctrl+Alt+\ to run the the code analysis for the project&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="debugging"&gt;Debugging&lt;/h2&gt;
&lt;table class="exampleTable"&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start Debugging&lt;/td&gt;
&lt;td&gt;F5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Start without Debugging&lt;/td&gt;
&lt;td&gt;Ctrl+F5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Toggle Breakpoint On/Off for line&lt;/td&gt;
&lt;td&gt;F9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Run to Cursor and then stop like there is a breakpoint on that line&lt;/td&gt;
&lt;td&gt;Ctrl+F10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step Through the Code Line by Line&lt;/td&gt;
&lt;td&gt;F11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Modify what the debugger displays for an object&lt;/td&gt;
&lt;td&gt;&lt;a href="http://tinyurl.com/vsdebugattrib"&gt;http://tinyurl.com/vsdebugattrib&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h2 id="searching"&gt;Searching&lt;/h2&gt;
&lt;table class="exampleTable"&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick Search&lt;/td&gt;
&lt;td&gt;Ctrl+Q&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Find in Files&lt;/td&gt;
&lt;td&gt;Ctrl+Shift+F&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next Search Result&lt;/td&gt;
&lt;td&gt;F8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Previous Search Result&lt;/td&gt;
&lt;td&gt;Shift+F8&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h2 id="navigation"&gt;Navigation&lt;/h2&gt;
&lt;table class="exampleTable"&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;/tr&gt;
&lt;tr &gt;
&lt;td&gt;Go To Definition&lt;/td&gt;
&lt;td&gt;F12 &lt;br /&gt;Ctrl+Click with &lt;a href="http://tinyurl.com/vsprodpower"&gt;Productivity Power Tools&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Close All Documents&lt;/td&gt;
&lt;td&gt;Alt+W,L&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h2 id="visual-studio-windows-layout"&gt;Visual Studio Windows Layout&lt;/h2&gt;
&lt;p&gt;In Visual Studio 2015, you can now save the Windows Layout and change them with a shortcut key.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To Save: In Visual Studio 2015, under the Windows menu, click the Save Windows Layout&lt;/li&gt;
&lt;li&gt;To Apply Layout: In Visual Studio 2015, under the Windows menu, select the Apply Windows layout popout and pick the layout to apply. There is also a shortcut key for each of the saved Windows layouts. By default it is Ctrl+Alt+# (e.g. Ctrl+Alt+1 for the 1st saved layout)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you have Visual Studio 2013 and below, you can use use the Visual Studio extension, &lt;a href="http://tinyurl.com/vslayout" target="_blank" &gt;Layouts O Rama&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="resharper-searchingnavigation"&gt;Resharper Searching/Navigation&lt;/h2&gt;
&lt;p&gt;If you have Resharper there are several additional options that you get for searching.&lt;/p&gt;
&lt;p&gt;Note: Assumes Visual Studio keyboard layout for Resharper&lt;/p&gt;
&lt;table class="exampleTable"&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go to Everything&lt;/td&gt;
&lt;td&gt;Ctrl + T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Go to File&lt;/td&gt;
&lt;td&gt;Ctrl + Shift + T&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go to Member in File&lt;/td&gt;
&lt;td&gt;Alt + \&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Find Current File In Solution Explorer&lt;/td&gt;
&lt;td&gt;Alt + Shift + L&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h2 id="format-document"&gt;Format Document&lt;/h2&gt;
&lt;table class="exampleTable"&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Shortcut&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Format Document&lt;/td&gt;
&lt;td&gt;Ctrl+K,D&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="alt"&gt;
&lt;td&gt;Resharper Format Document&lt;/td&gt;
&lt;td&gt;Ctrl+E,F
&lt;br /&gt;Note: Assumes Visual Studio keyboard layout
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h2 id="additional-shortcuts"&gt;Additional Shortcuts&lt;/h2&gt;
&lt;p&gt;There are a lot more shortcuts that are set in Visual Studio. I have only covered the ones that I use the most. To see the full list, visit &lt;a href="http://visualstudioshortcuts.com" target="_blank" &gt;http://visualstudioshortcuts.com&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Favorite Visual Studio Extensions</title><link>https://digitaldrummerj.me/favorite-visual-studio-extensions/</link><pubDate>Tue, 11 Aug 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/favorite-visual-studio-extensions/</guid><category>productivity</category><category>visual studio</category><description>&lt;p&gt;In the Visual Studio Extension Gallery there are hundreds of extensions that are available. The extensions add additional functionality to Visual Studio. Below are the extensions that I typically have installed.&lt;/p&gt;
&lt;h2 id="web-essentials"&gt;Web Essentials&lt;/h2&gt;
&lt;p&gt;Big pile of awesome for web developers. If you are doing web development you need to have this installed.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://vswebessentials.com/" target="_blank" &gt;http://vswebessentials.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enhanced browser link&lt;/li&gt;
&lt;li&gt;Markdown Editor/Preview&lt;/li&gt;
&lt;li&gt;Minification of javascript files&lt;/li&gt;
&lt;li&gt;JSHint / CSSHint integration&lt;/li&gt;
&lt;li&gt;TypeScript preview of compiled ts file&lt;/li&gt;
&lt;li&gt;Plus much much more&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ghostdoc"&gt;GhostDoc&lt;/h2&gt;
&lt;p&gt;Auto generate Xml doc comments based on your method/parameter names, exceptions through and return types.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tinyurl.com/ghostdoc" target="_blank" &gt;http://tinyurl.com/ghostdoc&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Default shortcut: ctrl+shift+d&lt;/p&gt;
&lt;h2 id="i-hate-regions"&gt;I Hate Regions&lt;/h2&gt;
&lt;p&gt;Regions is one of those features that you either love or hate. If you are not a fan of regions then this extension is for you. No longer will you even care that a file has a region in it.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tinyurl.com/regionssuck" target="_blank" &gt;http://tinyurl.com/regionssuck&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Auto expand regions when file opens&lt;/li&gt;
&lt;li&gt;Make the region text smaller and blend more into background so that it stays out of the way&lt;/li&gt;
&lt;li&gt;Can be configure to not allow shrinking of the regions&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="productiviy-power-tools"&gt;Productiviy Power Tools&lt;/h2&gt;
&lt;p&gt;This extension is written by Microsoft adds a lot of productivity enhancements to Visual Studio that you just wish came out of the box.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tinyurl.com/vsprodpower" target="_blank" &gt;http://tinyurl.com/vsprodpower&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Collapse projects to folders&lt;/li&gt;
&lt;li&gt;Copy and paste references between projects&lt;/li&gt;
&lt;li&gt;Copy and paste project as a reference&lt;/li&gt;
&lt;li&gt;Fix mixed tabs and spaces&lt;/li&gt;
&lt;li&gt;Peek definition&lt;/li&gt;
&lt;li&gt;Open containing folder for project&lt;/li&gt;
&lt;li&gt;Open command line&lt;/li&gt;
&lt;li&gt;Solution error visualizer&lt;/li&gt;
&lt;li&gt;Sort using statements&lt;/li&gt;
&lt;li&gt;Remove unused using statements&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="vs-commands"&gt;VS Commands&lt;/h2&gt;
&lt;p&gt;Another productivity tool for Visual Studio. Unfortunately, there is not yet a Visual Studio 2015 version.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tinyurl.com/vscommands13" target="_blank" &gt;http://tinyurl.com/vscommands13&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cancel build on first error&lt;/li&gt;
&lt;li&gt;Keep documents open when reloading project&lt;/li&gt;
&lt;li&gt;Start new instance without debugging&lt;/li&gt;
&lt;li&gt;Start new instance with debugging&lt;/li&gt;
&lt;li&gt;Sync zoom levels between pages&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="switch-startup-project"&gt;Switch Startup Project&lt;/h2&gt;
&lt;p&gt;Easily configure different startup project configurations and be able to switch between them or a select single project to start up.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tinyurl.com/vsswitchstart" target="_blank" &gt;http://tinyurl.com/vsswitchstart&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="layout-o-rama"&gt;Layout O Rama&lt;/h2&gt;
&lt;p&gt;Store Visual Studio windows layout and be able to quickly change to a new layout. This is really useful when moving to different monitor setups/resolution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In Visual Studio 2015 this is backed into Visual Studio. You can save layouts under the Windows menu in Visual Studio and quickly apply those saved layouts with a shortcut key or under the Windows -&amp;gt; Apply Layout menu.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tinyurl.com/vslayout" target="_blank" &gt;http://tinyurl.com/vslayout&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="task-runner-explorer"&gt;Task Runner Explorer&lt;/h2&gt;
&lt;p&gt;Run Grunt or Gulp task directly in Visual Studio.&lt;/p&gt;
&lt;p&gt;Can hook Grunt/Gulp task up to the Visual Studio build system (before/after build, on clean or solution open)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In Visual Studio 2015, this is included out of the box.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tinyurl.com/vstaskrunner" target="_blank" &gt;http://tinyurl.com/vstaskrunner&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="file-nesting"&gt;File Nesting&lt;/h2&gt;
&lt;p&gt;Easily group files by name and be able to collapse the group.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://tinyurl.com/vsfilenest" target="_blank" &gt;http://tinyurl.com/vsfilenest&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="add-new-file"&gt;Add New File&lt;/h2&gt;
&lt;p&gt;Fast and easy way to add new files to any project including files that start with a dart.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://visualstudiogallery.msdn.microsoft.com/3f820e99-6c0d-41db-aa74-a18d9623b1f3" target="_blank" &gt;https://visualstudiogallery.msdn.microsoft.com/3f820e99-6c0d-41db-aa74-a18d9623b1f3&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="package-intellisense"&gt;Package Intellisense&lt;/h2&gt;
&lt;p&gt;NPM and Bower package intellisense directly in the Visual Studio JSON editor.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://visualstudiogallery.msdn.microsoft.com/65748cdb-4087-497e-a394-2e3449c8e61e" target="_blank" &gt;https://visualstudiogallery.msdn.microsoft.com/65748cdb-4087-497e-a394-2e3449c8e61e&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="bundler--minifier"&gt;Bundler &amp;amp; Minifier&lt;/h2&gt;
&lt;p&gt;Bundle and minify JS, CSSS, and Html files. Can be wired into the build system in Visual Studio.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://visualstudiogallery.msdn.microsoft.com/9ec27da7-e24b-4d56-8064-fd7e88ac1c40" target="_blank" &gt;https://visualstudiogallery.msdn.microsoft.com/9ec27da7-e24b-4d56-8064-fd7e88ac1c40&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="open-command-line"&gt;Open Command Line&lt;/h2&gt;
&lt;p&gt;Open a command line at the root of the project. Supports all consoles: CMD, Powershell, Bash, etc.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A similar feature is also available as part of the Productivity Power Tools.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://visualstudiogallery.msdn.microsoft.com/4e84e2cf-2d6b-472a-b1e2-b84932511379" target="_blank" &gt;https://visualstudiogallery.msdn.microsoft.com/4e84e2cf-2d6b-472a-b1e2-b84932511379&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="stop-on-first-build-error"&gt;Stop on First build Error&lt;/h2&gt;
&lt;p&gt;Stops a solution build immediately after a project has a failed to build error.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://visualstudiogallery.msdn.microsoft.com/91aaa139-5d3c-43a7-b39f-369196a84fa5" target="_blank" &gt;https://visualstudiogallery.msdn.microsoft.com/91aaa139-5d3c-43a7-b39f-369196a84fa5&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="resharper"&gt;Resharper&lt;/h2&gt;
&lt;p&gt;This is the one extension that I cannot live without. It makes Visual Studio a much better IDE. Once you start using it, you will wonder how you ever lived without it. I could easily do several blog post on the features of Resharper. Below I picked out some of my favorite features.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://jetbrains.com/resharper" target="_blank" &gt;http://jetbrains.com/resharper&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="features"&gt;Features:&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Navigation&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to Everything / Type - quick navigation to all possible destinations (types, symbols or files).&lt;/li&gt;
&lt;li&gt;Go to file - quickly navigate to any file within your solution.&lt;/li&gt;
&lt;li&gt;Go to File member - quickly navigate to a particular method or field in the current file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Docs: &lt;a href="https://www.jetbrains.com/resharper/features/navigation_search.html" target="_blank" &gt;https://www.jetbrains.com/resharper/features/navigation_search.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code Generation&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create from usage&lt;/li&gt;
&lt;li&gt;Generate Type members&lt;/li&gt;
&lt;li&gt;Implement/Override Methods&lt;/li&gt;
&lt;li&gt;Generate formatting or equality members&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Docs: &lt;a href="https://www.jetbrains.com/resharper/features/code_generation.html" target="_blank" &gt;https://www.jetbrains.com/resharper/features/code_generation.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code Analysis / Refactoring / Code Templates&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Continuous code quality analysis&lt;/li&gt;
&lt;li&gt;Detect errors and code smells&lt;/li&gt;
&lt;li&gt;Quick fixes&lt;/li&gt;
&lt;li&gt;Code Templates (snippets, surround with, and file)&lt;/li&gt;
&lt;li&gt;Lots of refactoring options: &lt;a href="https://www.jetbrains.com/resharper/features/code_refactoring.html" target="_blank" &gt;https://www.jetbrains.com/resharper/features/code_refactoring.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Code Cleanup&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code formatting&lt;/li&gt;
&lt;li&gt;Optimize namespace declarations&lt;/li&gt;
&lt;li&gt;Remove code redundancies&lt;/li&gt;
&lt;li&gt;File and type layout&lt;/li&gt;
&lt;li&gt;Code style configuration and sharing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Camelhumps&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is not so much a feature but an option that you can turn on. I like it so much though that I consider it a feature.&lt;/p&gt;
&lt;p&gt;When turned on it allows you to auto filter intellisense and any search dialogs by typing the first letter of each capitalized word in what you are looking for. For example, if you have a class called MyClassExample you would search for it by typing MCE.&lt;/p&gt;
&lt;p&gt;&lt;font color="red"&gt;Warning:&lt;/font&gt; Camelhumps changes the behavior of Ctrl+Backspace to now only delete the previous word instead of the whole declaration. For example, MyClassExample would only delete Example and leave MyClass.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unit Test Runner&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run and debug test&lt;/li&gt;
&lt;li&gt;Can run multiple unit test sessions simultaneously and independently of each other&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://www.jetbrains.com/resharper/features/unit_testing.html" target="_blank" &gt;https://www.jetbrains.com/resharper/features/unit_testing.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Complete feature list at &lt;a href="https://www.jetbrains.com/resharper/features/" target="_blank" &gt;https://www.jetbrains.com/resharper/features/&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Favorite Windows Shortcuts</title><link>https://digitaldrummerj.me/windows-shortcuts/</link><pubDate>Thu, 06 Aug 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/windows-shortcuts/</guid><category>productivity</category><category>windows</category><description>&lt;p&gt;So many times I watch Windows users spending lots of time doing task that should just take a few seconds but instead are taking several minutes because they are taking the long way around to get the task completed.&lt;/p&gt;
&lt;p&gt;Here are some examples that I see people doing and below I will show you how to get them done the easiest way possible.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Getting 2 programs side by side half screened&lt;/li&gt;
&lt;li&gt;Navigating in Windows Explorer to find the Visual Studio solution or project directory&lt;/li&gt;
&lt;li&gt;Open programs as an administrator&lt;/li&gt;
&lt;li&gt;Just opening up task manager.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; All of these shortcuts are built directly into Windows.&lt;/p&gt;
&lt;h2 id="moving-program-between-half-screen-full-screen-and-monitors"&gt;Moving Program between half screen, full screen and monitors&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Move program to half screen on the left side: Windows Key + Left Arrow&lt;/li&gt;
&lt;li&gt;Move Program to half screen on the right side: Windows Key + Right Arrow&lt;/li&gt;
&lt;li&gt;Move to next monitor in same position: Add Shift key to commands&lt;/li&gt;
&lt;li&gt;Minimze Program: Windows Key + Down Arrow&lt;/li&gt;
&lt;li&gt;Maximize Program: Windows Key + Up Arrow&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="start-program-as-administrator"&gt;Start Program as Administrator&lt;/h2&gt;
&lt;p&gt;Windows 8:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Windows Key&lt;/li&gt;
&lt;li&gt;Start typing program name to bring up search bar&lt;/li&gt;
&lt;li&gt;Find the program that you want to open as admin in the results list&lt;/li&gt;
&lt;li&gt;Either Ctrl + Shift + Left Mouse Click or Ctrl + Shift + Enter&lt;/li&gt;
&lt;li&gt;When prompted to open as adminstrative, click the yes button&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="quickly-opening-up-visual-studio-solutions"&gt;Quickly Opening up Visual Studio Solutions&lt;/h2&gt;
&lt;p&gt;Windows has a feature called Jump List that allows you to pick items to an icon in the taskbar that are available when you right-click on the icons.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pin Visual Studio icon to taskbar&lt;/li&gt;
&lt;li&gt;Open a Visual Studio Solution&lt;/li&gt;
&lt;li&gt;Right-click on Visual Studio icon in taskbar&lt;/li&gt;
&lt;li&gt;Find the project that you just opened and click the pin icon next to it&lt;/li&gt;
&lt;li&gt;Now instead of having to open Visual Studio and navigating to the solution file, you can right-click on the Visual Studio icon and select the solution from the pinned item.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="quickly-opening-task-manager"&gt;Quickly Opening Task Manager&lt;/h2&gt;
&lt;p&gt;Ctrl+Shift+Esc&lt;/p&gt;
&lt;h2 id="windows-explorer-favorites"&gt;Windows Explorer Favorites&lt;/h2&gt;
&lt;p&gt;Windows explorer has the favorites links section that you can drag folders onto.&lt;/p&gt;
&lt;p&gt;This is a great place to put commonly used folders such as the folders for current project that you are working on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; That adding or removing folders from the Favorites list does not have any affect on the actual folder, it only removes it from the Favorites list.&lt;/p&gt;</description></item><item><title>Favorite Windows Programs</title><link>https://digitaldrummerj.me/favorite-windows-software/</link><pubDate>Tue, 28 Jul 2015 08:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/favorite-windows-software/</guid><category>productivity</category><category>windows</category><description>&lt;p&gt;Developer productivity is not just about maximizing your speed in editor. It also includes additional software to help you be more productive. Below is some of my favorite Windows software that I use.&lt;/p&gt;
&lt;h2 id="password-management"&gt;Password Management&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://lastpass.com" target="_blank" &gt;LastPass&lt;/a&gt; - Password Manager. Remembers passwords so you don&amp;rsquo;t have to. Auto log you into web sites, store secure notes, wifi passwords, etc. Web interface and desktop is free. Mobile device application is premium version at $12 per year.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="readers"&gt;Readers&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://getpocket.com" target="_blank" &gt;Pocket&lt;/a&gt; - Save web pages/rss feed articles for later and view when ready including offline. Way better than keeping a ton of bookmarks around that you never get back to.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://feedly.com" target="_blank" &gt;Feedly&lt;/a&gt; - RSS Feed Reader with Pocket integration.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="editors"&gt;Editors&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://linqpad.net" target="_blank" &gt;Linqpad&lt;/a&gt; - Scratchpad for .NET. Rich formatted output. Intellisense. If you are a .NET program this is a much have application. Well worth the small amount of money to get the license to use the Intellisense.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/" target="_blank" &gt;Visual Studio Code&lt;/a&gt; - great all around text editor and development environment. Focused on code first.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="planning"&gt;Planning&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://kanbanflow.com" target="_blank" &gt;Kanban Flow / Pomodoro&lt;/a&gt; - I used Kanban Flow to plan my week and use the Pomodoro technique to work on my tasks. I use the setup that John Sonmez talks about at &lt;a href="https://www.youtube.com/watch?v=W9k0OhJkjQ0" target="_blank" &gt;https://www.youtube.com/watch?v=W9k0OhJkjQ0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://todoist.com" target="_blank" &gt;Todoist&lt;/a&gt; - Todo Tasks list. Quick and easy way to add tasks from Chrome or mobile device. Ultimately I import move these into Kanban Flow but it is much quicker on a mobile device to use Todoist.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="utilities"&gt;Utilities&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://chocolatey.org" target="_blank" &gt;Chocolatey&lt;/a&gt; - Install Windows software the easy way. Take the guess work out of where to download software and how to get it installed.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://dropbox.com" target="_blank" &gt;Dropbox&lt;/a&gt; - sync directories and files between machines. Can either sync all or selectively sync file.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://getgreenshot.org" target="_blank" &gt;Greenshot&lt;/a&gt; - free utility to take and edit screenshots. has a basic built-in editor, can copy to clipboard or open an office product (Word, Excel, PowerPoint, outlook) if installed.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.launchy.net" target="_blank" &gt;Launchy&lt;/a&gt; - Forget about the start menu. Launch programs with a few keystrokes. Setup shortcuts for common web sites you use.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://vagrantup.com" target="_blank" &gt;Vagrant&lt;/a&gt; - Easily create lightweight and reproducible virtual machines. If you are using virtual machines on your Windows machine, let Vagrant manage the configurations of it. No longer will it only work on your machine. I have a blog series on using Vagrant and Chocolatey together at [{{ &amp;ldquo;/vagrant-overview&amp;rdquo; | prepend: site.baseurl | prepend: site.url }}]({{ &amp;ldquo;/vagrant-overview&amp;rdquo; | prepend: site.baseurl | prepend: site.url }})&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Presentation Environment Setup</title><link>https://digitaldrummerj.me/presentation-setup/</link><pubDate>Thu, 09 Jul 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/presentation-setup/</guid><category>speaking</category><category>windows</category><description>&lt;p&gt;One of the first things that you learn when giving presentations is that fonts and font size matters just as much as the content. Below is a listing of the various font settings that I have found to be effective and how to set them in the programs that I use.&lt;/p&gt;
&lt;p&gt;If you have other programs that you use, feel free to leave a comment on the font settings that you use.&lt;/p&gt;
&lt;h2 id="notepad"&gt;Notepad&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Format -&amp;gt; Fonts
&lt;ul&gt;
&lt;li&gt;Font: Lucida Console&lt;/li&gt;
&lt;li&gt;Font Style: Regular&lt;/li&gt;
&lt;li&gt;Size: 18-22&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="notepad-1"&gt;Notepad++&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Settings -&amp;gt; Style Configurator &amp;hellip;
&lt;ul&gt;
&lt;li&gt;Select Theme: Default (stylers.xml)&lt;/li&gt;
&lt;li&gt;Language: Default Styles&lt;/li&gt;
&lt;li&gt;Style: Global Override&lt;/li&gt;
&lt;li&gt;Font Name: Lucida Console&lt;/li&gt;
&lt;li&gt;Font Size: 18&lt;/li&gt;
&lt;li&gt;Bold: sometimes checked depending on the day&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="command-prompt"&gt;Command Prompt&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Open cmd&lt;/li&gt;
&lt;li&gt;Click on menu and select Defaults&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Font&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Font: Lucida Console&lt;/li&gt;
&lt;li&gt;Size: 14pt-18pt&lt;/li&gt;
&lt;li&gt;Check &amp;ldquo;Bold fonts&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Colors&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Screen Text: Lime (Kermit Green or Green: 255, Red: 0, Blue: 9)&lt;/li&gt;
&lt;li&gt;Screen Background: Black&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="powershell-and-azure-powershell-command-prompt"&gt;Powershell and Azure Powershell Command Prompt&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;For Win 8.1 On Start find PowerShell or Azure Powershell&lt;/li&gt;
&lt;li&gt;Open file location.&lt;/li&gt;
&lt;li&gt;Open shortcut Properties (right click menu).&lt;/li&gt;
&lt;li&gt;Security tab -&amp;gt; Edit your permissions to Modify.&lt;/li&gt;
&lt;li&gt;Select Font tab. (skip errors - nothing is set hence problem)&lt;/li&gt;
&lt;li&gt;Select Font Tab
&lt;ul&gt;
&lt;li&gt;Font: Lucida Console&lt;/li&gt;
&lt;li&gt;Size: 20&lt;/li&gt;
&lt;li&gt;Bold Fonts: Checked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Select Colors Tag
&lt;ul&gt;
&lt;li&gt;Screen Text: Gray / Red: 238, Green: 237, Blue: 240&lt;/li&gt;
&lt;li&gt;Screen background: blue / Red: 1, Green: 36, Blue: 86&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Select Layout Tab
&lt;ul&gt;
&lt;li&gt;Windows Size Width: 80 (match screen buffer size)&lt;/li&gt;
&lt;li&gt;Window Size Height: 27 (various depending on resolution)&lt;/li&gt;
&lt;li&gt;Let system position window: checked&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click Apply Button&lt;/li&gt;
&lt;li&gt;Security tab Edit permissions remove modify.&lt;/li&gt;
&lt;li&gt;Click Ok&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Remember to always launch powershell from the start menu. For some reason this does not seem to affect the fonts if you do win+r.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t forget to pin to start menu or task bar if going to be using for demo.&lt;/p&gt;
&lt;h2 id="powershell-ise"&gt;Powershell ISE&lt;/h2&gt;
&lt;p&gt;Powershell ISE: C:\Windows\System32\WindowsPowerShell\v1.0&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For Win 8.1, on start find powershell_ise.exe&lt;/li&gt;
&lt;li&gt;Tools -&amp;gt; Options -&amp;gt; Colors and Fonts
&lt;ul&gt;
&lt;li&gt;Click &amp;ldquo;Manage Themse&amp;hellip;&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Select Presentation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Don&amp;rsquo;t forget to pin to start menu or task bar if going to be using for demo.&lt;/p&gt;
&lt;h2 id="visual-studio-code"&gt;Visual Studio Code&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;File -&amp;gt; Preferences -&amp;gt; User Settings&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {
// Wrap based on screen size
&amp;quot;editor.wrappingColumn&amp;quot;: 0,
// Version: 1.03
//Controls the indentation of wrapped lines. Can be one of 'none', 'same' or 'indent'.
//&amp;quot;editor.wrappingIndent&amp;quot;: &amp;quot;indent&amp;quot;,
&amp;quot;editor.tabSize&amp;quot;: &amp;quot;2&amp;quot;,
// Controls the font family.
&amp;quot;editor.fontFamily&amp;quot;: &amp;quot;Consolas&amp;quot;,
// Controls the font size.
&amp;quot;editor.fontSize&amp;quot;: 22,
// Version: 1.0.3
//&amp;quot;editor.formatOnType&amp;quot;: true
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="visual-studio-community--ultimate--enterprise"&gt;Visual Studio Community / Ultimate / Enterprise&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Tools -&amp;gt; Options&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment -&amp;gt; General&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Change the color theme back to &amp;ldquo;Blue&amp;rdquo; if you changed it. You color scheme might be cool but it will distract attendees.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Environment -&amp;gt; Fonts and Colors&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Under the &lt;strong&gt;Show Settings For&lt;/strong&gt; drop down make the following changes:
&lt;ul&gt;
&lt;li&gt;Text Editor: Arial 14&lt;/li&gt;
&lt;li&gt;Editor Tooltip: Calibri 14&lt;/li&gt;
&lt;li&gt;Statement Completion: Calibri 14,&lt;/li&gt;
&lt;li&gt;All Text Tool Windows: Tahoma 13&lt;/li&gt;
&lt;li&gt;Watch, Locals, and Autos Tool Windows: Tahoma 14&lt;/li&gt;
&lt;li&gt;Environment Font: Calibri 14&lt;/li&gt;
&lt;li&gt;Package Manager Console: ??? (if using it for demos)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Environment -&amp;gt; Start up&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Change At startup dropdown to &amp;ldquo;Show Empty Environment&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Uncheck Download content every&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Environment -&amp;gt; Synchronized Settings&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Uncheck &amp;ldquo;Synchronize settings across devices when signed into Visual Studio&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Note: This is done so that your presentation settings do affect any other machines&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Environment -&amp;gt; Keyboard&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Build.BuildSelection -&amp;gt; Ctrl + \&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Go To &lt;strong&gt;Projects and Solutions&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Check &amp;ldquo;Track Active Item in Solution Explorer&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Check &amp;ldquo;Always Show Solution&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Check &amp;ldquo;Always show Error List if build finishes with errors&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Check &amp;ldquo;Show Output windows when build starts&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Go To &lt;strong&gt;Text Editor&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;** All Languages**
&lt;ul&gt;
&lt;li&gt;Check &amp;ldquo;Word Wrap&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Check &amp;ldquo;Line Numbers&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Check &amp;ldquo;Navigation Bar&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Check &amp;ldquo;Auto Brace Completion&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Go To &lt;strong&gt;All Languages -&amp;gt; Tabs&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;Set &amp;ldquo;Indenting&amp;rdquo; to Smart&lt;/li&gt;
&lt;li&gt;Set &amp;ldquo;Tab size&amp;rdquo; to 2&lt;/li&gt;
&lt;li&gt;Set &amp;ldquo;Indent size&amp;rdquo; to 2&lt;/li&gt;
&lt;li&gt;Check &amp;ldquo;Keep tabs&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="sources-used"&gt;Sources Used&lt;/h2&gt;
&lt;p&gt;Here are some various sources that I got information from.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://blogs.msdn.com/b/saraford/archive/2008/06/09/did-you-know-how-to-increase-your-visual-studio-environment-fonts-for-presentations-233.aspx" target="_blank" &gt;http://blogs.msdn.com/b/saraford/archive/2008/06/09/did-you-know-how-to-increase-your-visual-studio-environment-fonts-for-presentations-233.aspx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.hanselman.com/blog/PresentationTipsForPeopleRunningVirtualPCOrVMWare.aspx" target="_blank" &gt;http://www.hanselman.com/blog/PresentationTipsForPeopleRunningVirtualPCOrVMWare.aspx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.hanselman.com/blog/11TopTipsForASuccessfulTechnicalPresentation.aspx" target="_blank" &gt;http://www.hanselman.com/blog/11TopTipsForASuccessfulTechnicalPresentation.aspx&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Vagrant Part 6 - Behind A Proxy Server</title><link>https://digitaldrummerj.me/vagrant-behind-proxy-server/</link><pubDate>Mon, 06 Jul 2015 06:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vagrant-behind-proxy-server/</guid><category>vagrant</category><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;If you are working behind a proxy server you will need to configure both the host computer and the Vagrant virtual machines to communicate through the proxy server. It is easy to configure the proxy settings but finding the documentation is a different story. Below we will go through how to configure the proxy for the vagrant commands (up, status, box add, etc) and then how to configure the virtual machine proxy settings.&lt;/p&gt;
&lt;h2 id="vagrant-commands"&gt;Vagrant Commands&lt;/h2&gt;
&lt;p&gt;In order for the Vagrant commands that talk out to the internet to work there are several environment variables that need to be set.&lt;/p&gt;
&lt;p&gt;The typical proxy environment variables are http_proxy and https_proxy. You can either permanently set them so they are always available or you can set them only for the current open command prompt/terminal.&lt;/p&gt;
&lt;h2 id="on-windows"&gt;On Windows&lt;/h2&gt;
&lt;h3 id="current-open-command-prompt"&gt;Current Open Command Prompt&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;set http_proxy=http://yourproxyserver:port
set https_proxy=https://yourproxyserver:port
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="permantly-set"&gt;Permantly Set&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;setx http_proxy=http://yourproxyserver:port
setx https_proxy=https://yourproxyserver:port
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="view-proxy-settings"&gt;View Proxy Settings&lt;/h3&gt;
&lt;p&gt;If the commands below just echo out the text instead of the actual proxy server, it means that the proxy server is not set.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo %http_proxy%
echo %https_proxy%
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="removing-proxy-setting"&gt;Removing Proxy Setting&lt;/h3&gt;
&lt;p&gt;Unfortunately there is no way to remove the proxy settings from the command line without issuing a reg delete. For whatever reason, Microsoft only allows you to blank out an environment variable with setx but not remove it. Luckily, this does make it so that Vagrant will not use a proxy to connect to the internet.&lt;/p&gt;
&lt;h2 id="vagrant-boxes-virtual-machines"&gt;Vagrant Boxes (Virtual Machines)&lt;/h2&gt;
&lt;div class="panel"&gt;
&lt;span style="color: red"&gt;WARNING: &lt;/span&gt;As of September 13, 2015, the vagrant-proxyconf appears to no longer work on Windows machines. It has been throwing powershell errors on vagrant up. It still works on Linux vagrant machines.
&lt;/div&gt;
In order to configure the vagrant virtual machines to use a proxy server, you need to install the vagrant-proxyconf plugin.
&lt;pre&gt;&lt;code&gt;vagrant plugin install vagrant-proxyconf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The vagrant-proxyconf can configure the proxy settings for Generic Proxy environment variables, Chef, Apt, Docker, Git, npm, PEAR, Subversion, Yum, and Windows.&lt;/p&gt;
&lt;p&gt;Below we will walk through the basics of using the vagrant-proxyconf plugin. You can read the full documentation at &lt;a href="https://github.com/tmatilai/vagrant-proxyconf" target="_blank" &gt;https://github.com/tmatilai/vagrant-proxyconf&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="ensuring-the-plugin-is-installed"&gt;Ensuring the plugin is installed&lt;/h3&gt;
&lt;p&gt;There is no built-in vagrant command to make sure that a plugin is installed but since the Vagrantfile is a Ruby file, it is very easy to write a little bit of Ruby code to ensure that the plugin is installed.&lt;/p&gt;
&lt;p&gt;In the Vagrantfile before the &lt;strong&gt;Vagrant.configure(2) do |config|&lt;/strong&gt; line added the following code snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if !Vagrant.has_plugin?(&amp;quot;vagrant-proxyconf&amp;quot;)
system('vagrant plugin install vagrant-proxyconf')
raise(&amp;quot;vagrant-proxyconf installed. Run command again.&amp;quot;);
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="generic-proxy-settings"&gt;Generic Proxy Settings&lt;/h3&gt;
&lt;p&gt;You can either configure the Vagrant proxy settings on a per Vagrant virtual machine basis or globally.&lt;/p&gt;
&lt;p&gt;To configure on a per machine basis, add the code snippet below to the the machines VagrantFile.&lt;/p&gt;
&lt;p&gt;To configure global add the code snippet below to VagrantFile at .vagrant.d/Vagrantfile. You may need to create this file. To learn more about a global VagrantFile and the order that the VagrantFile&amp;rsquo;s are read see &lt;a href="http://docs.vagrantup.com/v2/vagrantfile/" target="_blank" &gt;http://docs.vagrantup.com/v2/vagrantfile/&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vagrant.configure(&amp;quot;2&amp;quot;) do |config|
if Vagrant.has_plugin?(&amp;quot;vagrant-proxyconf&amp;quot;)
config.proxy.http = &amp;quot;http://192.168.0.2:3128/&amp;quot;
config.proxy.https = &amp;quot;http://192.168.0.2:3128/&amp;quot;
config.proxy.no_proxy = &amp;quot;localhost,127.0.0.1,.example.com&amp;quot;
end
# ... rest of the configurations
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;Now you have everything you need to configure Vagrant to work from behind a proxy server. In the next lesson we will cover the different networking options for Vagrant.&lt;/p&gt;</description></item><item><title>Vagrant Part 5 - Installing Your Software</title><link>https://digitaldrummerj.me/vagrant-installing-your-software/</link><pubDate>Thu, 18 Jun 2015 05:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vagrant-installing-your-software/</guid><category>virtualbox</category><category>vagrant</category><description>&lt;p&gt;Welcome to the Vagrant lesson on how to use Boxstarter to configure Windows and install software as part of the Vagrant provisioning process.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;We have all of the needed software in place to start configuring and installing software onto our virtual machine.&lt;/p&gt;
&lt;p&gt;In this lesson, we will create an file with all of the install and configuration commands that will be executed with Boxstarter.&lt;/p&gt;
&lt;h2 id="can-i-only-install-chocolatey-packages"&gt;Can I only install Chocolatey Packages?&lt;/h2&gt;
&lt;p&gt;Even though Boxstarter is a Chocolatey package, you can install and configure more than just Chocolatey packages. At the end of the day it is running a Powershell script, so anything that you can do with Powershell you can put into your file to execute through Boxstarter.&lt;/p&gt;
&lt;h2 id="example-file"&gt;Example File&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;In the shell directory for MyFirstMachine, create a file called BoxStarterGist.txt.&lt;/li&gt;
&lt;li&gt;In the shell directory for MyFirstMachine, create a 2nd file called RunBoxStarterGist.bat.&lt;/li&gt;
&lt;li&gt;Open the RunBoxStarterGist.bat file and add the following command to copy the file to the temp directory and execute it with Boxstarter.
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The reason to copy the BoxStarterGist.txt file to a temp directory is that sometimes when Boxstarter reboots, the vagrant synced folder for Virtualbox is not mounted until after Boxstarter tries to continue executing the bat file, so it then fails.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; copy &amp;quot;%systemdrive%\vagrant\shell\BoxStarterGist.txt&amp;quot; &amp;quot;%temp%\BoxStarterGist.txt&amp;quot;
@powershell -NoProfile -ExecutionPolicy Bypass -Command &amp;quot;Install-BoxStarterPackage -PackageName %temp%\\BoxstarterGist.txt&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="populating-the-boxstartergisttxt-file"&gt;Populating the BoxstarterGist.txt file&lt;/h2&gt;
&lt;p&gt;We are going to do several things in the BoxstarterGist.txt file:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set some Windows configurations such as taskbar size, Windows explorer show extensions/protected OS file/hidden files, and enable rdp.&lt;/li&gt;
&lt;li&gt;Install some Chocolatey packages such as Visual Studio Code, nodejs, git, and Google Chrome.&lt;/li&gt;
&lt;li&gt;Install some npm packages such as Ionic and Cordova&lt;/li&gt;
&lt;li&gt;Run a git clone to pull down source code for a project&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are all examples of things that you can do. At the end of the day it is just a Powershell script so that options are nearly limitless. I encourage you to read up on Boxstarter at &lt;a href="http://boxstarter.org" target="_blank" &gt;http://boxstarter.org&lt;/a&gt; to see all of options that you have.&lt;/p&gt;
&lt;p&gt;Open up the BoxstarterGist.txt file in your favorite text editor and proceed to the configuration sections. I have the sections in the order that I like to install/configure but you can have them in any order in the BoxstarterGist.txt file.&lt;/p&gt;
&lt;h3 id="windows-configurations"&gt;Windows Configurations&lt;/h3&gt;
&lt;p&gt;This section is the Windows configuration options that we want to set. The full Windows configuration documentation for Boxstarter is available at &lt;a href="http://boxstarter.org/WinConfig" target="_blank" &gt;http://boxstarter.org/WinConfig&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Enable Remote Desktop&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; Enable-RemoteDesktop
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tell Windows that you want to use Powershell instead of a command prompt when you use the corner navigation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; Set-CornerNavigationOptions -EnableUsePowerShellOnWinX
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In Windows exploer turn on hidden files, protected operating system files, show file extensions, and show the full path in the title bar.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; Set-ExplorerOptions -EnableShowHiddenFilesFoldersDrives -EnableShowProtectedOSFiles -EnableShowFileExtensions -EnableShowFullPathInTitlebar
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set the taskbar size to small and lock it in place.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; Set-TaskbarOptions -Size Small -Lock
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="chocolatey-installs"&gt;Chocolatey Installs&lt;/h3&gt;
&lt;p&gt;Now it is time to install all of our Chocolatey Packages.&lt;/p&gt;
&lt;p&gt;The first step is to turn off the prompting from Chocolatey asking if you are sure you want to install this package. Don&amp;rsquo;t worry will turn it back on in a few lines after the Chocolatey package installs are completed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chocolatey feature enable -n=allowGlobalConfirmation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we are going to install the Chocolatey packages that you want. I picked a few but go to the Chocolatey gallery at &lt;a href="http://chocolatey.org" target="_blank" &gt;http://chocolatey.org&lt;/a&gt; and find the packages that meet your needs.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;choco install git
choco install nodejs
choco install visualstudiocode
choco install GoogleChrome
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we are done installing Chocolatey packages we are going to turn back on the prompting for confirmation that you wanted to install the packages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chocolatey feature disable -n=allowGlobalConfirmation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Last thing we are going to do is pin a couple of items that we installs to the taskbar using a built-in helper from Chocolatey called Install-ChocolateyPinnedTaskBarItem.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Install-ChocolateyPinnedTaskBarItem &amp;quot;${env:UserProfile}\Desktop\code.lnk&amp;quot;
Install-ChocolateyPinnedTaskBarItem &amp;quot;${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="npm-installs"&gt;npm installs&lt;/h3&gt;
&lt;p&gt;I play around with npm a lot and don&amp;rsquo;t like the spinner when running an npm command, so I turn that off and set the log level that I like. I then install the Ionic framework and Cordova npm packages.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm config set loglevel http
npm config set spin false
npm install -g ionic
npm install -g cordova
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="git-clone-repositories"&gt;git clone repositories&lt;/h3&gt;
&lt;p&gt;The last thing that I do as part of the BoxStarterGist.txt file is to create c:\projects which is where I store my project source code at and pull it down from Github.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$projectDir = &amp;quot;${env:systemdrive}\projects&amp;quot;
If (!(Test-Path $projectDir)) {
New-Item -Path $projectDir -ItemType Directory
}
cd $projectDir
git clone https://github.com/digitaldrummerj/VagrantTalk
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="how-to-install-your-software-part-of-vagrant-provision"&gt;How to install your software part of Vagrant provision?&lt;/h2&gt;
&lt;p&gt;Now that we have the BoxStarterGist.txt file configured the way that we want it, we need to tell Vagrant what to do with it.&lt;/p&gt;
&lt;p&gt;For this one we are using the file provision which copies the files to the virutal machine but does not execute it. The reason for this is that when Boxstarter reboots the machine, Vagrant thinks the script is done and proceeds to the next provision call which sometimes interfers with the Boxstarter run. So instead I put it on the desktop and manually login to the machine and execute the RunBoxStarterGist.bat file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; config.vm.provision &amp;quot;file&amp;quot;,
source: &amp;quot;shell/RunBoxStarterGist.bat&amp;quot;,
destination: &amp;quot;desktop\\RunBoxStarterGist.bat&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="running-the-runboxstartergistbat-file"&gt;Running the RunBoxStarterGist.bat file&lt;/h2&gt;
&lt;p&gt;After you do vagrant up and all of the provisioners runs, you will see the RunBoxstarterGist.bat on the desktop for the vagrant user. You will just need to manually execute this file. Once you kick it off you can walk away and let it do all of the installs.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;The next lesson will cover the different networking options that vagrant supports. By default Vagrant sets up the virtual machine with a NAT network so that you can only get to the machine from the host. You an however setup both a public and private network. We will dive into how to do this.&lt;/p&gt;</description></item><item><title>Vagrant Part 4 - Install Boxstarter</title><link>https://digitaldrummerj.me/vagrant-installing-boxstarter/</link><pubDate>Thu, 18 Jun 2015 03:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vagrant-installing-boxstarter/</guid><category>virtualbox</category><category>vagrant</category><description>&lt;p&gt;Welcome to the Vagrant lesson on installing Boxstarter as part of the Vagrant provisioning process.&lt;/p&gt;
&lt;h2 id="what-does-boxstarter-give-you"&gt;What does Boxstarter give you?&lt;/h2&gt;
&lt;p&gt;Boxstarter gives you the ability to bulk install Chocolatey packages plus several helper functions for Windows configuration options.&lt;/p&gt;
&lt;p&gt;When you bulk install using Boxstarter, it will detect any reboots that are triggered by MSI installers, reboot the machine and then run the Boxstarter script again.&lt;/p&gt;
&lt;p&gt;The Windows configuration helper functions that Boxstarter provides to enable or disable Windows features include items such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remote desktop.&lt;/li&gt;
&lt;li&gt;Microsoft update.&lt;/li&gt;
&lt;li&gt;User access control (UAC).&lt;/li&gt;
&lt;li&gt;Set taskbar options like size, postion, and lock the size.&lt;/li&gt;
&lt;li&gt;Set Windows explorer options like showing hidden files, protected OS files, and file extensions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-to-install-boxstarter-part-of-vagrant-provision"&gt;How to install Boxstarter part of Vagrant provision?&lt;/h2&gt;
&lt;p&gt;Since we already have Chocolatey installed as part of the previous lesson, installing Boxstarter just requires use to call the choco install Boxstarter command.&lt;/p&gt;
&lt;p&gt;However, there are a few things we need to do in order for this to work without user interaction.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have to run the install command as separate shell provision command in Vagrant instead of the main.cmd that we used to install Chocolatey. The reason for this is due to the environment path updates that are part of the Chocolatey install are not picked up until you open a new command prompt.&lt;/li&gt;
&lt;li&gt;You have to turn off the Chocolatey confirmation prompts that ask you if you want to install this package or not.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="creating-the-provisioning-scripts"&gt;Creating the Provisioning Scripts&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the shell directory in the MyFirstMachine directory that we created in previous lessons.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new file called InstallBoxStarter.bat&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following contents to the InstallBoxStarter.bat file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; chocolatey feature enable -n=allowGlobalConfirmation
choco install BoxStarter
chocolatey feature disable -n=allowGlobalConfirmation
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the MyFirstMachine folder, open the VagrantFile in a text editor and add the following provision command after the provision command that is running the main.cmd file in the previous lesson.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; config.vm.provision :shell, path: &amp;quot;shell/InstallBoxStarter.bat&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;So far in the provisioning process, we have had Vagrant install both Chocolatey and Boxstarter. In the next lesson, we will use Boxstarter to bulk install our software and configure Windows.&lt;/p&gt;</description></item><item><title>Vagrant Part 3 - Provisioning With Chocolatey</title><link>https://digitaldrummerj.me/vagrant-provisioning-with-chocolatey/</link><pubDate>Thu, 18 Jun 2015 02:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vagrant-provisioning-with-chocolatey/</guid><category>virtualbox</category><category>vagrant</category><description>&lt;p&gt;Welcome to the Vagrant lesson on installing Chocolatey as part of the Vagrant provisioning process.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;The first thing we are going to do as part our provisioning is to install Chocolatey onto the virtual machine.&lt;/p&gt;
&lt;p&gt;As we saw in the [Easy Virtual Machine Management]({{&amp;quot;/vagrant-overview&amp;quot; | prepend: site.baseurl | prepend: site.url }}) post, having Chocolatey on a machine allows you to easily install all of our software in an automated and repeatable fashion.&lt;/p&gt;
&lt;h2 id="how-to-install-as-part-of-vagrant-provision"&gt;How to install as part of Vagrant provision?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open Windows Explorer and navigate to the MyFirstMachine directory that we created in the [Easy Virtual Machine Management]({{ &amp;ldquo;/vagrant-overview&amp;rdquo; | prepend: site.baseurl | prepend: site.url }}) post.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a directory called shell&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the shell directory, create a file called main.cmd&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the main.cmd in your text editor and add the following line:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;This will tell powershell to run the file c:\vagrant\shell\InstallChocolatey.ps1 and set the execution policy to bypass so that the powershell script can be run.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; @powershell -NoProfile -ExecutionPolicy Bypass -File &amp;quot;%systemdrive%\vagrant\shell\InstallChocolatey.ps1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the shell directory, create a file called InstallChocolatey.ps1&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The file will check to see if Chocolatey is installed and if not, it will install it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ChocoInstallPath = &amp;quot;$env:SystemDrive\ProgramData\Chocolatey\bin&amp;quot;
if (!(Test-Path $ChocoInstallPath)) {
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last step is to tell VagrantFile to run the main.cmd file as part of the provisioning.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; config.vm.provision :shell, path: &amp;quot;shell/main.cmd&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;We now have Chocolatey installed on the virtual machine and are ready to start installing software. The next lesson will install Boxstarter and then you will bulk install all of our software.&lt;/p&gt;</description></item><item><title>Vagrant Part 2 - Provisioning Introduction</title><link>https://digitaldrummerj.me/vagrant-provisioning-intro/</link><pubDate>Thu, 18 Jun 2015 01:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vagrant-provisioning-intro/</guid><category>virtualbox</category><category>vagrant</category><description>&lt;p&gt;Welcome to the Vagrant lesson on what Vagrant provisioning is.&lt;/p&gt;
&lt;h2 id="what-is-vagrant-provisioning"&gt;What is Vagrant Provisioning?&lt;/h2&gt;
&lt;p&gt;Provisioners allow you to automatically install software and alter configurations during the Vagrant up process.&lt;/p&gt;
&lt;p&gt;This is useful since boxes typically aren&amp;rsquo;t built perfectly for your use case. Granted you could just login to the box and install all of the software by hand. However, by using the provisioning it automates the process, make it repeatable, and requires no human interaction.&lt;/p&gt;
&lt;p&gt;This means that you can run vagrant destory, then vagrant up and have a fully configured environment. This makes provisioning super powerful.&lt;/p&gt;
&lt;p&gt;Vagrant gives you multiple options for provisioning the machine, from simple command line scripts to more complex configuration management systems such as chef and puppet.&lt;/p&gt;
&lt;h2 id="when-does-vagrant-provisioning-happens"&gt;When Does Vagrant Provisioning Happens?&lt;/h2&gt;
&lt;p&gt;Provisioning happens at certain points during the lifetime of your Vagrant environment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On the first vagrant up that creates the environment, provisioning is run.&lt;/li&gt;
&lt;li&gt;When vagrant provision is used on a running environment.&lt;/li&gt;
&lt;li&gt;When vagrant reload &amp;ndash;provision is called. The &amp;ndash;provision flag must be present to force provisioning.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also bring up your environment and explicitly not run provisioners by specifying &amp;ndash;no-provision. You would typically only do this if you need to test out something in your vagrant configuration.&lt;/p&gt;
&lt;h2 id="vagrant-provisioning-providers"&gt;Vagrant Provisioning Providers&lt;/h2&gt;
&lt;p&gt;For this tutorial, we are going to use the shell and file providers as they are the simpliest to get started with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shell Provider&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The shell provider will run either batch files or powershell scripts depending on the file extension. The script needs to be able to run without user interaction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;File Provider&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The file provider, copies a file from your machine to the virtual machine but does not run the script. This is useful for scripts that need user interaction or configuration files that need to be put into place on the virtual machine.&lt;/p&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;Now that we understand what provisioning is, in the next couple of lessons we are going to expand on the provisioning to have it install Chocolatey, Boxstarter, and then all of our software using Chocolatey/BoxStarter.&lt;/p&gt;</description></item><item><title>Vagrant Part 1 - Easy Virtual Machine Management</title><link>https://digitaldrummerj.me/vagrant-overview/</link><pubDate>Tue, 16 Jun 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/vagrant-overview/</guid><category>vagrant</category><category>virtualbox</category><description>&lt;p&gt;Welcome to an overview of Vagrant and creating of your first Vagrant machine.&lt;/p&gt;
&lt;h2 id="what-is-vagrant"&gt;What is Vagrant?&lt;/h2&gt;
&lt;p&gt;Vagrant allows you to create and manage lightweight reproducible virtual machines.&lt;/p&gt;
&lt;p&gt;Essentially, all of the configurations to create and configure a virtual machine are kept separate from the virtual machine. This allows you to delete the virtual machine and then re-create it with all of the same configurations at any point.&lt;/p&gt;
&lt;p&gt;No longer do you have to be afraid to delete a virtual machine for a project that isn&amp;rsquo;t active. You can also give the Vagrant configuration to a co-worker or move it to another machine and be assured that everything will get setup correctly when you create the virtual machine on the new machine.&lt;/p&gt;
&lt;p&gt;Before, we can see Vagrant in action, we first need to install a little bit of software onto your machine.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;In this section, we will install all of the software needed to be able to Vagrant.&lt;/p&gt;
&lt;p&gt;There are 3 pieces of software that we need:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Chocolatey&lt;/li&gt;
&lt;li&gt;Virtualbox&lt;/li&gt;
&lt;li&gt;Vagrant&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="chocolatey"&gt;Chocolatey&lt;/h2&gt;
&lt;p&gt;Chocolatey is a Windows software install manager. It solves several common issues with software installed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where do I find the installer for software?&lt;/li&gt;
&lt;li&gt;How do I find the 64 bit vs 32 bit version?&lt;/li&gt;
&lt;li&gt;What default options should I select?&lt;/li&gt;
&lt;li&gt;Where should I install the software to?&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To install Chocolatey, open an administrative command prompt and run the command below or get the command right on the home for Chocolatey at &lt;a href="http://chocolatey.org" target="_blank" &gt;http://chocolatey.org&lt;/a&gt; .&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@powershell -NoProfile -ExecutionPolicy Bypass -Command &amp;quot;iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))&amp;quot; &amp;amp;&amp;amp; SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go ahead and close the command prompt that you used to install Chocolatey. This is needed so that all of the environment variables get refresh that are part of the Chocolatey installer.&lt;/p&gt;
&lt;p&gt;Now that we have Chocolatey installed, we can start installing software or as Chocolatey calls them, packages.&lt;/p&gt;
&lt;p&gt;To install a package, you would run the command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;choco install [package name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can find the package names on the Chocolatey Gallery at &lt;a href="http://chocolatey.org" target="_blank" &gt;http://chocolatey.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next we are going to install Virtualbox using Chocolatey.&lt;/p&gt;
&lt;h2 id="virtualbox"&gt;Virtualbox&lt;/h2&gt;
&lt;p&gt;For this tutorial, I am using Virtualbox as the virtual machine provider. You could also use Hyper-V (free) or VMWare (paid). If you are using Hyper-V already, you can not use Virtualbox at the same as they conflict with each other.&lt;/p&gt;
&lt;p&gt;If you already have Virtualbox installed, you can skip this step.&lt;/p&gt;
&lt;p&gt;Open an administrative command prompt and run the following Chocolatey command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;choco install virtualbox.extensionpack
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command above will also instal Virtualbox as it is listed as a dependency for the virtualbox.extensionpack package.&lt;/p&gt;
&lt;h2 id="vagrant"&gt;Vagrant&lt;/h2&gt;
&lt;p&gt;To install Vagrant, from the administrative command prompt, run the following Chocolatey command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;choco install vagrant
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are now ready to create our first virtual machine using Vagrant.&lt;/p&gt;
&lt;h2 id="your-first-machine"&gt;Your First Machine&lt;/h2&gt;
&lt;p&gt;Vagrant create a file called VagrantFile for each virtual machine, The VagrantFile contains the information about the box the virtual machine is based off of, how to setup the network, the virtualization provider (virtualbox, vmare, hyper-v, azure, etc), and any provisioning scripts to run (shell , puppet, chef, etc) .&lt;/p&gt;
&lt;p&gt;Vagrant starts with a base box which nothing more than the a portable skelton for building virtual machine. There are 2 types of boxes for Vagrant.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pre-Build:&lt;/strong&gt; meaning that everything that you need is installed on the box. You just create the vagrant machine and are ready to start developing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Base OS:&lt;/strong&gt; just the OS is installed and as part of the creation of the vagrant machine, you install all of the needed software.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For this tutorial, we are going to use the Base OS box and build out the box with all of the software we need.&lt;/p&gt;
&lt;h2 id="creating-vagrantfile"&gt;Creating VagrantFile&lt;/h2&gt;
&lt;p&gt;The first step is to create a directory to hold the VagrantFile. I use c:\VagrantBoxes to hold all of the my Vagrant machines configurations.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Within c:\VagrantBoxes, create a directory called MyFirstMachine.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open up a command prompt and navigate to the MyFirstMachine directory that you just created.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To initalize the Vagrant box, you need to run the vagrant init command. This command will initialize the directory to hold Vagrant information and creates the VagrantFile.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For this tutorial, we are going to use the box &amp;ldquo;opentable/win-8.1-enterprise-amd64-nocm&amp;rdquo;. The command below will initalize the MyFirstMachine directory.&lt;/p&gt;
&lt;p&gt;vagrant init &amp;ldquo;opentable/win-8.1-enterprise-amd64-nocm&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Additional boxes can be from the cloud at &lt;a href="https://atlas.hashicorp.com/boxes/search" target="_blank" &gt;https://atlas.hashicorp.com/boxes/search&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="configuring-the-vagrantfile"&gt;Configuring the VagrantFile&lt;/h2&gt;
&lt;p&gt;In the MyFirstMachine directory there is now a file called VagrantFile. Open this file up in your favorite text editor.&lt;/p&gt;
&lt;p&gt;Within the VagrantFile, there is a basic configuration already setup and a lot of very useful comments that explain the different possible configurations.&lt;/p&gt;
&lt;h3 id="vagrant-general-configurations"&gt;Vagrant general configurations&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;config.vm.box is the name of the Vagrant base box to use to start up the machine with. If this base box does not already exist on your machine it will attempt to download it from the Vagrant cloud.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; config.vm.box = &amp;quot;opentable/win-8.1-enterprise-amd64-nocm&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Configure the actual machine name of the virtual machine&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; config.vm.hostname = &amp;quot;MyFirstMachine&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How long Vagrant will keep trying to connect to the virtual machine before it assume something went wrong and times out.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; config.vm.boot_timeout = 600
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;How to commumicate with the machine. The two options are SSH and WinRM. Typically SSH is used for Linux and WinRM for Windows machines.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; config.vm.communicator = &amp;quot;winrm&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="virtualbox-configurations"&gt;Virtualbox Configurations&lt;/h3&gt;
&lt;p&gt;In the configuration below, it will configure the virtual machine with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;4 gigs of RAM&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set it to use 2 CPUs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make the video ram 128 megs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set the clipboard as bidirectional so you can copy and paste from the host machine to virtual machine as well as from the virtual machine to host machine.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Name the machine in the Virtualbox Manager UI.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; config.vm.provider &amp;quot;virtualbox&amp;quot; do |vb|
vb.memory = &amp;quot;4096&amp;quot;
vb.cpus = 2
vb.customize [&amp;quot;modifyvm&amp;quot;, :id, &amp;quot;--vram&amp;quot;, &amp;quot;128&amp;quot;]
vb.customize [&amp;quot;modifyvm&amp;quot;, :id, &amp;quot;--clipboard&amp;quot;, &amp;quot;bidirectional&amp;quot;]
vb.name = &amp;quot;My First Machine&amp;quot;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we are ready to start up the machine and start using it.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open a command prompt.&lt;/li&gt;
&lt;li&gt;Navigate to the MyFirstMachine folder.&lt;/li&gt;
&lt;li&gt;Run vagrant up to start up the machine.
&lt;ul&gt;
&lt;li&gt;The first time you run this command using a new base box, it will take a bit as it has to download the box from the cloud.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that the machine is started up, there are some additional Vagrant commands that you will need to know to be able to hibernate, reboot, shutdown, and delete the virtual machine&lt;/p&gt;
&lt;h2 id="vagrant-commands"&gt;Vagrant Commands&lt;/h2&gt;
&lt;p&gt;The commands below all need to run from the command line from within the MyFirstMachine folder.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;See Status of Machine&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vagrant status
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Hibernate Machine&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vagrant suspend
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Reboot Machine&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vagrant reload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Shutdown Machine&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vagrant shutdown
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Remove Virtual Machine&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vagrant destroy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Re-Create Machine after Destroying it&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vagrant up
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="next-steps"&gt;Next Steps&lt;/h2&gt;
&lt;p&gt;You have just create and started up your first Vagrant managed Virtual machine. This is only the beginning of what you can do with Vagrant. In future lessons in this series we will install software and configure the OS as part of the vagrant up command, we will create multiple machine with a single vagrant command, create azure virtual machines, and create own own base boxes.&lt;/p&gt;
&lt;p&gt;The next lesson, will cover the start of provisioning. We will install Chocolatey as part of the vagrant up command.&lt;/p&gt;</description></item><item><title>Add Git Branch Name to Bash Prompt</title><link>https://digitaldrummerj.me/add-git-branch-to-bash-shell/</link><pubDate>Thu, 11 Jun 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/add-git-branch-to-bash-shell/</guid><category>git</category><category>github</category><description>&lt;p&gt;When I am working on a git repository and using the git command line, one of the things that I often end up checking it which git branch I am on and if there are any pending changes. How awesome would it be if the bash shell prompt, told you the branch name if the directory is part of a git repository and if there are any changes. Well, thankfully someone has done this work already and with a little bit of configuration on your part, you can implement the changes.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;mkdir ~/.bash&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd ~/.bash&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;git clone git://github.com/jimeh/git-aware-prompt.git&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;edit your bash profile (~/.bashrc or ~/.bash_profile)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the top of your bash profile, add the following 2 lines.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; export GITAWAREPROMPT=~/.bash/git-aware-prompt
source $GITAWAREPROMPT/main.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the end of your bash profile, update the prompt with the following line.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; export PS1=&amp;quot;\${debian_chroot:+(\$debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\] \[$txtcyn\]\$git_branch\[$txtred\]\$git_dirty\[$txtrst\]\$ &amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you are in a git repository directory like I am in the ~blog directory, your prompt will now look like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;No Uncommited changes&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; vagrant@vagrant-ubuntu-trustry-64:~/blog (master) $[]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Changes that have not been committed. Notice the star after the word master&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; vagrant@vagrant-ubuntu-trustry-64:~/blog (master)* $[]
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you are in a non-git repository directory such as the ~/ directory, your prompt will now look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; vagrant@vagrant-ubuntu-trustry-64:~/ $
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Windows 8.1 - Powershell Script to Uninstall Default Programs</title><link>https://digitaldrummerj.me/windows8-script-uninstall-default-programs/</link><pubDate>Wed, 27 May 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/windows8-script-uninstall-default-programs/</guid><category>vagrant</category><category>windows</category><category>powershell</category><description>&lt;p&gt;When I am provisioning a new development virtual machine with vagrant, I do not need all of the Windows 8 modern applications such as bing maps, finance, skype, etc to be installed onto the virtual machine. These applications are nice on a non-virtualized machine but on a virtual machine it just uses extra resources that aren&amp;rsquo;t needed.&lt;/p&gt;
&lt;p&gt;The base install of Windows has all of these programs installed with live tiles turned on that I don&amp;rsquo;t need. This is a huge amount of clutter.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/vagrant/Vagrant-Pre-RemoveDefaultProgramsProvisioning.png" alt="&amp;ldquo;Windows before removing start menu default programs"&gt;&lt;/p&gt;
&lt;p&gt;Luckily enough Ben Hunter, wrote a powershell script that we will be going through and setting up as part of the vagrant provisioning process. &lt;a href="http://blogs.technet.com/b/deploymentguys/archive/2013/10/21/removing-windows-8-1-built-in-applications.aspx" target="_blank" &gt;View Original Script&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We will be going through 3 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Figure out which applications that you want to remove.&lt;/li&gt;
&lt;li&gt;Update the script with the list of applications to remove.&lt;/li&gt;
&lt;li&gt;Add the vagrant provisioning configuration to run the script when you initially run vagrant up&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="getting-a-list-of-possible-applications-to-remove"&gt;Getting a List of Possible Applications to Remove&lt;/h2&gt;
&lt;p&gt;The first step is to get the list of possible application to remove. We are going to get this list by running the Get-AppxPackage powershell function, saving output to a file named ModernApps.txt that will be saved to the Windows temp directory and open it in notepad.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up a powershell command prompt.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use Windows Key + R, type powershell and press enter&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to get the list of applications, save the output to a file and open it in notepad.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Get-AppxPackage&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Format-Wide&lt;/span&gt; &lt;span class="n"&gt;-Property&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="n"&gt;-Column&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Out-File&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;${env:temp}&lt;/span&gt;&lt;span class="s2"&gt;\ModernApps.txt&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;notepad&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;${env:temp}&lt;/span&gt;&lt;span class="s2"&gt;\ModernApps.txt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the script in the next section, modify the $AppsList variable with the programs that you wish to uninstall.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="powershell-script-to-remove-specified-applications"&gt;Powershell Script to Remove Specified Applications&lt;/h2&gt;
&lt;p&gt;The second step is to create the script to remove the specific applications. The script below takes a list of application and calls the Remove-AppxPackage command to uninstall the application. The list of applications to remove is stored in the $AppsList variable within the script.&lt;/p&gt;
&lt;p&gt;Some applications such as camera and photos are not programs that you can be uninstall and may still be pinned to your start menu after this script has run. Unfortunately, starting with Windows 8.1 Microsoft removed the ability to programmatically pin and unpin applications from the start menu, so you will have to manually unpin them. Having to manually unpin a couple of application is much better than having to manually uninstall 19 applications.&lt;/p&gt;
&lt;p&gt;Note: If you are testing out the script below by running it from the Powershell ISE, you will get a warning that the environment does not support Transcripts. This warning will not happen when run from the powershell command line.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#*********************&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Purpose: Remove built in apps specified in list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Pre-Reqs: Windows 8.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#*********************&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Main Routine&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Get log path.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Will log to Task Sequence log folder if the script is running in a Task Sequence&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Otherwise log to \windows\temp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$tsenv&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;New-Object&lt;/span&gt; &lt;span class="n"&gt;-COMObject&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;SMS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;TSEnvironment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$logPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$tsenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;LogPath&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;catch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Write-Host&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;This script is not running in a task sequence&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$logPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$env:windir&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;\temp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$logFile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$logPath&lt;/span&gt;&lt;span class="s2"&gt;\&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$myInvocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MyCommand&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Start logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Start-Transcript&lt;/span&gt; &lt;span class="nv"&gt;$logFile&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Write-Host&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Logging to &lt;/span&gt;&lt;span class="nv"&gt;$logFile&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# *********************&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Update the List of Applications to be removed with your own&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#*********************&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$AppsList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;microsoft.windowscommunicationsapps&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.BingFinance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.BingMaps&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.BingWeather&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.ZuneVideo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.ZuneMusic&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.Media.PlayReadyClient.2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.XboxLIVEGames&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.HelpAndTips&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.BingSports&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.BingNews&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.BingFoodAndDrink&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.BingTravel&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.WindowsReadingList&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.BingHealthAndFitness&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.WindowsAlarms&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.WindowsScan&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.WindowsSoundRecorder&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Microsoft.SkypeApp&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ForEach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$App&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$AppsList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$Packages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-AppxPackage&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Where-Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;-eq&lt;/span&gt; &lt;span class="nv"&gt;$App&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Packages&lt;/span&gt; &lt;span class="o"&gt;-ne&lt;/span&gt; &lt;span class="vm"&gt;$null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Write-Host&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Removing Appx Package: &lt;/span&gt;&lt;span class="nv"&gt;$App&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$Package&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$Packages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Remove-AppxPackage&lt;/span&gt; &lt;span class="n"&gt;-package&lt;/span&gt; &lt;span class="nv"&gt;$Package&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;PackageFullName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Write-Host&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Unable to find package: &lt;/span&gt;&lt;span class="nv"&gt;$App&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$ProvisionedPackage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-AppxProvisionedPackage&lt;/span&gt; &lt;span class="n"&gt;-online&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Where-Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;-eq&lt;/span&gt; &lt;span class="nv"&gt;$App&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ProvisionedPackage&lt;/span&gt; &lt;span class="o"&gt;-ne&lt;/span&gt; &lt;span class="vm"&gt;$null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Write-Host&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Removing Appx Provisioned Package: &lt;/span&gt;&lt;span class="nv"&gt;$App&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Remove-AppxProvisionedPackage&lt;/span&gt; &lt;span class="n"&gt;-online&lt;/span&gt; &lt;span class="n"&gt;-packagename&lt;/span&gt; &lt;span class="nv"&gt;$ProvisionedPackage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;PackageName&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;Write-Host&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Unable to find provisioned package: &lt;/span&gt;&lt;span class="nv"&gt;$App&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Stop logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Stop-Transcript&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="executing-powershell-script-as-part-of-vagrant-provisioning"&gt;Executing Powershell Script as Part of Vagrant Provisioning&lt;/h2&gt;
&lt;p&gt;The last step is to add the running of the script to the vagrant provisioning process.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the directory where you have your VagrantFile, create a directory called shell.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new file called RemoveDefaultPrograms.ps1 int the shell folder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the shell directory, create a file called main.cmd.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following contents to the main.cmd file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Removing Default Windows Programs&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;@powershell&lt;/span&gt; &lt;span class="n"&gt;-NoProfile&lt;/span&gt; &lt;span class="n"&gt;-ExecutionPolicy&lt;/span&gt; &lt;span class="n"&gt;Bypass&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%systemdrive%\vagrant\shell\RemoveDefaultPrograms.ps1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open up your VagrantFile in a text editor of your choice.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following line in the area where you have your provisioning setup.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;provision&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;shell/main.cmd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When you run vagrant up, the last step it will run is the provisioning scripts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you already have a machine controlled by vagrant up and running, you can run vagrant provision to rerun the provisioning scripts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="sample-output-from-the-vagrant-provisioning"&gt;Sample Output from the vagrant provisioning&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: C:&lt;span class="se"&gt;\W&lt;/span&gt;indows&lt;span class="se"&gt;\s&lt;/span&gt;ystem32&amp;gt;echo &lt;span class="s1"&gt;&amp;#39;Removing Default Windows Programs&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: &lt;span class="s1"&gt;&amp;#39;Removing Default Windows Programs&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: This script is not running in a task &lt;span class="nv"&gt;sequence&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Transcript started, output file is C:&lt;span class="se"&gt;\W&lt;/span&gt;indows&lt;span class="se"&gt;\t&lt;/span&gt;emp&lt;span class="se"&gt;\R&lt;/span&gt;emoveDefaultPrograms.ps1.log
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Logging to C:&lt;span class="se"&gt;\W&lt;/span&gt;indows&lt;span class="se"&gt;\t&lt;/span&gt;emp&lt;span class="se"&gt;\R&lt;/span&gt;emoveDefaultPrograms.ps1.log
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Unable to find package: microsoft.windowscommunicationsapps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Removing Appx Provisioned Package: microsoft.windowscommunicationsapps
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Path :
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Online : &lt;span class="nv"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Restart Needed : &lt;span class="nv"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Unable to find package: Microsoft.BingFinance
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Removing Appx Provisioned Package: Microsoft.BingFinance
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Path :
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ......
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;==&lt;/span&gt;&amp;gt; default: Transcript stopped, output file is C:&lt;span class="se"&gt;\W&lt;/span&gt;indows&lt;span class="se"&gt;\t&lt;/span&gt;emp&lt;span class="se"&gt;\R&lt;/span&gt;emoveDefaultPrograms.ps1.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After we have run the script, if you used the application list that I had in the script, the start menu will look like this.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/vagrant/Vagrant-Post-RemoveDefaultProgramsProvisioning.png" alt="Windows start menu after removing default programs"&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now the start menu is mine again control again and I can actually make it useful. No longer is the first 30 minutes of creating a new virtual machine nothing more than uninstall the default programs and messing with the start menu.&lt;/p&gt;</description></item><item><title>Javascript Debugging Made Easier with Sourcemaps</title><link>https://digitaldrummerj.me/javascript-sourcemaps/</link><pubDate>Sun, 17 May 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/javascript-sourcemaps/</guid><category>debugging</category><category>chrome</category><category>gulp</category><description>&lt;p&gt;Updated: Add clean task that uses rimraf to delete the bundle.min.js file if it already exist. Without this it would just append to the existing bundle.min.js file.&lt;/p&gt;
&lt;p&gt;When you release your web site to production, you should minify and concatenate your javascript files. You will have much better performance by doing this but unfortunately debugging becomes difficult with the minified code as it shortens all of the variable and method names. Luckily there is a simple solution to tell the browser developer tools to use the original javascript files when debugging the code, called source maps.&lt;/p&gt;
&lt;p&gt;Sourcemaps bacically are a way to map the combined/minified file back to the original file. As part of the minification process you generate a source map which holds the information about your original files. The developer tools will then parse the source map and make it appear as though you&amp;rsquo;re running unminified and uncombined files.&lt;/p&gt;
&lt;h2 id="creating-sourcemaps"&gt;Creating Sourcemaps&lt;/h2&gt;
&lt;p&gt;To generate the sourcemaps we are going to gulp with the gulp-concat, gulp-uglify, and gulp-sourcemaps modules. If you are not familiar with gulp, it is basically a javascript build system that allows you to write code to automate tasks.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="http://nodejs.org" target="_blank" &gt;node.js&lt;/a&gt; if you don&amp;rsquo;t have it installed already.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Gulp and the Gulp modules that we need.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; npm install --save gulp
npm install --save gulp-concat
npm install --save-dev gulp-uglify
npm install --save-dev gulp-sourcemaps
npm install --save-dev rimraf
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a file called gulpfile.js if you don&amp;rsquo;t already have one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the required gulp modules to the gulpfile.js.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; var gulp = require('gulp');
var gutil = require('gulp-util');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var rimraf = require('rimraf');
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the input paths variable to hold what files we should minify/concatenate. I keep my files in the www folder. The ! in the 2nd input path means to exclude this path. I typically keep my vendor skips like angular, ionic, jquery, etc in the lib folder, so I don&amp;rsquo;t want to minify those scripts again.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; var inputPaths = {
javascript: ['./www/**/*.js', '!./www/lib/**']
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the output path variable to tell gulp where to output the minified file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; var outputPaths = {
'javascript': './www/js'
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we need to create the actual gulp task that does the minification and source map creation. This tasks takes the inputPaths.javscript files, combines them, minifies them, creates the source maps and save it all as a file called bundle.min.js. By default the gulp.dest call will append the minified text to the bundle.min.js instead of overwriting it. Instead we need to call the clean task task first which will delete the minified file if it already exist.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; var minifiedJsFileName = 'bundle.min.js';
gulp.task('build-js', ['clean'], function() {
return gulp.src(inputPaths.javascript)
.pipe(sourcemaps.init())
.pipe(concat(minifiedJsFileName))
.pipe(uglify())
.pipe(sourcemaps.write())
.pipe(gulp.dest(outputPaths.javascript));
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the clean task&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gulp.task(&amp;quot;clean&amp;quot;, function (cb) {
rimraf(outputPaths.javascript + '/' + minifiedJsFileName, cb);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the task is created, we want to setup a gulp watch to regenerate the file when any of the javascript changes.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gulp.task('watch', function() {
gulp.watch(inputPaths.javascript, ['build-js']);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, we are going to create a default task to run the build-js task we created. The default task will typically what you want to run anytime you start up the web server.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gulp.task('default', ['build-js']);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="running-task"&gt;Running Task&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a command prompt or terminal.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the directory that contain the gulpfile.js we created.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gulp build-js
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If it worked successfully, you should have a minified file in your www/js folder.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To use the file, open up the index.html file and remove all of your javascript script tags that reference your js files in the www folder and replace it with a single script tag that references the www/js/bundle.min.js file. Make sure to leave any vendor script tags.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="enabling-sourcemaps-in-chrome-developer-tools"&gt;Enabling Sourcemaps in Chrome Developer Tools&lt;/h2&gt;
&lt;p&gt;Now that we have the source maps generated, we have to tell the Chrome Developer Tools to enable the source maps.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open Google Chrome.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the Developer Tools.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ChromeDevTools/ChromeDevTools-Open.png" alt="Open Chrome Developer Tools"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the Setting Cog.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ChromeDevTools/ChromeDevTools-SettingsCog.png" alt="Open Settings"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down in the General Settings under you see the Sources section and ensure that the &amp;ldquo;Enable JavaScript source maps&amp;rdquo; is checked.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ChromeDevTools/ChromeDevTools-JavascriptSourcemapsEnabled.png" alt="Enable Javascript Source Maps"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the X in the upper right of the Settings windows to close it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now view your web site in the browser to make sure everything is still working.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Note: If you are using Angular, you have to make sure that you use the minification safe syntax for the dependency injection. See the next section for examples.&lt;/li&gt;
&lt;li&gt;Note 2: If you are using the Ionic Framework, the default templates (blank, tabs, sidemenu) do not use the Angular minification safe syntax. You will need to modify the template after you generate your application.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="angular-minification-safe-syntax"&gt;Angular Minification Safe Syntax&lt;/h2&gt;
&lt;p&gt;If you are using the AngularJs framework it does a lot of dependency injection. Out of the box, if you just pass in your arguments to the different functions, it is not minification safe. There are a couple of easy ways to make it minification safe with a minimal amount of code changes.&lt;/p&gt;
&lt;p&gt;Option 1: $inject method
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;//Controllers
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;var myController = function(anyVariableName, $http) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;myController.$inject = [&amp;#39;$scope&amp;#39;, &amp;#39;$http&amp;#39;];&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Option 2: [] syntax. You start the 2nd parameter with a [, put your list of dependencies as string, your function to run and then the closing ].&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;//Modules
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;var myApp = angular.module(&amp;#39;myApp&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [ //Dependencies go here.]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;//Services
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;myApp.service(&amp;#39;serviceName&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [&amp;#39;$http&amp;#39;, &amp;#39;$location&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; function($http, $location) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;//Directives
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;myApp.directive(&amp;#39;directiveName&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [&amp;#39;$http&amp;#39;, &amp;#39;$location&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; function($http, $location)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;//Factories
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;myApp.factory(&amp;#39;factoryName&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; [&amp;#39;$http&amp;#39;, &amp;#39;$location&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; function($http, $location) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Chrome DevTools - Hiding Vendor Scripts</title><link>https://digitaldrummerj.me/chrome-devtools-black-boxing/</link><pubDate>Fri, 15 May 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/chrome-devtools-black-boxing/</guid><category>chrome</category><description>&lt;p&gt;The Chrome Developer tools are an amazing set of tools for debugging and trobuleshooting web sites. If you are a web developer and have not tried out these tools, you have been missing out. As amazing as the developer tools are, one of the most annoying features to me was not being able to skip over vendor javascript like jquery or angular. Getting stuck in a minified version of a angular or jquery takes you down a deep rabbit hole that you never wanted to go down and is a annoying to climb out of.&lt;/p&gt;
&lt;p&gt;Luckily, there is a super simple solution that I learned from &lt;a href="http://twitter.com/jaredthenerd" target="_blank" &gt;Jared Farris&lt;/a&gt; during his Chrome Developer Tools talk at the &lt;a href="http://www.codepalousa.com" target="_blank" &gt;Codepalousa Conference&lt;/a&gt; called Black Boxing. Black boxing allows you to tell the javascript debugger to skip over the file when stepping through the code.&lt;/p&gt;
&lt;p&gt;So how do you turn on black boxing for a file?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open Chrome&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the web site that you want to troubleshoot&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the Chrome Developer Tools&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ChromeDevTools/ChromeDevTools-Open.png" alt="Open Chrome Dev Tools Screenshot"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the Source tab&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right-click on your javascript file you want to black box&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select Blackbox script from the menu&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/ChromeDevTools/ChromeDevTools-BlackBoxing.png" alt="Black Box Script Screenshot"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now you are ready to set a breakpoint in your code, refresh your page and debug your javascript code.&lt;/p&gt;
&lt;p&gt;Check out the Javascript Debugging documentation for all of the features of the debugger, &lt;a href="https://developer.chrome.com/devtools/docs/javascript-debugging" target="_blank" &gt;https://developer.chrome.com/devtools/docs/javascript-debugging&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Removing the NPM spinner</title><link>https://digitaldrummerj.me/remove-npm-spinner/</link><pubDate>Thu, 07 May 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/remove-npm-spinner/</guid><category>npm</category><description>&lt;p&gt;One of the most annoying features of npm for me is the spinner. Many times it runs long enough that I am wondering if it is still working or hung. Thankfully you can easily change this with the .npmrc file.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On Linux/OSx, create the file ~/.npmrc&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On Windows, create the file %USERPROFILE%/.npmrc&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the 2 lines below to the file&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; spin=false
loglevel=http
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Save the file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Close your command prompt/terminal and reopen it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run an npm install command and you will see log messages instead of the spinner.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>Nuget - Incrementing Version Before Building</title><link>https://digitaldrummerj.me/nuget-auto-incrementing-version/</link><pubDate>Mon, 04 May 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/nuget-auto-incrementing-version/</guid><category>nuget</category><description>&lt;p&gt;When you are building nuget packages that are not directly using the AssemblyInfo.cs for the version number, you need to make sure to increment the nuget version number before building the package. Inevitable though you will forget to increment the version number and have to build the package a 2nd time. Wouldn&amp;rsquo;t it be great if you could automatically increment the version number before calling nuget pack. Well I have written a powershell script to do just this. Below are the details out the script.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In your project directory, create a file called IncrementNugetVersionNumber.ps1&lt;/li&gt;
&lt;li&gt;Add the following parameters to the top of the file to take in the nuget spec file location and any version parameters.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$NuSpecFile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="vm"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Mandatory parameter -NuSpecFile not supplied&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$MajorVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$MinorVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$RevisionVersion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next we are going to add a bunch of helper functions right under the param code from above. These helper functions will make it easier to deal with the xml queries of the nuget spec document.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;AssignVersionValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$newValue&lt;/span&gt; &lt;span class="o"&gt;-eq&lt;/span&gt; &lt;span class="vm"&gt;$null&lt;/span&gt; &lt;span class="o"&gt;-or&lt;/span&gt; &lt;span class="nv"&gt;$newValue&lt;/span&gt; &lt;span class="o"&gt;-eq&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$oldValue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$newValue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Get-XmlNamespaceManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;DocumentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;NamespaceURI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;System.Xml.XmlNamespaceManager&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$xmlNsManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;New-Object&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Xml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;XmlNamespaceManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NameTable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$xmlNsManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;AddNamespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ns&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$xmlNsManager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Get-FullyQualifiedXmlNodePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NodePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/ns:&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$NodePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vm"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;/ns:&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Get-XmlNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NodePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$xmlNsManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-XmlNamespaceManager&lt;/span&gt; &lt;span class="n"&gt;-XmlDocument&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt; &lt;span class="n"&gt;-NamespaceURI&lt;/span&gt; &lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$fullyQualifiedNodePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-FullyQualifiedXmlNodePath&lt;/span&gt; &lt;span class="n"&gt;-NodePath&lt;/span&gt; &lt;span class="nv"&gt;$NodePath&lt;/span&gt; &lt;span class="n"&gt;-NodeSeparatorCharacter&lt;/span&gt; &lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$node&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;SelectSingleNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fullyQualifiedNodePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$xmlNsManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Get-XmlNodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NodePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$xmlNsManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-XmlNamespaceManager&lt;/span&gt; &lt;span class="n"&gt;-XmlDocument&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt; &lt;span class="n"&gt;-NamespaceURI&lt;/span&gt; &lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$fullyQualifiedNodePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-FullyQualifiedXmlNodePath&lt;/span&gt; &lt;span class="n"&gt;-NodePath&lt;/span&gt; &lt;span class="nv"&gt;$NodePath&lt;/span&gt; &lt;span class="n"&gt;-NodeSeparatorCharacter&lt;/span&gt; &lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$nodes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;SelectNodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fullyQualifiedNodePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$xmlNsManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$nodes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Get-XmlElementsTextValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$ElementPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$node&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-XmlNode&lt;/span&gt; &lt;span class="n"&gt;-XmlDocument&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt; &lt;span class="n"&gt;-NodePath&lt;/span&gt; &lt;span class="nv"&gt;$ElementPath&lt;/span&gt; &lt;span class="n"&gt;-NamespaceURI&lt;/span&gt; &lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt; &lt;span class="n"&gt;-NodeSeparatorCharacter&lt;/span&gt; &lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;InnerText&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="vm"&gt;$null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Set-XmlElementsTextValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$ElementPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$TextValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$node&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-XmlNode&lt;/span&gt; &lt;span class="n"&gt;-XmlDocument&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt; &lt;span class="n"&gt;-NodePath&lt;/span&gt; &lt;span class="nv"&gt;$ElementPath&lt;/span&gt; &lt;span class="n"&gt;-NamespaceURI&lt;/span&gt; &lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt; &lt;span class="n"&gt;-NodeSeparatorCharacter&lt;/span&gt; &lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;InnerText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$TextValue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$elementName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ElementPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$ElementPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;LastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$element&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;CreateElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$elementName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;DocumentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NamespaceURI&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$textNode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;CreateTextNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$TextValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;AppendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$textNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="vm"&gt;$null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$parentNodePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ElementPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$ElementPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;LastIndexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$parentNode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Get-XmlNode&lt;/span&gt; &lt;span class="n"&gt;-XmlDocument&lt;/span&gt; &lt;span class="nv"&gt;$XmlDocument&lt;/span&gt; &lt;span class="n"&gt;-NodePath&lt;/span&gt; &lt;span class="nv"&gt;$parentNodePath&lt;/span&gt; &lt;span class="n"&gt;-NamespaceURI&lt;/span&gt; &lt;span class="nv"&gt;$NamespaceURI&lt;/span&gt; &lt;span class="n"&gt;-NodeSeparatorCharacter&lt;/span&gt; &lt;span class="nv"&gt;$NodeSeparatorCharacter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$parentNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$parentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;AppendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="vm"&gt;$null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$parentNodePath&lt;/span&gt;&lt;span class="s2"&gt; does not exist in the xml.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now that the helper functions are added, it is time to create the meat of the script where you will update the version number and write it back to the file.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get the Full Path of the Nuget spec file from parameter $NuSpecFile&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $NuSpecFile = Resolve-Path $NuSpecFile
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once we have the full path, we need to get the contents of the files as an Xml document so that we can run xpath queries on it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; [ xml ]$fileContents = Get-Content -Path $NuSpecFile
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then we need to get the current version number&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $originalVersion = Get-XmlElementsTextValue -XmlDocument $fileContents -ElementPath &amp;quot;package.metadata.version&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set a default version number&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $newVersion = &amp;quot;1.0.0&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If there is an existing version number, then we can update it. If not, then use the default version number. This bit of code will break apart the version number into individual segments that we can then update.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; if ($originalVersion -ne $null)
{
$segments=$originalVersion.Split(&amp;quot;.&amp;quot;)
$v1=&amp;quot;1&amp;quot;
$v2=&amp;quot;0&amp;quot;
$v3=&amp;quot;0&amp;quot;
if ($segments.Length -gt 0) { $v1=$segments[0] }
if ($segments.Length -gt 1) { $v2=$segments[1] }
if ($segments.Length -eq 3) { $v3=$segments[2] }
$v1 = AssignVersionValue $v1 $MajorVersion
$v2 = AssignVersionValue $v2 $MinorVersion
$v3 = AssignVersionValue $v3 $RevisionVersion
if ($v1 -eq $null) { $v1 = 1 }
if ($v2 -eq $null) { $v2 = 0 }
if ($v3 -eq $null) { $v3 = 0 }
$newVersion = &amp;quot;$v1.$v2&amp;quot;
#Write-Host &amp;quot;REV: $RevisionVersion&amp;quot;
if ($v3 -ne $null -and ($RevisionVersion -eq $null -or $RevisionVersion -eq &amp;quot;&amp;quot;)) {
$v3 = ($v3 -as [int]) + 1
}
$newVersion = &amp;quot;$newVersion.$v3&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lastly, we need to set the new version number and save the file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; write-host &amp;quot;Setting Version to $newVersion&amp;quot;
Set-XmlElementsTextValue -XmlDocument $fileContents -ElementPath &amp;quot;package.metadata.version&amp;quot; -TextValue $newVersion
$fileContents.Save($NuSpecFile)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now that we have the powershell script built to increment the version number, the next piece is to create the batch file that calls the script and nuget pack.&lt;/p&gt;
&lt;p&gt;For the batch file you can pass in /major, /minor, /revision, and /outputdir. If any of these are not passed in, it will use the existing values from the nuget spec and output into the current directory.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a file called build.cmd&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Setup the parameters&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; @echo off
SET OUTPUTDIR=&amp;quot;&amp;quot;
SET REVISION=&amp;quot;&amp;quot;
SET MAJOR=&amp;quot;&amp;quot;
SET MINOR=&amp;quot;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next we need to check the parameters and set each of the individual values.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; :checkparameters
:: REM Grab the first variable supplied as a whole. Ex. /action:start
set SWITCHPARSE=%1
:: REM Check to see if there are no more switches, if so goto end of
:: parsing, prevents endless loop
IF [%SWITCHPARSE%] == [] goto end
:: REM Reset variables as clean up.
set SWITCH=
set VALUE=
:: In the SWITCHPARSE variable, grab the two tokens separated
:: by a : and assign the first to SWITCH and the second to VALUE
for /F &amp;quot;tokens=1,2 delims=:&amp;quot; %%a IN (&amp;quot;%SWITCHPARSE%&amp;quot;) DO (
SET SWITCH=%%a
set VALUE=%%b
)
:: Check which action to perform based on the switch
IF [%SWITCH%] == [/revision] goto setrevision
IF [%SWITCH%] == [/major] goto setmajorversion
IF [%SWITCH%] == [/minor] goto setminorversion
IF [%SWITCH%] == [/outputdir] goto setoutputdir
:: Perform the action by setting the variable for later use and
:: shift the command line parameters so the next in line is
:: ready to be processed
goto end
:setrevision
set REVISION=%VALUE%
SHIFT
goto checkparameters
:setmajorversion
set MAJOR=%VALUE%
SHIFT
goto checkparameters
:setminorversion
set MINOR=%VALUE%
SHIFT
goto checkparameters
:setoutputdir
set OUTPUTDIR=%VALUE%
SHIFT
goto checkparameters
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally we need to call the powershell script we created above and increment the version number in the nuget spec.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; :end
echo.
echo PowerShell -NoProfile -ExecutionPolicy Bypass -File &amp;quot;..\IncrementNugetVersionNumber.ps1&amp;quot; -NuspecFile &amp;quot;log4net.blank.config.nuspec&amp;quot; -RevisionVersion %REVISION% -MajorVersion %MAJOR% -MinorVersion %MINOR%
PowerShell -NoProfile -ExecutionPolicy Bypass -File &amp;quot;..\IncrementNugetVersionNumber.ps1&amp;quot; -NuspecFile &amp;quot;log4net.appender.console.nuspec&amp;quot; -RevisionVersion %REVISION% -MajorVersion %MAJOR% -MinorVersion %MINOR%
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, we need to call nuget pack to build the nuget package&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; echo.
echo nuget pack -OUTPUTDIR &amp;quot;%OUTPUTDIR%&amp;quot;
nuget pack -OUTPUTDIR %OUTPUTDIR%
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now anytime that you need to build the nuget package, you just have to call build.cmd and it will spit out an new package with an incremented version number.&lt;/p&gt;
&lt;p&gt;I know this post was a lot of code. Both of the files that we create can be downloaded at:&lt;/p&gt;
&lt;p&gt;&lt;a href="/Downloads/2015-05-03-Nuget-Auto-Incrementing-Version/IncrementNugetVersionNumber.ps1" &gt;IncrementNugetVersionNumber.ps1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="/Downloads/2015-05-03-Nuget-Auto-Incrementing-Version/build.cmd" &gt;build.cmd&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Purchasing Business Cards</title><link>https://digitaldrummerj.me/business-cards/</link><pubDate>Mon, 04 May 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/business-cards/</guid><category>how-to</category><category>speaking</category><description>&lt;p&gt;If you are planning on going to any professional networking events, user groups, or speaking at events, you should have business cards with you. Many times people think, I only need to have business cards if I am looking for a job. This is far from the truth. You should have business cards so that you can connect with the people that you have met. People tend to meet lots of people at events and without a business card, it is difficult for people to remember who you are. It is also a good way for people to be able to follow up with you.&lt;/p&gt;
&lt;p&gt;You may be tempted to make your own business cards and print them off on your home printer but please don&amp;rsquo;t. At first this sounds like a great idea but almost never works out right. If you don&amp;rsquo;t get the right card stock they will feel cheap. If you don&amp;rsquo;t get them exactly aligned when printing, they will look unprofessional. When cutting the cards then need to be straight clean cuts which is not easy to do.&lt;/p&gt;
&lt;p&gt;Instead you need to purchase professionally printed cards. You might be thinking, having to purchase business cards is going to be expensive but this is no longer the case. You can easily get 100 business cards and a holder for under $30.&lt;/p&gt;
&lt;p&gt;I purchased my business cards from &lt;a href="http://www.moo.com/share/8fybwg" target="_blank" &gt;http://www.moo.com&lt;/a&gt; (affiliate link) and was very happy with their cards and service. It was very simple for me to design my cards. There was lots of different designs to select from and I was able to upload custom images to use. They also offer a pdf export of the cards so that you can send them to other to review them or print off one to see how it would look.&lt;/p&gt;
&lt;p&gt;For my cards, I went with the MiniCards. I liked that the cards were unique in size without being too unique. Plus with the MiniCard Holder, I was able to have up to 12 cards on my keychain at any given time. This meant that I never had to remember to bring cards with me and always had them if the need came about.&lt;/p&gt;</description></item><item><title>Automatically Add JS/CSS Files to Your Ionic Projects</title><link>https://digitaldrummerj.me/gulp-inject/</link><pubDate>Thu, 30 Apr 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/gulp-inject/</guid><category>ionic</category><category>gulp</category><description>&lt;p&gt;As you work on an Ionic based project or for that matter any web projects that have javascript or css file, you will at some point forget to add your new javascript or css file to the page and wonder why the page is broken. This is really annoying when it happens as many times you spend quite a bit of time troubleshooting before you realize that you just forgot to add the script or css tag. You can fix this problem using gulp and the gulp inject module to automatically add the script and css tags onto the page.&lt;/p&gt;
&lt;h2 id="installing-gulp"&gt;Installing Gulp&lt;/h2&gt;
&lt;p&gt;You should already have NodeJS installed. To install Gulp:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open command prompt&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On Windows, run the command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ npm install gulp -g
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On OSx, run the command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ sudo npm install gulp -g
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="installing-gulp-inject"&gt;Installing Gulp-Inject&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To add the gulp-inject module to the package.json file as a development dependency you need to install gulp-inject with the &amp;ndash;save-dev argument. From your Ionic project directory run the command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; $ npm install gulp-inject --save-dev
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="adding-inject-task-to-gulpfilejs"&gt;Adding Inject Task to gulpfile.js&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the gulpfile.js&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add gulp-inject as a required module. Name the variable used inject.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; var inject = require('gulp-inject');
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next you need to add to the paths where to look for changes to javascript and css files. Note taht the ! in front of the file/path means to exclude that file/path from being injected.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; var paths = {
sass: ['./scss/**/*.scss'],
javascript: [
'./www/**/*.js',
'!./www/js/app.js',
'!./www/lib/**'
],
css: [
'./www/**/*.css',
'!./www/css/ionic.app*.css',
'!./www/lib/**'
]
};
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next we need to add a new gulp task called index (could be called anything).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gulp.task('index', function(){
return gulp.src('./www/index.html')
.pipe(inject(
gulp.src(paths.javascript,
{read: false}), {relative: true}))
.pipe(gulp.dest('./www'))
.pipe(inject(
gulp.src(paths.css,
{read: false}), {relative: true}))
.pipe(gulp.dest('./www'));
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next add the index task to the gulp default task&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gulp.task('default', ['sass', 'index']);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, add the javascript and css paths to the gulp watch task and have it call the index task when a change is detected.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gulp.task('watch', function() {
gulp.watch(paths.sass, ['sass']);
gulp.watch([
paths.javascript,
paths.css
], ['index']);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="setting-up-indexhtml-to-accept-inject"&gt;Setting up index.html to Accept Inject&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up the index.html file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the head tag, replace all of the script tags for your controllers and services with the inject:js comment below. Make sure to leaveLeave the app.js script tag.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;!-- inject:js --&amp;gt;
&amp;lt;!-- endinject --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replace the css tag for the style.css with the inject:css command below&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;!-- inject:css --&amp;gt;
&amp;lt;!-- endinject --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="manually-running-it"&gt;Manually Running It&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run the gulp task index and then look at the index.html page. You should see all of the javascript and css files added back in.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; gulp index
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="running-it-with-ionic-serve"&gt;Running it with Ionic serve&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the ionic.project file and add the gulp index task to the gulpStartUpTasks. If you don&amp;rsquo;t have a gulpStartupTasks section, go ahead and add it without the sass task.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;quot;gulpStartupTasks&amp;quot;: [
&amp;quot;index&amp;quot;,
&amp;quot;watch&amp;quot;,
&amp;quot;sass&amp;quot;
],
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the index.html page, remove the javascript and css tags again. Then run the ionic serve command. You should that the gulp index task was run and all of the javascript and css files were added back in.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-125-further-reading"&gt;Section 12.5: Further Reading&lt;/h2&gt;
&lt;p&gt;There is a lot of different options for the gulp-inject package. If you want to read about all of the different options and see examples, the documentation is available at &lt;a href="https://www.npmjs.com/package/gulp-inject" target="_blank" &gt;https://www.npmjs.com/package/gulp-inject&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="wrap-up"&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;With just a little bit of code, you no longer will you have to wonder why a feature is not working, just to realize that it is because you forgot to include a javascript file. There is all of kinds of additional functionality that you can perform with gulp such as minifying of css and javascript files, running npm/bower commands, or running sass compile commands. The gulp-inject is just one module. I encourage you to add gulp into your normal workflow and automate the mundane tasks.&lt;/p&gt;</description></item><item><title>Jekyll Part 08: Using a Custom Domain</title><link>https://digitaldrummerj.me/blogging-on-github-part-8-using-a-custom-domain/</link><pubDate>Tue, 10 Mar 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-8-using-a-custom-domain/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we are going to setup a custom domain for your blog.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Part of having a blog is making it your own and giving it personality. One of the easiest ways to do this is to use a custom domain name that fits the blog. So far we have been using http://[username].github.io to get to your blog, but now we will go through the process of setting up a custom domain name like &lt;a href="http://digitaldrummerj.me" target="_blank" &gt;http://digitaldrummerj.me&lt;/a&gt;. At first it might seem like this is going to be complicated but it is actually very easy to set this up.&lt;/p&gt;
&lt;h2 id="section-1-purchasing-a-domain-name"&gt;Section 1: Purchasing a Domain Name&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you already have a domain name that you want to use for your Github blog purchased and are using DNSimple for your DNS provider. You can skip this section and move onto Section 2.&lt;/p&gt;
&lt;p&gt;The first step in this process is to get a domain name. The easiest way to do this is to purchase the domain name from the dns provider. For this tutorial we are going to use &lt;a href="http://dnsimple.com" target="_blank" &gt;dnsimple&lt;/a&gt;. You can use any DNS provider but I am only providing instructions for using &lt;a href="http://dnsimple.com" target="_blank" &gt;dnsimple&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go to &lt;a href="http://dnsimple.com" target="_blank" &gt;dnsimple&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &amp;ldquo;Get Started with DNSimple Today&amp;rdquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_get_started_dnsimple.png" alt="Getting Started with DNSimple Today button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &amp;ldquo;Add Domain&amp;rdquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_add_domain_button.png" alt="Add Domain button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill in the domain name that you want or if you are transferring the domain to DNSimple fill in the domain name that you want to transfer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check the &amp;ldquo;Register or transfer this domain&amp;rdquo; box&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you want you registration information private, check the &amp;ldquo;Enable WHOIS Privacy Protection&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you want the domain to auto renew each year, check the &amp;ldquo;Enable Auto Renewal&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &amp;ldquo;Add Domain&amp;rdquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_add_domain_button.png" alt="Add Domain button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill in your contact information&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &amp;ldquo;Register Domain&amp;rdquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_register_domain.png" alt="Register Domain button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You now have a domain name registered and are ready to complete the DNS setup in the next section.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-2-setting-up-dns"&gt;Section 2: Setting up DNS&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note 1:&lt;/strong&gt; If you are transferring a domain to DNSimple that process may take several days to be completed and you may not be able to complete this setup until the transfer process is completed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note 2:&lt;/strong&gt; If you just purchased your domain name from DNSimple, you will be able to complete this section.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click the &amp;ldquo;Services&amp;rdquo; button next to your domain name under the DNSimple domain list for your account.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down until you see the &amp;ldquo;Github Pages&amp;rdquo; service&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_dnsimple_github_service.png" alt="Github Pages Service"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the Add Button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_dnsimple_github_service_add_button.png" alt="Github Page Service Add Button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill in your Github username&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_dnsimple_github_service_setup.png" alt="Github Pages Name"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &amp;ldquo;Complete Github Pages Setup&amp;rdquo; button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_dnsimple_github_service_setup_complete.png" alt="Github Pages Service"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You now have DNSimple configured to point your domain name correctly to your Github blog at http://[username].github.io&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-3-configure-blog-with-domain-name"&gt;Section 3: Configure Blog with Domain Name&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note 1:&lt;/strong&gt; If you are transferring a domain to DNSimple, that process need to be completed before you can complete this section.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note 2:&lt;/strong&gt; If you just purchased your domain name from DNSimple, you will be able to complete this section.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Click on the + button to add a new file&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_add_button.png" alt="Github Plus Button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Name the file CNAME with no extension&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_add_cname_file.png" alt="Github Name the New File CNAME"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the very top of the file add your domain name&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_cname_domain_name.png" alt="Add Domain Name at top of CNAME file"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll to the &amp;ldquo;Commit changes&amp;rdquo; section on the Github Editor, put in your commit comment and click &amp;ldquo;Commit changes&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_8_cname_commit.png" alt="Commit CNAME Changes"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It will take a few minutes for Github to update before you will be able to browse to your Github blog with your domain name. Once your domain is working, even the old http://[username].github.io url will redirect to your domain name.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now users will be able to get to your blog with the domain name that you just configure. Even if the user tries to go to the http://[username].github.io it will redirect them to your domain name.&lt;/p&gt;
&lt;p&gt;In our next lesson, I will show you how to install jekyll so that you can edit your blog post and files locally instead of using the Github web site. This will allow you to make changes without having to publish them to the world just to test them.&lt;/p&gt;</description></item><item><title>Jekyll Part 07: Adding a custom Google search</title><link>https://digitaldrummerj.me/blogging-on-github-part-7-adding-a-custom-google-search/</link><pubDate>Sat, 21 Feb 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-7-adding-a-custom-google-search/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we are going to add the ability to search your blog using google.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;As your blog grows, you want to make it easy for your readers to find the content that they need on your blog. Out of the box, Jekyll does not have any type of search engine built-in. Thankfully, with Google you can easily tell Google to index your blog and then add a search box on the blog.&lt;/p&gt;
&lt;h2 id="section-1-adding-the-search-page"&gt;Section 1: Adding the Search Page&lt;/h2&gt;
&lt;p&gt;If you have been following along with the other lessons in the series, this should be familiar to you.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a web browser and navigate to your [username].github.io repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the + button to add a new file&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_add_button.png" alt="Github Plus Button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Name the file search.html&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_add_search_html_file.png" alt="Github Name New File search.html"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following front matter&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ---
layout: page
title: Search
permalink: /search/
sitemap: false
---
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following html to the page&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;div id=&amp;quot;home-search&amp;quot; class=&amp;quot;home&amp;quot;&amp;gt;
&amp;lt;script&amp;gt;
(function() {
var cx = '[Your CSE Search ID]';
var gcse = document.createElement('script');
gcse.type = 'text/javascript';
gcse.async = true;
gcse.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') +
'//www.google.com/cse/cse.js?cx=' + cx;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(gcse, s);
})();
&amp;lt;/script&amp;gt;
&amp;lt;gcse:search queryParameterName=&amp;quot;searchString&amp;quot;&amp;gt;&amp;lt;/gcse:search&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-2-adding-search-box"&gt;Section 2: Adding Search Box&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;We need to get back to the main directory of our repository. To do this click on one of the [yourname].github.io links.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then click on _layouts directory to go into it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the default.html file to open it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &lt;img src="/images/BloggingOnGitHub/github_edit_button.png" alt="github_edit_button.png"&gt; icon to edit the file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Before the &amp;lt;div id=&amp;ldquo;archives&amp;rdquo;&amp;gt; tag that we added previously, add the following html snippet.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;div id=&amp;quot;search&amp;quot;&amp;gt;
&amp;lt;form role=&amp;quot;search&amp;quot; method=&amp;quot;get&amp;quot; action=&amp;quot;{{ site.baseurl }}//search/&amp;quot;&amp;gt;
&amp;lt;input id=&amp;quot;searchString&amp;quot; name=&amp;quot;searchString&amp;quot;
placeholder=&amp;quot;Be a Better Developer, etc.&amp;quot; type=&amp;quot;text&amp;quot;&amp;gt;
&amp;lt;input id=&amp;quot;searchButton&amp;quot; name=&amp;quot;googleSearchName&amp;quot; type=&amp;quot;submit&amp;quot; value=&amp;quot;Search&amp;quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replace the &amp;lt;div class=&amp;ldquo;container&amp;rdquo;&amp;gt; above the search div with the following&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;div class=&amp;quot;grid&amp;quot; id=&amp;quot;searchBar&amp;quot;&amp;gt;
&amp;lt;div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After the &amp;lt;div id=&amp;ldquo;archives&amp;rdquo;&amp;gt; section add another &amp;lt;/div&amp;gt; tag.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The whole section for the header should look like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;div class=&amp;quot;grid&amp;quot; id=&amp;quot;searchBar&amp;quot;&amp;gt;
&amp;lt;div&amp;gt;
&amp;lt;div id=&amp;quot;search&amp;quot;&amp;gt;
&amp;lt;form role=&amp;quot;search&amp;quot; method=&amp;quot;get&amp;quot; action=&amp;quot;{{ site.baseurl }}//search/&amp;quot;&amp;gt;
&amp;lt;input id=&amp;quot;searchString&amp;quot; name=&amp;quot;searchString&amp;quot;
placeholder=&amp;quot;Learn Ionic, Be a Better Developer, etc.&amp;quot; type=&amp;quot;text&amp;quot;&amp;gt;
&amp;lt;input id=&amp;quot;searchButton&amp;quot; name=&amp;quot;googleSearchName&amp;quot; type=&amp;quot;button&amp;quot; value=&amp;quot;Search&amp;quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;archives&amp;quot;&amp;gt;
browse by &amp;lt;a title=&amp;quot;The complete archive of {{ site.name }}'s Blog by category&amp;quot;
href=&amp;quot;{{ site.baseurl }}//categoryview&amp;quot;&amp;gt;category&amp;lt;/a&amp;gt;
or &amp;lt;a title=&amp;quot;The complete archive of {{ site.name }}'s Blog by month&amp;quot;
href=&amp;quot;{{ site.baseurl }}//monthview&amp;quot;&amp;gt;date&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down to the bottom, add the commit comment, and click on the commit change button.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_commit_search_html.png" alt="Commit default.html changes"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next we need to update the theme so that the search box shows up in the correct spot on the page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-3-updating-the-stylesheet"&gt;Section 3: Updating the Stylesheet&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;We need to get back to the main directory of our repository. To do this click on one of the [yourname].github.io links.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the style.scss file to open it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &lt;img src="/images/BloggingOnGitHub/github_edit_button.png" alt="github_edit_button.png"&gt; icon to edit the file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following to the bottom of the file before the two @import statements.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; #searchBar {
font-size: 80%;
padding: 0.43em 0 0.57em;
#search {
float: right;
#searchString {
width: 283px;
border: none;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) inset;
padding: 0.3em 0.6em;
background-color: #f6f5ea;
margin-right: 0;
}
#searchButton {
paddign: 0.3em 0.6em;
background-color: #0B5485;
border: 1px solid #f6f5ea;
margin-left: 0;
color: $white;
-webkit-appearance: none;
border-radius: 0;
}
#archives {
line-height: 2;
float: left;
color: $black;
text-wrap: avoid;
}
}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down to the bottom, add the commit comment, and click on the commit change button.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_commit_style.png" alt="Commit default.html changes"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now go view your blog&amp;rsquo;s home page at http://[username].github.io/. You should now see the search box in the header along with the &amp;ldquo;browse by category or date&amp;rdquo; links.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_browse_search_in_header.png" alt="Blog&amp;rsquo;s Home Page with Search and Browse By Category or Date Link in Header"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now that the search box and search pages are done, we need to setup Google to actually search our blog.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-4-configuring-google"&gt;Section 4: Configuring Google&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to &lt;a href="https://www.google.com/cse/all" target="_blank" &gt;https://www.google.com/cse/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you do not have a Google account, you will need to create one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you already have a Google account, please login to it now.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_signin_to_cse.png" alt="Signin to Google Custom Search"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once signed in, click the Add button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_add_cse_button.png" alt="Add Button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill in your web site url. Should be http://[username].github.io/*&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_add_cse_url.png" alt="Url to Search Textbox"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Give the search a name that you will remember.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_add_cse_name.png" alt="Name of the Search"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the create button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_add_cse_create.png" alt="Create Button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You search should now be created&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_add_cse_done.png" alt="Search Created"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the Get Code button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_get_cse_code.png" alt="Click Get Code Button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the var cx = line&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_cse_code.png" alt="Copy the code"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go back to github and edit the search.html page. Replace the var cx = line with the line that you just copied.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Commit the changes to Github for the search.html page.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now we are ready to test the search&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-5-testing-the-search"&gt;Section 5: Testing the Search&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a web browser and navigate to http://[username].github.io&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type some text into the search box and click Search&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_search_term.png" alt="Type in Search text and click search button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It should take you to your search page and then do a Google search&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_7_search_in_browser.png" alt="Search View"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;You now have the ability for Google to index and search your blog. This will make it much easier for your reader to find older posts that they may be interested in and hopefully keep them on your blog longer.&lt;/p&gt;
&lt;p&gt;In our next lesson, I will show you how to host your http://[username].github.io blog with a custom domain name like I am doing with &lt;a href="http://digitaldrummerj.me" target="_blank" &gt;http://digitaldrummerj.me&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Jekyll Part 06: Adding Post by Date Page</title><link>https://digitaldrummerj.me/blogging-on-github-part-6-adding-post-by-date/</link><pubDate>Tue, 17 Feb 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-6-adding-post-by-date/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we will go through creating a page to show blog post by date.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;A typical blog has a way for your readers to view posts by either category or date, so that they can look at your archives without having to go through the blog post one by one and page by page. In the last lesson, we added the post by category page. In this lesson we will add the post by date page.&lt;/p&gt;
&lt;h2 id="section-1-creating-the-post-by-date-page"&gt;Section 1: Creating the Post by Date Page&lt;/h2&gt;
&lt;p&gt;If you have been following along with the other lessons in the series, this should be familiar to you.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a web browser and navigate to your [username].github.io repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the + button to add a new file&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_add_button.png" alt="Github Plus Button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Name the file archivebydate.md&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_6_archivebydate_file_name.png" alt="Github Name the New File archivebydate.md"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-2-adding-the-metadata"&gt;Section 2: Adding the Metadata&lt;/h2&gt;
&lt;p&gt;Add the following front matter to the top of the archivebycategory.md file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
layout: page
title: Post by Date
permalink: /monthview/
sitemap: false
---
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="section-3--html-to-display-post"&gt;Section 3: Html to Display Post&lt;/h2&gt;
&lt;p&gt;After the front matter, add the following code to display the post by Year and Month.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;index&amp;quot;&amp;gt;
{% for post in site.posts %}
{% unless post.next %}
&amp;lt;h2&amp;gt;{{ post.date | published: '%Y' }}&amp;lt;/h2&amp;gt;
{% else %}
{% capture year %}{{ post.date | published: '%Y' }}{% endcapture %}
{% capture nyear %}{{ post.next.date | published: '%Y' }}{% endcapture %}
{% if year != nyear %}
{% if forloop.index != 1 %}&amp;lt;/ul&amp;gt;{% enffdif %}
&amp;lt;h2&amp;gt;{{ post.date | published: '%Y' }}&amp;lt;/h2&amp;gt;
{% endif %}
{% endunless %}
{% capture month %}{{ post.date | published: '%m%Y' }}{% endcapture %}
{% capture nmonth %}{{ post.next.date | published: '%m%Y' }}{% endcapture %}
{% if month != nmonth %}
{% if forloop.index != 1 %}&amp;lt;/ul&amp;gt;{% endif %}
&amp;lt;h2&amp;gt;{{ post.date | published: '%B %Y' }}&amp;lt;/h2&amp;gt;&amp;lt;ul&amp;gt;
{% endif %}
{% if post.link %}
&amp;lt;h3 class=&amp;quot;link-post&amp;quot;&amp;gt;
&amp;lt;a href=&amp;quot;{{ site.baseurl }}{{ post.url }}&amp;quot; title=&amp;quot;{{ post.title }}&amp;quot;&amp;gt;{{ post.title }}&amp;lt;/a&amp;gt;
&amp;lt;a href=&amp;quot;{{ post.link }}&amp;quot; target=&amp;quot;_blank&amp;quot; title=&amp;quot;{{ post.title }}&amp;quot;&amp;gt;&amp;lt;i class=&amp;quot;fa fa-link&amp;quot;&amp;gt;&amp;lt;/i&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
{% else %}
&amp;lt;h3&amp;gt;&amp;lt;a href=&amp;quot;{{ site.baseurl }}{{ post.url }}&amp;quot; title=&amp;quot;{{ post.title }}&amp;quot;&amp;gt;{{ post.title }}&amp;lt;p class=&amp;quot;date&amp;quot;&amp;gt;{{ post.date | published: &amp;quot;%B %e, %Y&amp;quot; }}&amp;lt;/p&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;{{ post.excerpt | strip_html | truncate: 160 }}&amp;lt;/p&amp;gt;
{% endif %}
{% endfor %}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="section-4-viewing-the-post-by-date-page"&gt;Section 4: Viewing the Post by Date Page&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;After you have added the above text, scroll to the bottom of the page, add your commit note, and click the commit button.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_6_commit_archivebydate.png" alt="Github Commit archivebydate.md"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To view the category page, navigate to http://[username].github.io/monthview&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your page should look like the following but with your avatar, site name and description in the header of the page.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_6_archivebydate_in_browser.png" alt="month view screenshot"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right now the page is published but not linked to from anywhere. In the next section we will add it to the header section of the page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-5-adding-browse-by-date-into-header"&gt;Section 5: Adding browse by date into Header&lt;/h2&gt;
&lt;p&gt;We are going to add the &amp;ldquo;show by date&amp;rdquo; link into the header section next to the link to view by category that we added in the [previous lesson]({{ &amp;ldquo;blogging-on-github-part-5-adding-a-category-page&amp;rdquo; | prepend: site.baseurl | prepend: site.url}}).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go into the _layouts directory by clicking on _layouts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the default.html file to open it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &lt;img src="/images/BloggingOnGitHub/github_edit_button.png" alt="github_edit_button.png"&gt; icon to edit the file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &amp;lt;div id=&amp;ldquo;archives&amp;rdquo;&amp;gt; tag that we added previously, add the following html snippet after the category view link.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; or &amp;lt;a title=&amp;quot;The complete archive of
{{ site.name }}'s Blog by month&amp;quot;
href=&amp;quot;{{ site.url}}{{ site.baseurl }}//monthview&amp;quot;&amp;gt;date&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_6_browse_by_category_in_header_html.png" alt="GitHub Header Add Html"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down to the bottom, add the commit comment, and click on the commit change button.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_6_commit_default.png" alt="Commit default.html changes"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now go view your blog&amp;rsquo;s home page at http://[username].github.io/. You should now see the &amp;ldquo;browse by category or date&amp;rdquo; link in the header.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_6_browse_by_category_and_date_in_header.png" alt="Blog&amp;rsquo;s Home Page with Browse By Category or Date Link in Header"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With just a few simple steps, you were able to add the post by month page and put it in the header. In the next lesson we will discuss how the blog theme works and how to modify it.&lt;/p&gt;</description></item><item><title>Jekyll Part 05: Adding Category Page</title><link>https://digitaldrummerj.me/blogging-on-github-part-5-adding-a-category-page/</link><pubDate>Sun, 15 Feb 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-5-adding-a-category-page/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we will go through creating a page to show blog post by category.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;A typical blog has a way for your readers to view posts by either category or date, so that they can look at your archives without having to go through the blog post one by one. Unfortunately, the Jekyll-Now repository that we cloned your blog from, does not have these pages. Luckily, these pages are really easy to create.&lt;/p&gt;
&lt;h2 id="section-1-creating-the-category-page"&gt;Section 1: Creating the Category Page&lt;/h2&gt;
&lt;p&gt;If you have been following along with the other lessons in the series, this should be familiar to you.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a web browser and navigate to your [username].github.io repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the + button to add a new file&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_add_button.png" alt="Github Plus Button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Name the file archivebycategory.md&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_5_archivebycategory_file_name.png" alt="Github Name the New File archivebycategory.md"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-2-adding-the-metadata"&gt;Section 2: Adding the Metadata&lt;/h2&gt;
&lt;p&gt;Add the following front matter to the top of the archivebycategory.md file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ---
layout: page
title: Post by Category
permalink: /categoryview/
sitemap: false
---
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="section-3-category-list"&gt;Section 3: Category List&lt;/h2&gt;
&lt;p&gt;After the front matter, add the following code to display the categories and the number of post per category. Each category will link to further down in the page where is will show the post for that category.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;
{% assign categories = site.categories | sort %}
{% for category in categories %}
&amp;lt;span class=&amp;quot;site-tag&amp;quot;&amp;gt;
&amp;lt;a href=&amp;quot;#{{ category | first | slugify }}&amp;quot;&amp;gt;
{{ category[0] | replace:'-', ' ' }} ({{ category | last | size }})
&amp;lt;/a&amp;gt;
&amp;lt;/span&amp;gt;
{% endfor %}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="section-4-blog-post-by-category"&gt;Section 4: Blog Post by Category&lt;/h2&gt;
&lt;p&gt;Next you need to add the code to display the list of blog post by category and sorted by title&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;index&amp;quot;&amp;gt;
{% for category in categories %}
&amp;lt;a name=&amp;quot;{{ category[0] }}&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;
&amp;lt;h2&amp;gt;{{ category[0] | replace:'-', ' ' }} ({{ category | last | size }})&amp;lt;/h2&amp;gt;
{% assign sorted_posts = site.posts | sort: 'title' %}
{% for post in sorted_posts %}
{%if post.categories contains category[0]%}
&amp;lt;h3&amp;gt;&amp;lt;a href=&amp;quot;{{ site.url }}{{ site.baseurl }}{{ post.url }}&amp;quot; title=&amp;quot;{{ post.title }}&amp;quot;&amp;gt;{{ post.title }} &amp;lt;p class=&amp;quot;date&amp;quot;&amp;gt;{{ post.date | published: &amp;quot;%B %e, %Y&amp;quot; }}&amp;lt;/p&amp;gt;&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;p&amp;gt;{{ post.excerpt | strip_html | truncate: 160 }}&amp;lt;/p&amp;gt;
{%endif%}
{% endfor %}
{% endfor %}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="section-5-viewing-the-category-page"&gt;Section 5: Viewing the Category Page&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;After you have added the above text, scroll to the bottom of the page, add your commit note, and click the commit button.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_5_commit_archivebycategory.png" alt="Github Commit archivebycategory.md"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To view the category page, navigate to http://[username].github.io/categoryview&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your page should look like the following but with your avatar, site name and description in the header of the page.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_5_archivebycategory_in_browser.png" alt="category view screenshot"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You will notice that the &amp;ldquo;You&amp;rsquo;re up and running&amp;rdquo; post does not show up on the categories page. This is because there is no categories front matter tag for that blog post. Go ahead and open 2014-3-3-Hello-World.md and add the categories front matter tag.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; categories: ['welcome']
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right now the page is published but not linked to from anywhere. In the next section we will add it to the header section of the page.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-6-adding-category-view-into-header"&gt;Section 6: Adding Category View into Header&lt;/h2&gt;
&lt;p&gt;Unlike the portfolio page that we created in the last lesson, this time we are not going to add the category page into the menu. Instead we are going to create a row below the header with a link to the page.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Go into the _layouts directory by clicking on _layouts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the default.html file to open it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &lt;img src="/images/BloggingOnGitHub/github_edit_button.png" alt="github_edit_button.png"&gt; icon to edit the file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right after the &amp;lt;/header&amp;gt; tag, add the following html snippet&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;div class=&amp;quot;container&amp;quot; &amp;gt;
&amp;lt;div id=&amp;quot;archives&amp;quot;&amp;gt;
browse by &amp;lt;a title=&amp;quot;The complete archive of {{ site.name }}'s Blog by category&amp;quot;
href=&amp;quot;{{ site.url}}{{ site.baseurl }}/categoryview&amp;quot;&amp;gt;category&amp;lt;/a&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down to the bottom, add the commit comment, and click on the commit change button.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_5_commit_default.png" alt="Commit default.html changes"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now go view your blog&amp;rsquo;s home page at http://[username].github.io/. You should now see the &amp;ldquo;browse by category&amp;rdquo; link in the header.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part_5_browse_by_category_in_header.png" alt="Blog&amp;rsquo;s Home Page with Browse By Category Link in Header"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With just a few simple steps, you were added the post by category page and put it in the header. In the next lesson we will add in a new page for browsing blog post by year and month.&lt;/p&gt;</description></item><item><title>Jekyll Part 04: Adding Additional Pages</title><link>https://digitaldrummerj.me/blogging-on-github-part-4-creating-additional-pages/</link><pubDate>Wed, 11 Feb 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-4-creating-additional-pages/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we will go through how to add an additional page such as the &lt;a href="/speaking" &gt;speaking&lt;/a&gt; page on this blog.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;As your blog grows you are going to want to add additional information besides just blog post. If you start speaking at events or want to show off your portfolio or blog post by category/date, you are going to want these as separate pages. These pages most likely will have there own look and feel as well.&lt;/p&gt;
&lt;h2 id="section-1-creating-the-file-for-the-page"&gt;Section 1: Creating the file for the page&lt;/h2&gt;
&lt;p&gt;If you have been following along with the other part in this series, the step should be familiar to you.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a web browser and navigate to your [username].github.io reporepository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the + button to add a new file&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_add_button.png" alt="Github Plus Button"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Name the file portfolio.md&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_name_file_portfolio.png" alt="Github Name the New File portfolio.md"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to the next section to decide on the layout for the page&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-2-choosing-a-layout"&gt;Section 2: Choosing a layout&lt;/h2&gt;
&lt;p&gt;To tell the jekyll engine what the layout of the page should be, you need to add the front matter layout tag.&lt;/p&gt;
&lt;p&gt;From &lt;a href="/blogging-on-github-part-2-your-first-post" &gt; Part 2 Creating your first blog post&lt;/a&gt;, you will remember that the front matter is the metadata about blog post but it is used in jekyll for any page as well. All of the front matter tags are the same as a blog post.&lt;/p&gt;
&lt;p&gt;To change the layout from being a blog post to a page, you will change the layout front matter tag to page instead of post.&lt;/p&gt;
&lt;p&gt;By default jekyll adds files to the sitemap that search engines use to find pages on your site. Setting it to false will stop this page from being added. We will remove the sitemap tag once we are ready to share the page.&lt;/p&gt;
&lt;p&gt;In order for the page to available at [username].github.io/portfolio, you need to set the front matter permalink to /portfolio/ .&lt;/p&gt;
&lt;h3 id="front--matter-tags"&gt;front matter tags&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;---
title: My Portfolio
permalink: /portfolio/
layout: page
sitemap: false
---
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="section-3-adding-content"&gt;Section 3: Adding content&lt;/h2&gt;
&lt;p&gt;The content is written in markdown just like blog post are. In &lt;a href="/blogging-on-github-part-2-your-first-post" &gt; Part 2 Creating your first blog post&lt;/a&gt;, we covered the common markdown tags.&lt;/p&gt;
&lt;p&gt;For now just add some simple markdown like below:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;This is a placeholder page for my portfolio.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your page should now look like this in the editor&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_portfolio_page_markdown.png" alt="Github Editor for Portfolio.md"&gt;&lt;/p&gt;
&lt;p&gt;After you have added the above text, scroll to the bottom of the page, add your commit note, and click the commit button.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_portfolio_page_commit_change.png" alt="Github Commit Portfolio.md"&gt;&lt;/p&gt;
&lt;h2 id="section4-previewing-page"&gt;Section4: Previewing page&lt;/h2&gt;
&lt;p&gt;To view the page navigate to http://[username].github.io/portfolio&lt;/p&gt;
&lt;p&gt;Your page should look like the following but with your avatar, site name and description in the header of the page.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_portfolio_view_in_browser.png" alt="first page"&gt;&lt;/p&gt;
&lt;p&gt;Right now the page is published but not linked to from anywhere.&lt;/p&gt;
&lt;h2 id="section-5-adding-page-to-menu"&gt;Section 5: Adding page to menu&lt;/h2&gt;
&lt;p&gt;Once you are ready to share the page with your readers, you will want to add it to your main menu and have it indexed by search engines.&lt;/p&gt;
&lt;p&gt;To tell search engines to index the page, we need to remove the front matter tag, sitemap:false.&lt;/p&gt;
&lt;p&gt;To add it to the menu:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the _layout directory in the repository by clicking on the repository title to get back to the main directory of the repository&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_navigate_to_top.png" alt="Github Click on Repository Title"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on _layouts directory&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_click_layout.png" alt="Github Click on Layout"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the default.html page.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_click_default.png" alt="Github Click on Default.html"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &lt;img src="/images/BloggingOnGitHub/github_edit_button.png" alt="github_edit_button.png"&gt; icon to edit the file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Find the main menu. You can search for home or about to find the section&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_menu_html.png" alt="Github Main Menu Html"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following line in the menu at the position you want the portfolio page link to be displayed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;a href=&amp;quot;/portfolio&amp;quot;&amp;gt;Portfolio&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_menu_with_portfolio.png" alt="Github Menu with Portfolio Html"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We need to commit the file to the repository by scrolling to the bottom of the page, adding the commit comment, and clicking on the commit button.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_default_commit_changes.png" alt="Github Default.html Commit changes"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now navigate to your blog and the page link should show in the main menu. Click on the portfolio menu item and make sure it takes you to the portfolio page.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_part4_menu_with_portfolio_in_browser.png" alt="Github menu includes portfolio link"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With just a few steps, you were able to add a new page into your blog&amp;rsquo;s web site. You can either continue to build out the portfolio page with the markdown needed to show off your portfolio or you can remove it from the menu until you are ready to do build it out.&lt;/p&gt;
&lt;p&gt;In the next lesson we will build on this lesson by adding in a page to show the blog post by category.&lt;/p&gt;</description></item><item><title>Jekyll Part 03: Adding Commenting to Post</title><link>https://digitaldrummerj.me/blogging-on-github-part-3-adding-comments/</link><pubDate>Sat, 07 Feb 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-3-adding-comments/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial you will learn how to add the ability for users to comment on your blog post.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;You may be thinking that commenting isn&amp;rsquo;t really that important but it is a key feature for you to be able to interact with your readers. Users want to have conversations about your blog post with you and to feel connected to you. It is also a good way for you to learn from them as well.&lt;/p&gt;
&lt;p&gt;Unfortunately, out of the box Jekyll does not have this ability. Luckily, it is really easy to add this ability, thanks to a wonderful tool called Disqus. Disqus is free but still full featured.&lt;/p&gt;
&lt;p&gt;We will go through the process of getting a Disqus account, configuring the account, configuring your blog and testing it all out.&lt;/p&gt;
&lt;h2 id="section-1--configuring-disqus"&gt;Section 1: Configuring Disqus&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open another browser window and navigate to &lt;a href="https://disqus.com/admin/signup/?utm_source=New-Site" target="_blank" &gt;https://disqus.com/admin/signup/?utm_source=New-Site&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill out the information request and click the Finish button. Note: That you can not change the short name value once it is created. You can have multiple sites under the same account though with different short name values.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/DisqusRegisterSite.png" alt="DisqusRegisterSite.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You will be greeted with a &amp;ldquo;Choose Your Platform&amp;rdquo; screen like the follow. Don&amp;rsquo;t worry about any of these options as we are not going to use them.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/DisqusChooseAPlatform.png" alt="DisqusChooseAPlatform.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the General link on the left side of the links above the &amp;ldquo;Choose Your Platform&amp;rdquo; title.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/DisqusGeneralSettingMenuHighlight.png" alt="DisqusGeneralSettingMenuHighlight.png"&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down to the Site Identity and make sure that the values are correct. The WebSite Name will appear on your blog when the comments show, so make sure it is what you want it to say. Take note of your shortname as you will need it in the next section when we config your blog to show Disqus.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/DisqusGeneralSettingSiteIdentity.png" alt="DisqusGeneralSettingSiteIdentity.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down to the next section which should be Community Rules and match the settings to the image below. To prevent comment spam I always set comments to have to be approved before showing on the site. The volume on the site will be low enough for now that you will be able to easily manage approving the comments.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/DisqusGeneralSettingCommunityRules.png" alt="DisqusGeneralSettingCommunityRules.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down to the next section which should be Social Platform Intergration and add your twitter account if you have one. This will ensure that when people share comments on your blog that it includes you in the post.&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/DisqusGeneralSettingSocialIntegration.png" alt="DisqusGeneralSettingSocialIntegration.png"&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the Save Button&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-2-configuring-your-blog"&gt;Section 2: Configuring Your Blog&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a web browser and navigate to your [username].github.io repository.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the _config.yml file to navigate to it&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &lt;img src="/images/BloggingOnGitHub/github_edit_button.png" alt="github_edit_button.png"&gt; icon to edit the file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Look for the text disqus: and put in your disqus shortname name like below.&lt;/p&gt;
&lt;p&gt;disqus: digitaldrummerj&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll to the bottom of the screen and click on the commit button. You should put in a comment as well so that you know what changed with the file.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/Github_Save_Changes.png" alt="Github_Save_Changes.png"&gt;&lt;/p&gt;
&lt;h2 id="section-3-testing-it-out"&gt;Section 3: Testing it out&lt;/h2&gt;
&lt;p&gt;Open up a web browser and navigate to your blog and click on the title for the blog post that we published in our last lesson.&lt;/p&gt;
&lt;p&gt;Scroll to the bottom of the post and you should see a section to add a comment like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/DisqusCommentInBlog.png" alt="DisqusCommentInBlog.png"&gt;&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Having comments is an essential part of any blog to open up the communication with your readers. As you can see Disqus is very easy to add to your blog.&lt;/p&gt;
&lt;p&gt;In the next lesson, where you will learn how to create additional pages that are not blog post such as portfolio or about pages.&lt;/p&gt;</description></item><item><title>Jekyll Part 02: Your First Post</title><link>https://digitaldrummerj.me/blogging-on-github-part-2-your-first-post/</link><pubDate>Thu, 05 Feb 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-2-your-first-post/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome the continuing series on using Jekyll. In this tutorial we will go through making your first blog post.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;We will go through all of the steps to create a new post, add metadata such as title/categories/tags/date, and then make it live on the site.&lt;/p&gt;
&lt;h2 id="section-1-creating-the-file"&gt;Section 1: Creating the file&lt;/h2&gt;
&lt;p&gt;Thr first step is to create a new file to hold the content of the blog post. In Jekyll all of the blog post are markdown files and are stored in the _post directory. When you commit this file to Github, it will be compiled into a static html page.&lt;/p&gt;
&lt;p&gt;The filename is in the format of year-month-day-title-separated-by-dashes.md.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2015-02-17-my-first-post.md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Go ahead and create your file now.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To create a file open a web browser and navigate to your [username.github.io repository.&lt;/li&gt;
&lt;li&gt;Go into the _post directory and click the + icon to add a new file.&lt;/li&gt;
&lt;li&gt;Name the file yyyy-mm-dd-my-first-post.md where yyyy = 4digit year, mm = 2 digit month, and dd = 2 digit day of montmonth.&lt;/li&gt;
&lt;li&gt;Head to the next section and fill out the metadata about the post (ie: title, date, is published, categories, etc)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="section-2-creating-metadata"&gt;Section 2: Creating metadata&lt;/h2&gt;
&lt;p&gt;Now we need to define some information about our blog post. All of the information such as title, is published, categories, publish date, etc are stored at the top of the file in what is called Front Matter.&lt;/p&gt;
&lt;p&gt;To define the front matter section you have a line with 3 dashes in it and then repeat this same line to signal the end of the front matter section.&lt;/p&gt;
&lt;p&gt;Below are common metadata you will want to fill out.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;layout: is the name of the layout from the _layouts directory. If followed part 1 and cloned the jekyll-now repository, then the name is post.&lt;/li&gt;
&lt;li&gt;title: the title of the blog post that will showbon the site. Should be in quotes to avoid conflict with front matter parameter.&lt;/li&gt;
&lt;li&gt;or false. Determines if the post shows on the website or not.&lt;/li&gt;
&lt;li&gt;published: date of the post. this is optional and if not defined will use date in filename.&lt;/li&gt;
&lt;li&gt;categories: list of categories. comma delimited. put in quotes for multi word categories. optional but recommended.&lt;/li&gt;
&lt;li&gt;tags: list of tags that would be used to build a tag cloud. optional but recommended.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="sample-front-matter"&gt;Sample Front Matter&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;---
layout: post
title: Your First Post
draft: true
published: 2015-02-01
categories: [blogging]
tags: [blogging]
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now on to creating actual content in your post.&lt;/p&gt;
&lt;h2 id="section-3--creating-content"&gt;Section 3: Creating content&lt;/h2&gt;
&lt;p&gt;The content of the post will be written in markdown and will be directly below the front matter section .&lt;/p&gt;
&lt;p&gt;Below are common markdown tags that you will want to use. headers, lists, bold, bullets, links and code highlighting.&lt;/p&gt;
&lt;h3 id="common-markdown-tags"&gt;Common Markdown Tags&lt;/h3&gt;
&lt;h4 id="headers"&gt;Headers&lt;/h4&gt;
&lt;p&gt;A &amp;lsquo;#&amp;rsquo; starting a line indicates to makebitba header and the number &amp;lsquo;#&amp;rsquo; indicates the size of the header tag.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# = h1
## = h2
and so on up the an h6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;h1 id="h1"&gt;h1&lt;/h1&gt;
&lt;h2 id="h2"&gt;h2&lt;/h2&gt;
&lt;h2 id="h3"&gt;h3&lt;/h2&gt;
&lt;h3 id="h4"&gt;h4&lt;/h3&gt;
&lt;h4 id="h5"&gt;h5&lt;/h4&gt;
&lt;h4 id="h6"&gt;h6&lt;/h4&gt;
&lt;h4 id="numbered-list--or-bullets"&gt;Numbered list or Bullets&lt;/h4&gt;
&lt;p&gt;For bullets: start a line with a * or -.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;* sample list
* next bullet
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;sample list&lt;/li&gt;
&lt;li&gt;next bullet&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For numbered list start with a 1 or the number you want to start with.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. sample ordered list
2. number 2
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;sample ordered list&lt;/li&gt;
&lt;li&gt;number 2&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="bold"&gt;Bold&lt;/h4&gt;
&lt;p&gt;surround the text you to build with 2 ** and then put 2 more after the text to end the bold.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;**sample bold**
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;sample bold&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="links"&gt;Links&lt;/h4&gt;
&lt;p&gt;link to other pages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[link name display](http://myurl.com)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href="http://myurl.com" target="_blank" &gt;link name display&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="code-highlighting"&gt;Code Highlighting&lt;/h3&gt;
&lt;p&gt;There are 2 ways: tab the line in and it will group the text like the examples above.
or use the highlighter markup&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{% highlight csharp %}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// some c# code
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;var a = &amp;#34;bad variable name&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{% endhighlight %}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="highlighting-text-without-the-code-highlighting"&gt;Highlighting text without the code highlighting&lt;/h2&gt;
&lt;p&gt;Start the line with a tab and it will automatically do it for you.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;you will get text like this if you start the line with a tab.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="further-reading-on-markdown"&gt;Further Reading on Markdown&lt;/h2&gt;
&lt;p&gt;Github documentation on their markdown: &lt;a href="https://help.github.com/articles/github-flavored-markdown/" target="_blank" &gt;https://help.github.com/articles/github-flavored-markdown/&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="section-4--saving-as-draft"&gt;Section 4: Saving as draft&lt;/h2&gt;
&lt;p&gt;Most of the time you are not going to write and publish the blog post in one sitting but you need to save your work without it showing up in the website. This is called saving a draft and it is super simple to do. By setting the front matter published tag to false it will tell jekyll to not publish it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
other front matter tags
draft: true
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also create a drafts folder and save the file in the but if you do that then you will need to move the file to the _post folder when you are ready to publish.&lt;/p&gt;
&lt;h2 id="section--5-publishing-post"&gt;Section 5: Publishing post&lt;/h2&gt;
&lt;p&gt;Publishing a post is as simple as changing the front matter published to true and committing the change to the github repository.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
other front matter tags
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Github will take care of the conversion from markdown to an html page. This should happen automatically within a minute and show up on the blog home page at the top.&lt;/p&gt;
&lt;p&gt;If it doesn&amp;rsquo;t show up then check your email that you registered on github with to see if there was a jekyll compile error email.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This lesson is one of the key lessons as you need to kow how to create new blog post. Creating new blog post is really easy with Jekyll and a little bit of markdown. Once you learn the different markdown tags, you will be writing blog post in no time at all.&lt;/p&gt;
&lt;p&gt;See you in the next lesson, where we will discuss how to add the ability to comment on blog post.&lt;/p&gt;</description></item><item><title>IIS Express - Turning on Windows Authentication</title><link>https://digitaldrummerj.me/iis-express-windows-authentication/</link><pubDate>Wed, 04 Feb 2015 20:45:35 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/iis-express-windows-authentication/</guid><category>iis</category><description>&lt;p&gt;So I brought up a new machine and tried to run my ASP.NET web site in IIS Express that uses Windows Authentication and was greeted with the following error:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Error&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The requested page cannot be accessed because the related configuration data for the page is invalid.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Details:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault=&amp;ldquo;Deny&amp;rdquo;), or set explicitly by a location tag with overrideMode=&amp;ldquo;Deny&amp;rdquo; or the legacy allowOverride=&amp;ldquo;false&amp;rdquo;.&lt;/p&gt;
&lt;h2 id="solution"&gt;Solution&lt;/h2&gt;
&lt;p&gt;Every time I bring up a new machine I always forget to update the IIS Express setting to fix this error and have to do a google search to figure out where the IIS Express configuration is stored. So I figured I should finally document the fix for it.&lt;/p&gt;
&lt;p&gt;The error is caused by this section in the web.config&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;system.webServer&amp;gt;
&amp;lt;security&amp;gt;
&amp;lt;authentication&amp;gt;
&amp;lt;windowsAuthentication enabled=&amp;quot;true&amp;quot; /&amp;gt;
&amp;lt;anonymousAuthentication enabled=&amp;quot;false&amp;quot; /&amp;gt;
&amp;lt;/authentication&amp;gt;
&amp;lt;/security&amp;gt;
&amp;lt;/system.webServer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To fix this open up the IIS Express applicationhost.config. This file is stored at C:\Users[your user name]\Documents\IISExpress\config\applicationhost.config&lt;/p&gt;
&lt;p&gt;Update for VS2015+: config file location is $(solutionDir).vs\config\applicationhost.config&lt;/p&gt;
&lt;p&gt;Look for the following lines&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;section name=&amp;quot;windowsAuthentication&amp;quot; overrideModeDefault=&amp;quot;Deny&amp;quot; /&amp;gt;
&amp;lt;section name=&amp;quot;anonymousAuthentication&amp;quot; overrideModeDefault=&amp;quot;Deny&amp;quot; /&amp;gt;
&amp;lt;add name=&amp;quot;WindowsAuthenticationModule&amp;quot; lockItem=&amp;quot;true&amp;quot; /&amp;gt;
&amp;lt;add name=&amp;quot;AnonymousAuthenticationModule&amp;quot; lockItem=&amp;quot;true&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Change those lines to&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;section name=&amp;quot;windowsAuthentication&amp;quot; overrideModeDefault=&amp;quot;Allow&amp;quot; /&amp;gt;
&amp;lt;section name=&amp;quot;anonymousAuthentication&amp;quot; overrideModeDefault=&amp;quot;Allow&amp;quot; /&amp;gt;
&amp;lt;add name=&amp;quot;WindowsAuthenticationModule&amp;quot; lockItem=&amp;quot;false&amp;quot; /&amp;gt;
&amp;lt;add name=&amp;quot;AnonymousAuthenticationModule&amp;quot; lockItem=&amp;quot;false&amp;quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save the file and refresh your asp.net web page. It may take a moment to load as it load the new configurations into IIS Express.&lt;/p&gt;</description></item><item><title>Jekyll Part 01: Getting Started</title><link>https://digitaldrummerj.me/blogging-on-github-part-1-getting-started/</link><pubDate>Fri, 30 Jan 2015 00:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/blogging-on-github-part-1-getting-started/</guid><category>blogging</category><category>jekyll</category><description>&lt;p&gt;Welcome to the first tutorial of a multi part series on blogging using Jekyll on Github. Github has an awesome free option for hosting a blog for you and you can get a blog up and running in 10 minutes or less. This series will cover everything that you to know to host, manage and customized a Jekyll blog that is hosted on Github.&lt;/p&gt;
&lt;h2 id="section-1-overview"&gt;Section 1: Overview&lt;/h2&gt;
&lt;p&gt;Github uses the Jekyll engine which turns markdown into static Html pages. The advantage of this is that performance is better since you are just serving up html and you don&amp;rsquo;t have to worry about hosting a database somewhere .&lt;/p&gt;
&lt;p&gt;The quickest and easiest way to get started with Jekyll is fork an existing Jekyll repository. Forking in Git means creating a copy of the repository into your account.&lt;/p&gt;
&lt;p&gt;The repository that I used and recommend to fork is the &amp;ldquo;Jekyll Now&amp;rdquo; repository at &lt;a href="https://github.com/barryclark/jekyll-now" target="_blank" &gt;https://github.com/barryclark/jekyll-now&lt;/a&gt;. The Jekyll based repository includes a lot of very useful features for a blog out of the box for you such as: a nice looking theme, code syntax highlighter, social buttons (twitter, Facebook, Github, etc), Disqus blog commenting api, and Google analytics.&lt;/p&gt;
&lt;h2 id="section-2-forking-the-repository"&gt;Section 2: Forking the Repository&lt;/h2&gt;
&lt;p&gt;Forking a repository in Github is extremely simple. Go to &lt;a href="https://github.com/barryclark/jekyll-now" target="_blank" &gt;https://github.com/barryclark/jekyll-now&lt;/a&gt; and click the &lt;img src="/images/BloggingOnGitHub/github_fork_button.png" alt="github fork button"&gt; button. Github will now create a fork of the repository for you into your account.&lt;/p&gt;
&lt;h2 id="section-3-renaming-the-repository"&gt;Section 3: Renaming the Repository&lt;/h2&gt;
&lt;p&gt;Once you fork the repository, you will need to go into the settings for your new repository and rename it to [username].github.io. Your blog will now be live within a few minutes at http://[username].github.io. However, before you go look at the blog, you need to take a minute to configure a few items in the configuration file _config.yml.&lt;/p&gt;
&lt;h3 id="getting-to-settings"&gt;Getting to settings&lt;/h3&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_settings_button.png" alt="github settings"&gt;&lt;/p&gt;
&lt;h3 id="renaming"&gt;Renaming&lt;/h3&gt;
&lt;p&gt;Finding the settings:&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_repo_rename.png" alt="github repo rename"&gt;&lt;/p&gt;
&lt;p&gt;Update the name and click the Rename button&lt;/p&gt;
&lt;p&gt;&lt;img src="/images/BloggingOnGitHub/github_repo_rename_done.png" alt="github repo rename done"&gt;&lt;/p&gt;
&lt;h2 id="section-4-configuring-the-repository"&gt;Section 4: Configuring the Repository&lt;/h2&gt;
&lt;p&gt;There is only 3 values that you are going to want to change:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;name - this is the blog title&lt;/li&gt;
&lt;li&gt;description - this is the tag line&lt;/li&gt;
&lt;li&gt;url - this is the url of the blog, will be [username].github.io.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Optionally, you can also configure your social networks that show up in the footer of the site by filling in the various account names in the &amp;ldquo;footer-links&amp;rdquo; section of the _config.yml file.&lt;/p&gt;
&lt;p&gt;You can edit the _config.yml directly in Github. Click on the file and then click the &lt;img src="/images/BloggingOnGitHub/github_edit_button.png" alt="github edit file button"&gt; icon. Once you have completed your edits, click the green commit button at the bottom of the screen.&lt;/p&gt;
&lt;h2 id="section-5-viewing-the-blog"&gt;Section 5: Viewing the Blog&lt;/h2&gt;
&lt;p&gt;Now you are ready to view your new blog. Open a browser and navigate to http://[username].github.io, replacing [username] with your actual username.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In just a few simple steps, you were able to get your blog up and running. Through out the series of lessons, you will learn how to manage your blog, create new pages, customize the theme, and host it with a custom domain name.&lt;/p&gt;
&lt;p&gt;In the next lesson, where we create your first blog post.&lt;/p&gt;</description></item><item><title>Ionic - Using behind a proxy server</title><link>https://digitaldrummerj.me/ionic-behind-a-proxy/</link><pubDate>Tue, 20 Jan 2015 20:45:35 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ionic-behind-a-proxy/</guid><category>proxy</category><category>ionic</category><description>&lt;p&gt;I ran into an issue today on the vagrant IonicBox when I tried to create a new Ionic project at work behind the firewall/proxy even with all of the configurations for npm, git, bower, and bash setup for the proxy as detailed at &lt;a href="/proxy-configurations/" &gt;proxy-configurations&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Luckily, Ionic had a fix for this already.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;All I had to do was put PROXY=http://myserver:myport in front of the ionic start command.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; PROXY=http://myserver:myport ionic start todo blank
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;All I had to do was set the http_proxy environment variable and then run the ionic start command as I normally would.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ionic start todo blank
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Ionic - The Ionicbox and How To Use It</title><link>https://digitaldrummerj.me/ionicbox-notes/</link><pubDate>Sat, 17 Jan 2015 20:45:35 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ionicbox-notes/</guid><category>ionic</category><category>vagrant</category><category>virtualbox</category><description>&lt;p&gt;If you have looked at setting up the &lt;a href="http://www.ionicframework.com" target="_blank" &gt;Ionic Framework&lt;/a&gt; or have it done it before, you know on much of a pain it can be, especially when something doesn&amp;rsquo;t work. Luckily, Ionic offers a free virtual machine called &lt;a href="https://github.com/driftyco/ionic-box" target="_blank" &gt;Ionicbox&lt;/a&gt; that is already configured with all of the software that you need.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites:&lt;/h2&gt;
&lt;p&gt;Before you can use &lt;a href="https://github.com/driftyco/ionic-box" target="_blank" &gt;Ionicbox&lt;/a&gt; you need to install &lt;a href="http://www.virtualbox.org" target="_blank" &gt;VirtualBox&lt;/a&gt; and &lt;a href="http://www.vagrantup.com" target="_blank" &gt;Vagrant&lt;/a&gt;. If you are on Windows and using &lt;a href="http://www.chocolatey.org" target="_blank" &gt;Chocolatey&lt;/a&gt;, you can install both using cinst virtualbox and cinst vagrant.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.virtualbox.org" target="_blank" &gt;VirtalBox&lt;/a&gt; is a free virtual machine program. A virtual machine is a complete computer and operating system run from within your current operating system. It makes it possible to run Linux on a Windows Machine, Windows on a Mac, etc.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.vagrantup.com" target="_blank" &gt;Vagrant&lt;/a&gt; is an easy way to manage virtual machines. A full explaination is out of scope for this article.&lt;/p&gt;
&lt;h2 id="ionic-box"&gt;Ionic Box&lt;/h2&gt;
&lt;p&gt;Make sure that you installed VirtualBox and Vagrant before proceeding.&lt;/p&gt;
&lt;h2 id="installing"&gt;Installing&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open Windows Explorer and create a directory where you want to hold all of your Vagrant configuration files&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Instead the directory created in step 1, create a directory called IonicBox&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In Windows Explorer, create the directory under the c drive called projects (i.e. c:\projects)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In Windows Explorer, do a shift+right click on the IonicBox directory and select Open Command Prompt&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: vagrant init drifty/ionic-android&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open notepad&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In notepad, open the VagrantFile created in the IonicBox directory.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;select all of the text in the VagrantFile and remove it&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the following text into the VagrantFile&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# -*- mode: ruby -*-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# vi: set ft=ruby :&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;Vagrant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;drifty/ionic-android&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;[Replace with what you want your Host Name to be]&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;# No Spaced Allowed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;boot_timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="ss"&gt;:forwarded_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="ss"&gt;:forwarded_port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35729&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;guest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;35729&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;synced_folder&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;c:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;projects&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/home/vagrant/vagrant_projects&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;virtualbox&amp;#34;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customize&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--vram&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;128&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customize&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--usb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;on&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customize&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;usbfilter&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;add&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;android&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--vendorid&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0x18d1&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2048&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;IonicBox&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Need This If On Windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customize&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;setextradata&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="so-what-do--all-of-those-options-in-the-vagrantfile-mean"&gt;So what do all of those options in the VagrantFile mean?&lt;/h2&gt;
&lt;p&gt;In the configuration we configure the video and physical memory of the virtual machine. Since IonicBox is just a shell without a GUI, it doesn&amp;rsquo;t need a lot of resources, so we only give it 2 gigs of memory. Now if your host system doesn&amp;rsquo;t have a lot of memory, you will need to dial this down.&lt;/p&gt;
&lt;p&gt;Ionic uses port 8100 for the web site and the live reload function use port 35729. We forwarded these ports from IonicBox to the host machine so we can access the web server. The following two lines do the port forwarding&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;forwarded_port&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;8100&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guest&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;8100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;forwarded_port&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;35729&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;guest&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;35729&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The IonicBox is just a Linux shell with no GUI so you will want to use a feature in VirtualBox called Shared Folders to be able to edit the files from your host machine. In our case we are using c:\projects on the host machine which is linked to ~/vagrant_projects on the IonicBox&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;synced_folder&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;c:\\projects&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/home/vagrant/projects&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Next we set up the VirtualBox options&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We don&amp;rsquo;t need the VirtualBox GUI since we are going to SSH into the machine so we can turn it off with. Set this to true the first time, just so you can see it&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gui&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;We set the Video Ram to 128 megs&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customize&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;--vram&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;128&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Turn on the USB drivers so that we can connect an Android device&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customize&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;modifyvm&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;--usb&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;on&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Add a usb device filter for a Android Device&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customize&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;usbfilter&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;add&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;0&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;--target&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;--name&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;android&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;--vendorid&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;0x18d1&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;On Windows, You need to turn on Symlinks to the synced_folders. This is needed if your Host Operating System is Windows in order node/npm to work correctly. Remember you have to run your command prompt as an administrator for this command to work.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customize&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;setextradata&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Set the system memory for the virtual machine. If you host machine is low on memory you can reduce this down. You must have this much memory free when the virtual machine starts up&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2048&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Number of Physical CPUs to allocate. My machine only has 2 physical CPUs. You can allocate more or take it down to 1&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cpus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;The name to use for the virtual machine in the VirtualBox Manager UI&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;IonicBox&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="starting-up-the-ionicbox-and-getting-logged-in"&gt;Starting up the IonicBox and Getting logged in&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a command prompt and navigate to the IonicBox folder that contains the VagrantFile.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;font color="red"&gt;If on Windows, open the command prompt as an Administrator.&lt;/font&gt;&lt;/li&gt;
&lt;li&gt;To open the command prompt as an administrator in Windows 8 go to the Start Menu Screen, type cmd, then ctrl+shift+click or ctrl+shift+enter&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run command below. This command will take a while the first time you run it since it has to download the vagrant box container which is about 1 gig in size.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you have the vb.gui = true in your VagrantFile, the first thing you will notice when you boot up the IonicBox is that it just comes to a command prompt and it leaves you wondering now what. Luckily, this is exactly what we want and it is very easy to manage it. The IonicBox basically just replaces the command prompt that we would normally use for all of the Ionic commands with a linux machine.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To login to the machine, use the same command prompt as previous step and run&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt; &lt;span class="n"&gt;ssh&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;if everything went successful you should be logged in&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="testing-the-ionicbox"&gt;Testing the IonicBox&lt;/h2&gt;
&lt;p&gt;Now lets create our first Ionic project.&lt;/p&gt;
&lt;p&gt;On the IonicBox ssh connection:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;projects&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ionic&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="n"&gt;firstApp&lt;/span&gt; &lt;span class="n"&gt;tabs&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt; &lt;span class="n"&gt;firstApp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ionic&lt;/span&gt; &lt;span class="n"&gt;serve&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You now have a Ionic project in a directory called firstApp and the web server is running.&lt;/p&gt;
&lt;p&gt;Open up your web browser and navigate to http://localhost:8100&lt;/p&gt;
&lt;p&gt;You can view all of the files that make up this project on your host machine under c:\projects\firstApp&lt;/p&gt;
&lt;p&gt;If you are NOT using Windows as your host operating system that we are done with configurations. Unfortunately, if you are using Windows as your host operating system, we have one more step to get npm working correctly, so that you can download all of the dependencies for Ionic.&lt;/p&gt;
&lt;p&gt;&lt;font color="red"&gt;WARNING: Unless you have npm 3.0+. This may not fix the issue with node and long file names on Windows with npm.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;We need to setup a symbolic link for the node modules folder since windows has a length limitation when using shared folders. A symbolic link is basically a point from one directory to another. Windows has a directory name length limitation that we encounter when host our files through a shared folder. Since our npm dependencies (node modules) folder doesn&amp;rsquo;t need to be checked into source control, we can move it to a directory on the IonicBox and just point to that from within our Ionic projects.&lt;/p&gt;
&lt;p&gt;On the IonicBox from the firstApp folder run the following commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt; &lt;span class="err"&gt;~&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;node_modules_&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Your&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ln&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="err"&gt;~&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;node_modules_&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Your&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="n"&gt;node_modules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;npm&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;font color="red"&gt;Remember:&lt;/font&gt;&lt;/strong&gt; If on Windows, you need to run the vagrant up command from an administrative command prompt.&lt;/p&gt;
&lt;p&gt;You may also need to manually install bower&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;npm&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;bower&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="i-am-done-with-ionicbox-now-what"&gt;I am done with IonicBox, now what?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;To exit vagrant ssh session, just type exit from the command prompt that is logged into the IonicBox. This will put you back into the original command prompt.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hibernate: from the IonicBox directory with the VagrantFile run&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt; &lt;span class="n"&gt;suspend&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Shutdown and Turn Off: from the IonicBox directory with the VagrantFile run&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt; &lt;span class="n"&gt;halt&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Delete the whole IonicBox Virtual Machine: from the IonicBox directory with the VagrantFile run&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-io" data-lang="io"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vagrant&lt;/span&gt; &lt;span class="n"&gt;destroy&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Note that sometimes this leaves behind the directory that contained the Virtual Machine. Before you can run vagrant up again, you will need to manually delete this directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This is a nice easy way to get started with ionic without having to install much onto your current machine.&lt;/p&gt;
&lt;p&gt;However, if you are going to spend a lot of time developing ionic applications, it won&amp;rsquo;t be long before you go down the route of installing everything onto your machine to do the development work.&lt;/p&gt;
&lt;p&gt;Check out my installation guides to help you out:&lt;/p&gt;
&lt;p&gt;[Mac Installation]({{ &amp;ldquo;ionic-setup-osx&amp;rdquo; | prepend: site.baseurl | prepend: site.url }})&lt;/p&gt;
&lt;p&gt;[Windows Installation]({{ &amp;ldquo;Ionic-Setup-Windows&amp;rdquo; | prepend: site.baseurl | prepend: site.url }})&lt;/p&gt;</description></item><item><title>Npm, Bower, Git, and Bash Proxy Configurations</title><link>https://digitaldrummerj.me/proxy-configurations/</link><pubDate>Fri, 16 Jan 2015 20:00:00 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/proxy-configurations/</guid><category>proxy</category><category>node</category><category>npm</category><category>bash</category><description>&lt;p&gt;When you are using npm, bower, and git behind a proxy server you have to do a little bit of configuration. Luckily it is super easy to do these configurations. Almost all of the programs have command line commands to set and unset the proxy server.&lt;/p&gt;
&lt;h2 id="updates"&gt;Updates:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Updated 2015-Feb-01&lt;/strong&gt;: Added running source command for Bash and Ruby Gems section&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Updated 2015-May-07&lt;/strong&gt;: Added the Ionic Start command&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Updated 2015-May-08&lt;/strong&gt;: Added the Android SDK&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Updated 2015-Aug-03&lt;/strong&gt;: Added command lines to set proxy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Updated 2015-Oct-20&lt;/strong&gt;: Added Gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="windows-command-prompt"&gt;Windows Command Prompt&lt;/h2&gt;
&lt;h3 id="current-command-prompt-only"&gt;Current Command Prompt Only&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;set http_proxy=[Your Proxy]:[Proxy Port]
set https_proxy=[Your Proxy]:[Proxy Port]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="unset-current-session"&gt;Unset Current Session&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;set http_proxy=
set https_proxy=
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="globally-as-a-system-environment-variable"&gt;Globally as a System Environment Variable&lt;/h3&gt;
&lt;p&gt;Run from an administrative command prompt&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setx http_proxy=[Your Proxy]:[Proxy Port] /M
setx https_proxy=[Your Proxy]:[Proxy Port] /M
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will need to close and re-open command prompt for settings to take effect&lt;/p&gt;
&lt;h3 id="globally-as-a-user-environment-variable"&gt;Globally as a User Environment Variable&lt;/h3&gt;
&lt;p&gt;Run from a non-administrative command prompt&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setx http_proxy=[Your Proxy]:[Proxy Port]
setx https_proxy=[Your Proxy]:[Proxy Port]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will need to close and re-open command prompt for settings to take effect&lt;/p&gt;
&lt;h3 id="unset-globally-system-environment-variable"&gt;Unset Globally System Environment Variable&lt;/h3&gt;
&lt;p&gt;Run from an administrative command prompt&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setx http_proxy=&amp;quot;&amp;quot; /M
setx https_proxy=&amp;quot;&amp;quot; /M
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Need to close and re-open command prompt for settings to take effect&lt;/p&gt;
&lt;h3 id="unset-globally-user-environment-variable"&gt;Unset Globally User Environment Variable&lt;/h3&gt;
&lt;p&gt;Run from a non-administrative command prompt&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setx http_proxy=&amp;quot;&amp;quot;
setx https_proxy=&amp;quot;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Need to close and re-open command prompt for settings to take effect&lt;/p&gt;
&lt;h3 id="view-proxy-settings"&gt;View Proxy Settings&lt;/h3&gt;
&lt;p&gt;If the commands below just echo out the text instead of the actual proxy server, it means that the proxy server is not set.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo %http_proxy%
echo %https_proxy%
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="bash-shell"&gt;Bash Shell&lt;/h2&gt;
&lt;p&gt;File Name: .bash_profile or .bashrc&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export http_proxy=[Your Proxy]:[Proxy Port]
export https_proxy=[Your Proxy]:[Proxy Port]
export npm_config_proxy=[Your Proxy]:[Proxy Port]
export npm_config_https_proxy=[Your Proxy]:[Proxy Port]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: After updated the .bash_profile or .bashrc, you should run one of the following commands to make the configuration active for the current session.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source ~/.bashrc
or
source ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="bower"&gt;Bower&lt;/h2&gt;
&lt;p&gt;There is no command line that I found for configuring bower. Instead you need to create a .bowerrc file in the users home directory.&lt;/p&gt;
&lt;p&gt;On Windows: %userprofile% directory.&lt;/p&gt;
&lt;p&gt;On Linux: ~/&lt;/p&gt;
&lt;h3 id="creating-bowerrc-file-on-windows"&gt;Creating .bowerrc file on Windows&lt;/h3&gt;
&lt;p&gt;Windows Explorer unfortunately does not allow you to create files without extensions but using notepad you can create a file without an extension.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Notepad&lt;/li&gt;
&lt;li&gt;Ctrl + S to save the file&lt;/li&gt;
&lt;li&gt;Navigate to the %UserProfile% directory&lt;/li&gt;
&lt;li&gt;Change the &amp;ldquo;Save as Type&amp;rdquo; to &amp;ldquo;All Files (&lt;em&gt;.&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Name the file .bowerrc&lt;/li&gt;
&lt;li&gt;Click the Save button&lt;/li&gt;
&lt;li&gt;Now you can edit the file in your text editor of choice&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="proxy-setting-in-bowerrc"&gt;Proxy Setting in .bowerrc.&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;{
&amp;quot;proxy&amp;quot;:&amp;quot;http://[Your Proxy]:[Proxy Port]&amp;quot;,
&amp;quot;https-proxy&amp;quot;:&amp;quot;http://[Your Proxy]:[Proxy Port]&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="git"&gt;Git&lt;/h2&gt;
&lt;p&gt;You can also set the proxy settings below to be system wide with the &amp;ndash;system switch.&lt;/p&gt;
&lt;h3 id="set-proxy"&gt;Set Proxy:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git config --add http.proxy http://[Your Proxy]:[Proxy Port]
git config --add https.proxy http://[Your Proxy]:[Proxy Port]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="unset-proxy"&gt;Unset Proxy:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git config --unset http.proxy
git config --unset https.proxy
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="view-configuration"&gt;View Configuration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Just Proxy Configs&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --get http.proxy
git config --get https.proxy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;All Configs&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --list
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="manually-update-gitconfig-not-recommended"&gt;Manually Update .gitconfig (not recommended)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;[http]
proxy = http://[Your Proxy]:[Proxy Port]
[https]
proxy = http://[Your Proxy]:[Proxy Port]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="npm"&gt;NPM&lt;/h2&gt;
&lt;h3 id="set-proxy-1"&gt;Set Proxy:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm config set https-proxy http://[Your Proxy]:[Proxy Port]
npm config set proxy http://[Your Proxy]:[Proxy Port]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="unset-proxy-1"&gt;Unset Proxy:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm config delete https-proxy
npm config delete proxy
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="view-proxy-configurations"&gt;View Proxy Configurations:&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm config get https-proxy
npm config get proxy
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="manually-update-npmrc-not-recommended"&gt;Manually Update .npmrc (not recommended)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;proxy=http://[Your Proxy]:[Proxy Port]
https-proxy=http://[Your Proxy]:[Proxy Port]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="ruby-gem-install"&gt;Ruby Gem Install&lt;/h2&gt;
&lt;p&gt;If you have set the proxy in the .bash_profile or .bashrc, then Ruby should pick it up.&lt;/p&gt;
&lt;p&gt;If you need to manually set it&lt;/p&gt;
&lt;h3 id="linux"&gt;Linux&lt;/h3&gt;
&lt;p&gt;export http_proxy=[Your Proxy]:[Proxy Port]
sudo gem install [your gem name]&lt;/p&gt;
&lt;h3 id="windows"&gt;Windows&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;setx http_proxy &amp;quot;[Your Proxy Server]:[Proxy Port]&amp;quot; /M
gem install [your gem name]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="ionic-start-command"&gt;Ionic Start Command&lt;/h2&gt;
&lt;p&gt;In order to run the ionic start command behind a proxy, you need start the command out with the Proxy information.&lt;/p&gt;
&lt;h3 id="linux-1"&gt;Linux&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;PROXY=http://[Your Proxy]:[Proxy Port] ionic start [App Name] [Template Name]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="windows-1"&gt;Windows&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;setx http_proxy &amp;quot;[Your Proxy Server]:[Proxy Port]&amp;quot; /M
ionic start [App Name] [Template Name]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="android-sdk"&gt;Android SDK&lt;/h2&gt;
&lt;p&gt;The android SDK uses ~/.android/androidtool.cfg file to define the proxy information. If the file does not exist, go ahead and create it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http.proxyHost=[Your Proxy]
http.proxyPort=[Proxy Port]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id="gradle"&gt;Gradle&lt;/h2&gt;
&lt;p&gt;When trying to build an Android project that uses Gradle, you may need to configure the proxy for it.&lt;/p&gt;
&lt;p&gt;On Windows: %userprofile%/.gradle.properties&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemProp.http.proxyHost=[Your Proxy]
systemProp.http.proxyPort=[Proxy Port]
systemProp.http.nonProxyHosts=*.nonproxyrepos.com|localhost
systemProp.https.proxyHost=[Your Https Proxy]
systemProp.https.proxyPort=[Https Proxy Port]
systemProp.https.nonProxyHosts=*.nonproxyrepos.com|localhost
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Git Command Notes</title><link>https://digitaldrummerj.me/git-command-notes/</link><pubDate>Wed, 14 Jan 2015 20:45:35 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/git-command-notes/</guid><category>github</category><category>git</category><description>&lt;p&gt;I am finally making myself learn the git command line instead of just using a UI so that I can actually understand what git is really doing. Plus I have started playing a lot with the IonicBox and running a Ubuntu vagrant controlled VM for this blog and both of those are just linux shell command prompt only machines.&lt;/p&gt;
&lt;p&gt;Below are my notes on various commands so that I can stop having to Google each time I forgot one of them.&lt;/p&gt;
&lt;h2 id="caching-credentials"&gt;Caching Credentials&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;To cache credentials for 1 hour. The timeout is in seconds.&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --global credential.helper 'cache --timeout 3600'
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="basic-commands"&gt;Basic Commands&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Getting code to local machine&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In Git terms, this is called cloning a repository and the command to use is git clone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Directory name will match repository name&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone [Remote Repository Url]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Use this command if you want to name the directory different than the repository name&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone [Remote Repository Url] [Directory to Clone into]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Where did the local repository come from?&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Change the Remote Origin Url&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git remote set-url origin [https or ssh url]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Get Latest Changes but don&amp;rsquo;t merge&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git fetch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Get Latest Changes and Merge&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git pull
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Generate SSH Keys&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adding Key&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ls -al ~/.ssh
ssh-keygen -t rsa -C &amp;quot;your_email@example.com&amp;quot;
ssh-agent -s or eval(ssh-agent) or eval $(ssh-agent)
ssh-add ~/.ssh/id_rsa
clip &amp;lt; ~/.ssh/id_rsa.pub
Go to github settings for your account
Click on SSH Keys
Click Add Key
Give it a name and paste in the key
Put in your github password
Click confirm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;To test&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Should get a response like: Hi username! You&amp;rsquo;ve successfully authenticated, but GitHub does not provide shell access.&lt;/p&gt;
&lt;p&gt;Full details at &lt;a href="https://help.github.com/articles/generating-ssh-keys/" target="_blank" &gt;https://help.github.com/articles/generating-ssh-keys/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;See what files have changed&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Long Form: &lt;code&gt;git status&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Short Form: &lt;code&gt;git status -s&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Show Files: &lt;code&gt;git status -u&lt;/code&gt; or to always show &lt;code&gt;git config status.showuntrackedfile=yes&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Turning a directory into a repository on the local machine&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Adding files to the repository&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add [file name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Adding all files in a directory except the ones in the .gitignore file&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git add .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Reverting a file that has changes not been added or staged&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout [file name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Unadding a file that has been added and not committed but leave file&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Unadding a file that has been added and not committed but delete file&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git reset --hard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Ignoring files&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create .gitignore file&lt;/li&gt;
&lt;li&gt;starter files available at &lt;a href="https://github.com/github/gitignore" target="_blank" &gt;https://github.com/github/gitignore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create a .gitignore file for any number of languages at &lt;a href="https://www.toptal.com/developers/gitignore/" target="_blank" &gt;https://www.toptal.com/developers/gitignore/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Committing files to the local repository&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git commit -m &amp;quot;Your Message&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Removing Files&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git rm [File Name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Moving Files&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git mv [Old File Name] [New File Name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Publishing files to the remote repository&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="merge"&gt;Merge&lt;/h2&gt;
&lt;h4 id="storing-work-that-you-want-to-keep-but-not-commit"&gt;Storing work that you want to keep but not commit&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Storing the work&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git stash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Seeing what work is stored&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git stash list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Applying the last stash to the current code&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git stash apply
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Applying a different stash then the last one to the current code&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git stash apply stash@{[Number for stash from git stash list command]}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="branching"&gt;Branching&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;See Available Branch Including Remote Branches&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Creating a Branch&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch [Branch Name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Switch to a branch&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout [Branch Name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Switch to last branch you were on&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Merging a branch into the master (HEAD)&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;First make sure you are on the master branch: git checkout master
Then issue the merge command: git merge [branch name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;List the branches for the repository&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Note: the * in the results indicates the branch currently checked out.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;See last commit of each branch&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;See branches already merged&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch --merged
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;See branches NOT already merged&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch --no-merged
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Push branch to remote repository&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push origin [branch name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Delete a Local branch&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git branch -d [branch name]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Deleting Remote Branch&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push origin --delete [branch name]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="proxy-settings"&gt;Proxy Settings&lt;/h2&gt;
&lt;p&gt;See Post on &lt;a href="/proxy-configurations" &gt;Proxy Configurations&lt;/a&gt; for configuring your proxy settings with Git&lt;/p&gt;
&lt;h2 id="configurations"&gt;Configurations&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Listing of Configurations:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config -l
git config -l --global
git config -l --system
git config -l --local
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Set Configuration&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --add [variable name] [value]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Unset Configuration&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --unset [variable name]
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Ionic - How to setup on Windows</title><link>https://digitaldrummerj.me/ionic-setup-windows/</link><pubDate>Sun, 11 Jan 2015 20:45:35 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ionic-setup-windows/</guid><category>ionic</category><description>&lt;p&gt;Updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2016-08-13: Added Gradle and VS Code to software installed. Changed from JDK7 to JDK8. Removed Ant. Added Android SDK Apis install to Chocolatey script. Switched suggested emulator to Visual Studio Emulator for Android.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are like me and just starting to work with the &lt;a href="http://www.ionicframework.com" target="_blank" &gt;Ionic Framework&lt;/a&gt; and don&amp;rsquo;t already have a machine setup to do Android, iOS, Node, etc development then many of the guides out there leave out a number of steps that you need to do in order to get everything working.&lt;/p&gt;
&lt;p&gt;It is really easy to get everything working though once you know the steps. Since I am a Windows user and love to automate work that is easily repeatable, I used &lt;a href="http://www.chocolatey.org" target="_blank" &gt;Chocolatey&lt;/a&gt; and &lt;a href="http://www.boxstarter.org" target="_blank" &gt;Boxstarter&lt;/a&gt; to automate the setup for the Ionic Framework.&lt;/p&gt;
&lt;p&gt;On Windows, you will only be able to setup Android development. Apple requires a Mac in order to do iOS development.&lt;/p&gt;
&lt;h2 id="software-to-be-installed"&gt;Software to be installed&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/nodejs.install" target="_blank" &gt;NodeJS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/git" target="_blank" &gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/gradle" target="_blank" &gt;Gradle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/jdk8" target="_blank" &gt;JDK8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/android-sdk" target="_blank" &gt;Android SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/AndroidStudio" target="_blank" &gt;Android Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chocolatey.org/packages/GoogleChrome" target="_blank" &gt;Google Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Npm Modules:
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/cordova" target="_blank" &gt;cordova&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/gulp-cli" target="_blank" &gt;gulp cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/bower" target="_blank" &gt;bower&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/ionic" target="_blank" &gt;ionic&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.visualstudio.com/en-us/features/msft-android-emulator-vs.aspx" target="_blank" &gt;Visual Studio Emulator for Android (Hyper-V Based)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/" target="_blank" &gt;VS Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="how-to-install-software"&gt;How to install software&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install Chocolatey from &lt;a href="http://www.chocolatey.org" target="_blank" &gt;http://www.chocolatey.org&lt;/a&gt;. Command is on the front-page of the site or below. Open an administrative command prompt to run the command. To open an administrative command prompt on Windows 8, go to the start menu, type cmd and then ctrl+shift+click on the cmd search result.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; Command Prompt: @powershell -NoProfile -ExecutionPolicy unrestricted -Command &amp;quot;iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))&amp;quot; &amp;amp;&amp;amp; SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the &lt;a href="http://boxstarter.org" target="_blank" &gt;BoxStarter&lt;/a&gt; Chocolatey package&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; choco install -y BoxStarter
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Close the command prompt that you opened to install Chocolatey and BoxStarter&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the desktop there should be a BoxStarter Shell icon, double-click on that to run it. If the icon is not on the desktop, then open up a command prompt and type BoxStarterShell.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I have setup a gist file that has all of the Chocolatey commands to run to install the rest of the software and configure it. Run the gist file from the Boxstarter Shell:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; Install-BoxStarterPackage -PackageName https://gist.githubusercontent.com/digitaldrummerj/3fe2eb057004b6742b89/raw/3da48d349c313684077d7103547dfe79f7052617/IonicSetup -DisableReboots
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; If you want to install any of the optional software you will need to fork the gist file and remove the # in front of the line for the package you want to install.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="post-install-steps"&gt;Post Install Steps&lt;/h2&gt;
&lt;h3 id="configure-visual-studio-emulator-for-android"&gt;Configure Visual Studio Emulator for Android&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Open up the Visual Studio Emulator for Android application&lt;/li&gt;
&lt;li&gt;Find the devices that you want to download. Any device will do. I normally pick one of the latest ones as a starting point (Marshmellow 6.0 at the time of this writing)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="verify-that-everything-works"&gt;Verify that everything works&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Open a command prompt&lt;/li&gt;
&lt;li&gt;Navigate the directory where you store you development projects (I use c:\projects)&lt;/li&gt;
&lt;li&gt;From c:\projects type: ionic start todo tabs&lt;/li&gt;
&lt;li&gt;cd into c:\projects\todo (directory was created by the ionic start command)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first test that we are going to run is to make sure that we can test the todo app that we generated in the web browser by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic serve --lab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will start up a node based web server and the &amp;ndash;lab will tell it to launch a page that shows what the app would look like on an iOS and Android phone. Granted the node based serve is about 80% accurate but good enough to do a majority of our testing. Ultimately you should test on a device before releasing into the app stores.&lt;/p&gt;
&lt;p&gt;Next we are going to test our Android device setup. The first thing we need to do is tell ionic that we want to add the Android platform to our todo app by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ionic platform add android
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This sets up the todo app to be able to be build and deployed to an Android device. To validate that we can build for Android, run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ionic build android
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last thing we need to verify is that we can deploy the todo app to the Visual Studio Emulator for Android. Before we can deploy the application, we need to start up the emulator.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up the Visual Studio Emulator for Android&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Find the device that we downloaded&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the green arrow to start it up&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the emulator is started, you can deploy to it by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic run android
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are now ready to go create your ionic applications.&lt;/p&gt;</description></item><item><title>Ionic - Setup on OSx</title><link>https://digitaldrummerj.me/ionic-setup-osx/</link><pubDate>Sun, 11 Jan 2015 20:45:35 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/ionic-setup-osx/</guid><category>ionic</category><description>&lt;p&gt;Updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2016-08-13: Added Gradle and VS Code. Changed from JDK7 to JDK8. Removed Ant.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are like me and just starting to work with the Ionic Framework and don’t already have a machine setup to do Android, iOS, Node, etc development then many of the guides out there leave out a number of steps that you need to do in order to get everything working.&lt;/p&gt;
&lt;p&gt;Even being a Windows user I was able to pretty easily get Ionic working on a Mac. The only difference between the Windows setup and the OSx setup is that you can build for an iOS device on a Mac.&lt;/p&gt;
&lt;h2 id="software-to-be-installed"&gt;Software to be installed&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org" target="_blank" &gt;NodeJS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.gradle.org/current/userguide/installation.html" target="_blank" &gt;Gradle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_blank" &gt;JDK8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.android.com/sdk/index.html#Other" target="_blank" &gt;Android SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.android.com/sdk/index.html#Other" target="_blank" &gt;Android Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.google.com/chrome/browser/desktop/" target="_blank" &gt;Google Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Global Npm Modules:
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/cordova" target="_blank" &gt;cordova&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/gulp-cli" target="_blank" &gt;gulp cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/bower" target="_blank" &gt;bower&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/ionic" target="_blank" &gt;ionic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/ios-sim" target="_blank" &gt;ios-sim&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.genymotion.com/" target="_blank" &gt;Genymotion&lt;/a&gt; (Android emulator replacement)
`- XCode&lt;/li&gt;
&lt;li&gt;XCode Command Line Tools&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="general-install-steps"&gt;General Install Steps&lt;/h2&gt;
&lt;h3 id="google-chrome"&gt;Google Chrome&lt;/h3&gt;
&lt;p&gt;We need Google Chrome in order to debug our application when it is running on a device. The device emulation and developer tools are also extremely useful to have.&lt;/p&gt;
&lt;p&gt;Download from &lt;a href="https://www.google.com/chrome/browser/desktop/" target="_blank" &gt;https://www.google.com/chrome/browser/desktop/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="node"&gt;Node&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Install the LTS version of node from &lt;a href="https://www.nodejs.org" target="_blank" &gt;https://www.nodejs.org&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="npm-packages"&gt;Npm Packages&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and run the following commands to install the Global NPM packages that we need:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; sudo npm install -g cordova
sudo npm install -g ionic
sudo npm install -g gulp
sudo npm install -g Bower
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, we can create ionic projects and test them in a web browser. To make sure that functionality is working:&lt;/p&gt;
&lt;h3 id="verifying-general-install"&gt;Verifying General Install&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In your open terminal, navigate to the directory where you store your development projects (I use ~/projects)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From ~/projects run:&lt;/p&gt;
&lt;p&gt;ionic start todo blank&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into todo (directory was created by the ionic start command)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to run the todo app we generated in a web browser&lt;/p&gt;
&lt;p&gt;ionic serve &amp;ndash;lab&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the next section, we will install everything needed to deploy to an Android device.&lt;/p&gt;
&lt;h2 id="android-setup-steps"&gt;Android Setup Steps&lt;/h2&gt;
&lt;p&gt;In order to deploy to an Android device you need to install the Java JDK, Android Studio, and Android SDK. We will walk through installing all of the required software setup.&lt;/p&gt;
&lt;h3 id="jdk-8"&gt;JDK 8&lt;/h3&gt;
&lt;p&gt;Download from &lt;a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_blank" &gt;http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After the install, launch a terminal and create a ~/.bash_profile if it doesn&amp;rsquo;t already exist. You can use touch ~/.bash_profile to create the file.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open up either vi or nano and add the following line:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_72.jdk/Contents/Home&lt;/p&gt;
&lt;h3 id="android-studio"&gt;Android Studio&lt;/h3&gt;
&lt;p&gt;Download from &lt;a href="http://developer.android.com/sdk/index.html#Other" target="_blank" &gt;http://developer.android.com/sdk/index.html#Other&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="android-sdk"&gt;Android SDK&lt;/h3&gt;
&lt;p&gt;Download the Android SDK from [http://developer.android.com/sdk/index.html#Other](&lt;a href="http://developer.android.com/sdk/index.html#Other" target="_blank" &gt;http://developer.android.com/sdk/index.html#Other&lt;/a&gt;_&lt;/p&gt;
&lt;p&gt;Unzip the downloaded SDK to to ~/Development&lt;/p&gt;
&lt;p&gt;Open ~/.bash_profile and add the following line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export PATH=${PATH}:/users/[Your UserName]/Development/android-sdk-macosx/tools:/users/[Your Username]/Development/android-sdk-macosx/platform-tools
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the terminal type source ~/.bash_profile to load the ~/.bash_profile changes.&lt;/p&gt;
&lt;p&gt;Now we need to download the Android APIs versions that we care about. In this case, we are only going to install the API 23 Android SDK Tools and Android SDK Build-Tools&lt;/p&gt;
&lt;p&gt;In the terminal window, type android to launch the Android SDK Manager.&lt;/p&gt;
&lt;p&gt;Install the the following&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tools -&amp;gt; Android SDK Tools&lt;/li&gt;
&lt;li&gt;Tools -&amp;gt; Android SDK Build-Tools&lt;/li&gt;
&lt;li&gt;Tools -&amp;gt; Android Platform-Tools&lt;/li&gt;
&lt;li&gt;API 23 -&amp;gt; SDK Platform&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="genymotion-setup"&gt;Genymotion Setup&lt;/h3&gt;
&lt;p&gt;Download Genymotion from &lt;a href="https://www.genymotion.com/" target="_blank" &gt;https://www.genymotion.com/&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Genymotion installer will also install VirtualBox if it is not already installed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once Genymotion is installed, we need to download a device.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open up the Genymotion UI and click on the Add Button.&lt;/li&gt;
&lt;li&gt;Then click the Sign in button and follow the login instructions to login with the account that you create as part of the Genymotion download.&lt;/li&gt;
&lt;li&gt;After you are logged in, select from the Android Version drop down 4.4.4&lt;/li&gt;
&lt;li&gt;From the Device model drop down select a device type&lt;/li&gt;
&lt;li&gt;Then select a device from the available list&lt;/li&gt;
&lt;li&gt;Click the Next button.&lt;/li&gt;
&lt;li&gt;Click the Next button and wait for the device to download&lt;/li&gt;
&lt;li&gt;Click the Finish button.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="verify-the-android-setup"&gt;Verify the Android Setup&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the todo app that we create as part of the general install verification. (I used ~/projects/todo)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you need to ceate the todo app run the following from ~/projects:&lt;/p&gt;
&lt;p&gt;ionic start todo blank&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into todo (directory was created by the ionic start command)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The todo app is now setup to be able to deploy to an Android device. To validate that we can build for Android, run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic platform add android
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The todo app is now setup to be able to deploy to an Android device. To validate that we can build for Android, run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic build android
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last thing we need to verify is that we can deploy the todo app to the Genymotion Emulator. Before we can deploy the application, we need to start up the emulator.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start the Genymotion device you downloaded and run:&lt;/p&gt;
&lt;p&gt;ionic run android&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are now ready to go create ionic applications that you deploy to an Android device.&lt;/p&gt;
&lt;h2 id="ios-setup-steps"&gt;iOS Setup Steps&lt;/h2&gt;
&lt;h3 id="xcode-setup"&gt;XCode Setup&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install Xcode from app store. This will take awhile since it is ~2 gigs in size.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Once install is completed, open xcode and accept the license&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the iOS Simulator that Ionic will use.&lt;/p&gt;
&lt;p&gt;npm install -g iOS-sim&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that if you just install NodeJs without using HomeBrew, you may have to add sudo in front of the npm install commands.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="verify-the-ios-setup"&gt;Verify the iOS Setup&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open terminal&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Navigate the directory where you store you development projects (I use ~/projects)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you need to generate the todo app, run the following command from the ~/projects directory&lt;/p&gt;
&lt;p&gt;ionic start todo tabs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;cd into todo (directory was created by the ionic start command)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We need to do is tell ionic that we want to add the iOS platform to our todo app by running:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic platform add iOS
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The todo app is now setup to be able to deploy to an iOS device. To validate that we can build for iOS, run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ionic build iOS
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The last thing we need to verify is that we can deploy the todo app to the iOS Simulator by running:&lt;/p&gt;
&lt;p&gt;ionic run iOS&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You are now ready to go create your ionic applications.&lt;/p&gt;</description></item><item><title>Ionic - Using Android x86 Virtual Machine Instead of Emulator</title><link>https://digitaldrummerj.me/android-x86-virtual-machine-instead-of-emulator/</link><pubDate>Sun, 11 Jan 2015 20:45:35 +0000</pubDate><author>Justin James</author><guid>https://digitaldrummerj.me/android-x86-virtual-machine-instead-of-emulator/</guid><category>ionic</category><description>&lt;p&gt;The Android emulator is super super slow and I could never get it working on my development virtual machine. I thought no problem I will just use Genymotion but due to a video card driver issue on my laptop (not Genymotion&amp;rsquo;s fault), I couldn&amp;rsquo;t use it either. I was thinking ok I will just have to use a real device and always have it on me when I do Android development work. This wouldn&amp;rsquo;t have been ideal though since Android development work is just a side project and who wants to carry an extra device just in case you get a few minutes to work on the project.&lt;/p&gt;
&lt;p&gt;Well then I ran across someone that mentioned that Android x86 project. This project allows you to run Android on a pc and virtualbox machine. So I follow the guide at &lt;a href="http://www.android-x86.org/documents/virtualboxhowto" target="_blank" &gt;Android x86 virtualbox install&lt;/a&gt; and was up and running in no time.&lt;/p&gt;
&lt;p&gt;Once I was up and running, the next step was to figure out how to deploy my applications to the machine just like I would with the emulator or a real device. Turns out this is pretty easy to do with the Android Debugging Bridge (adb)&lt;/p&gt;
&lt;p&gt;Following the [Debug How To] (&lt;a href="http://www.android-x86.org/documents/debug-howto" target="_blank" &gt;http://www.android-x86.org/documents/debug-howto&lt;/a&gt;) guide I was able to get it working pretty quickly.&lt;/p&gt;
&lt;p&gt;As the article says, it was easiest to get it working with a bridged adapter. Because I was using a virtualbox machine for both development and android x86, this made using NAT way more complicated then if just the Android x86 was the virtual machine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bridged Adapter Setup:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get Android Virtual Machine IP
&lt;ul&gt;
&lt;li&gt;Open Terminal (alt + F1)&lt;/li&gt;
&lt;li&gt;type netcfg&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;On the host machine (development machine)
&lt;ul&gt;
&lt;li&gt;Open command prompt&lt;/li&gt;
&lt;li&gt;type adb kill-server&lt;/li&gt;
&lt;li&gt;type adb tcpip 5555
&lt;ul&gt;
&lt;li&gt;type adb connect [Android Virtual Machine IP]:5555&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item></channel></rss>