public static async Task Upload(Stream inStream, string fileName)
    {
      var packageName = $"{fileName}#{DateTime.UtcNow.Ticks}";
      var packagePath = Path.Combine(AppPaths.Uploads, packageName);

      using (var outStream = new FileStream($"{packagePath}.tmp", FileMode.Create, FileAccess.Write))
        await inStream.CopyToAsync(outStream);

      System.IO.File.Move($"{packagePath}.tmp", $"{packagePath}.zip");

      var package = Package.FromFile(new FileInfo($"{packagePath}.zip"));
      
      lock (_locker) {
        var branch = _branches.SingleOrDefault(b => string.Compare(b.Name, package.BranchName, true) == 0);

        var isNewBranch = false;
        if (branch == null) {
          isNewBranch = true;
          branch = new Branch {Name = package.BranchName};
          _branches.Add(branch);
        }

        branch.Packages.Add(package);

        try
        {
          SaveBranch(branch);
          log.Info($"Package {package.FileName} added to branch {branch.Name} queue");
        }
        catch (Exception ex)
        {
          if (isNewBranch)
            _branches.Remove(branch);
          else
            branch.Packages.Remove(package);

          File.Delete(package.FullName);

          log.Error(ex, $"Error save branch {branch.Name}");
          throw;
        }
      }

      _mainEvent.Set();
    }
 private static void Deploy(Branch branch)
 {
   log.Info($"Deploying branch {branch.Name}");
   
   Package package = null;
   try
   {
     Thread.Sleep(3000); // DO DEPLOY
     lock (_locker) {
       package = branch.Packages[0];
       branch.Packages.RemoveAt(0);
       SaveBranch(branch);
     }
     try {
       File.Delete(package.FullName);
     }
     catch (Exception ex) {
       log.Warn(ex, $"Error deleting package file {package.FileName}. File will be deleted on later.");
     }
   }
   catch (Exception ex)
   {
     log.Error(ex, $"Error deploying package {package.FileName} of branch {branch.Name}");
     lock (_locker) {
       if (package != null) {
         package.State = PackageState.Pending;
         branch.Packages.Insert(0, package);
       }
       else {
         branch.Packages[0].State = PackageState.Pending;
       }
       //TODO mark branch as postponed
     }
   }
   Interlocked.Increment(ref DeployThreadsCount);
   _mainEvent.Set();
 }
    public static void SaveBranch(Branch branch)
    {
      var dirPath = Path.Combine(AppPaths.Branches, branch.Name);
      if (!Directory.Exists(dirPath))
        Directory.CreateDirectory(dirPath);

      var jsonFile = Path.Combine(dirPath, ".data.json");
      var tmpFile = Path.ChangeExtension(jsonFile, "tmp");
      var oldFile = Path.ChangeExtension(jsonFile, "old");

      // restore in case of previous failed state
      if (File.Exists(oldFile)) {
        if (File.Exists(jsonFile))
          File.Delete(jsonFile);
        File.Move(oldFile, jsonFile);
      }

      if (File.Exists(tmpFile))
        File.Delete(tmpFile);

      // write to tmp file
      using (var stream = File.Open(tmpFile, FileMode.Create, FileAccess.Write, FileShare.Write))
      using (var writer = new StreamWriter(stream)) {
        var serializer = new JsonSerializer();
        serializer.Serialize(writer, branch); 
      }

      // rename .json -> .old
      if (File.Exists(jsonFile))
        File.Move(jsonFile, oldFile);

      // rename .tmp -> .json
      File.Move(tmpFile, jsonFile);
      
      // remove .old
      if (File.Exists(oldFile))
        File.Delete(oldFile);  
    }