<?xml version="1.0" encoding="utf-8"?>

<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator>
  <link href="https://www.softwaredeveloper.blog/feed.xml" rel="self" type="application/atom+xml" />
  <link href="https://www.softwaredeveloper.blog/" rel="alternate" type="text/html" hreflang="en" />
  <updated>2026-02-26T06:03:45+00:00</updated>
  <id>https://www.softwaredeveloper.blog/</id>

  
  
  

  
    <title type="html">Software Developer Blog</title>
  

  
    <subtitle>Software craftsmanship and agile project management</subtitle>
  

  

  
    
      
    
  

  
  

  
    
    
    
    <entry>
      <title type="html">Summary of The State of AI-assisted Software Development Report</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/organisation-ai.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/organisation-ai.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/organisation-ai.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/organisation-ai.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/organisation-ai.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/organisation-ai.jpg</url>
          <title>Summary of The State of AI-assisted Software Development Report</title>
          <link>https://www.softwaredeveloper.blog/dora-2025</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/dora-2025" rel="alternate" type="text/html" title="Summary of The State of AI-assisted Software Development Report" />
      <published>2026-02-26T04:00:00+00:00</published>
      <updated>2026-02-26T04:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/dora-2025</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/dora-2025">&lt;p&gt;This year’s edition, of the DORA research, entitled &lt;em&gt;State of AI-assisted Software Development&lt;/em&gt;, focused on the impact of AI assistance on software development.&lt;/p&gt;

&lt;h2 id=&quot;what-is-dora&quot;&gt;What is DORA&lt;/h2&gt;
&lt;p&gt;DORA is &lt;strong&gt;the largest and longest-running research program&lt;/strong&gt;, analyzing IT organizations of various sizes and in many different industries around the world each year.&lt;/p&gt;

&lt;p&gt;The researchers used a rigorous statistical evaluation methodology to identify the &lt;strong&gt;common characteristics of successful companies&lt;/strong&gt;.
The analysis covered &lt;strong&gt;practices and factors affecting the effectiveness of teams&lt;/strong&gt; involved in software development.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;
Full report available at &lt;a href=&quot;https://dora.dev/research/2025/dora-report/&quot;&gt;dora.dev/research/2025/dora-report&lt;/a&gt;.
&lt;/small&gt;&lt;/p&gt;

&lt;h2 id=&quot;ai-is-already-the-standard-but-it-doesnt-replace-thinking&quot;&gt;AI Is Already the Standard, but It Doesn’t Replace Thinking&lt;/h2&gt;

&lt;p&gt;AI in software development is no longer an experiment:
&lt;strong&gt;90% of surveyed developers use AI at work, and more than 80% report increased productivity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At the same time, as many as &lt;strong&gt;30% of respondents have low trust in AI-generated code&lt;/strong&gt;, which demonstrates a mature approach to verifying the results of AI “work.”&lt;/p&gt;

&lt;p&gt;AI genuinely accelerates development, but it requires critical thinking and solid engineering practices.
&lt;img src=&quot;assets/images/trsut-but-verify.jpg&quot; alt=&quot;Image of Trust but Verify slogan&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;ai-amplifies-whats-good--and-whats-bad&quot;&gt;AI Amplifies What’s Good — and What’s Bad&lt;/h2&gt;
&lt;p&gt;The key takeaway from the report: &lt;strong&gt;AI does not fix systemic problems — it amplifies them&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Organizations with strong architecture, clear processes, and a healthy work culture gain the greatest benefits. Where chaos, technical debt, or poor collaboration prevail, AI leads only to local improvements that get lost in later stages of delivery.
&lt;img src=&quot;assets/images/robot-groceries-small.jpg&quot; alt=&quot;Two robots in a shop, one with broken shopping cart&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;with-ai-productivity-increases--but-stability-still-suffers&quot;&gt;With AI, Productivity Increases — but Stability Still Suffers&lt;/h2&gt;
&lt;p&gt;In 2025, for the first time in DORA research, &lt;strong&gt;AI genuinely increases throughput&lt;/strong&gt; (the speed of delivering changes).&lt;/p&gt;

&lt;p&gt;At the same time, an &lt;strong&gt;increase in delivery instability&lt;/strong&gt; is still being observed (more rollbacks, hotfixes, and unplanned work).&lt;/p&gt;

&lt;p&gt;This signals that teams have learned to write code faster with AI, but &lt;strong&gt;quality control and feedback systems are not keeping pace with this pace&lt;/strong&gt;.
&lt;img src=&quot;assets/images/productivity-stability-small.jpg&quot; alt=&quot;One robot going down, the other going to crash&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;platform-engineering-is-the-foundation-of-ai-success&quot;&gt;Platform Engineering Is the Foundation of AI Success&lt;/h2&gt;
&lt;p&gt;As many as &lt;strong&gt;90% of organizations have adopted platform engineering&lt;/strong&gt;, following the concept introduced in the book &lt;strong&gt;Team Topologies&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The report clearly shows that &lt;strong&gt;the quality of the internal developer platform is crucial for realizing tangible benefits from AI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Additionally, &lt;strong&gt;Value Stream Management (VSM)&lt;/strong&gt; acts as a “force multiplier” — it enables organizations to convert local time savings (e.g., faster code writing) into &lt;strong&gt;real business outcomes&lt;/strong&gt;, rather than generating more downstream chaos.
&lt;img src=&quot;assets/images/team-topologies-book.jpg&quot; alt=&quot;Cover of Team Topologies Book&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;ai-adoption-is-an-organizational-transformation&quot;&gt;AI Adoption Is an Organizational Transformation&lt;/h2&gt;
&lt;p&gt;The greatest &lt;strong&gt;return from AI comes from investing in the system&lt;/strong&gt; — not merely in models or licenses.&lt;/p&gt;

&lt;p&gt;The new &lt;strong&gt;DORA AI Capabilities Model&lt;/strong&gt; identifies &lt;strong&gt;seven key capabilities&lt;/strong&gt; that determine success:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A clear and well-communicated AI position&lt;/li&gt;
  &lt;li&gt;Working in small batches&lt;/li&gt;
  &lt;li&gt;Healthy data ecosystems&lt;/li&gt;
  &lt;li&gt;User focus&lt;/li&gt;
  &lt;li&gt;Internal data accessible to AI&lt;/li&gt;
  &lt;li&gt;High-quality internal platforms&lt;/li&gt;
  &lt;li&gt;Strong version control practices
&lt;img src=&quot;assets/images/organisation-ai.jpg&quot; alt=&quot;Robots with people in the office&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;AI is now a standard part of software development, but it does not solve organizational problems.&lt;/strong&gt; It acts as an amplifier: the best teams become even better, while weak systems become even more chaotic.&lt;/p&gt;

&lt;p&gt;Real value from AI is achieved only by organizations with &lt;strong&gt;solid technical, process, and cultural foundations&lt;/strong&gt;.
&lt;img src=&quot;assets/images/summary.jpg&quot; alt=&quot;One robot cooperating with people well, the other one in chaos&quot; /&gt;&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="git" />
      

      
        <summary type="html">This year’s edition, of the DORA research, entitled State of AI-assisted Software Development, focused on the impact of AI assistance on software development.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Replacing Planning Poker with Team Estimation Game</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/estimation.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/estimation.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/estimation.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/estimation.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/estimation.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/estimation.jpg</url>
          <title>Replacing Planning Poker with Team Estimation Game</title>
          <link>https://www.softwaredeveloper.blog/planning-poker-vs-team-estimation-game</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/planning-poker-vs-team-estimation-game" rel="alternate" type="text/html" title="Replacing Planning Poker with Team Estimation Game" />
      <published>2025-12-20T04:00:00+00:00</published>
      <updated>2025-12-20T04:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/planning-poker-vs-team-estimation-game</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/planning-poker-vs-team-estimation-game">&lt;p&gt;Planning Poker has been with us for years, and it works to a certain extent. But it has its drawbacks as well…&lt;/p&gt;

&lt;h2 id=&quot;what-is-the-team-estimation-game&quot;&gt;What is the Team Estimation Game&lt;/h2&gt;
&lt;p&gt;The Team Estimation Game is a collaborative, fast-paced estimation technique focused on relative comparison rather than individual voting.
Instead of estimating one item at a time, the team works with many backlog items simultaneously.&lt;/p&gt;

&lt;p&gt;On the internet, you can find many complex variants of it, yet I’m a big fan of its simplest form.&lt;/p&gt;

&lt;h3 id=&quot;keep-the-big-picture-during-estimation&quot;&gt;Keep the Big Picture during estimation&lt;/h3&gt;
&lt;p&gt;In the Team Estimation Game, the team works on the whole sprint scope and looks at all stories/tasks at the same time, reasoning about them all at once.
In Planning Poker, you focus on one story at a time, estimate it, and then forget about it.
That’s a meaningful difference.&lt;/p&gt;

&lt;h2 id=&quot;preparation&quot;&gt;Preparation&lt;/h2&gt;
&lt;p&gt;You can play remotely using any online board you like (like Miro/Lucid), or locally with classic sticky notes and a whiteboard or even just on the table.&lt;/p&gt;

&lt;h3 id=&quot;draw-columns-according-to-your-scale&quot;&gt;Draw columns according to your scale&lt;/h3&gt;
&lt;p&gt;Not to end up with estimation more granular than your current scale, prepare columns upfront.
For example, assuming you estimate in story points: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0, 1, 2, 3, 5, 8, 13, 21&lt;/code&gt;, draw 8 columns.&lt;/p&gt;

&lt;h3 id=&quot;list-players&quot;&gt;List players&lt;/h3&gt;
&lt;p&gt;You’ll play in turns, so to avoid messing up the move order, prepare a list of players in some accessible place.&lt;/p&gt;

&lt;h3 id=&quot;bring-storiestasks&quot;&gt;Bring stories/tasks&lt;/h3&gt;
&lt;p&gt;Prepare sticky notes (literally or virtually) with stories/tasks - those will be moved back and forth during the game.&lt;/p&gt;

&lt;h2 id=&quot;the-game&quot;&gt;The Game&lt;/h2&gt;
&lt;p&gt;As a facilitator call players by name, start by calling first person on the list instructing them to move one card.
Keep up the pace by calling the next player shortly after the previous person finishes, especially during online collaboration, prone to distraction.&lt;/p&gt;

&lt;h3 id=&quot;move-one-card-or-pass&quot;&gt;Move one card or pass&lt;/h3&gt;
&lt;p&gt;Each player moves one card either from the stack to the board or from one board column to another.
It’s allowed to move a card in between, moving existing cards to the column on the right or left.
When everything already looks good to you, you can pass your turn.
Everyone is allowed to do any move they want, explaining the reasoning behind. Others can ask questions, but cannot ‘block’ someone’s move.
Obviously at some point when some players disagree, deeper discussion and some compromise is inevitable.&lt;/p&gt;

&lt;h3 id=&quot;the-results&quot;&gt;The Results&lt;/h3&gt;
&lt;p&gt;At the end, when everyone is satisfied with the current state of the board, the game is finished and you have nice estimations focused on references to each other :)&lt;/p&gt;

&lt;h3 id=&quot;the-matrix&quot;&gt;The Matrix&lt;/h3&gt;
&lt;p&gt;Instead of columns you can prepare two-dimensional matrix, for example with value/effort or risk/effort, to gain deeper understanding of the tasks and maybe give up some less valuable stories or those with bigger risk.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;That’s it — a very simple exercise that brings more precise estimations and deeper understanding (through the discussions that took place) than classic Planning Poker.
Obviously, quick Poker sessions still have their place, but when planning a whole sprint, the Team Estimation Game is my go-to approach now.&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="git" />
      

      
        <summary type="html">Planning Poker has been with us for years, and it works to a certain extent. But it has its drawbacks as well…</summary>
      
    </entry>
  
    
    
    
      
    
    
    
      
    
    
    
      
    
    
    
    <entry>
      <title type="html">Optimize Docker .NET Core project and docker-compose solution to reduce build image time</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/docker-optimize.jpg</url>
          <title>Optimize Docker .NET Core project and docker-compose solution to reduce build image time</title>
          <link>https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image" rel="alternate" type="text/html" title="Optimize Docker .NET Core project and docker-compose solution to reduce build image time" />
      <published>2020-03-20T14:00:00+00:00</published>
      <updated>2020-06-12T20:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image">&lt;p&gt;&lt;em&gt;Docker&lt;/em&gt; has built in great cache mechanism, but to be able to use it, you have to understand how it works.
Let’s dive into it, to build &lt;em&gt;.NET Core Docker&lt;/em&gt; images faster.&lt;/p&gt;

&lt;h2 id=&quot;how-docker-cache-works&quot;&gt;How Docker cache works&lt;/h2&gt;
&lt;p&gt;First we should understand how &lt;em&gt;Docker&lt;/em&gt; cache works. This part is described nice in &lt;a href=&quot;https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache&quot;&gt;official documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;Starting with a base image that is already in the cache, the next instruction is compared against all child images derived from that base image to see if one of them was built using the exact same instruction. If not, the cache is invalidated.&lt;/li&gt;
    &lt;li&gt;In most cases simply comparing the instruction in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; with one of the child images is sufficient. However, certain instructions require a little more examination and explanation.&lt;/li&gt;
    &lt;li&gt;For the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ADD&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; instructions, the contents of the file(s) in the image are examined and a checksum is calculated for each file. The last-modified and last-accessed times of the file(s) are not considered in these checksums. During the cache lookup, the checksum is compared against the checksum in the existing images. If anything has changed in the file(s), such as the contents and metadata, then the cache is invalidated.&lt;/li&gt;
    &lt;li&gt;Aside from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ADD&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; commands, cache checking will not look at the files in the container to determine a cache match. For example, when processing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN apt-get -y update&lt;/code&gt; command the files updated in the container will not be examined to determine if a cache hit exists. In that case just the command string itself will be used to find a match.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;Once the cache is invalidated, all subsequent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; commands will generate new images and the cache will not be used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So it always checks if given instruction is started in exactly the same situation as in the past.
If it is, it doesn’t start it, but this layer is taken from cache.
If there is even &lt;em&gt;one bit&lt;/em&gt; changed, for example one copied file differs, then cache is invalidated and instruction is invoked &lt;em&gt;truly&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;how-to-decrease-image-build-time&quot;&gt;How to decrease image build time&lt;/h2&gt;
&lt;p&gt;To build images faster, put everything which will rarely change, above things which change often.
For example, when you compile your application inside &lt;em&gt;Dockerfile&lt;/em&gt;, then your source files often change, which invalidates cache all the way down.
But you probably download some dependencies, for example &lt;em&gt;Nuget&lt;/em&gt; packages, which change
less often than source code, so you can put it before compilation. This way as long as you stick to existing dependencies, they are taken from cache.
When you add some new dependency or change version of existing one, cache is invalidated and packages are downloaded from the Internet (and new cache is created).&lt;/p&gt;

&lt;h3 id=&quot;ignore-what-you-dont-need&quot;&gt;Ignore what you don’t need&lt;/h3&gt;
&lt;p&gt;When you copy files from host to &lt;em&gt;build context&lt;/em&gt;, Docker calculates checksum for them. If exactly the same files were copied to exactly
the same directory in Docker image, then this layer (as every operation in Dockerfile creates new layer) is taken from cache.
So copy only those files which you need, because you can invalidate cache in vain, with files which are not required in your image, for example
every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;, or files generated by your IDE and so on.
Remember, that &lt;em&gt;.dockerignore&lt;/em&gt; file has to be located in &lt;em&gt;build context&lt;/em&gt; root. Example rules:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;*/bin
*/obj
.dockerignore // Docker files
.env
.git // Git files
.gitignore // Git files
.gitattributes // Git files
.vs // IDE files
.vscode // IDE files
**/.toolstarget
.idea // IDE files
**/Samples // Additional files
build
Docker // Docker scripts and docker-compose files (in this example in &apos;Docker&apos; directory, in context root level)
**/Dockerfile // Docker files
**/docker-compose // Docker files
*Test*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;try-to-create-your-own-cache-yarn-build-example&quot;&gt;Try to create your own cache (yarn build example)&lt;/h3&gt;
&lt;p&gt;When you know where is your bottleneck and there is no obvious way to reduce it, try to figure out some none obvious solution.
For example a few projects in one of products which I maintain, have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn install&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yarn build&lt;/code&gt; sewn into &lt;em&gt;csproj&lt;/em&gt; file, like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;Target Name=&quot;PublishRunWebpack&quot; AfterTargets=&quot;ComputeFilesToPublish&quot; &amp;gt;
  &amp;lt;Exec WorkingDirectory=&quot;$(SpaRoot)&quot; Command=&quot;yarn install&quot; /&amp;gt;
  &amp;lt;Exec WorkingDirectory=&quot;$(SpaRoot)&quot; Command=&quot;yarn build&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;but this was extremely slow - it took (in &lt;em&gt;Docker for Windows&lt;/em&gt;) around 4 minutes.
And web files in those projects were changed rarely, usually C# code got changed, so I wanted to reuse &lt;em&gt;yarn&lt;/em&gt; build results. I didn’t find a way to use &lt;em&gt;npm&lt;/em&gt;/&lt;em&gt;yarn&lt;/em&gt; cache in this situation, so I
cached it myself. I moved copying web files to the top of Dockerfile, like this:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app/WebClient/WebFiles&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; WebClient/WebFiles . # Taken form cache as long as you don&apos;t change files in WebFiles directory&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;yarn &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Taken form cache as long as you don&apos;t change files in WebFiles directory&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;yarn build &lt;span class=&quot;c&quot;&gt;# Taken form cache as long as you don&apos;t change files in WebFiles directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and changed &lt;em&gt;csproj&lt;/em&gt; like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;Target Name=&quot;PublishRunWebpack&quot; AfterTargets=&quot;ComputeFilesToPublish&quot; Condition=&quot; &apos;$(Configuration)&apos; == &apos;Release&apos; And !Exists(&apos;$(ProjectDir)WebFiles/build&apos;) &quot;&amp;gt;
  &amp;lt;Exec WorkingDirectory=&quot;$(SpaRoot)&quot; Command=&quot;yarn install&quot; /&amp;gt;
  &amp;lt;Exec WorkingDirectory=&quot;$(SpaRoot)&quot; Command=&quot;yarn build&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So Docker cached &lt;em&gt;yarn&lt;/em&gt; build output and it was invalidated only when web files changed. With target condition, C# project reused already existing &lt;em&gt;yarn&lt;/em&gt; build output.
Of course, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY WebClient/WebFiles&lt;/code&gt; was on top of my Dockerfile, every time when some web files changed, it invalidated cache all the way down,
so if it was changed often, I would do it some different way, but in my case it worked as expected.
And I know that in theory incorrect build directory could exist somehow, but it’s only theory, because in &lt;em&gt;Docker&lt;/em&gt; situation is repeatable and on
developer machines publish wasn’t invoked that way or another, only debug configuration, so in practice it’s not a risk. Maybe with &lt;a href=&quot;http://www.digitallycreated.net/Blog/67/incremental-builds-in-msbuild-and-how-to-avoid-breaking-them&quot;&gt;incremental build&lt;/a&gt; I could be able to achieve the same with a bit cleaner way - if your case is similar to mine, you can try it.&lt;/p&gt;

&lt;h3 id=&quot;reuse-what-you-can&quot;&gt;Reuse what you can&lt;/h3&gt;
&lt;p&gt;If there is something which you can reuse, do it.
For example when I have multiple &lt;em&gt;Docker&lt;/em&gt; images in one solution, I build them all at once, instead of building every project in separation.
I have observed, that this way it’s faster. For example, I have solution &lt;em&gt;Application.sln&lt;/em&gt; which contains 5 executable projects (which are packed to separate &lt;em&gt;Docker&lt;/em&gt; images)
and additional projects like tests, usage samples and so on. So to lower number of projects to build, I have copied it to &lt;em&gt;Application-minimal.sln&lt;/em&gt; and left only necessary projects.
Then in every &lt;em&gt;Dockerfile&lt;/em&gt;, instead of compiling only one, chosen project, like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet publish PROJECT_NAME -c Release -o out&lt;/code&gt;
I compile whole solution like this: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet publish Application-minimal.sln -c Release&lt;/code&gt;. After compilation, I copy whole build output (with all projects) 
to final image (see &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image#docker-multi-stage-build&quot;&gt;&lt;em&gt;Docker&lt;/em&gt; multi-stage build explained&lt;/a&gt;), 
for example this way: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from=build-env /app/build .&lt;/code&gt;. Finally, at the very bottom of &lt;em&gt;Dockerfile&lt;/em&gt; I invalidate cache specifying which project I want to run:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app/ProjectA/publish # Pay attention to have correct path, especially if you set custom build output path.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;dotnet&quot;, &quot;ProjectA.dll&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Everything above is reused from cache, so when you want to build &lt;em&gt;n&lt;/em&gt; projects, only first project takes a while to compile, other projects are taken from cache practically whole.
In my case I’ve observed that it works faster than doing it in separation (running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet publish PROJECTX&lt;/code&gt; for every project). It will probably be faster in your case too, but as with every optimization,
I encourage to measure it before applying :)&lt;/p&gt;

&lt;h3 id=&quot;experiment-with-caching-nuget-dependencies&quot;&gt;Experiment with caching nuget dependencies&lt;/h3&gt;
&lt;p&gt;In your developer PC, you have nuget cached locally. But in &lt;em&gt;Docker&lt;/em&gt; you have clean situation every time you invoke build, so nuget packages are taken from the internet every time, so it’s good to cache them,
because we don’t change project dependencies as often as project source files.
When you compile only one project, then situation is clear, from project directory you can run:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; *.csproj ./ # Taken form cache as long as you don&apos;t change your project file&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet restore &lt;span class=&quot;c&quot;&gt;# Taken form cache as long as you don&apos;t change your project file &lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Copy everything else and build&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet publish &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; out
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;But when you want to restore files for &lt;a href=&quot;#reuse-what-you-can&quot;&gt;whole solution&lt;/a&gt;, then it gets more complicated, because (at the time of writing) you cannot point
to &lt;em&gt;.sln&lt;/em&gt; file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet restore&lt;/code&gt; command, and you cannot point to multiple &lt;em&gt;csproj&lt;/em&gt; files neither.
Fortunately, you can resolve it in many ways, for example with simple bash script:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;projectName &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.csproj&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# It can be saved as one-linear straight in Dockerfile&lt;/span&gt;
	dotnet restore &lt;span class=&quot;nv&quot;&gt;$projectName&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;used this way:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; restore-all.sh . # Taken form cache always&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x restore-all.sh
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; */*.csproj ./ # Taken form cache as long as you don&apos;t change your csproj files&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;./restore-all.sh &lt;span class=&quot;c&quot;&gt;# Taken form cache as long as you don&apos;t change your csproj files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;alternatively-build-image-from-dedicated-container&quot;&gt;Alternatively: build image from dedicated container&lt;/h3&gt;
&lt;p&gt;Some people do not compile application in &lt;em&gt;Dockerfile&lt;/em&gt;, but on their own PC and just put build output to image.
This is faster, but it has one big downside: you’re not 100% sure that image is properly built, and generally speaking
how it was made, so I’ll never wont to put such image on production. But I’ve also heard about another solution - to create
container dedicated to create &lt;em&gt;input&lt;/em&gt; to &lt;em&gt;Docker&lt;/em&gt; image… Sounds a bit like overengineering, and probably in many cases it is, but maybe
sometimes it’s worth doing it. You have more options in container, for example you can mount directories, so there is no problem with any cache you need.
You can even use already existing cache from host machine. I’ve never tried such approach, above methods work well
for me, but maybe sometimes it’s worth to organize &lt;em&gt;CI/CD pipeline&lt;/em&gt; this way.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Proper caching in &lt;em&gt;Dockerfile&lt;/em&gt; is quite broad subject, so here I only touched it in generals. The point of this article 
is not only to give you some ready to use tricks (like those with &lt;em&gt;yarn&lt;/em&gt; or &lt;em&gt;Nuget&lt;/em&gt; packages), but also to show you how cache in &lt;em&gt;Docker&lt;/em&gt; works and encourage you to investigate your own situation.
Pay attention to every build layer, measure which step takes too much time and where is your bottleneck.
Sometimes with one line moved in &lt;em&gt;Dockerfile&lt;/em&gt;, or one file added to &lt;em&gt;.dockerignore&lt;/em&gt; you can make (really) big difference.
And sometimes with small investigation you can resolve huge problem, for example &lt;em&gt;.dockerignore&lt;/em&gt; file stored somewhere else than build context root directory.&lt;br /&gt;
If you have feeling that cache caused some problem (which in theory may be the case, but I never had such situation) you can always check
how &lt;em&gt;Docker&lt;/em&gt; builds with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;no-cache&lt;/code&gt; parameter, which forces to build everything from scratch.&lt;/p&gt;

&lt;p&gt;I hope the article was helpful for you, please publish it on Facebook or Twitter and of course ask me anything, which is still not clear.
If you know some other trick related to cache, please share it through comments, someone for sure will appreciate it :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="docker" />
      
        <category term="dotnet" />
      

      
        <summary type="html">Docker has built in great cache mechanism, but to be able to use it, you have to understand how it works. Let’s dive into it, to build .NET Core Docker images faster.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Introduction to Git Bisect with CodeceptJS automation example</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/git-bisect.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/git-bisect.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/git-bisect.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/git-bisect.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/git-bisect.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/git-bisect.jpg</url>
          <title>Introduction to Git Bisect with CodeceptJS automation example</title>
          <link>https://www.softwaredeveloper.blog/git-bisect</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/git-bisect" rel="alternate" type="text/html" title="Introduction to Git Bisect with CodeceptJS automation example" />
      <published>2020-01-10T06:00:00+00:00</published>
      <updated>2020-01-10T06:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/git-bisect</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/git-bisect">&lt;p&gt;Many developers have heard about &lt;em&gt;Git Bisect&lt;/em&gt;, but only few gave it a try. It’s a pity, because it can… find a bug without your attention!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Git Bisect&lt;/em&gt; is a tool which comes pre-installed with &lt;em&gt;Git&lt;/em&gt;, which is meant to find particular commit. But unlike &lt;em&gt;Git log&lt;/em&gt; it’s not meant
to find commit by its details, for example commit message or introduced changes, but by behaviour which it introduces to system.&lt;/p&gt;

&lt;h2 id=&quot;how-git-bisect-works&quot;&gt;How &lt;em&gt;Git Bisect&lt;/em&gt; works&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Git Bisect&lt;/em&gt; uses binary search algorithm to check commit. You need to set commits range and then &lt;em&gt;Git Bisect&lt;/em&gt; checks commit in the middle of the range.
If behaviour which we are looking for exist in this commit, then &lt;em&gt;Git&lt;/em&gt; knows that it was introduced in first part of the range, otherwise in the second part.
This way range gets smaller by half and then algorithm repeats up to the point, when &lt;em&gt;Git&lt;/em&gt; is sure which commit has introduced given behaviour.
If you create small commits and each of commit has one, consistent change into the system, then you not only see which commit 
introduced given behaviour, but also which changes in code are &lt;em&gt;guilty&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;how-git-bisect-knows-that-particular-commit-has-given-behaviour&quot;&gt;How &lt;em&gt;Git Bisect&lt;/em&gt; knows that particular commit has given behaviour&lt;/h3&gt;
&lt;p&gt;When &lt;em&gt;Git Bisect&lt;/em&gt; has selected the commit, there are two modes to check if system at this point of time had given behaviour.&lt;br /&gt;
You can do it manually - simply run the system and tell Git the answer or…&lt;br /&gt;
You can do it automatically. Just pass to &lt;em&gt;Git Bisect&lt;/em&gt; script or program which tests system and returns &lt;em&gt;0 exit code&lt;/em&gt; if system works &lt;em&gt;good&lt;/em&gt;
or exit with a code between 1 and 127 (inclusive, except 125), if currently there is &lt;em&gt;bad&lt;/em&gt; behaviour.&lt;/p&gt;

&lt;h3 id=&quot;can-git-bisect-be-used-to-other-purpose-than-finding-a-bug&quot;&gt;Can &lt;em&gt;Git Bisect&lt;/em&gt; be used to other purpose than finding a bug&lt;/h3&gt;
&lt;p&gt;Usually we talk about &lt;em&gt;bad&lt;/em&gt; behaviour - a bug, that’s why by default we operate with terms &lt;em&gt;good&lt;/em&gt; and &lt;em&gt;bad&lt;/em&gt;, but you can use &lt;em&gt;Git Bisect&lt;/em&gt; to find other type of introduced behaviour,
for example improved performance, so you can use &lt;a href=&quot;https://git-scm.com/docs/git-bisect#_alternate_terms&quot;&gt;alternate terms&lt;/a&gt;. By default, you have
two alternate terms given: &lt;em&gt;old&lt;/em&gt; and &lt;em&gt;new&lt;/em&gt;, but you can set up your, to make bisecting easier to understand.
If you want to know how to achieve it, &lt;a href=&quot;https://git-scm.com/docs/git-bisect#_alternate_terms&quot;&gt;it’s described nice in documentation&lt;/a&gt;, I don’t want to lose focus on general usage here, so let’s move forward.&lt;/p&gt;

&lt;h2 id=&quot;git-bisect-manual-demo&quot;&gt;&lt;em&gt;Git Bisect&lt;/em&gt; manual demo&lt;/h2&gt;
&lt;p&gt;Let’s imagine, that we have noticed, that our system at the newest version has regression bug, which for sure
did not exist in last release, tagged as &lt;em&gt;v1.2&lt;/em&gt;.
To start bisecting we need to write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect start&lt;/code&gt;.
Then we need to set end of commits range, by writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect bad *COMMIT_ID*&lt;/code&gt;.
As we know that the newest version has it, we can skip &lt;em&gt;*COMMIT_ID*&lt;/em&gt;, to have &lt;em&gt;HEAD&lt;/em&gt; taken.
Then we need to set beginning of commits range, by writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect good *COMMIT_ID*&lt;/code&gt;, which in our case
it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect good v1.2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When commits range is set, &lt;em&gt;Git&lt;/em&gt; chooses first commit. Output may look like this:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Bisecting: 10 revisions left to &lt;span class=&quot;nb&quot;&gt;test &lt;/span&gt;after this &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;roughly 4 steps&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;3007a714657da73173b7236d3821bbea0555bd2b] Adjust fonts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now we need to check system: recompile, run, and test manually if bug exist.
If so, then write: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect bad&lt;/code&gt;. If bug doesn’t exist, then write: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect good&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This process repeats until commit which introduced the bug is found, which is indicated by commit details
printed out.
At this moment we are still in bisecting mode. To finish it, we need to write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect reset&lt;/code&gt;, which returns us to the
commit that was checked out before &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect start&lt;/code&gt; (unless you don’t specify other commit id as parameter to &lt;em&gt;reset&lt;/em&gt; command).&lt;/p&gt;

&lt;p&gt;There are other commands to use during bisecting, but they are additional. If you are curios which are those, &lt;a href=&quot;https://git-scm.com/docs/git-bisect#_description&quot;&gt;they are described clearly in documentation&lt;/a&gt;.
I don’t want to distract us, so will skip them here, with exception to one convenient command: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect visualize&lt;/code&gt;, which shows
us current commits range and currently selected commit. If you have &lt;em&gt;gitk&lt;/em&gt; installed it will show you there, otherwise in terminal.&lt;/p&gt;

&lt;h2 id=&quot;git-bisect-automated-demo-with-codeceptjs&quot;&gt;&lt;em&gt;Git Bisect&lt;/em&gt; automated demo with &lt;em&gt;CodeceptJS&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Of course, checking if bug exist is a boring process which usually can be easily automated.
As our system is website, we will use &lt;em&gt;CodeceptJS&lt;/em&gt;.
Initializing it, and creating test scenarios is out of scope of this article, so if you’re new to it, just follow &lt;a href=&quot;https://codecept.io&quot;&gt;theirs official manuals&lt;/a&gt;.
We need to describe behaviour in test scenario, which need to be initialized in directory outside of our repo, or in untracked
file in our repo, to have it unchanged when &lt;em&gt;Git Bisect&lt;/em&gt; changes commits. Of course after finding bug, we will commit those new
scenarios to rest of our system test scenarios, to not have this regression any more in the future.&lt;/p&gt;

&lt;p&gt;Let’s assume, that in our case we run webserver once, and it refreshes itself when files change, during commits switching (like for example &lt;em&gt;Jekyll&lt;/em&gt; does).&lt;/p&gt;

&lt;p&gt;Then we need to start bisecting and set commits range &lt;a href=&quot;#git-bisect-manual-demo&quot;&gt;like before&lt;/a&gt;.
Next, instead of telling &lt;em&gt;Git&lt;/em&gt; whether given commit is &lt;em&gt;good&lt;/em&gt; or &lt;em&gt;bad&lt;/em&gt; we run automated testing:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect run node codeceptjs run --steps&lt;/code&gt;. The first argument after &lt;em&gt;run&lt;/em&gt; keyword is the script/program to run, next
are arguments which will be passed along. In our case it’s &lt;em&gt;CodeceptJS&lt;/em&gt; with its arguments. If you don’t have
&lt;em&gt;CodeceptJS&lt;/em&gt; globally installed you can pass path for example from &lt;em&gt;node_modules&lt;/em&gt; directory: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect run node node_modules/.bin/codeceptjs run --steps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That’s it, &lt;em&gt;Git Bisect&lt;/em&gt; will check every chosen commit with this script to the point, when it knows which commit introduced given behaviour,
in our case - a bug. Like &lt;a href=&quot;#git-bisect-manual-demo&quot;&gt;in manual process&lt;/a&gt; you need to reset bisecting mode at the end - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git bisect reset&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What if you need to compile system during every check, or do some other preparations? It’s not a problem, you simply need to put
those tasks in script/program which got executed by bisect.&lt;/p&gt;

&lt;h2 id=&quot;video-demo&quot;&gt;Video demo&lt;/h2&gt;
&lt;p&gt;In my view &lt;em&gt;Git bisect&lt;/em&gt; with automated tests look spectacular, so I have created a &lt;a href=&quot;https://www.youtube.com/watch?v=4mDhLbZSlQc&quot;&gt;video Demo&lt;/a&gt;, if you prefer to watch before trying yourself :)
&lt;a href=&quot;https://www.youtube.com/watch?v=4mDhLbZSlQc&quot; title=&quot;Watch Git Bisect demo&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;assets/images/git-bisect-video.jpg&quot; alt=&quot;Video player with Git Bisect demo&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;To summarize, &lt;em&gt;Git Bisect&lt;/em&gt; is a simple tool, which can be really helpful. When? In situations, when you have a bug (or other behaviour),
which is hard to debug, so it’s easier to find which commit introduced it, especially when you create small, consistent commits.
I have seen developers who do &lt;em&gt;binary search&lt;/em&gt; manually, counting and switching commits.&lt;/p&gt;

&lt;p&gt;It’s giant help especially, when you have good automated tests, so you can just add new test scenario, then &lt;em&gt;Git Bisect&lt;/em&gt; will take care of rest of the job.&lt;/p&gt;

&lt;p&gt;Do you use &lt;em&gt;Git Bisect&lt;/em&gt; or have some thoughts about it? Please share it through comments. And send this article or &lt;a href=&quot;https://www.youtube.com/watch?v=4mDhLbZSlQc&quot;&gt;video demo&lt;/a&gt;
to colleagues who hasn’t heard of it :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="git" />
      

      
        <summary type="html">Many developers have heard about Git Bisect, but only few gave it a try. It’s a pity, because it can… find a bug without your attention!</summary>
      
    </entry>
  
    
    
    
      
    
    
    
    <entry>
      <title type="html">Initialize MS SQL in Docker container - create database at startup</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/docker-mssql.jpg</url>
          <title>Initialize MS SQL in Docker container - create database at startup</title>
          <link>https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container" rel="alternate" type="text/html" title="Initialize MS SQL in Docker container - create database at startup" />
      <published>2019-12-27T06:00:00+00:00</published>
      <updated>2019-12-30T15:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/initialize-mssql-in-docker-container">&lt;p&gt;&lt;em&gt;Microsoft SQL Server&lt;/em&gt; is available for Linux so we can run it from &lt;em&gt;Docker&lt;/em&gt; container, but usually we need to initialize database at startup, which currently is a bit tricky.&lt;/p&gt;

&lt;p&gt;If you have seen my &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database&quot;&gt;post which explains &lt;em&gt;Docker&lt;/em&gt; compose&lt;/a&gt;,
then you know that &lt;em&gt;MySQL&lt;/em&gt; team has prepared very convenient way to run initialization scripts when DB has started.
You only need to pass your script to proper directory and &lt;em&gt;MySQL&lt;/em&gt; will run it when it’s successfully stared, so you can for example create database.&lt;/p&gt;

&lt;p&gt;Unfortunately &lt;em&gt;Microsoft SQL Server&lt;/em&gt; currently doesn’t have such a mechanism, and &lt;a href=&quot;https://hub.docker.com/_/microsoft-mssql-server&quot;&gt;official documentation&lt;/a&gt;
redirects to only one demo, which is coupled with &lt;em&gt;node.js&lt;/em&gt; and without &lt;em&gt;node&lt;/em&gt; it doesn’t work.&lt;/p&gt;

&lt;p&gt;So I have created &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/mssql-docker-initialization-demo&quot;&gt;simplified demo&lt;/a&gt;, and here will explain how it works step by step.&lt;/p&gt;

&lt;h3 id=&quot;running-ms-sql-server-from-docker-container-without-initialization&quot;&gt;Running MS SQL Server from &lt;em&gt;Docker&lt;/em&gt; container without initialization&lt;/h3&gt;
&lt;p&gt;First let’s take a look at &lt;a href=&quot;https://hub.docker.com/_/microsoft-mssql-server&quot;&gt;official information&lt;/a&gt; of how to start Microsoft SQL Server in container:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;ACCEPT_EULA=Y&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;SA_PASSWORD=yourStrong(!)Password&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;MSSQL_PID=Express&apos;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 1433:1433 &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; mcr.microsoft.com/mssql/server:2017-latest-ubuntu 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I prefer to manage base image and environment variables in text file, so let’s put all above to &lt;em&gt;Dockerfile&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/mssql/server:2017-latest-ubuntu&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; SA_PASSWORD yourStrong(!)Password&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ACCEPT_EULA Y&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; MSSQL_PID Express&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now we can add this file to &lt;em&gt;Git&lt;/em&gt;,&lt;br /&gt;
build this image - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build -t db-demo .&lt;/code&gt;&lt;br /&gt;
and run container in simpler way:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 1433:1433 &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; db-demo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This way we run clean &lt;em&gt;MS SQL server&lt;/em&gt;.&lt;br /&gt;
We can additionally add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;EXPOSE 1433&lt;/code&gt; entry to &lt;em&gt;Dockerfile&lt;/em&gt;, to be able to access it from composed containers.&lt;/p&gt;

&lt;p&gt;We should also use explicit image version (not &lt;em&gt;latest&lt;/em&gt;) to be sure that this &lt;em&gt;Dockerfile&lt;/em&gt; will always be valid (new versions will not break down &lt;em&gt;Docker build&lt;/em&gt; or runtime initialization in container).
So for example let’s choose version: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM mcr.microsoft.com/mssql/server:2017-CU17-ubuntu&lt;/code&gt; (all tags list is usually given in &lt;a href=&quot;https://hub.docker.com/_/microsoft-mssql-server&quot;&gt;&lt;em&gt;Docker Hub&lt;/em&gt; image description&lt;/a&gt;).&lt;/p&gt;

&lt;h2 id=&quot;how-to-initialize-ms-sql-server-in-docker-container&quot;&gt;How to initialize MS SQL server in &lt;em&gt;Docker&lt;/em&gt; container&lt;/h2&gt;
&lt;p&gt;Now it’s time to add extra stuff to have initialization in our server.&lt;/p&gt;

&lt;p&gt;The hack is to run at the same time &lt;em&gt;SQL Server&lt;/em&gt; and script which will sleep 90seconds (to be sure that server already started) then do whole initialization (we’ll see how).
It’s not my idea, this is solution currently recommended by Microsoft and nothing better comes to my mind neither.&lt;/p&gt;

&lt;p&gt;First we override &lt;em&gt;Dockerfile&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMD&lt;/code&gt; entry from base image (which runs &lt;em&gt;SQL server&lt;/em&gt;), with our own &lt;em&gt;CMD&lt;/em&gt; command, which runs our custom script:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CMD /bin/bash ./entrypoint.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and this &lt;em&gt;entrypoint.sh&lt;/em&gt; script looks like this:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Run Microsoft SQl Server and initialization script (at the same time)&lt;/span&gt;
/usr/src/app/run-initialization.sh &amp;amp; /opt/mssql/bin/sqlservr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Which means: run &lt;em&gt;run-initialization.sh&lt;/em&gt; scripts and at the same time run &lt;em&gt;SQL Server&lt;/em&gt;.
Base image runs server exactly the same way, which we can check with &lt;em&gt;Docker inspect&lt;/em&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker inspect mcr.microsoft.com/mssql/server:2017-CU17-ubuntu | grep CMD&lt;/code&gt;.
The only difference is that we add our script here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: server command must be after &lt;em&gt;&amp;amp;&lt;/em&gt;, otherwise &lt;em&gt;Docker&lt;/em&gt; container will stop when &lt;em&gt;run-initialization.sh&lt;/em&gt; script finishes job.
In &lt;a href=&quot;https://github.com/twright-msft/mssql-node-docker-demo-app/blob/d9d64c9bfbe33091dbfd86913e3103be883c9000/entrypoint.sh&quot;&gt;example referenced by &lt;em&gt;Microsoft&lt;/em&gt;&lt;/a&gt; this order is reversed, but container works because of &lt;em&gt;node.js&lt;/em&gt; server running.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note2: be sure that you pass to &lt;em&gt;Docker&lt;/em&gt; image script files with Unix line endings, otherwise you may &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-run-problem-no-such-file-or-directory-or-other-strange-message&quot;&gt;encounter strange bugs&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So now let’s see how &lt;em&gt;run-initialization.sh&lt;/em&gt; script looks like:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Wait to be sure that SQL Server came up&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;90s

&lt;span class=&quot;c&quot;&gt;# Run the setup script to create the DB and the schema in the DB&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Note: make sure that your password matches what is in the Dockerfile&lt;/span&gt;
/opt/mssql-tools/bin/sqlcmd &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; localhost &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; sa &lt;span class=&quot;nt&quot;&gt;-P&lt;/span&gt; yourStrong&lt;span class=&quot;o&quot;&gt;(!)&lt;/span&gt;Password &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; master &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; create-database.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We assume that after 90 seconds DB server will already be ready to use, then invoke &lt;em&gt;sqlcmd&lt;/em&gt; utility to run &lt;em&gt;SQL&lt;/em&gt; script - 90 seconds is time suggested by Microsoft, in my local environments (for development and tests) I’ve set 30seconds and never had problem with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: credentials passed to &lt;em&gt;sqlcmd&lt;/em&gt; tool must match those given as environment variable in &lt;em&gt;Dockerfile&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s it, in your SQL script you can do whatever you need to do as startup. In our demo we just create database with schema:&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DemoData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DemoData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Products&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProductName&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nvarchar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At the end we need to copy those scripts inside &lt;em&gt;Docker&lt;/em&gt; image. First we’ll create new directory for them with &lt;em&gt;RUN&lt;/em&gt; entry in &lt;em&gt;Dockerfile&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; /usr/src/app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;then copy files there:&lt;/p&gt;
&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /usr/src/app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . /usr/src/app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and to be sure that &lt;em&gt;run-initialization.sh&lt;/em&gt; script will be treated as executable we use &lt;em&gt;chmod&lt;/em&gt; command:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x /usr/src/app/run-initialization.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it, whole &lt;em&gt;Dockerfile&lt;/em&gt; is available in &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/mssql-docker-initialization-demo/blob/master/Dockerfile&quot;&gt;mentioned GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;how-to-initialize-database-with-data&quot;&gt;How to initialize database with data&lt;/h2&gt;
&lt;p&gt;If you need to have database filled with data, there are several ways to achieve that.&lt;/p&gt;

&lt;h3 id=&quot;docker-commit&quot;&gt;&lt;em&gt;Docker&lt;/em&gt; commit&lt;/h3&gt;
&lt;p&gt;If it’s only for debugging purposes you can do &lt;em&gt;Docker commit&lt;/em&gt; to save container state as new image, to recreate it whenever you want.
It can be useful for example when you reproduced bug and don’t want to lose it during debugging. Just save database state, then you can recreate whole environment with bug already &lt;em&gt;prepared&lt;/em&gt;, as many times as you need.
But this way is not recommended for production, because you don’t see explicitly what is in this data so it’s hard to maintain.&lt;/p&gt;

&lt;h3 id=&quot;docker-volume&quot;&gt;&lt;em&gt;Docker&lt;/em&gt; volume&lt;/h3&gt;
&lt;p&gt;You can have your database files already on disk and new server instance just &lt;em&gt;mounted&lt;/em&gt; to it with &lt;em&gt;Docker volume&lt;/em&gt;.
It’s good solution, because you will probably use volume that way or another, to easily persist database.&lt;/p&gt;

&lt;h3 id=&quot;script-database-schema-and-data&quot;&gt;Script database schema and data&lt;/h3&gt;
&lt;p&gt;You can script whole database with data included, straight from &lt;em&gt;Microsoft SQL Server Management studio&lt;/em&gt;, just click on database name, choose &lt;em&gt;Tasks&lt;/em&gt; -&amp;gt; &lt;em&gt;Generate Scripts…&lt;/em&gt; and
you can choose in advanced options &lt;em&gt;Types of data to scripts - Schema and data&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/mssms-generate-scrips1.jpg&quot; alt=&quot;Mssms screnshot with generate scripts tool shown&quot; title=&quot;Choose Generate Scripts...&quot; /&gt;
&lt;img src=&quot;/assets/images/mssms-generate-scrips2.jpg&quot; alt=&quot;Mssms screnshot with advanced generate scripts tool options shown&quot; title=&quot;Choose what you want to have inside scripts: data, schema or both&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can also separate scripts to create schema (empty database) and to add data (fill database), and just run &lt;em&gt;sqlcmd&lt;/em&gt; utility several times:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sleep &lt;/span&gt;90s
/opt/mssql-tools/bin/sqlcmd &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; localhost &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; sa &lt;span class=&quot;nt&quot;&gt;-P&lt;/span&gt; yourStrong&lt;span class=&quot;o&quot;&gt;(!)&lt;/span&gt;Password &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; master &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; create-database.sql
/opt/mssql-tools/bin/sqlcmd &lt;span class=&quot;nt&quot;&gt;-S&lt;/span&gt; localhost &lt;span class=&quot;nt&quot;&gt;-U&lt;/span&gt; sa &lt;span class=&quot;nt&quot;&gt;-P&lt;/span&gt; yourStrong&lt;span class=&quot;o&quot;&gt;(!)&lt;/span&gt;Password &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; master &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; fill-database.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To do so first generate script with &lt;em&gt;Schema only&lt;/em&gt;, then once again with &lt;em&gt;Data only&lt;/em&gt;. I prefer do it this way to separate schema from data, but it’s just my preference,
I feel that it’s cleaner this way but don’t have any tangible evidence that it’s better.&lt;/p&gt;

&lt;h2 id=&quot;troubleshooting-ms-sql-docker-initialization&quot;&gt;Troubleshooting &lt;em&gt;MS SQL Docker&lt;/em&gt; initialization&lt;/h2&gt;
&lt;p&gt;Like I have written before, always remember to &lt;strong&gt;set Unix &lt;em&gt;EOL&lt;/em&gt;&lt;/strong&gt; in files which you copy to &lt;em&gt;Docker&lt;/em&gt; image, otherwise you may have &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-run-problem-no-such-file-or-directory-or-other-strange-message&quot;&gt;incomprehensible bugs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moreover, at the moment of writing this article, &lt;em&gt;Microsoft SQL Server Management Studio&lt;/em&gt; must be restarted every time container is stopped and recreated from scratch, because
&lt;a href=&quot;https://www.softwaredeveloper.blog/mssms-invalid-urn-filter-on-server&quot;&gt;&lt;em&gt;MSSMS&lt;/em&gt; caches internal DB name and doesn’t refresh it, so there is mismatch&lt;/a&gt;. That way or another, &lt;strong&gt;upgrade &lt;em&gt;MSSMS&lt;/em&gt; to the newest version&lt;/strong&gt;, because as far as I know
older versions works even worse.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Starting new &lt;em&gt;Docker&lt;/em&gt; container with &lt;em&gt;Microsoft SQL Server&lt;/em&gt; is really simple. Initializing DB server could be equally simple &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database#mysql-db&quot;&gt;like in MySQL&lt;/a&gt;, but
unfortunately Microsoft didn’t get this done so far, so at this moment we need to use ugly hack with sleep. Luckily workaround is already prepared, and I hope that
after reading this article it’s easy to understand how exactly it works.&lt;/p&gt;

&lt;p&gt;If you want to see &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/Docker-compose-dotnet-core-and-mssql&quot;&gt;MS SQL Server database composed up with Dotnet Core app&lt;/a&gt;, I have created demo for it as well (even if you are not interested in &lt;em&gt;.NET&lt;/em&gt;, you may want
to see &lt;em&gt;SQL Server&lt;/em&gt; in &lt;em&gt;Docker Compose&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Please tell me your feelings in comment, about &lt;em&gt;MS SQL server&lt;/em&gt; in &lt;em&gt;Docker&lt;/em&gt; in general, or about this article if you have any suggestions, or share it with others :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="docker" />
      
        <category term="sql" />
      

      
        <summary type="html">Microsoft SQL Server is available for Linux so we can run it from Docker container, but usually we need to initialize database at startup, which currently is a bit tricky.</summary>
      
    </entry>
  
    
    
    
      
    
    
    
      
    
    
    
      
    
    
    
    <entry>
      <title type="html">Store Git SSH key pair in Linux with Libsecret to skip asking for credentials</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/shell-key.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/shell-key.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/shell-key.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/shell-key.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/shell-key.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/shell-key.jpg</url>
          <title>Store Git SSH key pair in Linux with Libsecret to skip asking for credentials</title>
          <link>https://www.softwaredeveloper.blog/store-git-ssh-credentials-in-linux</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/store-git-ssh-credentials-in-linux" rel="alternate" type="text/html" title="Store Git SSH key pair in Linux with Libsecret to skip asking for credentials" />
      <published>2019-11-04T10:00:00+00:00</published>
      <updated>2020-03-06T08:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/store-git-ssh-credentials-in-linux</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/store-git-ssh-credentials-in-linux">&lt;p&gt;If you have to connect to Git via SSH, your usual credential manager may be not enough. Read how to avoid entering password every time on your Linux device.&lt;/p&gt;

&lt;p&gt;I have already introduced &lt;a href=&quot;https://www.softwaredeveloper.blog/git-credential-storage-libsecret&quot;&gt;Libsecret Git credential manager&lt;/a&gt;, which works fine if you connect to remote repo via http(s),
which is what we usually do. But sometimes we are forced to use SSH protocol, and once again we need to write password every time we interact with remote repo. 
Luckily it can be solved in secure way quite easily, but unfortunately descriptions which I found on the internet are not clear, moreover usually skip securing private keys. 
My goal is to explain it in this article in simple words, so you can apply it and understand what you do.&lt;/p&gt;

&lt;h2 id=&quot;how-automate-ssh-connection-works&quot;&gt;How automate SSH connection works&lt;/h2&gt;
&lt;p&gt;Mechanism which is used in SSH to automate connections is really simple.
You generate public-private keys pair (those keys are not related to credentials nor any other data), hide private key with you and share public key with server.
Then when you try to connect to server, your clients uses private key as prove that you’re allowed to connect - that’s it.&lt;/p&gt;

&lt;p&gt;It sounds quite magically, but it’s really simple. Those keys are just text files, stored somewhere on your device, usually in &lt;em&gt;~/.ssh&lt;/em&gt; directory.
They are not related to any data, it’s just a pair which suits each other, in mathematical meaning, you generate it with &lt;em&gt;OpenSSH&lt;/em&gt; tool.
Look at this example (with default file names &lt;em&gt;id_rsa&lt;/em&gt; and &lt;em&gt;id_rsa.pub&lt;/em&gt;):&lt;/p&gt;

&lt;p&gt;Private key file: &lt;em&gt;~/.ssh/id_rsa&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Public key file: &lt;em&gt;~/.ssh/id_rsa.pub&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0
FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/
3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQAB
-----END PUBLIC KEY-----
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You store both files in your device and save public one in server which you want to connect, at proper path - that’s it.
If you want to read more about key pair mechanism, &lt;a href=&quot;https://www.ssh.com/ssh/public-key-authentication&quot;&gt;article on ssh.com&lt;/a&gt; explains it nice.&lt;/p&gt;

&lt;h2 id=&quot;how-to-configure-automatic-ssh-connection&quot;&gt;How to configure automatic SSH connection&lt;/h2&gt;
&lt;p&gt;Once you know the theory, it’s time to try automated connection in practice.
First I will show you the process with &lt;em&gt;Seahorse&lt;/em&gt; tool which I find convenient for this task, 
but of course you can achieve the same with command line, which &lt;a href=&quot;#automate-ssh-login-using-command-line&quot;&gt;I will show later&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;em&gt;Ubuntu&lt;/em&gt; and &lt;em&gt;Linux Mint&lt;/em&gt; Seahorse comes pre installed with OS and you can find it as &lt;em&gt;Seahorse&lt;/em&gt; or &lt;em&gt;Passwords and Keys&lt;/em&gt;.
I’m pretty sure it’s not difficult to install it in other distros, so if you don’t have it already, just search how to install it in your case.
It uses &lt;em&gt;libsecret&lt;/em&gt; abstraction which was created to unify access to sensitive data, so it should work on all popular Linux distributions.&lt;/p&gt;

&lt;p&gt;If you don’t see secrets categories on the left side of &lt;em&gt;Seahorse&lt;/em&gt;, set &lt;em&gt;View&lt;/em&gt; » &lt;em&gt;By Keyring&lt;/em&gt;.
&lt;img src=&quot;/assets/images/seahorse-view.jpg&quot; alt=&quot;Screenshot of Seahorse tool with view configuration opened&quot; title=&quot;Seahorse view configuration&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;create-ssh-keys&quot;&gt;Create SSH keys&lt;/h3&gt;
&lt;p&gt;Choose &lt;em&gt;OpenSSH Keys&lt;/em&gt; category and press &lt;em&gt;+&lt;/em&gt; button
&lt;img src=&quot;/assets/images/seahorse-add-ssh-keys.jpg&quot; alt=&quot;Screenshot of Seahorse tool with cursor on add ssh keys button with plus sign&quot; title=&quot;Press add SSH keys button&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;em&gt;Secure Shell Key&lt;/em&gt;
&lt;img src=&quot;/assets/images/seahorse-select-ssh.jpg&quot; alt=&quot;Screenshot of Seahorse tool with Secure Shell Key option selected&quot; title=&quot;Select Secure Shell Key option&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now give your keys some description, to remember what is this for.
This name will be seen on server, so you can write here your email address, to simplify server administration
(so maintainer could know who uses this and if it’s still required).&lt;/p&gt;

&lt;p&gt;Choose encryption type - at the time of writing the only available options in Seahorse are &lt;a href=&quot;https://en.wikipedia.org/wiki/RSA_(cryptosystem)&quot;&gt;&lt;em&gt;RSA&lt;/em&gt;&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Digital_Signature_Algorithm&quot;&gt;&lt;em&gt;DSA&lt;/em&gt;&lt;/a&gt; - 
it’s a bit subjective, but I suggest choosing &lt;em&gt;RSA&lt;/em&gt; as &lt;a href=&quot;https://security.stackexchange.com/questions/5096/rsa-vs-dsa-for-ssh-authentication-keys&quot;&gt;explained on security.stackexchange.com&lt;/a&gt;.
There is a way to choose &lt;a href=&quot;https://ed25519.cr.yp.to/&quot;&gt;&lt;em&gt;Ed25519&lt;/em&gt; algorithm&lt;/a&gt; which is more secure and gives better performance, but currently only &lt;a href=&quot;#automate-ssh-login-using-command-line&quot;&gt;from command line&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;RSA&lt;/em&gt; key strength can be left 2048 bits as is by default. You can increase it, but be aware that some devices may not support it and that it takes a bit more of resources to work with bigger number.
But if you insist, 4096bits should be fine.
Here is nice &lt;a href=&quot;https://danielpocock.com/rsa-key-sizes-2048-or-4096-bits/&quot;&gt;article which explains RSA key size topic&lt;/a&gt; with details.&lt;/p&gt;

&lt;p&gt;From this point you can choose two options:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Just Create Key&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Create and Set Up&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With second option you will populate newly created key to server right from this wizard, so that’s what we will do.
If you don’t have access to server at this moment, you can just create key and populate it later &lt;a href=&quot;#automate-ssh-login-using-command-line&quot;&gt;from command line&lt;/a&gt;.
&lt;img src=&quot;/assets/images/seahorse-new-secure-shell-key.jpg&quot; alt=&quot;Screenshot of Seahorse tool with new Secure Shell Key configuration&quot; title=&quot;Configure new Secure Shell Key&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;encrypt-ssh-keys-with-password&quot;&gt;Encrypt SSH keys with password&lt;/h3&gt;
&lt;p&gt;Next you will be asked to write passphrase for new secure shell key. It may be a bit confusing - you may wonder if this password must match SSH user password - 
it doesn’t need to, moreover - it shouldn’t - it is different password so it should be different (you won’t need to remember it).
If you leave it blank your private key will be saved on disk unencrypted, which causes security risks. 
Despite some people advice to leave it this way I don’t see any reason not to encrypt it,
that way or another you won’t need to remember it - &lt;a href=&quot;#connect-and-store-encryption-password-in-keyring&quot;&gt;we will use keyring&lt;/a&gt; for that (but it this moment save it somewhere, preferably in some password manager).
Enter strong passphrase, I recommend using some tool which will generate it (I use &lt;a href=&quot;https://keepassxc.org/&quot;&gt;&lt;em&gt;KeepassXC&lt;/em&gt;&lt;/a&gt;), then enter it again as confirmation.
&lt;img src=&quot;/assets/images/seahorse-passphrase.jpg&quot; alt=&quot;Screenshot of Seahorse tool with opened window with passphrase for new secure shell key&quot; title=&quot;Enter good quality password&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;check-created-keys-files&quot;&gt;Check created keys files&lt;/h3&gt;
&lt;p&gt;Now you should have new files in &lt;em&gt;~/.ssh&lt;/em&gt; directory, as &lt;a href=&quot;#how-automate-ssh-connection-works&quot;&gt;described earlier&lt;/a&gt; - &lt;em&gt;id_rsa&lt;/em&gt; with private key and &lt;em&gt;id_rsa.pub&lt;/em&gt; with public key.
If you &lt;a href=&quot;#encrypt-ssh-keys-with-password&quot;&gt;skipped encrypting private key&lt;/a&gt; you will see it in plain text,
otherwise it will be encrypted and will contain header which indicates encryption, for example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Proc-Type: 4,ENCRYPTED&lt;/code&gt;.
&lt;img src=&quot;/assets/images/ssh-directory.jpg&quot; alt=&quot;Screenshot of ~/.ssh directory with rsa keys&quot; title=&quot;Private and public keys on disk&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;send-public-key-to-server&quot;&gt;Send public key to server&lt;/h3&gt;
&lt;p&gt;If you decided to create key and set it up, you will now see new Window - &lt;em&gt;Set Up Computer for SSH Connection&lt;/em&gt;.
Write your server address and a port in format &lt;em&gt;server:port&lt;/em&gt; then your login name and press &lt;em&gt;Set Up&lt;/em&gt; button. You will be asked for given user password.
&lt;img src=&quot;/assets/images/seahorse-server-address.jpg&quot; alt=&quot;Screenshot of Seahorse tool with server address configuration&quot; title=&quot;Write proper server address with port and user name&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;connect-and-store-encryption-password-in-keyring&quot;&gt;Connect and store encryption password in keyring&lt;/h3&gt;
&lt;p&gt;If setting up succeeds, then in user directory on server will be copy of your public key in &lt;em&gt;~/.ssh&lt;/em&gt; directory in &lt;em&gt;authorized_keys&lt;/em&gt; file - connect to server and check if it is there.
It may look like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCxGhrhgMsQqOvXf+M1z04toa65/8vY6b+lzjAas7YJqut65o0ONWItpz2UHXSl0mFlbN49UR6VDR9jesUKW+vfuIRUaPs+8/bNEP5tIzPq3lSvw1msYNBT2kbQWO5+Qqiy7EXf/RJ7z4BGiBDEZ4rRhjxIrPAuaxeOpWU6KEDlsxCczxZWgl2xcldhk5oJwzItNd2piQlr7r2CYibHU36XtELvAhlTI1/p11rfSf/BwGtCxb25dApg8k/yRw2zpdV/JD0nfNQiESnCSmLxDr66WK5dScpXQ8zRMRSM3ekoo2ElkDEl9gQ9PhrqdKDLiPqgbobhi22ro4GKsHHAy2VF GitGitRsa
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Do it as usual, just: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh user@server.com -p 22&lt;/code&gt;
To see file you can for example write: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat ~/.ssh/authorized_keys&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When you try to connect for the first time, you should be asked for password to &lt;em&gt;Unlock private key&lt;/em&gt;.
Set &lt;em&gt;Automatically unlock this key whenever I’m logged in&lt;/em&gt; checkbox and since now it will be unlocked along with your login keyring, when you log in. You will no longer see this window and
your private key will be encrypted - like I told, there is no reason not to encrypt it, it’s secure and convenient at the same time.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/seahorse-unlock-private-key.jpg&quot; alt=&quot;Screenshot of Seahorse window to unlock private key&quot; title=&quot;Unlock private key and set automate unlock checkbox&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;automate-ssh-login-using-command-line&quot;&gt;Automate SSH login using command line&lt;/h2&gt;
&lt;p&gt;In my view if Seahorse works in your case, it’s convenient to use it’s wizard, but there is one exception:
when you want to use &lt;a href=&quot;https://ed25519.cr.yp.to/&quot;&gt;&lt;em&gt;Ed25519&lt;/em&gt; algorithm&lt;/a&gt; which, like I told earlier in this article, 
is more secure and gives better performance, you must use command line, as at the time of writing it’s not supported in Seahorse wizard.&lt;/p&gt;

&lt;p&gt;Bellow steps are exact equivalent of previous from Seahorse tool, just written from command line 😺&lt;/p&gt;

&lt;p&gt;First generate a new &lt;em&gt;Ed25519&lt;/em&gt; SSH key pair (if you haven’t done it already):&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh-keygen &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; ed25519 &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;description or your email&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If server which you want to reach doesn’t support &lt;em&gt;Ed25519&lt;/em&gt; or if you have old version of &lt;em&gt;OpenSSH&lt;/em&gt; on your PC (&lt;em&gt;Ed25519&lt;/em&gt; was introduced to &lt;em&gt;OpenSSH&lt;/em&gt; in version 6.5), use &lt;em&gt;RSA&lt;/em&gt; instead:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh-keygen &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; rsa &lt;span class=&quot;nt&quot;&gt;-b&lt;/span&gt; 2048 &lt;span class=&quot;nt&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;description or your email&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Description or email is optional, but it’s good to put some convenient information here, because as I &lt;a href=&quot;#create-ssh-keys&quot;&gt;described earlier&lt;/a&gt;, this name will be seen on server, 
so it should identify you, so administrator will know who uses it.&lt;br /&gt;
If you wonder how many bits should have &lt;em&gt;RSA&lt;/em&gt; key size (in above example 2048 bits), it’s the same case which I described during &lt;a href=&quot;#create-ssh-keys&quot;&gt;key creation with Seahorse tool&lt;/a&gt;.
The bigger number you choose, the better security you have, but it comes with a bit bigger resources usage, and with risk, that it will not work - some devices don’t support
length bigger than 2048. I recommend &lt;a href=&quot;https://danielpocock.com/rsa-key-sizes-2048-or-4096-bits/&quot;&gt;good article about RSA key size length&lt;/a&gt;,
for those who want to understand choice better.&lt;/p&gt;

&lt;p&gt;First you will be asked about path where to save keys - use default option by hitting &lt;em&gt;Enter&lt;/em&gt;.
If you’re warned that key already exist, you must choose custom path and later set up &lt;em&gt;~/.ssh/config&lt;/em&gt; file.&lt;/p&gt;

&lt;p&gt;Next you will be asked for encryption password. This is not your SSH user password, so create strong password, different than user’s one, the best option is to generate it with some tool.
You won’t need to remember it, but before you configure everything, save it somewhere - the best option is to use passwords manager - I recommend &lt;a href=&quot;https://keepassxc.org/&quot;&gt;&lt;em&gt;KeepassXC&lt;/em&gt;&lt;/a&gt;.
I described why to do that &lt;a href=&quot;#encrypt-ssh-keys-with-password&quot;&gt;in earlier part of this article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now you can &lt;a href=&quot;#check-created-keys-files&quot;&gt;check created keys files&lt;/a&gt; to ensure everything worked as expected.&lt;/p&gt;

&lt;p&gt;If you had to enter custom path for keys, now you should configure it:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Only if created keys at custom path &lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;ssh-agent &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
ssh-add ~/.ssh/YOUR_NEW_PRIVATE_KEY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And save &lt;em&gt;~/.ssh/config&lt;/em&gt; file as in below example:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Only if created keys at custom path 
Host gitlab.com
  Preferredauthentications publickey
  IdentityFile ~/.ssh/GITLAB_COM_KEY

Host github.com
  Preferredauthentications publickey
  IdentityFile ~/.ssh/GITHUB_COM_KEY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next you need to copy your public key to server, this can be done with one command:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh-copy-id user@server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;or if &lt;em&gt;ssh-copy-id&lt;/em&gt; utility is not available on your device, you can use plain &lt;em&gt;ssh&lt;/em&gt; solution&lt;br /&gt;
(change &lt;em&gt;PUBLIC_KEY.pub&lt;/em&gt; to what you have generated, for example &lt;em&gt;id_rsa.pub&lt;/em&gt;):&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; ~/.ssh/PUBLIC_KEY.pub | ssh &amp;lt;user&amp;gt;@&amp;lt;&lt;span class=&quot;nb&quot;&gt;hostname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;cat &amp;gt;&amp;gt; .ssh/authorized_keys &amp;amp;&amp;amp; echo &quot;Key copied&quot;&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next &lt;a href=&quot;#connect-and-store-encryption-password-in-keyring&quot;&gt;check if public key is copied as expected and store encryption password in keyring&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;troubleshooting-ssh-key-pairing&quot;&gt;Troubleshooting SSH key pairing&lt;/h2&gt;
&lt;p&gt;If automate connection doesn’t work and you don’t know what has failed, and you have only one remote SSH server,
you may want to try everything from scratch - maybe you made some mistake?
That is what helped me, but &lt;strong&gt;use it only if you’re sure you don’t have any other keys already&lt;/strong&gt; and just in case copy files as backup.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Remove all files from local &lt;em&gt;~/.ssh&lt;/em&gt; directory.&lt;/li&gt;
  &lt;li&gt;Remove all files from remote (server) &lt;em&gt;~/.ssh&lt;/em&gt; directory.&lt;/li&gt;
  &lt;li&gt;Try &lt;a href=&quot;#how-to-configure-automatic-ssh-connection&quot;&gt;setting up everything&lt;/a&gt; again.
I suggest reading &lt;a href=&quot;#how-automate-ssh-connection-works&quot;&gt;short description how key pair automation works&lt;/a&gt;, to know what is happening during process.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you see that keys are generated correctly, but copying to server failed, try to copy it manually (it may require &lt;em&gt;sudo&lt;/em&gt; access):&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Copy public key to server, for example with &lt;em&gt;scp&lt;/em&gt; - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp ~/.ssh/PUBLIC_KEY.pub user@server:/home/user/PUBLIC_KEY.pub&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Login to server as usual, for example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh user@server.com -p 22&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Create &lt;em&gt;.ssh&lt;/em&gt; directory, for example with command: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkdir ~/.ssh&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Append the public key content to &lt;em&gt;authorized_keys&lt;/em&gt; file: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat ~/PUBLIC_KEY.pub &amp;gt;&amp;gt; ~/.ssh/authorized_keys&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your key is copied to &lt;em&gt;~/.ssh/authorized_keys&lt;/em&gt; file on server but automate connection doesn’t work, try changing remote user permissions to &lt;em&gt;711&lt;/em&gt;
(some people advice to do it as well for user home directory) - you may need &lt;em&gt;sudo&lt;/em&gt; access:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Login to server as usual, for example: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh user@server.com -p 22&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd ~&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 711 .&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 711 .ssh/&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 711 .ssh/authorized_keys&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chown -R USERANAME .ssh/&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;If you have any questions feel free to ask them in comments, or just to tell if this article has helped you.
If you know someone who also uses SSH protocol to manage remote Git repo, don’t hesitate to send him or her link, 
it may help yet another person and I will be glad too :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="linux" />
      
        <category term="git" />
      
        <category term="security" />
      

      
        <summary type="html">If you have to connect to Git via SSH, your usual credential manager may be not enough. Read how to avoid entering password every time on your Linux device.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Docker compose introduction - Dotnet core app composed with MySQL Database</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/docker-compose-dotnet-core-with-mysql.jpg</url>
          <title>Docker compose introduction - Dotnet core app composed with MySQL Database</title>
          <link>https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database" rel="alternate" type="text/html" title="Docker compose introduction - Dotnet core app composed with MySQL Database" />
      <published>2019-09-13T06:00:00+00:00</published>
      <updated>2019-09-13T06:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database">&lt;p&gt;Introduction to docker-compose - basic aspects explained with .NET Core app and MySQL DB composed together.&lt;/p&gt;

&lt;p&gt;If you are familiar with &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image&quot;&gt;basics of creating Docker images&lt;/a&gt;, now
it’s time to learn how to compose more than one Docker container together. Once again we’ll learn with .NET Core 2.2 example app, but
you should be able to understand Docker compose concepts here, even if you’re not familiar with C# language.&lt;/p&gt;

&lt;p&gt;Whole solution is available in &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/Docker-compose-dotnet-core-and-mysql&quot;&gt;SoftwareDeveloperBlog repo on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;docker-compose-file&quot;&gt;Docker-compose file&lt;/h2&gt;
&lt;p&gt;Heart of Docker composing is one text file (in &lt;em&gt;yaml&lt;/em&gt; format).&lt;br /&gt;
By default it’s named &lt;em&gt;docker-compose.yml&lt;/em&gt; (or &lt;em&gt;docker-compose.yaml&lt;/em&gt; if you prefer),
but unlike &lt;em&gt;Dockerfile&lt;/em&gt; this name can be changed and specified as command parameter.&lt;/p&gt;

&lt;h3 id=&quot;where-to-keep-docker-compose-file&quot;&gt;Where to keep docker-compose file?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Short answer&lt;/strong&gt; - it doesn’t matter, put it where it’s convenient for you.&lt;br /&gt;
&lt;strong&gt;Long answer&lt;/strong&gt; - some people advice to put it along with deployment scripts, for example in your CI pipeline, because composing is strictly
related with deploying. Moreover, this way you can compose images from different repositories.&lt;br /&gt;
&lt;strong&gt;Where I put &lt;em&gt;docker-compose.yml&lt;/em&gt; file?&lt;/strong&gt; So far I’ve never containerized system spread across multiple repositories, so I put it along with sources, to
have it under version control, next to solution file (&lt;em&gt;.sln&lt;/em&gt;) - to &lt;em&gt;manage&lt;/em&gt; it from the &lt;em&gt;highest perspective&lt;/em&gt; (above project directories).
Usually I also create solution directory called &lt;em&gt;Docker&lt;/em&gt;, referencing docker-compose and &lt;em&gt;.dockerignore&lt;/em&gt; files, to be able to edit it from my IDE.&lt;/p&gt;

&lt;h3 id=&quot;docker-compose-file-example&quot;&gt;Docker-compose file example&lt;/h3&gt;
&lt;p&gt;In our example we will use following &lt;em&gt;docker-compose.yml&lt;/em&gt; file, &lt;a href=&quot;#where-to-keep-docker-compose-file&quot;&gt;kept as I usually do&lt;/a&gt;, next to solution file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&apos;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./Db&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Aspnetcoreapp/Dockerfile&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8080:80&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;db&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;First we specify docker-compose file &lt;strong&gt;format version&lt;/strong&gt;. I just took the newest version, I don’t see any reasons to take older ones
while creating new system.&lt;/p&gt;

&lt;p&gt;Then we specify our &lt;strong&gt;services&lt;/strong&gt;. We could specify other aspects, for example networks, volumes, configs, 
but it’s too wide for this introduction.&lt;/p&gt;

&lt;p&gt;Every service has its name, and this &lt;strong&gt;service name is important&lt;/strong&gt;, because we will use it to resolve container ip address and to refer it from other service (for example to specify dependencies).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build parameter&lt;/strong&gt; can just point to directory with Dockerfile (like in &lt;em&gt;db&lt;/em&gt; service in example above) - then this directory is also taken as build context.
If you want to use different directory for build context, than the one with Dockerfile inside, you can separate &lt;em&gt;context&lt;/em&gt; and &lt;em&gt;dockerfile&lt;/em&gt; options, like in &lt;em&gt;app&lt;/em&gt; service above.
If you wonder why to do that, I explained &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image&quot;&gt;choosing build context in previous post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Specify &lt;strong&gt;ports&lt;/strong&gt; to bind port inside container with one from host machine, like in usual &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image#docker-run-command&quot;&gt;&lt;em&gt;Docker run&lt;/em&gt; command&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lastly we specify that our app &lt;strong&gt;depends on&lt;/strong&gt; db, with &lt;em&gt;depends_on&lt;/em&gt; option, taking service name as value. This way we determine services start and stop order (and few other things, not mentioned here for simplification).&lt;/p&gt;

&lt;h2 id=&quot;docker-compose-commands&quot;&gt;Docker-compose commands&lt;/h2&gt;
&lt;p&gt;Managing composed system is easy, you don’t need to remember many commands and parameters.&lt;/p&gt;

&lt;h3 id=&quot;docker-compose-up&quot;&gt;Docker-compose up&lt;/h3&gt;
&lt;p&gt;From directory with &lt;em&gt;docker-compose.yml&lt;/em&gt; file you can just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt; command. 
It will run containers in order forced by &lt;em&gt;depends_on&lt;/em&gt; configuration (if specified).
By default images are build only when you run &lt;em&gt;docker-compose up&lt;/em&gt; command for the first time, so if you have changed something, you should
extend it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--build&lt;/code&gt; parameter.
If you want to run it from different location then the one with &lt;em&gt;docker-compose.yml&lt;/em&gt; file, or if you named compose file not default way, or if you want to pass more than one compose file at once, pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f FILE_PATH&lt;/code&gt; parameter.
Last parameter which I use often is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; to detach console from containers. 
There are other commands to start composed containers, but usually we use this one, so I don’t mention them in this introduction. If you’re curious, they are
described well &lt;a href=&quot;https://docs.docker.com/compose/faq/#whats-the-difference-between-up-run-and-start&quot;&gt;in official documentation&lt;/a&gt;.
Usually I run &lt;em&gt;docker-compose up&lt;/em&gt; command this way:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker-compose up &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;docker-compose-down&quot;&gt;Docker-compose down&lt;/h3&gt;
&lt;p&gt;If you want to stop composed containers, remove them and theirs networks, just type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose down&lt;/code&gt; from directory with &lt;em&gt;docker-compose.yml&lt;/em&gt; file.
If you want just stop containers without cleaning everything, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose stop&lt;/code&gt; command.
Note, that if you have stopped containers with &lt;em&gt;CTRL+C&lt;/em&gt; shortcut in attached terminal, you didn’t clean stuff, you just stopped containers and finally will need to run &lt;em&gt;docker-compose down&lt;/em&gt; as usual.&lt;/p&gt;

&lt;h2 id=&quot;example-system&quot;&gt;Example system&lt;/h2&gt;
&lt;p&gt;Now with mandatory knowledge, lets create example system.
As mentioned in &lt;a href=&quot;#introduction&quot;&gt;introduction&lt;/a&gt;, whole repo is available &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/Docker-compose-dotnet-core-and-mysql&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;net-core-app&quot;&gt;.NET Core app&lt;/h3&gt;
&lt;p&gt;First we create simple ASP.NET Core application.
Small startup configuration:&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Startup&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configuration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IConfiguration&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Configuration&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IServiceCollection&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;AddMvc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;SetCompatibilityVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompatibilityVersion&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Version_2_2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IApplicationBuilder&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IHostingEnvironment&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;IsDevelopment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;UseDeveloperExceptionPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

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

&lt;p&gt;Program entry point:&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Program&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;CreateWebHostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IWebHostBuilder&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;CreateWebHostBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;WebHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UseStartup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Controller with one &lt;em&gt;GET&lt;/em&gt; method:&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProductsController&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ControllerBase&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ProductsProvider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_provider&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProductsProvider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HttpGet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IEnumerable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_provider&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Exception during providing products, maybe DB is not fully initialized yet? &quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt;
                              &lt;span class=&quot;s&quot;&gt;$&quot;Try again in a few minutes and if it doesn&apos;t help, check your docker-compose configuration.\n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Controller is coupled with &lt;em&gt;Product&lt;/em&gt; type:&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Product&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Controller is also coupled with &lt;em&gt;ProductsProvider&lt;/em&gt; class, which uses &lt;em&gt;Dapper&lt;/em&gt; to access MySQL DB.
Note, that we &lt;strong&gt;provide docker-compose service name as DB address&lt;/strong&gt; (as &lt;a href=&quot;#docker-compose-file-example&quot;&gt;mentioned earlier&lt;/a&gt; - that’s why service name is important). Be careful not to make typo - &lt;strong&gt;service name is case-sensitive&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProductsProvider&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CONN_STRING&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Server=db;Port=3306;Database=product-db;Uid=root; Pwd=myPass;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;QUERY&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;SELECT Id, Name, Description FROM product&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;GetAll&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;MySqlConnection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CONN_STRING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Product&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QUERY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Dockerfile&lt;/em&gt; is written following &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image&quot;&gt;&lt;em&gt;Docker image from multi project .NET Core solution&lt;/em&gt; post&lt;/a&gt;.
Note that &lt;em&gt;Aspnetcoreapp&lt;/em&gt; is our project name, and project directory name at the same time:&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet publish Aspnetcoreapp &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; out

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/aspnet:2.2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=build-env /app/Aspnetcoreapp/out .&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;dotnet&quot;, &quot;Aspnetcoreapp.dll&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;mysql-db&quot;&gt;MySQL DB&lt;/h3&gt;
&lt;p&gt;We could point DB image in docker-compose.yml file and configure environment variables and so on there, but I prefer to 
configure service via Dockerfile. So we just create new directory with Dockerfile. It can be located anywhere, but I prefer the same level as C# project.
So I just create new &lt;em&gt;Db&lt;/em&gt; directory, next to project directory (&lt;em&gt;Aspnetcoreapp&lt;/em&gt;).
If you want to see this Dockerfile in IDE, you can reference it in solution directory.&lt;/p&gt;
&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mysql:5.7.13&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; *.sql /docker-entrypoint-initdb.d&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; MYSQL_ROOT_PASSWORD myPass&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;EXPOSE&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; 3306&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;With above &lt;em&gt;Dockerfile&lt;/em&gt; we do several things:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Specify which version of MySQL image to use&lt;/li&gt;
  &lt;li&gt;Set root user password to &lt;em&gt;myPass&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Expose 3306 port to be able to access it from our app&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Initialize database&lt;/strong&gt;, by copying SQL scripts to &lt;em&gt;/docker-entrypoint-initdb.d&lt;/em&gt; directory inside DB container, because 
&lt;a href=&quot;https://hub.docker.com/_/mysql#initializing-a-fresh-instance&quot;&gt;official MySQL image description tells us, that scripts 
from this directory are invoked when DB is started for the first time&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next we need some &lt;em&gt;.sql&lt;/em&gt; script located next to &lt;em&gt;Dockerfile&lt;/em&gt; to initialize our database. We use &lt;em&gt;*.sql&lt;/em&gt; wildcard, so name doesn’t matter, it just need to end up with &lt;em&gt;.sql&lt;/em&gt; extension - for example &lt;em&gt;Init.sql&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&quot;language-sql highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;DATABASE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`product-db`&lt;/span&gt; &lt;span class=&quot;cm&quot;&gt;/*!40100 COLLATE &apos;latin1_swedish_ci&apos; */&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;USE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`product-db`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`product`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;`Id`&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;`Name`&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;nv&quot;&gt;`Description`&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;TEXT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;INDEX&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;`Id`&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;`Id`&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COLLATE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;latin1_swedish_ci&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;IGNORE&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;INTO&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;product&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;VALUES&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Dependency Injection Principles, Practices, and Patterns&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Book by Steven van Deursen and Mark Seemann&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Agile Software Development, Principles, Patterns, and Practices&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;&quot;Book by Robert C. Martin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;docker-composeyml-and-dockerignore&quot;&gt;Docker-compose.yml and .dockerignore&lt;/h3&gt;
&lt;p&gt;We take &lt;em&gt;docker-compose.yml&lt;/em&gt; file described &lt;a href=&quot;#docker-compose-file-example&quot;&gt;earlier in this article&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&apos;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./Db&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;dockerfile&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Aspnetcoreapp/Dockerfile&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8080:80&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;depends_on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Very last thing - &lt;em&gt;.dockerignore&lt;/em&gt; file located next to solution (&lt;em&gt;.sln&lt;/em&gt;) file. If you don’t know what is this file or why it has to be in
this location, I’ve explained it in &lt;a href=&quot;https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image#where-to-keep-dockerignore-file&quot;&gt;previous post&lt;/a&gt;.
Mine currently looks like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.dockerignore
.env
.git
.gitignore
.vs
.vscode
*/bin
*/obj
**/.toolstarget
.idea
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;running-composed-system&quot;&gt;Running composed system&lt;/h3&gt;
&lt;p&gt;To run this example system, just go to solution directory, type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose up&lt;/code&gt; command (&lt;a href=&quot;#docker-compose-up&quot;&gt;explained earlier&lt;/a&gt;) and after a moment (DB initialization takes a while) you
can open browser and see data pulled from DB with &lt;em&gt;http://localhost:8080&lt;/em&gt; GET method:
&lt;img src=&quot;/assets/images/dotnet-core-with-mysql-app.jpg&quot; alt=&quot;Browser with products taken from DB seen as JSON array&quot; title=&quot;See data from DB in Browser&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;persisting-db-data&quot;&gt;Persisting DB data&lt;/h3&gt;
&lt;p&gt;As you know, killed container is killed along with the data, which of course is not what we want from production database. To persist
changes which we introduced to DB during production runtime, we need to use &lt;em&gt;volumes&lt;/em&gt;, but I thought that it’s too much for simple introduction
article. If you succeeded previous steps, now you can &lt;a href=&quot;https://docs.docker.com/compose/compose-file/#volume-configuration-reference&quot;&gt;read about docker volumes&lt;/a&gt;.
Maybe some day I will explain it in whole different post.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;As you see, composing in general is simple, and basically requires one text file which instructs Docker how to run and couple containers together.&lt;/p&gt;

&lt;p&gt;I hope you find this introduction useful. 
If there is something not clear, ask me a question in comment, and if you think it can help other too, share this article on &lt;a href=&quot;https://www.facebook.com/sharer/sharer.php?u=https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database&quot;&gt;Facebook&lt;/a&gt; or &lt;a href=&quot;https://twitter.com/share?text=Docker+compose+introduction+-+Dotnet+core+app+with+MySQL+Database&amp;amp;url=https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database&quot;&gt;Twitter&lt;/a&gt; :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="docker" />
      
        <category term="dotnet" />
      
        <category term="sql" />
      

      
        <summary type="html">Introduction to docker-compose - basic aspects explained with .NET Core app and MySQL DB composed together.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Multi-project .net core solution in Docker image</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/docker-multiproject-solution.jpg</url>
          <title>Multi-project .net core solution in Docker image</title>
          <link>https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image" rel="alternate" type="text/html" title="Multi-project .net core solution in Docker image" />
      <published>2019-07-27T08:00:00+00:00</published>
      <updated>2020-03-20T16:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/multi-project-dotnet-core-solution-in-docker-image">&lt;p&gt;Creating Docker images from dotnet solution with project references is easy when you understand basics of Docker commands, but writing proper Dockerfile can be tricky for beginners.&lt;/p&gt;

&lt;p&gt;Most of the examples show how to dockerize dotnet project, assuming that it has no local dependencies.
So let’s analyse what can we do, when our project has references to other projects from solution.
We will start with diving into simple example without dependencies first, to understand what changes we introduce and why.&lt;br /&gt;
If you just want to &lt;a href=&quot;#solution&quot;&gt;skip to solution&lt;/a&gt; and copy-paste it, of course you can do it, but it’s not recommended, because 
sooner or later you will be blocked with another obstacle due to not understanding what is happening, and in result you will waste more time.
This article is good to start learning Docker instructions and commands, because everything is explained with short, plain language.
We will use &lt;em&gt;.net core 2.2&lt;/em&gt; as it’s current version at the moment of writing.&lt;/p&gt;

&lt;p&gt;Example dockerized dotnet core application is &lt;a href=&quot;https://github.com/SoftwareDeveloperBlog/Docker-dotnet-core-multi-project-solution&quot;&gt;available on GitHub&lt;/a&gt;, feel free to use it for your needs.&lt;/p&gt;

&lt;h2 id=&quot;analysis-of-official-docker-example&quot;&gt;Analysis of official Docker example&lt;/h2&gt;
&lt;p&gt;Official &lt;a href=&quot;https://docs.docker.com/engine/examples/dotnetcore&quot;&gt;dockerize an .NET Core application article&lt;/a&gt; shows us this Dockerfile located in project folder (where &lt;em&gt;.csproj&lt;/em&gt; file is stored):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Copy csproj and restore as distinct layers&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; *.csproj ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet restore

&lt;span class=&quot;c&quot;&gt;# Copy everything else and build&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet publish &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; out

&lt;span class=&quot;c&quot;&gt;# Build runtime image&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/aspnet:2.2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=build-env /app/out .&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;dotnet&quot;, &quot;PROJECT_NAME.dll&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and those two commands, to be run from project folder where Dockerfile is located:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;docker build &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; aspnetapp &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
docker run &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; 8080:80 &lt;span class=&quot;nt&quot;&gt;--name&lt;/span&gt; myapp aspnetapp&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;dockerfile-from-instruction&quot;&gt;Dockerfile FROM instruction&lt;/h3&gt;
&lt;p&gt;Our Dockerfile starts with &lt;em&gt;FROM&lt;/em&gt; instruction:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env&lt;/code&gt;&lt;br /&gt;
which means that we base our image on official &lt;em&gt;Microsoft Dotnet Core SDK&lt;/em&gt; in version 2.2.
We use SDK at this moment, not production runtime, because we will compile our application in Docker during building image.
So you don’t even need to have .net core SDK installed on your host machine, this Dockerfile is prepared in a way that you won’t compile 
your app for Docker image yourself - Docker will compile it. You could use binaries built on your host machine, but it’s not safe - it may not work due to compatibility troubles.
Why there is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AS build-env&lt;/code&gt; instruction - we will come to this later (in &lt;a href=&quot;#docker-multi-stage-build&quot;&gt;Docker multi-stage build&lt;/a&gt; section).&lt;/p&gt;

&lt;h3 id=&quot;dockerfile-workdir-instruction&quot;&gt;Dockerfile WORKDIR instruction&lt;/h3&gt;
&lt;p&gt;In second line we see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WORKDIR /app&lt;/code&gt; instruction which means, that following RUN, CMD, ENTRYPOINT, COPY and ADD instructions in our Dockerfile
will be executed in &lt;em&gt;/app&lt;/em&gt; directory. If it doesn’t exist it will be created (even if it wouldn’t be used).&lt;/p&gt;

&lt;h3 id=&quot;dockerfile-copy-instruction&quot;&gt;Dockerfile COPY instruction&lt;/h3&gt;
&lt;p&gt;Next we see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY *.csproj ./&lt;/code&gt; instruction, which means that all csproj files from Docker build context will be copied to workdir (&lt;em&gt;/app&lt;/em&gt;) directory
inside Docker image. &lt;a href=&quot;#docker-build-command&quot;&gt;Docker build command will be explained later&lt;/a&gt;, but in short - build context is the directory from your host
machine, pointed in Docker build command. If you point &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; path, the directory where you execute command is taken.
So in our case we copy only one &lt;em&gt;csproj&lt;/em&gt; file, because we run build command with project directory set as build context.&lt;/p&gt;

&lt;h3 id=&quot;dockerfile-run-instruction&quot;&gt;Dockerfile RUN instruction&lt;/h3&gt;
&lt;p&gt;Next is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet restore&lt;/code&gt; instruction, which simply runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet restore&lt;/code&gt; command in our workdir (&lt;em&gt;/app&lt;/em&gt;) directory.
At this moment inside &lt;em&gt;/app&lt;/em&gt; directory in our image, is nothing but &lt;em&gt;.csproj&lt;/em&gt; of our project, because we copied only it
in previous step, but it’s enough for restoring nuget dependencies.&lt;/p&gt;

&lt;h3 id=&quot;copy-and-compile-app-source&quot;&gt;Copy and compile app source&lt;/h3&gt;
&lt;p&gt;Again we see &lt;a href=&quot;#dockerfile-copy-instruction&quot;&gt;COPY instruction&lt;/a&gt; - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY . ./&lt;/code&gt; to copy everything from our build context - in our case 
it means project files (.cs files etc.), because we run &lt;a href=&quot;#docker-build-command&quot;&gt;docker build command&lt;/a&gt; with project directory set as build context.
Then with &lt;a href=&quot;#dockerfile-run-instruction&quot;&gt;run instruction&lt;/a&gt; - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet publish -c Release -o out&lt;/code&gt; we simply run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet publish&lt;/code&gt; in our workdir (&lt;em&gt;/app&lt;/em&gt;) 
directory inside image, with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-c Release -o out&lt;/code&gt; parameters. This dotnet command
compiles our app with release configuration and publishes results in &lt;em&gt;out&lt;/em&gt; directory (in our case &lt;em&gt;/app/out&lt;/em&gt;).
We can compile source because we &lt;a href=&quot;#dockerfile-from-instruction&quot;&gt;base this image on developer’s sdk&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;docker-multi-stage-build&quot;&gt;Docker multi-stage build&lt;/h3&gt;
&lt;p&gt;Once again we see &lt;a href=&quot;#dockerfile-from-instruction&quot;&gt;FROM instruction&lt;/a&gt;, which sets on which image we base our image…
How it’s possible to specify it again, with different base? It’s quite fresh Docker feature (since Docker 17.05 version) called multi-stage builds.
When we use FROM keyword again, we mean that previous image specified above is temporary, and was used only to serve some purpose.
In our case it was made only to compile our application - that’s why we used SDK as base image. Now we specify base image again and this time
we are preparing our &lt;em&gt;real&lt;/em&gt; image - the one which will be deployed to production, and this one doesn’t base on SDK, only on production runtime,
which gives smaller size in result. We will just copy our compiled app from temporary image. So again we specify working directory to &lt;em&gt;/app&lt;/em&gt;
catalog and then we copy our binaries - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from=build-env /app/out .&lt;/code&gt; which means copy files from &lt;em&gt;/app/out/&lt;/em&gt; from &lt;em&gt;build-env&lt;/em&gt; image
(that’s why we gave it a name in the first line) to current working directory (&lt;em&gt;/app&lt;/em&gt;).&lt;/p&gt;

&lt;h3 id=&quot;dockerfile-entrypoint-instruction&quot;&gt;Dockerfile ENTRYPOINT instruction&lt;/h3&gt;
&lt;p&gt;Very last instruction in this Dockerfile is &lt;em&gt;ENTRYPOINT&lt;/em&gt;, which (in simple terms) specifies a command that will be executed when the container starts.
So in our case - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENTRYPOINT [&quot;dotnet&quot;, &quot;PROJECT_NAME.dll&quot;]&lt;/code&gt; - Docker will run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PROJECT_NAME.dll&lt;/code&gt; parameter (which should be
replaced to our project name of course), to start our app.&lt;/p&gt;

&lt;h3 id=&quot;docker-build-command&quot;&gt;Docker build command&lt;/h3&gt;
&lt;p&gt;With such Dockerfile, we are told to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build -t aspnetapp .&lt;/code&gt; command in project directory (where Dockerfile is stored).
Option: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-t name&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--tag name&lt;/code&gt;) is not mandatory - it allows to tag image (to name it and optionally give it a tag in ‘name:tag’ format), so don’t focus on it,
and look on this command this way: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build .&lt;/code&gt;, because important thing is after options - build context parameter.
Build context is path on host machine which will be accessible during building image for Dockerfile instructions.
In our case it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; path which means that the directory where we run this command is passed as build context.
Because we are told to run this command in project directory (where &lt;em&gt;.csproj&lt;/em&gt; file is stored), our project files are passed as build context.&lt;/p&gt;

&lt;h3 id=&quot;docker-run-command&quot;&gt;Docker run command&lt;/h3&gt;
&lt;p&gt;Docker run command creates container from image.
Image is readonly &lt;em&gt;manual&lt;/em&gt; for Docker, to create container, and container is working &lt;em&gt;virtual machine&lt;/em&gt; where our app lives.
We can think about it this way: image is like a class in object-oriented programming, and container is like an instance, created from this class.
So we can create as many containers (instances) as we want, and it doesn’t affect image (class) - image is only necessary to let Docker know how to create container.
We are told to run it this way:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -d -p 8080:80 --name myapp aspnetapp&lt;/code&gt;&lt;br /&gt;
Without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--detach&lt;/code&gt; option (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt;) we will start seeing app console output from container. 
With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--publish&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p&lt;/code&gt;) option we bind container’s port(s) to the host (by default with TCP, but you can specify UDP and SCTP as well).
With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--name&lt;/code&gt; option we assign a name to the container (without this option Docker will choose some funny name for us).
At the end we pass image name, which Docker will read to create container. Because we named our image &lt;em&gt;aspnetapp&lt;/em&gt;, we use this name here.&lt;/p&gt;

&lt;h2 id=&quot;solution&quot;&gt;Solution&lt;/h2&gt;
&lt;h3 id=&quot;proper-docker-commands&quot;&gt;Proper Docker commands&lt;/h3&gt;
&lt;p&gt;Once we understand what happens in basic example, let’s see how to change it, to make it work when our project has references to other
solution projects.&lt;br /&gt;
The problem is of course, that we run Docker build command from project directory passing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; path as build context.
This means, that files only from this directory will be accessible during building image, and depending projects are of course in other
directories. We have several options to fix this. We can move Dockerfile one level up (to solution directory) and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build&lt;/code&gt; from there.
But it’s recommended to have Dockerfile in project directory, to be able to have more than one Dockerfile in solution (for different projects).
You could also run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build&lt;/code&gt; as before (from project directory), but change build context path to one level up (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;..&lt;/code&gt;).
In my view more elegant is third solution - to &lt;strong&gt;run docker build from solution directory&lt;/strong&gt;, pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; as build context and to specify which Dockerfile we want
to read with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--file&lt;/code&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt;) option, &lt;strong&gt;like this&lt;/strong&gt;:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker build -f PROJECT_DIRECTORY/Dockerfile -t IMAGE_NAME .&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;how-to-adjust-dockerfile&quot;&gt;How to adjust Dockerfile&lt;/h3&gt;
&lt;p&gt;Next we need to adjust Dockerfile, because the one from official example assume that we have project directory as build context.
My version looks like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; . ./&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;dotnet publish PROJECT_NAME &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; Release &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; out

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/dotnet/core/aspnet:2.2&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=build-env /app/PROJECT_NAME/out .&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;dotnet&quot;, &quot;PROJECT_NAME.dll&quot;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I have skipped restoring nuget packages as single step to simplify, restore is included in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet publish&lt;/code&gt; and if it fails due to nuget failure,
error message is legible. But if you have many nuget dependencies you may want to have separate step, because this way Docker treats it as distinct layer and reuses it
if none of &lt;em&gt;csproj&lt;/em&gt; file changed, which gives &lt;a href=&quot;https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image&quot;&gt;smaller build time&lt;/a&gt; (I have described it in &lt;a href=&quot;https://www.softwaredeveloper.blog/optimize-building-net-core-docker-image&quot;&gt;another post&lt;/a&gt;). In this demo restoring nugets is fast enough to skip it, but remember about this if you have long building time (in particular - &lt;em&gt;Restore completed in…&lt;/em&gt; time).&lt;/p&gt;

&lt;p&gt;So we copy all projects (because build context is now solution directory) to &lt;em&gt;/app&lt;/em&gt; directory inside container.
Next from &lt;em&gt;/app&lt;/em&gt; workdir we run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet publish&lt;/code&gt; command, specifying which project to compile -
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RUN dotnet publish PROJECT_NAME -c Release -o out&lt;/code&gt; - here PROJECT_NAME is the directory name with &lt;em&gt;.csproj&lt;/em&gt; file inside.&lt;/p&gt;

&lt;p&gt;Other instructions stays untouched with one small change - during copying compiled app from temporary image, this time we need to pass project name to path:&lt;br /&gt;
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from=build-env /app/PROJECT_NAME/out .&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;where-to-keep-dockerignore-file&quot;&gt;Where to keep .dockerignore file&lt;/h3&gt;
&lt;p&gt;Official article says to add &lt;em&gt;.dockerignore&lt;/em&gt; file to project directory, to make build context as small as possible and lower risk of invalidating cache, which of course is
reasonable. But Docker CLI looks for &lt;em&gt;.dockerignore&lt;/em&gt; file in root directory of the build context, so now we need to move it to solution
directory. But in my view it’s even better, because we don’t need to create and maintain many &lt;em&gt;.dockerignore&lt;/em&gt; files for many projects,
we keep one for all of them. Example rules:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-dockerfile&quot; data-lang=&quot;dockerfile&quot;&gt;*/bin
*/obj
.dockerignore
.env
.git
.gitignore
.vs
.vscode
**/.toolstarget
.idea&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;When I had to dockerize .net core app for the first time I just took Dockerfile from mentioned article, copy-pasted Docker commands
and when I faced obstacle I tried to solve it without analysis of how Docker works. After wasting some time this way, I wasted time again - trying
to just copy-paste solution from the internet - again without analysis of what I do, and without success too. Then I learned (once again in my life…), that haste didn’t save
time, but do the opposite - waste it. 
Because I didn’t find proper article nor tutorial to start I came though official documentation and manuals, which are written nice, but have too many
details for beginners.
This article shows essentials of analysis I took and explains basics with plain language. I hope this way it is good to start writing proper Dockerfile,
without having trouble with situation like the one presented here - dotnet project with references to other projects from solution, nor with any other obstacle.&lt;/p&gt;

&lt;p&gt;What to do when you have already containerized your app, but need to use some dependent system, for example DB?
You can compose them together with docker-compose, which I have described with simple &lt;a href=&quot;https://www.softwaredeveloper.blog/docker-compose-introduction-dotnet-core-app-composed-with-mysql-database&quot;&gt;.NET Core app and MySQL DB as composed system example&lt;/a&gt;, in next article.&lt;/p&gt;

&lt;p&gt;Don’t hesitate to write comment whether it was helpful for you, or to share it on Facebook or Twitter :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="docker" />
      
        <category term="dotnet" />
      

      
        <summary type="html">Creating Docker images from dotnet solution with project references is easy when you understand basics of Docker commands, but writing proper Dockerfile can be tricky for beginners.</summary>
      
    </entry>
  
    
    
    
      
    
    
    
      
    
    
    
    <entry>
      <title type="html">Fare (xeger) - generate string that matches regex pattern in C#</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/regex-xeger.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/regex-xeger.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/regex-xeger.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/regex-xeger.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/regex-xeger.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/regex-xeger.jpg</url>
          <title>Fare (xeger) - generate string that matches regex pattern in C#</title>
          <link>https://www.softwaredeveloper.blog/fare-generate-string-matching-regex-in-csharp</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/fare-generate-string-matching-regex-in-csharp" rel="alternate" type="text/html" title="Fare (xeger) - generate string that matches regex pattern in C#" />
      <published>2019-03-18T10:00:00+00:00</published>
      <updated>2019-04-01T09:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/fare-generate-string-matching-regex-in-csharp</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/fare-generate-string-matching-regex-in-csharp">&lt;p&gt;Matching text using regex patterns is extremely useful.
But sometimes we need to invert this process and create text which will match the regex.
The solution has already been written.&lt;/p&gt;

&lt;p&gt;When I faced this problem for the first time, and quick search on the internet didn’t bring up anything useful, I thought:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;I will implement simple generator. I know basic rules of how regex patterns work, I just need to invert them with correct random value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hey, I’m a software developer, so called &lt;em&gt;Not Invented Here Syndrome&lt;/em&gt; is our frequent problem :)
Looking through the internet I can tell that I’m not the only one who had the same idea about generating regex inputs.&lt;/p&gt;

&lt;p&gt;So I started implementation, but after a while I realized that I don’t have enough time to satisfy all cases, so I focused only on basics, which could be enough for my problem.&lt;/p&gt;

&lt;p&gt;Luckily I decided to repeat internet research, this time putting bigger effort into it.
There were no many articles around this topic, but this time I found solution which was easy to use and better than my own implementation.&lt;/p&gt;

&lt;h2 id=&quot;fare-project&quot;&gt;Fare project&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/moodmosaic/Fare&quot;&gt;Fare&lt;/a&gt; is a .NET port of libraries appreciated by Java community: &lt;a href=&quot;https://www.brics.dk/automaton/&quot;&gt;dk.brics.automaton&lt;/a&gt; and &lt;a href=&quot;https://github.com/bluezio/xeger&quot;&gt;xeger&lt;/a&gt;.
The first library has focus in &lt;a href=&quot;https://en.wikipedia.org/wiki/Nondeterministic_finite_automaton&quot;&gt;nondeterministic and deterministtic finite automaton&lt;/a&gt;. On top of it is built xeger, which is meant to be opposite of regex - it uses finite state machine for creating text which match passed regular expression.&lt;/p&gt;

&lt;p&gt;In Java world you need them both. In .NET we have them under one project called Fare, which stands for &lt;strong&gt;F&lt;/strong&gt;inite &lt;strong&gt;A&lt;/strong&gt;utomata and &lt;strong&gt;R&lt;/strong&gt;egular &lt;strong&gt;E&lt;/strong&gt;xpressions.&lt;/p&gt;

&lt;p&gt;It’s convenient library known by many companies as well as open source projects, for example &lt;a href=&quot;https://github.com/AutoFixture/AutoFixture/search?q=fare&amp;amp;unscoped_q=fare&quot;&gt;AutoFixture&lt;/a&gt;
or &lt;a href=&quot;https://github.com/WireMock-Net/WireMock.Net/search?q=fare&amp;amp;unscoped_q=fare&quot;&gt;WireMock.Net&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;fare-hello-world-example&quot;&gt;Fare &lt;em&gt;Hello world&lt;/em&gt; example&lt;/h2&gt;
&lt;p&gt;You can download Fare &lt;a href=&quot;https://www.nuget.org/packages/Fare/&quot;&gt;from nuget&lt;/a&gt;, it works for .NET Framework and for .NET Core.
For example, I’m writing following instructions with Fare version 2.1.1 on Linux Mint 19 Tara in .NET Core 2.2 console application.&lt;/p&gt;

&lt;p&gt;You just need to create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Xeger&lt;/code&gt; object with your regex pattern (as string) and with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Random&lt;/code&gt; object.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t.m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Xeger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xeger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Xeger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Input text matching regex: &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos; is: &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xeger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;cm&quot;&gt;/* Example output:
Input text matching regex: &apos;t.m&apos; is: &apos;t8m&apos;
*/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Basically that’s it. In above example input will be random every time.
To see how generated inputs might differ, let’s replace &lt;em&gt;t.m&lt;/em&gt; pattern with &lt;em&gt;t.*m&lt;/em&gt; pattern and loop generating ten times. 
As you will see, with &lt;em&gt;.*&lt;/em&gt; pattern results might be any size, including 0 (see last input in my example, it’s real value copied from my console output).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t.*m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Xeger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xeger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Xeger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Input text matching regex: &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos; is: &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xeger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                
&lt;span class=&quot;cm&quot;&gt;/* Example output:
Input text matching regex: &apos;t.*m&apos; is: &apos;trmYwmm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;tsm!tw-m:s}mm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;t}}*3molmrxBGmmss-mm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;tLy4mmmm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;tCkm=!?iI|mm&quot;LmmV}wm&quot;63mmnb.G+mxzumNm`wn[m&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;tux@mByyQ~8vxm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;tn|mmu npmj/~w#mmmmmm9mm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;t}Zm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;tomX}8kkV{j)x&amp;lt;S}_mTmm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;tm&apos;
*/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;deterministic-fare-input&quot;&gt;Deterministic Fare input&lt;/h2&gt;
&lt;p&gt;When we pass &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new Random()&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Xeger&lt;/code&gt; constructor, Fare generates random input. Usually that’s what we want, but sometimes we need to have the same input for 
given regex every time (that was my production case). How to achieve that? You can simply pass constant seed to dotnet &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Random&lt;/code&gt; constructor,
for example 0 :) That’s it, now under the same runtime your input generation sequence will be the same.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot; data-lang=&quot;csharp&quot;&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;t.*m&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;Xeger&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xeger&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Xeger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Note zero in Random constructor&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;$&quot;Input text matching regex: &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos; is: &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;xeger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                                
&lt;span class=&quot;cm&quot;&gt;/* Output sequence in my environment will be every run the same:
Input text matching regex: &apos;t.*m&apos; is: &apos;tm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;t~6x~bm^m&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;tmoBlz=~z5mC1zvcmmqm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;t|lym&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;teDsmmmmy6m&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;tmmmm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;t~pm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;t|5mmrw5|ommxmpNlo`x~G^wym&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;t%+u=mm~mrmmkm*mmmlU/w7\OyncmrDdx&amp;lt;lnm&apos;
Input text matching regex: &apos;t.*m&apos; is: &apos;t$X`w`mm:m~o~m&apos;
*/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;benchmarks&quot;&gt;Benchmarks&lt;/h2&gt;
&lt;p&gt;Usually fare is quite fast and should be sufficient for production requirements.
I have created a few benchmarks, with usual process priority, just for orientation.
As you will see, reusing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xeger&lt;/code&gt; is really fast (and generation time is repeatable) and creating xeger requires acceptable time as well.
But remember that xeger with complex regex needs much more time than simple one, for example creating xeger with regex matching email address (taken from
&lt;a href=&quot;https://www.regextester.com/19&quot;&gt;regextester.com&lt;/a&gt;) takes for me 50 milliseconds: 
&lt;sub&gt;&lt;sup&gt;&lt;code&gt;^[a-zA-Z0-9.!#$%&amp;amp;&apos;*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$&lt;/code&gt;&lt;/sup&gt;&lt;/sub&gt;.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Measured operation&lt;/th&gt;
      &lt;th&gt;Preparation&lt;/th&gt;
      &lt;th&gt;Mean time&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;new Xeger(_regex, _random);&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_random = new Random(); _regex = &quot;t&quot;;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;274.6 ns &lt;sub&gt;&lt;sup&gt;(nanos.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;new Xeger(_regex, _random);&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_random = new Random(); _regex = &quot;t.m&quot;;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;105.9 us &lt;sub&gt;&lt;sup&gt;(micros.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;new Xeger(_regex, _random);&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_random = new Random(); _regex = &quot;t.*m&quot;;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;160.7 us &lt;sub&gt;&lt;sup&gt;(micros.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;new Xeger(_regex, _random);&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_random = new Random(); _regex = &quot;alice|tom&quot;;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;343.6 us &lt;sub&gt;&lt;sup&gt;(micros.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;new Xeger(_regex, _random);&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_random = new Random();&lt;/code&gt;&lt;br /&gt;&lt;sub&gt;&lt;sup&gt;&lt;code&gt;_regex = &quot;[a-z.!#$%&amp;amp;&apos;*+=?^_`{|}~-]+@.*(?:[a-z]{0,61}.*)?(?:\\..*(?:[a-z]{0,61}[a-z])?)*&quot;;&lt;/code&gt;&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
      &lt;td&gt;9.042 ms &lt;sub&gt;&lt;sup&gt;(millis.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;_xeger.Generate();&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_xeger = new Xeger(&quot;t&quot;, new Random());&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;650.5 ns &lt;sub&gt;&lt;sup&gt;(nanos.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;sub&gt;&lt;sup&gt;&lt;code&gt;for(int i=0; i&amp;lt;1000; i++) _xeger.Generate();&lt;/code&gt;&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_xeger = new Xeger(&quot;t&quot;, new Random());&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;656.0 us &lt;sub&gt;&lt;sup&gt;(micros.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;_xeger.Generate();&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_xeger = new Xeger(&quot;t.m&quot;, new Random());&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;1.420 us &lt;sub&gt;&lt;sup&gt;(micros.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;sub&gt;&lt;sup&gt;&lt;code&gt;for(int i=0; i&amp;lt;1000; i++) _xeger.Generate();&lt;/code&gt;&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_xeger = new Xeger(&quot;t.m&quot;, new Random());&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;1.437 ms &lt;sub&gt;&lt;sup&gt;(millis.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;_xeger.Generate();&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_xeger = new Xeger(&quot;t.*m&quot;, new Random());&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;9.989 us &lt;sub&gt;&lt;sup&gt;(micros.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;sub&gt;&lt;sup&gt;&lt;code&gt;for(int i=0; i&amp;lt;1000; i++) _xeger.Generate();&lt;/code&gt;&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_xeger = new Xeger(&quot;t.*m&quot;, new Random());&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;10.05 ms &lt;sub&gt;&lt;sup&gt;(millis.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;_xeger.Generate();&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_xeger = new Xeger(&lt;sub&gt;&lt;sup&gt;&quot;alice|tom&quot;&lt;/sup&gt;&lt;/sub&gt;, new Random());&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;2.217 us &lt;sub&gt;&lt;sup&gt;(micros.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;sub&gt;&lt;sup&gt;&lt;code&gt;for(int i=0; i&amp;lt;1000; i++) _xeger.Generate();&lt;/code&gt;&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;_xeger = new Xeger(&lt;sub&gt;&lt;sup&gt;&quot;alice|tom&quot;&lt;/sup&gt;&lt;/sub&gt;, new Random());&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;2.183 ms &lt;sub&gt;&lt;sup&gt;(millis.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;_xeger.Generate();&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;&lt;sub&gt;&lt;sup&gt;_xeger = new Xeger(&quot;[a-z.!#$%&amp;amp;&apos;*+=?^_`{|}~-]+@.*(?:[a-z]{0,61}.*)?(?:\\..*(?:[a-z]{0,61}[a-z])?)*&quot;&lt;br /&gt;, new Random());&lt;/sup&gt;&lt;/sub&gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;8.435 us &lt;sub&gt;&lt;sup&gt;(micros.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;sub&gt;&lt;sup&gt;&lt;code&gt;for(int i=0; i&amp;lt;1000; i++) _xeger.Generate();&lt;/code&gt;&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code&gt;&lt;sub&gt;&lt;sup&gt;_xeger = new Xeger(&quot;[a-z.!#$%&amp;amp;&apos;*+=?^_`{|}~-]+@.*(?:[a-z]{0,61}.*)?(?:\\..*(?:[a-z]{0,61}[a-z])?)*&quot;&lt;br /&gt;, new Random());&lt;/sup&gt;&lt;/sub&gt;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;8.160 ms &lt;sub&gt;&lt;sup&gt;(millis.)&lt;/sup&gt;&lt;/sub&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p style=&quot;text-align: right&quot;&gt;
&lt;sub&gt;&lt;sup&gt;&lt;sup&gt;
BenchmarkDotNet=v0.11.4, OS=linuxmint 19 Intel Core i7-4702MQ CPU 2.20GHz (Haswell), 1 CPU, 8 logical and 4 physical cores&lt;br /&gt;
.NET Core SDK=2.2.105 [Host] : .NET Core 2.2.3 (CoreCLR 4.6.27414.05, CoreFX 4.6.27414.05), 64bit RyuJIT&lt;br /&gt;
&lt;/sup&gt;&lt;/sup&gt;&lt;/sub&gt;
&lt;/p&gt;

&lt;h2 id=&quot;downsides-of-fare&quot;&gt;Downsides of Fare&lt;/h2&gt;
&lt;h3 id=&quot;pattern-with-unspecified-length-might-take-long-time-to-generate&quot;&gt;Pattern with unspecified length might take long time to generate&lt;/h3&gt;
&lt;p&gt;Sometimes Fare wants to generate extremally long phrase for pattern which doesn’t specify length.
I have seen cases for which xeger needed 10minutes to generate! Unfortunately for now there is no way to configure it in Fare (but it’s open source, you can improve it 😺).
You can assume that it’s nondeterministic, because you’ll never know for which regex and with which random (or not random) value it will happen.
What I do? Before I pass pattern to Xeger, I remove every &lt;em&gt;*&lt;/em&gt; character. Asterisk means 0 or more characters, so this way it will always be one, which is correct, but of course it’s trade-off for more limited results.
I know that there are still other possible regexs of unknown length, for example &lt;em&gt;\w{3,}&lt;/em&gt; or &lt;em&gt;\d+&lt;/em&gt; but in my case it’s enough, I don’t expect such patterns.
If it’s not enough in your case, then you should process other possibilities too, to remove every option which takes any length, 
or not to remove, but to provide simple values for those basic rules. Or fork Fare and allow to configure this behaviour in it :)&lt;/p&gt;

&lt;h3 id=&quot;always-trycatch-and-be-prepared-for-bugs-and-exceptions&quot;&gt;Always try..catch, and be prepared for bugs and exceptions&lt;/h3&gt;
&lt;p&gt;Unfortunately Fare has bugs. Sometimes it crashes, even with simple patterns.
For example creating xeger with pattern &lt;em&gt;&amp;lt;a.*b&amp;gt;&lt;/em&gt; will cause &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;System.ArgumentException: &apos;a.*b&apos; not found&lt;/code&gt;.
Once I have seen a bug, which allowed creating not valid input. It turned out that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Xeger&lt;/code&gt; treated multiplied &lt;em&gt;start/end of line&lt;/em&gt; marks like regular characters. Input would be fine, but it had &lt;em&gt;^&lt;/em&gt; or &lt;em&gt;$&lt;/em&gt; character at the beginning and/or at the end.
Such pattern came to my system only once, but this particular one was important to me, so I changed my code to check whether generated input is valid
and if it’s not, then try to fix it with trimming every &lt;em&gt;^&lt;/em&gt; and &lt;em&gt;$&lt;/em&gt; characters.
I know that it’s naive, but if it doesn’t help I just skip the pattern, it is allowed in my case (in general) - not every regex need to be inverted,
occasionally I can skip one and it’s fine. You can reproduce this bug this way: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new Xeger(&quot;^0$|^1$&quot;, new Random())&lt;/code&gt; (in my environment it’s always &lt;em&gt;0$&lt;/em&gt; or &lt;em&gt;^1&lt;/em&gt;).&lt;/p&gt;

&lt;h3 id=&quot;you-may-find-unsupported-pattern&quot;&gt;You may find unsupported pattern&lt;/h3&gt;
&lt;p&gt;As you know regex is wide and you might find construction not implemented by xeger. But majority of patterns are satisfied and to be honest -
I can’t tell which are not. There is no documentation in Fare project, and the one in &lt;a href=&quot;https://github.com/bluezio/xeger&quot;&gt;Java’s xeger&lt;/a&gt; is not up
to date, because I have checked some limitations they have mentioned and those not supported patterns (as they say) work properly… So there are probably some not implemented cases, but it’s minority :)&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;So to summarise, fare is a great library and does the job but… not entire job.
You need to add your own &lt;em&gt;few lines of code&lt;/em&gt; and get prepared for exceptional situations and bugs.
In my case it’s enough and loosing input occasionally is acceptable in my situation.
If it’s not acceptable in yours, I still recommend using Fare but with fixing/extending it first.
That way or another it’s cheaper to start from theirs code base than writing everything from scratch.
&lt;a href=&quot;https://github.com/moodmosaic/Fare&quot;&gt;They’re open source&lt;/a&gt; so you can share your work with pull request, to help others and bring yourself a bit of splendour :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="dotnet" />
      

      
        <summary type="html">Matching text using regex patterns is extremely useful. But sometimes we need to invert this process and create text which will match the regex. The solution has already been written.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Libsecret - remember Git credentials in Linux Mint and Ubuntu securely</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/git-key.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/git-key.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/git-key.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/git-key.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/git-key.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/git-key.jpg</url>
          <title>Libsecret - remember Git credentials in Linux Mint and Ubuntu securely</title>
          <link>https://www.softwaredeveloper.blog/git-credential-storage-libsecret</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/git-credential-storage-libsecret" rel="alternate" type="text/html" title="Libsecret - remember Git credentials in Linux Mint and Ubuntu securely" />
      <published>2019-03-06T06:00:00+00:00</published>
      <updated>2019-10-29T06:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/git-credential-storage-libsecret</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/git-credential-storage-libsecret">&lt;p&gt;Typing username and password every push is burdensome and irritating… libsecret git credential storage to the rescue.&lt;/p&gt;

&lt;p&gt;When you want to use external git repository hosting service, like &lt;em&gt;Gitlab&lt;/em&gt; or &lt;em&gt;GitHub&lt;/em&gt; you need to authorise yourself.
Git has built in credential helper mechanism, which allows choosing the way you have credentials persisted.
It gives you two options out of the box, but you’re not limited to them - you can install third party solution.&lt;/p&gt;

&lt;h3 id=&quot;cache-credential-helper-out-of-the-box&quot;&gt;&lt;em&gt;Cache&lt;/em&gt; credential helper (out of the box)&lt;/h3&gt;
&lt;p&gt;Cache is quite secure because keeps data only in memory. It’s fine for security, but every time you open new session, you need to type credentials again.
Memory is purged after 900 seconds (15 min) by default, but it can be changed with optional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timeout&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;If for some reason you don’t want to install anything, at least use cache :)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git config &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; credential.helper &lt;span class=&quot;s1&quot;&gt;&apos;cache --timeout=300&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Official description - &lt;a href=&quot;https://git-scm.com/docs/git-credential-cache&quot;&gt;git-scm.com/docs/git-credential-cache&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;store-credential-helper-out-of-the-box&quot;&gt;&lt;em&gt;Store&lt;/em&gt; credential helper (out of the box)&lt;/h3&gt;
&lt;p&gt;Store keeps your username and password in… plain text file! It’s totally insecure and use it only if you don’t care about your account (for example during some kind of workshops).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;git config &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; credential.helper store&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Official description - &lt;a href=&quot;https://git-scm.com/docs/git-credential-store&quot;&gt;git-scm.com/docs/git-credential-store&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;git-credential-manager-for-mac-and-linux-by-microsoft&quot;&gt;&lt;em&gt;Git Credential Manager for Mac and Linux&lt;/em&gt; (by Microsoft)&lt;/h3&gt;
&lt;p&gt;Windows users are familiar with convenient &lt;em&gt;Git Credential Manager for Windows&lt;/em&gt; which is integrated with OS way of storing credentials. It can be installed with official wizard or during git installation on Windows (there is checkbox to select).
And recently Microsoft announced release of… &lt;em&gt;Git Credential Manager for Mac and Linux&lt;/em&gt;. I was positively surprised, till I tried to use it on my &lt;em&gt;Linux Mint 19 Tara&lt;/em&gt;. Installation was successful (but not without complications) and attempt to use it ended up with error:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;Fatal: java.lang.RuntimeException encountered. Details: 
Secure credential storage is not available on this operating system. You may opt-in to store credentials &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;an unencrypted file under your user home directory by running &lt;span class=&quot;s1&quot;&gt;&apos;git config --global credential.canFallBackToInsecureStore true&apos;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
fatal: credential helper &lt;span class=&quot;s1&quot;&gt;&apos;!/usr/lib/jvm/java-11-openjdk-amd64/bin/java -Ddebug=false -Djava.net.useSystemProxies=true -jar /home/linuxbrew/.linuxbrew/Cellar/git-credential-manager/2.0.4/libexec/git-credential-manager-2.0.4.jar&apos;&lt;/span&gt; told us to quit&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I have opened the issue and till now I haven’t received any response (more than 2 months) and recently I have seen the same issue created for &lt;em&gt;Ubuntu 18.04&lt;/em&gt;. So currently it doesn’t work on Linux Mint and Ubuntu.&lt;/p&gt;

&lt;p&gt;Official description - &lt;a href=&quot;https://github.com/Microsoft/Git-Credential-Manager-for-Mac-and-Linux&quot;&gt;github.com/Microsoft/Git-Credential-Manager-for-Mac-and-Linux&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;libsecret-git-credential-storage-by-gnome&quot;&gt;&lt;em&gt;Libsecret&lt;/em&gt; git credential storage (by GNOME)&lt;/h2&gt;
&lt;p&gt;The best way to store Git credentials on Linux used to be &lt;em&gt;GNOME Keyring&lt;/em&gt; (libgnome-keyring), but as it is specific to GNOME, &lt;a href=&quot;https://mail.gnome.org/archives/commits-list/2014-January/msg01585.html&quot;&gt;it is deprecated since January 2014&lt;/a&gt;.
For Git versions 2.11+ you should use credential helper based on &lt;em&gt;libsecret&lt;/em&gt;.
Installation and configuration takes only 4 bash commands :)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt-get &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;libsecret-1-0 libsecret-1-dev
&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /usr/share/doc/git/contrib/credential/libsecret
&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;make
git config &lt;span class=&quot;nt&quot;&gt;--global&lt;/span&gt; credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;That’s all, next time when you’ll be asked for your username and password will be the last time on this device :)&lt;/p&gt;

&lt;p&gt;Official description - &lt;a href=&quot;https://wiki.gnome.org/Projects/Libsecret&quot;&gt;wiki.gnome.org/Projects/Libsecret&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-secure-ssh-credentials&quot;&gt;How to secure SSH credentials&lt;/h2&gt;
&lt;p&gt;Above credential managers work only if you use http(s), which is usual case. If you have to use SSH protocol,
I have described how to skip asking for credentials with SSH key-pair in safe manner, in &lt;a href=&quot;https://www.softwaredeveloper.blog/store-git-ssh-credentials-in-linux&quot;&gt;another article&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;As you see it’s not difficult to protect your credentials and not have to type them all the time.
If it doesn’t work for you or you have some question or unusual case, feel free to ask me in comments, I will do my best to help you.&lt;br /&gt;
If you know someone who uses unsafe credential manager or doesn’t use manager at all, send him this article, I will appreciate it :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      
        <category term="linux" />
      
        <category term="git" />
      
        <category term="security" />
      

      
        <summary type="html">Typing username and password every push is burdensome and irritating… libsecret git credential storage to the rescue.</summary>
      
    </entry>
  
    
    
    
    <entry>
      <title type="html">Welcome to Software Developer Blog</title>
      
        
         
        <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.softwaredeveloper.blog/assets/images/welcome.jpg"/>
        <media>https://www.softwaredeveloper.blog/assets/images/welcome.jpg</media>
        <media><img src="https://www.softwaredeveloper.blog/assets/images/welcome.jpg"/></media>
        <media url="https://www.softwaredeveloper.blog/assets/images/welcome.jpg" />
        <img src="https://www.softwaredeveloper.blog/assets/images/welcome.jpg" />
        <image>
          <url>https://www.softwaredeveloper.blog/assets/images/welcome.jpg</url>
          <title>Welcome to Software Developer Blog</title>
          <link>https://www.softwaredeveloper.blog/welcome</link>
        </image>
      
      <link href="https://www.softwaredeveloper.blog/welcome" rel="alternate" type="text/html" title="Welcome to Software Developer Blog" />
      <published>2019-03-04T10:00:00+00:00</published>
      <updated>2019-03-05T12:00:00+00:00</updated>
      <id>https://www.softwaredeveloper.blog/welcome</id>
      <content type="html" xml:base="https://www.softwaredeveloper.blog/welcome">&lt;p&gt;Hey! Welcome to Software Developer Blog, it’s great to have your attention :)&lt;/p&gt;

&lt;p&gt;I’m &lt;a href=&quot;/author/tometchy&quot;&gt;Tometchy&lt;/a&gt; and I’m about to share with you some coder thoughts.&lt;/p&gt;

&lt;p&gt;Mainly &lt;strong&gt;code and frameworks focused solutions&lt;/strong&gt;, but also general &lt;strong&gt;work environment tips and tricks&lt;/strong&gt;, including version control, IDE, operating system and continuous integration/delivery.
From time to time I plan to write about higher level solutions, like &lt;strong&gt;planning or design methodologies&lt;/strong&gt; (for example &lt;em&gt;Scrum&lt;/em&gt; or &lt;em&gt;Event Storming&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;My primary language is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;C#&lt;/code&gt; so it will probably dominate, but definitely won’t be lonely here. Some frontend and scripting languages will be here as well.&lt;/p&gt;

&lt;p&gt;Despite I grew up with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Windows&lt;/code&gt; and started professional collaborating with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SVN&lt;/code&gt;, for the last few years I’m excited with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Linux&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Git&lt;/code&gt;, and great possibilities they offer. Therefore, expect focus on the latter, rather than the former.&lt;/p&gt;

&lt;p&gt;Details about site structure are described in &lt;a href=&quot;/about&quot;&gt;about&lt;/a&gt; section.&lt;/p&gt;

&lt;p&gt;I hope you will have valuable time, and won’t hesitate to come back again :)&lt;/p&gt;</content>

      
      
      
      
      

      <author>
          <name>Tometchy</name>
        
        
      </author>

      

      

      
        <summary type="html">Hey! Welcome to Software Developer Blog, it’s great to have your attention :)</summary>
      
    </entry>
  
</feed>
