Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to manage releases with ASP.NET pointing to new versions of Webpacked JS files?

I'm working with a client who has a monolithic ASP.NET application as their backend, but also hosts Webpacked JS files for their React.js frontend in the same GIT repo. The obvious problem is that anytime they want to do a frontend release, they have to release and build the entire .NET application along with it, due to the manifest.json that is in the EC2 instance. The idea is that there will also be many versions over many instances, canaried out to different users, going through various levels of post-production testing, etc., standard DevOps CI/CD pipeline with post-production health checks and automated rollbacks. That is the end goal. In the meantime, they need to split their frontend/backend releases apart, which means separate versioning for both. So after much preamble, the question to the community is two-fold:

  1. Has anyone experienced this type of environment and come up with a viable solution?
  2. Does anyone have a good suggestion for how to approach this problem?

Keep in mind that this solution should also expect local development, PR testing, and a blue/green prod setup.

like image 810
Blairg23 Avatar asked Sep 06 '19 07:09

Blairg23


2 Answers

Assuming that you can refactor your project so that the client-side stuff will be a folder of its own, let's call this folder client-{version}, you can create a setting for your project for client-side path, which would be client-v1/ and then, if you need to change v1 -> v2, a command-line command would be needed in order to ensure that this setting can be changed for your project at runtime. So, deploying the client-side would have this algorithm:

  • deploy the client-side
  • change the client-{version} to a newer version
  • trigger command-line command to the project which will ensure that the new client-side path will be used from now on
like image 132
Lajos Arpad Avatar answered Nov 18 '22 15:11

Lajos Arpad


I have used following solution, (I learned from JSDelivr, UnPkg ..)

  1. Private NPM Repository (ProGet)
  2. Version Table (stores package name and production version to be used)
  3. A controller to download and extract package to a local folder for requested package with version.

Here is how it works,


   /// lets assume this will serve JS/image/css everything from
   /// from a path /js-pkg/packageName[@version]/....

   [RoutePrefix("js-pkg")]
   public class JSContent: Controller {

      [HttpGet("{packageName}/{*path}")]
      public ActionResult Get(
          string packageName,
          string path) {

          string version = null;

          string releasedVersion = db.Versions
                  .Where(x => x.Package == packageName)
                  .Select(x => x.Version)
                  .First();

          if (packageName.Contains("@")) {
              var tokens = packageName.Split("@");
              version = tokens[1];
              packageName = tokens[0];
          }

          if (version == null) {
              // this is for performance reason...
              // explained in next line...
              return Redirect($"/js-pkg/{packageName}@{releasedVersion}/{all}");
          }

          // since all requests will be versioned...
          // you don't have to worry about stale cache ...
          Response.CacheControl = "public, max-age=36000000";

          // you need to setup a file based locking here 
          string folder = $"d:\\temp\\{packageName}\\{version}";
          if (!System.IO.Directory.Exists(folder))
          {
              DownloadAndExtract(
                  $"https://proget...../npm/repo/{package}/-/{package}-{version}.tgz",
                  folder
              );
          }

          string file = $"{folder}//{path}";
          if(!System.IO.File.Exists(file)) {
              return HttpNotFound();
          }

          return File(file);

      }

   }

Now to host file, you can simply put a CSHTML or view with following line

<script src="/js-pgk/packageName/dist/ui/start.packed.js" ></script>
<link rel="stylesheet" href="/js-pgk/packageName/dist/ui/start.css" ></link>

There are multiple advantages to it,

  1. All your JS packages can stay outside the main repository, as most of the time UI becomes more and more complicated, your UI can be divided into multiple packages and they all can exist independently
  2. Multiple teams can independently manage packages, and undoing released package is easy as you can simply go in database and change version.
  3. In debug environment, you can also supply version in querystring or in package name to test different JS package for production/staging backend
  4. Separate Frontend will remove all node related code from backend, backend will not need unnecessary frontend processing.
  5. You will eventually have many small repositories, each independently developed and maintained. Your commits will be smaller and clearer. Single repository for frontend/backend has too many commits for single feature/change.
like image 2
Akash Kava Avatar answered Nov 18 '22 14:11

Akash Kava