Category: Git

Moving an Azure DevOps repo to use Github Actions instead

In this blog post, I am going to take an existing web application that resides in Azure DevOps and port it to build and deploy within GitHub and use GitHub Actions to build and deploy the same site to GitHub.

Here you can see I have a website in Visual Studio which is currently pointing at a repository inside Azure Devops.

And here is what it looks like inside Visual Studio 2019 with the connection to Azure DevOps.

Now I am going to remove the connection from the Azure DevOps repo by clicking on remove like so:-

When I click on remove, this removes the connection from the code to the Azure DevOps repository. Then I go to the Sync area and it now asks me where do I want to push the code to.

This time I choose to Publish to GitHub.

Give the new repository a name (for within GitHub) and press Publish

This will push the code to a new GitHub repository called AzureGlobalBootCamp2020 which you can now see below.

Now we need to create a GitHub Action so that the code is built and pushed to Azure (like it was from within Azure DevOps previously).

From within your new GitHub repo click on Actions at the top.

I then chose Setup a new workflow yourself

This will take you to a screen and create a main.yaml file.

name: Deploy ASP.NET Core app to Azure Web App

on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - '*'
# CONFIGURATION
# For help, go to https://github.com/Azure/Actions
#
# 1. Set up the following secrets in your repository:
#   AZURE_WEBAPP_PUBLISH_PROFILE
#
# 2. Change these variables for your configuration:
env:
  AZURE_WEBAPP_NAME: AzureGlobalBootCamp2020     # set this to your application's name
  AZURE_WEBAPP_PACKAGE_PATH: '.'                 # set this to the path to your web app project, defaults to the repository root
  DOTNET_VERSION: '3.1.100'                      # set this to the dot net version to use

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:

      # Checkout the repo
      - uses: actions/checkout@master
      
      # Setup .NET Core SDK
      - name: Setup .NET Core
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }} 
      
      # Run dotnet build and publish
      - name: dotnet build and publish
        run: |
          dotnet build --configuration Release
          dotnet publish -c Release -o '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/myapp' 
          
      # Deploy to Azure Web apps
      - name: 'Run Azure webapp deploy action using publish profile credentials'
        uses: azure/webapps-deploy@v2
        with: 
          app-name: ${{ env.AZURE_WEBAPP_NAME }} # Replace with your app name
          publish-profile: ${{ secrets.azureWebAppPublishProfile  }} # Define secret variable in repository settings as per action documentation
          package: '${{ env.AZURE_WEBAPP_PACKAGE_PATH }}/myapp'

# For more samples to get started with GitHub Action workflows to deploy to Azure, refer to https://github.com/Azure/actions-workflow-samples

I then pasted this into the main.yaml file and changed the following:-

AZURE_WEBAPP_NAME: AzureGlobalBootCamp2020
DOTNET_VERSION: ‘3.1.100
publish-profile: ${{ secrets.azureWebAppPublishProfile }}

The last entry above publish-profile requires you to create a new secret in GitHub under Settings -> Secrets and call it azureWebAppPublishProfile and you need to paste in the publishing profile from your Azure Web App

The above screen shows me in the Azure Portal and I’ve clicked into my Azure App Service and when I click on Get Publish Profile it downloads the content of the Publish profile which I paste into the new Secret with GitHub.

And with that we are done, GitHub will kick off the GitHub Action and built and deploy my web app changes when I publish any change to GitHub right into Azure for me.

Note
To read more on using GitHub Actions with .Net you can read more on GitHub here -> https://github.com/actions/setup-dotnet

Feel free to comment below if this is useful or if you have any feedback etc.



VSTS and Git Integration for Deploying to Azure – Part 1

At work we use Visual Studio Team Services (VSTS) with git and in this post I’ll walk you through our development process for writing code and deploying it to a demo site on Azure.

I have become a big fan of VSTS, it has some cracking functionality built-in which saves you a lot of time and effort.

I will cover the following:-

Using Visual Studio 2017 with the git integration tools

So I open up Visual Studio 2017 and I first of all need to update my local copy of the master branch and pull down the latest version.

To do this I select Sync as shown above and then I choose fetch to fetch the latest commits and pull to pull them all down as below.


Ok, so now I have the latest code from the master branch as seen below.

Now I want to run the master branch and see if everything is good and check whats changed with the commits I pulled down but before I can do this I need to apply some local settings.

The reason for this is we have a number of Azure Services being used like event hubs etc and I have a local settings json file with my settings which I never check in. To apply these local settings I saved them into a stash and I will show you how I apply my stashes next.

So I click Stashes within Team Explorer and then I see the following.

 

And now I right-click on local settings and select apply stash, and this will apply my local settings to the project allowing me to run my own event hub instead of the one on our demo site as an example.

In order to get the Git Stash Extension you can download it from Extensions and Updates from the Tools menu within Visual Studio.

 

In Part 2 https://gregorsuttie.com/2018/08/24/vsts-and-git-integration-for-deploying-to-azure-part-2/ – I’ll show you how to use VSTS build pipelines to build and test your code and then deploy it to Azure.



Squirrel – replace ClickOnce the easy way

Last week I was taking a look at Squirrel which states

Squirrel: It’s like ClickOnce but Works™

The goal of me looking at Squirrel was to find a decent replacement to ClickOnce, I read a tweet from @paulcbetts mentioning his work and how he had created Squirrel as a replacement to using ClickOnce.

Having spent some time implementing this and coming across one or two issues which I overcame, the good news is ClickOnce is hopefully no longer needed.

Squirrel works and does exactly what we require, our windows forms application’s can have a ‘check for updates’ option, which updates to the latest release, close the old application down after its updated, re-open from the original desktop or start menu shortcut and your done.

Steps to get Squirrel working

  1. Sourcecode is on GitHub.
  2. Create a simple Windows Forms Application
  3. Add the nuget package called squirrel.windows nugetinstalllike so:-
  4. Add the following to the program.cs like so (line 19 is the only one I added) :-

    [sourcecode language=”csharp”]
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    using Squirrel;

    namespace WindowsFormsApplication1
    {
    static class Program
    {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
    SquirrelAwareApp.HandleEvents(onAppUpdate: Form1.OnAppUpdate,
    onAppUninstall: Form1.OnAppUninstall,
    onInitialInstall: Form1.OnInitialInstall);

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
    }
    }
    }
    [/sourcecode]

  5. Add some content like a simple button or a menu with the choice to check for updates
  6. in the code behind we implement squirrel like so:-

    [sourcecode language=”csharp”]
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    using System.Configuration;
    using Squirrel;
    using System.Reflection;
    using System.IO;

    namespace WindowsFormsApplication1
    {
    public partial class Form1 : Form
    {
    private const ShortcutLocation DefaultLocations = ShortcutLocation.StartMenu
    | ShortcutLocation.Desktop;

    public Form1()
    {
    InitializeComponent();
    }

    private void checkForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
    {
    // Check for Squirrel Updates
    var t = UpdateApp();
    }

    public async Task UpdateApp()
    {
    var updatePath = ConfigurationManager.AppSettings["UpdatePathFolder"];
    var packageId = ConfigurationManager.AppSettings["PackageID"];

    using (var mgr = new UpdateManager(updatePath, packageId, FrameworkVersion.Net45))
    {
    var updates = await mgr.CheckForUpdate();
    if (updates.ReleasesToApply.Any())
    {
    var lastVersion = updates.ReleasesToApply.OrderBy(x => x.Version).Last();
    await mgr.DownloadReleases(new[] { lastVersion });
    await mgr.ApplyReleases(updates);
    await mgr.UpdateApp();

    MessageBox.Show("The application has been updated – please close and restart.");
    }
    else
    {
    MessageBox.Show("No Updates are available at this time.");
    }
    }
    }

    public static void OnAppUpdate(Version version)
    {
    // Could use this to do stuff here too.
    }

    public static void OnInitialInstall(Version version)
    {
    var exePath = Assembly.GetEntryAssembly().Location;
    string appName = Path.GetFileName(exePath);

    var updatePath = ConfigurationManager.AppSettings["UpdatePathFolder"];
    var packageId = ConfigurationManager.AppSettings["PackageID"];

    using (var mgr = new UpdateManager(updatePath, packageId, FrameworkVersion.Net45))
    {

    // Create Desktop and Start Menu shortcuts
    mgr.CreateShortcutsForExecutable(appName, DefaultLocations, false);
    }
    }

    public static void OnAppUninstall(Version version)
    {
    var exePath = Assembly.GetEntryAssembly().Location;
    string appName = Path.GetFileName(exePath);

    var updatePath = ConfigurationManager.AppSettings["UpdatePathFolder"];
    var packageId = ConfigurationManager.AppSettings["PackageID"];

    using (var mgr = new UpdateManager(updatePath, packageId, FrameworkVersion.Net45))
    {
    // Remove Desktop and Start Menu shortcuts
    mgr.RemoveShortcutsForExecutable(appName, DefaultLocations);
    }
    }
    }
    }
    [/sourcecode]

  7. We now need to create a .nuspec file which we will use to package up our application, Squirrel needs to have every file within the lib\net45 folder.

    To create a .nuspec file I simply copied nuget.exe into the solution and then went to a command prompt and typed:- nuget spec, this creates a .nuspec file which we can then hand edit inside Visual Studio and looks like this:-
    [sourcecode language=”csharp”]
    <?xml version="1.0"?>
    <package >
    <metadata>
    <id>WindowsFormsAppliation1</id>
    <version>1.0.0.0</version>
    <authors>Gregor Suttie</authors>
    <title>WindowsFormsAppliation1 Squirrel Tester</title>
    <description>Testing out Squirrel</description>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <releaseNotes>None</releaseNotes>
    <copyright>Copyright 2015</copyright>
    </metadata>
    <files>
    <file src="bin\Debug\*.*" target="lib\net45\"></file>
    </files>
    </package>
    [/sourcecode]

  8. Next we need to pack the .nuspec file to generate the nuget package, so run the following:-
    Nuget pack WindowsFormsApplication1.nuspec
  9. From the package manager console inside Visual Studio we now need to run the following command:-

    squirrel –releasify C:\Squirrel\WindowsFormsApplication1\WindowsFormsApplication1\WindowsFormsApplication1.1.0.0.0.nupkg

  10. In the folder above we should now have a folder called Releases, containing a setup.exe which we use to install the application.
  11. Run setup.exe and your app will install with shortcuts on the desktop and the start menu if you use the code above which has defined these as shortcut options.
  12. If you then run check for updates, the code will go off and check the folder you specify in the app.config like so:-

    [sourcecode language=”csharp”]
    <add key="UpdatePathFolder" value="c:\updatesForMyApp\"/>
    [/sourcecode]

  13. In order to update our app we also need the following appsetting which is the id of the Nuget package we created earlier, be careful not to have the id with a space or a dot (.) within the Nuget package Id.:-
    [sourcecode language=”csharp”]
    <add key="PackageID" value="WindowsFormsApplication1"/>
    [/sourcecode]
  14. Also add this one line to the assmblyinfo.cs:-
    [sourcecode language=”csharp”]
    [assembly: AssemblyMetadata("SquirrelAwareVersion", "1")]
    [/sourcecode]

  15. Once you install the application, go back and make a change to the code, save it, update the .nuspec file version number from 1.0.0 to 1.0.1 and then redo steps 7 and 8 (with the path to the new WindowsFormsApplication1.1.0.0.1.nupkg file)
  16. Almost done, copy the content of the releases folder to our location where we store the updates (our app.config setting for UpdatePathFolder which was set to c:\updatesForMyApp\)
  17. Run the app and choose the menu option ‘Check for Updates’ and you’ll get the latest version.
  18. Also note of you add a Debugger.Launch() statement in the code you can debug the code and step through line by line.

To find out more about further options available and much more about Squirrel click here.




AspConf – My Review

Last week we had AspConf – “aspConf is a virtual conference focused on one thing: showcasing the ASP.NET stack as the platform for great web applications.

Over 2 days they had 5 virtual rooms used to present lots of fascinating stuff on .Net, including talks about all manner of things in the Asp.Net world.

The sessions on both days covered topics such as, Async in .Net 4.5, Glimpse, TDD, Azure and Cloud Computing and lots lots more, you can read more about the sessions here.

Although it had some technical difficulties, due to its popularity I may add, the conference is an awesome way to learn for free – and free is always good. It’s a superb way to learn about content you havent had time to look at it, or new technologies you havent heard of. Hopefully next year they might try to use something different from livemeeting.

If you missed any of the session then fret not, most of them if not all of them were recorded and put up on Channel 9 here

I only managed to see sessions from day 2 so I am off to go watch more content on Channel 9 – a great resource for free learning.