{
  "$type": "site.standard.document",
  "canonicalUrl": "https://johnnyreilly.com/posts/surfacing-azure-pipelines-build-info-in-an-aspnet-react-app",
  "description": "Surface build metadata using Azure Pipelines and ASP.NET for both client and server builds in your app with this tutorial.",
  "path": "/posts/surfacing-azure-pipelines-build-info-in-an-aspnet-react-app",
  "publishedAt": "2021-01-29T00:00:00.000Z",
  "site": "at://did:plc:yy3apqjlms24kso7ahn7lbmb/site.standard.publication/3mova7c4nho2b",
  "tags": [
    "azure pipelines"
  ],
  "textContent": "How do you answer the question: \"what version of my application is running in Production right now?\" This post demonstrates how to surface the build metadata that represents the version of your app, from your app using Azure Pipelines and ASP.NET.\n\n\n\nMany is the time where I've been pondering over why something isn't working as expected and burned a disappointing amount of time before realising that I'm playing with an old version of an app. Wouldn't it be great give our app a way to say: \"Hey! I'm version 1.2.3.4 of your app; built from this commit hash, I was built on Wednesday, I was the nineth build that day and I was built from the main branch. And I'm an Aries.\" Or something like that.\n\nThis post was inspired by Scott Hanselman's similar post on the topic. Ultimately this ended up going in a fairly different direction and so seemed worthy of a post of its own.\n\nA particular difference is that this is targeting SPAs. Famously, cache invalidation is hard. It's possible for the HTML/JS/CSS of your app to be stale due to aggressive caching. So we're going to make it possible to see build information for both when the SPA (or \"client\") is built, as well as when the .NET app (or \"server\") is built. We're using a specific type of SPA here; a React SPA built with TypeScript and Material UI, however the principles here are general; you could surface this up any which way you choose.\n\nPutting build info into azure-pipelines.yml\n\nThe first thing we're going to do is to inject our build details into two identical buildinfo.json files; one that sits in the server codebase and which will be used to drive the server build information, and one that sits in the client codebase to drive the client equivalent. They'll end up looking something like this:\n\nWe generate this by adding the following yml to the beginning of our azure-pipelines.yml (crucially before the client or server build take place):\n\nAs you can see, we're placing the following variables that are available at build time in Azure Pipelines, into the buildinfo.json:\n\n- BuildNumber - The name of the completed build; which usually takes the form of a date in the yyyyMMdd format, suffixed by .x where x is a number that increments representing the number of builds that have taken place on the given day.\n- BuildId - The ID of the record for the completed build.\n- SourceVersion - This is the commit hash of the source code in Git\n- SourceBranchName - The name of the branch in Git.\n\nThere's many variables available in Azure Pipelines that can be used - we've picked out the ones most interesting to us.\n\nSurfacing the server build info\n\nOur pipeline is dropping the buildinfo.json over pre-existing stub buildinfo.json files in both our client and server codebases. The stub files look like this:\n\nIn our .NET app, the buildinfo.json file has been dropped in the root of the app. And as luck would have it, all JSON files are automatically included in a .NET build and so it will be available at runtime. We want to surface this file through an API, and we also want to use it to stamp details into our logs.\n\nSo we need to parse the file, and for that we'll use this:\n\nThe above code reads the buildinfo.json file and deserialises it into a BuildInfo record which is then surfaced up by the GetBuildInfo method. We initialise this at the start of our Program.cs like so:\n\nNow we need a controller to surface this information up. We'll add ourselves a BuildInfoController.cs:\n\nThis exposes an api/build endpoint in our .NET app that, when hit, will display the following JSON:\n\nSurfacing the client build info\n\nOur server now lets the world know which version it is running and this is tremendous. Now let's make our client do the same.\n\nVery little is required to achieve this. Again we have a buildinfo.json sat in the root of our codebase. We're able to import it as a module in TypeScript because we've set the following property in our tsconfig.json:\n\nAs a consequence, consumption is as simple as:\n\nWhich provides us with a clientBuildInfo which TypeScript automatically derives as this type:\n\nHow you choose to use that information is entirely your choice. We're going to add ourselves an \"about\" screen in our app, which displays both client info (loaded using the mechanism above) and server info (fetched from the /api/build endpoint).\n\nWhen the above page is viewed it looks like this:\n\nAnd that's it! Our app is clearly telling us what version is being run, both on the server and in the client. Thanks to Scott Hanselman for his work which inspired this.",
  "title": "Azure Pipelines Build Info in an ASP.NET React app"
}