DuckieTV icon indicating copy to clipboard operation
DuckieTV copied to clipboard

Implement auto-upgrade service for standalone

Open SchizoDuckie opened this issue 8 years ago • 1 comments

For a different project that uses nwjs i've built an angular module that fetches a .tgz from an internal gitlab server, extracts it, and overwrites the files in the current directory.

For this to work for DuckieTV there's some todos:

  • [x] disable usage of packing sources zip file into duckietv binary for linux
  • [x] disable usage of packing sources zip file into duckietv binary for osx
  • [ ] disable usage of packing sources zip file into duckietv binary for windows
  • [ ] add node_modules requirements to package.json, clean it up a bit
  • [ ] add node_modules to dist
  • [ ] have the nightly build a zipfile that hosts just the build, not the binaries
  • [ ] implement something like this w.i.p. module:
/**
 * Compares latest release on github to the one stored in settings.
 * If older:
 * - downloads new version full tarbal to temp dir
 * - extracts temp tarbal
 * - overwrites contents of current install with updated install
 * - temp dir cleanup on process exit.
 */
DuckieTV.factory("UpdateService", ["SettingsService", "GithubService", "$q", "$http", function(SettingsService, GithubService, $q, $http) {
const TARGET_FILE = 'download.tgz';

  /**
   * Update check stores last known release here. Will be set to current release when upgrade is complete.
   */
  var lastKnownCommit = null;

  /**
   * Convert a browser ArrayBuffer to nodejs Buffer.
   * Needed because $http performs the request to fetch this update, it only gives arraybuffers
   * for binary data. Nodejs (that does the unpacking) requires a Buffer to write to disk. 
   */
  function toBuffer(ab) {
      var buf = new Buffer(ab.byteLength);
      var view = new Uint8Array(ab);
      for (var i = 0; i < buf.length; ++i) { buf[i] = view[i]; }
      return buf;
  }


  var service = {
    status: 'idle',
    
    /**
     * Fetch latest release from github
     */
    check: function() {
      service.status = 'fetching latest commit';
      var currentCommit = SettingsService.get('currentcommit.hash');
      return GithubService.getLastRelease('SchizoDuckie/DuckieTV').then(function(commitHash) {
          lastKnownCommit = commitHash;
          return commitHash != currentCommit;
      });

    },

    /**
     * Check if an upgrade is needed and execute it.
     * Stores hash of last kown commit in settings
     * @return Promise(bool upgraded)  
     */
    upgradeIfNeeded: function() {
        service.status = 'checking for update';
        return service.check().then(function(upgradeNeeded) {          
          if (upgradeNeeded) {
            return service.downloadAndUpgrade().then(function() {
              SettingsService.set('currentcommit.hash', lastKnownCommit);
              return true;
            })
          } else {
            return false;
            service.status = 'no update needed';
          }
        });
    },

    /**
     * Perform download and upgrade
     * @return Promise(result msg)
     */
    downloadAndUpgrade: function() {
      service.status = 'downloading';
      return $http.get(DOWNLOAD_URL, { responseType: "arraybuffer" }).then(function(result) {
          service.status = 'saving';
          var tarball = require('tarball-extract'),
              ncp = require('ncp').ncp,
              temp = require('temp'),
              path = require('path'),
              fs = require('fs');

          temp.track(); // auto-cleanup the mess
          return $q(function(resolve, reject) {
              temp.mkdir('discipline', function(err, dirPath) {
                var tempPath = path.join(dirPath),
                    download = path.join(tempPath, TARGET_FILE);
                var writeStream = fs.createWriteStream(download, {
                    flags: 'w',
                    encoding: 'binary'
                });

                writeStream.on('close', function() {
                    service.status = 'extracting';
                    tarball.extractTarball(download, tempPath, function(err, result) {
                        service.status = 'installing';
                        ncp(path.join(dirPath, 'discipline.git'), '.', {}, function(err) {
                            if(err) {
                              console.error('error copying!', err);
                              service.status = 'failure';
                              reject(err);
                            } else {
                              service.status = 'cleaning up';
                            } 
                            temp.cleanup(function(err, stats) {
                                console.log("upgrade complete!", stats, err);
                                service.status = 'upgrade complete';
                                resolve("installed version "+lastKnownCommit);
                            });
                        });
                    });
                })

                writeStream.write(toBuffer(result.data));
                writeStream.end();
            });
          });
          
      });
    }
  }

  return service;
}]);

For the upcoming 1.1.4 release i want to at least implement the first three parts of this since the build scripts need to be updated for nw.js 0.18.* anyway

SchizoDuckie avatar Nov 16 '16 22:11 SchizoDuckie