What Helm Is

Helm is a tool used to create and deploy templates that define entities in Kubernetes. It’s kind of like taking Kubernetes YAML and adding handlebars template support. For example, you might see something like this:

apiVersion: v1
kind: ReplicationController
  name: deis-database
  namespace: deis
    app.kubernetes.io/managed-by: deis
  replicas: 1
    app.kubernetes.io/name: deis-database
        app.kubernetes.io/name: deis-database
      serviceAccount: deis-database
        - name: deis-database
          image: {{.Values.imageRegistry}}/postgres:{{.Values.dockerTag}}
          imagePullPolicy: {{.Values.pullPolicy}}
            - containerPort: 5432
            - name: DATABASE_STORAGE
              value: {{default "minio" .Values.storage}}

In this case, you can see there are some values poked in from another YAML document that has some configuration parameters. The values document might look like this:

imageRegistry: "quay.io/deis"
dockerTag: "latest"
pullPolicy: "Always"
storage: "gcs"

When Helm does an installation or upgrade, it takes the parameters, fills in the appropriate blanks in the larger YAML deployment templates, and does the Kubernetes work to execute the deployment.

Since there are a lot of pieces to putting things in Kubernetes - maybe you have some deployments, services, etc. that all need to go in at the same time - Helm uses a concept called “charts” to bundle these up as an atomic entity. You can think of a Helm chart as a zip file with a bunch of Kubernetes YAML in it and a small manifest that explains what the zip file installs.

There are some great benefits to using Helm to deploy things:

  • Separation of deployment template from configuration. You don’t need to keep rafts of YAML around for different environments/clusters/whatever. You can have different parameters and one larger template.
  • Ability to roll back a deployment. Helm tracks the installations and upgrades you make along with the values. If you deploy something that doesn’t work, you can roll it back to the previous version.
  • Ability to list deployments and versions. You can use Helm to list out the charts and versions that have been deployed to Kubernetes. This makes things that are logically spread around namespaces into a nice, central list.
  • Charts can have dependencies. Let’s say your application needs a Redis instance when you deploy it. Cool! You can set up your chart to have a dependency on installing/upgrading Redis at the same time using the Redis Helm chart.

Things to be aware of that will come into play later:

  • Helm installations are global. When you do a Helm installation of a chart, even if it’s into a specific namespace, the installation itself is a global concept. Let’s say you want to install Redis twice (separately from your applications) - once into a test namespace and once into a prod namespace. Make sure you name those installations in a unique way - the output of the installation may go into the namespace but the installation itself is a global thing outside the namespace. (Unclear if this will change with Helm 3.0.)
  • Helm isn’t using kubectl. For version 2.0 Helm uses a service running in the cluster called tiller to do its installations. Tiller is going away in Helm 3.0 but it’s still not using kubectl - it’s using the Kubernetes API directly. What that means is if you’re looking to use kubectl later (even in an automated fashion) to modify things, you’ll get messages like Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply.
  • Helm installation tracks chart version and install version, not container version. When you install something with Helm, later you can do helm list to get the installations. Doing that, you’ll see the chart version and the data last updated but no information about what’s in that installation.

For the purposes of this article, I’m not going into exactly how Helm does its work. If you’re interested in diving deep, the docs are a good place to start. The important things to understand are some of the benefits and limitations.

Continuous Delivery vs. Continuous Deployment

Let’s talk about “CD.” Some folks differentiate between “continuous delivery” and “continuous deployment.” The apparent differentiator is that “delivery” implies software that’s been tested and is ready to be deployed to production but isn’t; while “deployment” implies another step - the software is actually automatically deployed into production and verified.

I guess you can do that, but I don’t really separate those things in my mind. If it’s ready to go to production… why isn’t it going there? I’d argue that if you differentiate then maybe it’s just that your pipeline (or your org, or your process, or whatever) just isn’t ready for a complete check-in-code to deploy-code-in-production execution. It’s an incomplete pipeline, not “two different things.”

Folks Love Helm for CD

There are a lot of articles like this one pointing out how Helm is the missing link in the CI/CD chain for Kubernetes.

It sort of is, but not the way they explain in articles. At least, not the way I see it.

The process most of the articles describe roughly follows this:

  • Create a Helm chart for your application.
  • In the CD pipeline, create a set of parameters that can be used to helm upgrade your app in Kubernetes.
  • If anything fails, use helm rollback to return to the previous state.

Seems simple enough, right? And it is simple. But this is really the “continuous delivery” sort of pipeline - the pipeline where you’re not actually trying to automate things into production. I can’t imagine you’d ever want to stomp your production deployment like this, hope that it works, and depend on helm rollback to return to a previous state if it doesn’t.

That’s a hugely important differentiator. If all you need to do is get things deployed into some sort of development/testing environment and that’s the limit of your pipeline, I guess that works. I feel like the goal should be bigger than that. I’m a fan of test in production and I’m not a fan of trying to replicate environments across dev, test, perf, and production (or however you break it up).

Given that…

Why I Don’t Like Helm for CD

If I’m thinking about actually deploying into production on a regular basis, I want support for more complex scenarios like canary testing. Let’s think about how that works for Kubernetes.

  • Existing deployment of the service in production is taking traffic.
  • New deployment of the service in production goes in alongside the existing deployment, but takes no traffic.
  • Testing against the new deployment runs internal to the cluster.
  • Traffic handling is tweaked to allow some small amount of production traffic to the new deployment whilst the majority of the traffic is still going to the original deployment. This may be accomplished in a few different ways. For example…
    • With a standard Kubernetes service, you can adjust how much traffic goes to old vs. new based on the number of deployed pods. If you want 10% of the traffic on the new and 90% on old, you’d need that proportion of pods - 1 new, 9 old.
    • With a service mesh like Istio, you can use the traffic management built in to control the percentage of traffic routed to each set of pods. This is a lot cleaner than the standard mechanism but means you have some complexity with a service mesh in the mix.
  • Testing runs on the service and both old and new versions of the deployment are monitored. If the new version starts misbehaving, all traffic is routed back to the original version of the service and the “canary” new version is killed. If the new version behaves, more traffic is directed to the new version and removed from the old version until either all traffic is pointed to the new version or the canary is killed.

How do you do something like that with Helm? In its stock form, you really can’t… or your Helm chart is going to be pretty complicated.

  • It’ll have to allow for parameterization of both existing and new deployments or you’re going to have two deployments side-by-side. Remember how deployments are a global concept? That makes it complicated.
  • You’ll have to figure out how to only have one Kubernetes service that can route to both sets of pods (old and new) or you’ll need something external to the Kubernetes load balancer to handle traffic control across the Kubernetes services.
  • If your chart has the Istio bits in it, again, you want one set of Istio load balancing/ingress controls across both pods, so the canary installation wouldn’t want to deploy those.
  • If you control traffic using Kuberenetes constructs (either by adjusting the ratio of deployed pods or tweaking Istio values) those are all helm upgrade operations. If the canary fails, how many helm rollback operations do you need to perform to get back to the original state?

There are other reasons Helm isn’t great for CD, too. Here’s a pretty good article that talks about some of the challenges and shortcomings you can hit.

Helm is Great for Infrequent Deployments

Helm is great for installing things that you don’t deploy often. Need to deploy Istio? Sweet, Helm to the rescue. You don’t update Istio every day. Need to get an ElasticSearch instance installed that your services can share? Boom! Helm, baby! You won’t be upgrading that every day.

Helm as a way to manage infrastructure or shared services is awesome. Things that don’t require canary testing, continuous rollout, that sort of thing.

Helm is Great for Templating in CD Pipelines

Helm is great as a way to package up a set of YAML and handle parameterization and some calcuations to generate a final Kubernetes YAML for deployment… and in a continuous delivery/continuous deployment context, that’s what I would recommend using it for.

A great example of this is the way Spinnaker uses Helm to deploy things. It’s not using helm install or helm upgrade - instead it can take a Helm chart or Helm-formatted YAML document and it uses helm template to generate the final template with the parameters all populated. It then takes that output and executes the deployment in Kubernetes. The note at the top of the Spinnaker “Deploy Helm Charts” page pretty much says it all:

Note: This stage is intended to help you package and deploy applications that you own, and are actively developing and redeploying frequently. It is not intended to serve as a one-time installation method for third-party packages. If that is your goal, it’s arguably better to call helm install once when bootstrapping your Kubernetes cluster.

If you think about what you get with helm list, you’re getting the chart version, right? Honestly, once you have the chart down, the chart version has no meaning in continuous deployment. The important stuff is the version(s) of the container(s) that are deployed and making up the application. That stuff isn’t tracked, so helm list becomes pretty useless.

Things like Helm chart repositories are also really not interesting. In fact, the YAML for the Helm template may just be embedded right in the CD pipeline itself, not a separate thing stored elsewhere. This allows you to separate things like tweaking Istio load balancer settings from the concept of helm install. It also avoids needing to ensure deployment names are unique.

Finally, it means helm rollback won’t save you. You’re not helm installing anything, so there’s nothing to roll back. You’ll need to ensure your CD pipeline can appropriately kill the canary. However, if you’re doing canary testing and not just stomping your existing production deployment with a new untested deployment… you should be able to easily kill the canary and leave the existing deployment untouched, with no one the wiser that something went awry.

docker, vs comments edit

Container Tools in Visual Studio offers the ability to develop an application and have it run inside a container while Visual Studio debugs it on your host machine. It’s a cool way to see how your app will behave in the container environment with the flexibility of edit-and-refresh functionality that doesn’t require the overhead of rebuilding the container each time.

I ran into a bunch of trouble getting some things working the other day which caused me to dive a little deeper into how this works and I found a few interesting things. Gotchas? Tips? Sure. That.

I’m primarily using the single-container support - not the Docker Compose multi-container support. If you’re all Docker Compose up in there, this may or may not be helpful to you.

You Only Need ‘base’ for VS

The Dockerfile that gets generated has a bunch of named intermediate containers in it - base, build, publish. This is helpful if you don’t already have a Dockerfile, but if you’re really just trying to get debugging working with VS, you only need the base container. You can delete or modify the others.

UPDATE August 16, 2019: Microsoft has some documentation now on how Container Tools builds Dockerfiles. It’s not necessarily base that’s a magic target - it’s “the first stage found in the Dockerfile.”

When VS builds the container, you can see in the Container Tools output window it’s running a command like this:

docker build -f "C:\src\solution\project\Dockerfile" -t project:dev --target base --label "com.microsoft.created-by=visual-studio" "C:\src\solution"

The --target base is the key - it’s not going to build the rest.

(You can change this using <DockerfileFastModeStage> in your project - see below.)

VS Controls Container Startup and Teardown

In Visual Studio the container for debugging will get built and will start as soon as you select the Docker configuration for running. Even if you don’t actually start a debug setting, the container will be pulled, built, and run in the background. The container will continue to run until VS shuts down.

docker run -dt -v "C:\Users\yourname\vsdbg\vs2017u5:/remote_debugger:rw" -v "C:\src\solution\project:/app" -v "C:\Users\yourname\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" -v "C:\Users\yourname\.nuget\packages\:/root/.nuget/fallbackpackages2" -v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_ENVIRONMENT=Development" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -p 58260:80 --entrypoint tail project:dev -f /dev/null

There are some interesting things to note here:

  • A ton is mounted through volumes. Look at all those -v commands. There’s a remote debugger; your application code/source; user secrets; your local NuGet package cache; and your local installation of fallback packages. You’ll get a warning that pops up if you don’t have volume sharing enabled in Docker. You have to allow the drive with your source code to be mounted. VPNs and firewalls can really mess this up by blocking the SMB port.
  • The remote debugger isn’t associated with a VS 2017 install. The path to the remote debugger you see is C:\Users\yourname\vsdbg\vs2017u5 but this isn’t part of a VS 2017 install. Even if you only have VS 2019, it’s still this path. It could change later, but don’t be fooled.
  • The default environment is Development. The Container Tools put the ASPNETCORE_ENVIRONMENT=Development thing in there. You can override this by updating launchSettings.json (see below).
  • The entrypoint is not your application. Notice the entrypiont is tail -f /dev/null. This just ensures the container keeps running but isn’t tied to your application. A separate docker run call will be issued when it’s time to start your app.

During build, you’ll see something like this in the Build output window:

docker exec -i 40b49d8d963bb682a08fed17248212bcfd939456c8030689e9a28f17f5b067e3 /bin/sh -c "if PID=$(pidof dotnet); then kill $PID; fi"

What this is doing is killing the running dotnet command in the container so any files that might be getting regenerated by Visual Studio or whatever won’t mess up the running process.

When you start debugging, the remote debugger starts in the container. I used Process Explorer and Process Monitor to look for docker commands going by. I see that the command to start the remote debugger is:

"docker" exec -i 40b49d8d963bb682a08fed17248212bcfd939456c8030689e9a28f17f5b067e3 /bin/sh -c "ID=.; if [ -e /etc/os-release ]; then . /etc/os-release; fi; if [ $ID = alpine ] && [ -e /remote_debugger/linux-musl-x64/vsdbg ]; then VSDBGPATH=/remote_debugger/linux-musl-x64; else VSDBGPATH=/remote_debugger; fi; $VSDBGPATH/vsdbg --interpreter=vscode"

UPDATE June 18, 2019: After publishing this post I found out that Visual Studio communicates the dotnet startup command directly to the remote debugger. The debugger is what launches the dotnet command and provides the additional environment variables from launchSettings.json. This allows VS to catch any startup errors.

Using ps -axwwe on a running container being debugged, I can see the command line and the environment for the running dotnet process. The command line looks like:

/usr/bin/dotnet --additionalProbingPath /root/.nuget/fallbackpackages2 --additionalProbingPath /root/.nuget/fallbackpackages bin/Debug/netcoreapp2.1/project.dll

The environment is big so I won’t paste it all here, but I can see environmentVariables things (from launchSettings.json) show up.

launchSettings.json Gets Extra Stuff

Once you’ve right-clicked in VS and added Docker support to your ASP.NET Core project, launchSettings.json will be updated to include a Docker configuration that looks something like this:

  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "https://localhost:44308",
      "sslPort": 44308
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    "IIS Express (Development)": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
    "Kestrel (Development)": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      "applicationUrl": "https://localhost:44308"
    "Docker": {
      "commandName": "Docker",
      "launchBrowser": true,
      "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/",
      "httpPort": 58260,
      "useSSL": false,
      "sslPort": 44308

There are some things to note in here.

  • environmentVariables will work, but just in the app. Just like in the other configruations that have an environmentVariables node, you can add that to the Docker node and the environment variables there will be available in your application. However, they won’t be added as global environment variables to the container - they’ll instead get passed in to your application. If you launch a separate shell process in there to poke around, you won’t see them.
  • iisSettings is still important. Even if you don’t use it, the iisSettings.iisExpress.sslPort and iisSettings.iisExpress.applicationUrl values are still important.
  • Docker as the command name is the key. This seems to be a magic thing interpreted by the add-in to know to launch Docker. The name of the configuration doesn’t appear to matter.
  • There are curly-brace magic strings that work in the launchUrl field. I can’t find any beyond {Scheme}, {ServiceHost}, and {ServicePort} that do anything, though in the Microsoft.Docker assembly I see a definition for {ServiceIPAddress} that doesn’t seem used.

You Can Affect docker run Through Project Settings

UPDATE August 16, 2019: Microsoft has added documentation about the available MSBuild properties that can influence the Container Tools behavior. I’ve updated my doc below based on the official docs, but there’s more to be seen over there.

There are some magic XML elements you can put in your project properties that will affect the docker run command. These are found in the .targets files in the Microsoft.VisualStudio.Azure.Containers.Tools.Targets package, which you can find in your local cache in a spot like C:\Users\yourname\.nuget\packages\microsoft.visualstudio.azure.containers.tools.targets\1.4.10\build.

Ones that seem interesting which I have pulled straight out of the Container.targets file…

  • <DockerfileTag>: The default tag used when building the Docker image. When using the container debugging tools this defaults to dev.
  • <DockerDefaultTargetOS>: The default target OS used when building the Docker image.
  • <DockerfileBuildArguments>: Additional arguments passed to the Docker build command. I’m not sure what you might do with that, but it may be an interesting hook.
  • <DockerfileRunArguments>: Additional arguments passed to the Docker run command. You can use this to add volume mounts and such to the VS process that starts up the container for your project. You can add environment variables this way, too, if you don’t want to use launchSettings.json.
  • <DockerfileRunEnvironmentFiles>: Semicolon-delimited list of environment files applied during Docker run.
  • <DockerfileFastModeStage>: The Dockerfile stage (i.e. target) to be used when building the Docker image in fast mode for debugging. This defaults to the first stage found in the Dockerfile, which is usually base.

I’ve only really tried the DockerfileRunArguments to see if I could use that for environment variables (before I figured out the launchSettings.json part) and it seemed to work. On the others, YMMV.

Troubleshooting Container Startup

If you have an error starting the container, restart Visual Studio after resolving it. I can’t figure out a way to manually force the container to restart and still have it controlled (start/stop/cleanup) by VS.

If you see…

docker: Error response from daemon: driver failed programming external connectivity on endpoint hungry_nash (09d5705dc88b7afc229be8c3ed8c92bc30c3c4a2e11fdc9ece423cfb4bfe50b3): Error starting userland proxy:.

…then close VS and restart the Docker daemon. I’ve seen this a lot when my machine starts up and maybe has a race condition between networking getting established and the Docker daemon needing networking. A simple restart usually fixes it.

If you see…

Launching failed because the directory '/remote_debugger' in the container is empty. This might be caused by the Shared Drives credentials used by Docker Desktop being out of date. Try resetting the credentials in the Shared Drives page of the Docker Desktop Settings and then restarting Docker.


Error response from daemon: error while creating mount source path '/host_mnt/c/Users/yourname/vsdbg/vs2017u5': mkdir /host_mnt/c: file exists.

…then there’s a problem with drive mounting. Make sure your drive sharing settings in Docker allow the drive with your source and the drive with the remote debugger to both be mounted. Further, if you’re on a VPN like Cisco Anyconnect, chances are the SMB sharing port 445 is blocked. Try getting off the VPN. You’ll need to close VS and restart the Docker daemon once you’ve resolved that.

No, I haven’t found a fix for the VPN thing. I wish I had.

If the container fails to start for whatever reason, you may be left with zombie containers or images.

  • Use docker ps -a to see the containers that are created but will never be used again. When VS is closed these will remain in Created state. Use docker rm image_name_here to clean them up.
  • Use docker images to see the images on your machine. If you see one that’s named like your project with the tag dev, that’s the dev container. Clean that up with docker rmi project:dev (using the appropriate project name in there).

The Actual Documentation

UPDATE August 16, 2019: I opened an issue to request some on June 18, 2019 to get some documentation. There is now some official doc on how Container Tools builds the container as well as the MSBuild properties available to control that process. They are working on additional docs.

docker, linux, windows comments edit

Working in an environment of mixed containers - both Windows and Linux - I wanted to run them all on my dev machine at the same time if possible. I found some instructions from a while ago about this but following them didn’t work.

Turns out some things have changed in the Docker world so here are some updated instructions.

As of this writing, I’m on Docker Desktop for Windows (31259) Community Edition. The instructions here work for that; I can’t guarantee more won’t change between now and whenever you read this.

  1. Clean up existing containers before switching to Windows containers. Look to see if you’re using Windows containers. Right-click on the Docker icon in the system tray. If you see “Switch to Windows containers…” then you are not currently using Windows containers. Stop any running containers you have and remove all images. You’ll need to switch to Windows containers and the image storage mechanism will change. You won’t be able to manage the images once you switch.

  2. Switch to Windows Containers. Right-click on the Docker icon in the system tray and select “Switch to Windows containers…” If you’re already using Windows containers, great!

  3. Enable experimental features. Right-click on the Docker icon in the system tray and select “Settings.” Go to the “Daemon” tab and check the box marked “Experimental features.”

Enable experimental features.

That’s it! You’re ready to run side-by-side containers.

The big key is to specify --platform as linux or windows when you run a container.

Open up a couple of PowerShell prompts.

In one of them, run docker images just to make sure things are working. The list of images will probably be empty if you had to switch to Windows containers. If you were already on Windows containers, you might see some.

In that same PowerShell window, run:

docker run --rm -it --platform windows microsoft/nanoserver:1803

This is a command-prompt-only version of Windows Server. You should get a C:\> prompt once things have started up.

Leave that there, and in the other PowerShell window, run:

docker run --rm -it --platform linux ubuntu

This will get you to an Ubuntu shell.

See what you have there? Windows and Linux running side by side!

Windows and Linux containers - side by side!

Type exit in each of these containers to get out of the shell and have the container clean itself up.

Again, the big key is to specify --platform as linux or windows when you run a container.

If you forget to specify the --platform flag, it will default to Windows unless you’ve already downloaded the container image. Once you have used the image, the correct version will be found and used automatically:

# Works because you already used the image once.
docker run --rm -it ubuntu

If you try to run a Linux container you haven’t already used, you may get a message like this:

no matching manifest for windows/amd64 10.0.18362 in the manifest list entries

I’m not sure on the particulars on why sometimes --platform is required and sometimes it’s not. Even after removing all my container images, I was able to run an Ubuntu container without specifying platform, like some cache was updated to indicate which platform should be used by default. YMMV. It doesn’t hurt to include it, however, if you try to use --platform on another machine it may not work - you can only use it when experimental features are enabled.

UPDATE June 14, 2019

I’ve found since working in this mixed environment that there are things that don’t work as one might entirely expect.

  • Networking: With Linux-only containers on Windows you get a DockerNAT virtual network switch you can tweak if needed to adjust network connectivity. Under mixed containers, you use the Windows Container network switch, nat and you really can’t do too much with that. I’ve had to reset my network a few times while trying to troubleshoot things like network connections whilst on VPN.
  • Building container images that reference files from other images: A standard .NET Core build-in-container situation is to create, in one Dockerfile, two container images - the first builds and publishes the app, the second copies the published app into a clean, minimal image. When in mixed container world, I get a lot of errors like, “COPY failed: file does not exist.” I can look in the intermediate containers and the files are all there, so there’s something about being unable to mount the filesystem from one container to copy into the other container.

Unrelated to mixed containers, it seems I can’t get any container to connect to the internet when I’m on my company’s VPN. VPN seems to be a pretty common problem with Docker on Windows. I haven’t solved that.

It appears there’s still a lot to work out here in mixed container land. You’ve been warned.

git comments edit

Are you stuck unable to update your version of Atlassian Sourcetree for Windows because when you update and restart, Sourcetree hangs?

I was stuck on 3.0.17. Every time I updated (to 3.1.2), Sourcetree would restart and then… hang. Unresponsive. Unable to see if there were any new updates, unable to do anything but kill the app.

It turns out the reason for this is that Sourcetree didn’t handle monitor scaling very well. Say you have a 4K monitor set to scale to 150% - that’s when you’d see the hang.

There are two workarounds for this:

The first option is to stop monitor scaling and switch back to 100%. Not the greatest, I know, but that’ll get you through temporarily… and it only needs to be temporary. (I’ll get there.)

The other option is to do a tweak to the Sourcetree configuration file. First, make sure Sourcetree is closed. Now go to %LocalAppData%\SourceTree\app-version like C:\Users\tillig\AppData\Local\SourceTree\app-3.1.2. Open the file SourceTree.exe.config. Find this line:

<AppContextSwitchOverrides value="Switch.System.Windows.DoNotScaleForDpiChanges=false"/>

Update it to look like this:

<AppContextSwitchOverrides value="Switch.System.Windows.DoNotScaleForDpiChanges=false;Switch.System.Windows.Controls.Grid.StarDefinitionsCanExceedAvailableSpace=true"/>

When you start Sourcetree, it should be responsive.

This is the default setting in 3.1.3. If you can get yourself upgraded to 3.1.3, you won’t have to do any workarounds anymore. So if you temporarily set your monitor to 100%, take the upgrades in SourceTree up to 3.1.3 or later, then you can switch your monitor back. (Or, of course, you can tweak the configuration on your hanging version of Sourcetree until you get to 3.1.3 or later.)

I had to upgrade from 3.0.17 to 3.1.2 and then to 3.1.3. For some reason I couldn’t just go straight from 3.0.17 to 3.1.3. YMMV.

net, build, gists comments edit

I’ve been enjoying the addition of dotnet CLI global tools and figured I’d blog the ones I use. I’ll also include a PowerShell script that is used to install them (or, if they’re installed, update them).

The list is current as of this writing, but you can visit the gist (below) to see the set of tools I’m using at any given time.

  • dotnet-depends: Shows the dependency tree for a .NET project.
  • dotnet-outdated: Shows outdated packages referenced by a .NET project.
  • dotnet-format: Formats code based on EditorConfig settings.
  • dotnet-script: Run C# script (.csx) files.
  • dotnet-symbol: Download symbols from the command prompt. [I’m still trying this one out to see if I use it much or like it.]
  • dotnetsdkhelpers: A global tool version of the original SDK installer helpers that addresses the need for external tools and fixes a couple of bugs with the original.
  • gti: Install plugins from a .gti file/manifest. [I’m still trying this one out to see if I like it. If it’s good, I can replace my PowerShell script with a manifest.]
  • microsoft.web.librarymanager.cli: dotnet CLI access to the libman dependency manager for JS.

Here’s the gist with the PowerShell script that I use to install these: