Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Commit

Permalink
Allows Raven to auto-update (Fixes #11)
Browse files Browse the repository at this point in the history
  • Loading branch information
robotlolita committed Nov 23, 2014
1 parent ddf3757 commit e17fd59
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 30 deletions.
15 changes: 11 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ $(LIB_DIR)/%.js: $(SRC_DIR)/%.sjs
--module lambda-chop/macros \
--module es6-macros/macros/destructure \
--module macros.operators/macros \
--module sparkler/macros \
--module adt-simple/macros \
--output $@ \
$<

Expand Down Expand Up @@ -67,17 +69,22 @@ run: prebuild
LD_LIBRARY_PATH="$(pwd):$LD_LIBRARY_PATH" $(nw) .


package: prebuild
rm -rf dist
package.nw: prebuild
rm -rf dist/app
rm -f dist/package.nw
mkdir -p dist/app
cp package.json dist/app
cp www dist/app -R
cp resources dist/app -R
cd dist/app && npm install --production
cd dist/app && zip ../package.nw * -r

package: prebuild
rm -rf dist
$(MAKE) package.nw
node ./tools/build
./tools/fix-linux-builds
./tools/zip-packages
rm -rf dist/app


.PHONY: css css-watch static clean
.PHONY: css css-watch static clean package.nw
15 changes: 13 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
"main": "www/index.html",
"description": "A minimalist editor for writers.",
"version": "0.1.1",
"releases": {
"channel": "beta",
"url": "https://api.github.com/repos/robotlolita/raven/releases"
},
"keywords": [
"text editor",
"minimalist",
Expand Down Expand Up @@ -55,7 +59,8 @@
"stylus": "~0.49.2",
"nib": "~1.0.4",
"sweet.js": "0.7.1",
"jsx-reader": "~0.1.1"
"jsx-reader": "~0.1.1",
"sparkler": "~0.3.4"
},
"dependencies": {
"react": "~0.12.0",
Expand All @@ -77,6 +82,12 @@
"html-entities": "~1.1.1",
"to-slug": "0.0.0",
"marked": "~0.3.2",
"encoding": "~0.1.11"
"encoding": "~0.1.11",
"semver": "~4.1.0",
"net.http-client": "~0.2.4",
"temp": "~0.8.1",
"request": "~2.48.0",
"adt-simple": "~0.1.3",
"mv": "~2.0.3"
}
}
2 changes: 1 addition & 1 deletion platform/linux/Raven.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

BIN=app
BIN=nw
HERE=`dirname $(readlink -f $0)`

LIBUDEV_0=libudev.so.0
Expand Down
118 changes: 101 additions & 17 deletions source/scripts/screens/about.sjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,41 +19,125 @@
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

var Maybe = require('data.maybe');
var updater = require('../updater');
var { Base } = require('adt-simple');

module.exports = function(screenManager, storage) {

var React = require('react');
var { run } = require('../utils');
var React = require('react/addons');
var { run, spawn } = require('../utils');
var Dialogs = require('./dialogs')(screenManager);

union Mode {
About,
Updating
} deriving (Base);

var Screen = React.createClass({
getInitialState: function() {
return { version: WebkitUI.App.manifest.version }
return {
version: WebkitUI.App.manifest.version,
update: Maybe.Nothing(),
checkingUpdates: false,
mode: Mode.About
}
},

goBack: function() {
run(screenManager.back());
},

componentWillMount: function() {
this.computeUpdateInformation();
},

computeUpdateInformation: function() {
var { version, releases: { url, channel } } = WebkitUI.App.manifest;
var self = this;

this.setState({ checkingUpdates: true });
run($do {
update <- updater.hasUpdates(url)(version)(updater.channels[channel]);
return self.setState({ checkingUpdates: false, update: update })
})
},

updateTo: function(version) {
var self = this;
return function() {
self.setState({ mode: Mode.Updating });
updater.doUpdate(version).fork(
λ error -> run($do {
Dialogs.message('Raven updater', 'An error ocurred while trying to update Raven: ' + error.message);
return console.log(error);
screenManager.back();
return self.setState({ mode: Mode.About });
}),
λ _ -> run($do {
Dialogs.message('Raven updater', 'Raven was updated successfully. Please restart the application for the changes to take effect.');
screenManager.back();
return self.setState({ update: Maybe.Nothing(), mode: Mode.About });
})
)
}
},

renderUpdate: function(update) {
var self = this;

return update.cata({
Nothing: λ(_) -> (
self.state.checkingUpdates? <span className="busy">Checking for updates...</span>
: /* otherwise */ <span />
),
Just: λ(v) -> (
<a href="#" className="button submit-button" onClick={ self.updateTo(v) }>Update to { v.tag_name }</a>
)
})
},

render: function() {
var classes = React.addons.classSet({
'screen': true,
'regular-screen': true,
'mode-about': this.state.mode.equals(Mode.About),
'mode-updating': this.state.mode.equals(Mode.Updating)
});

return (
<div id="about-screen" className="screen regular-screen">
<div className="screen-heading">
<div className="heading-buttons">
<a href="#" className="heading-button icon-back" onClick={ this.goBack } />
<div id="about-screen" className={ classes }>
<div className="about-section">
<div className="screen-heading">
<div className="heading-buttons">
<a href="#" className="heading-button icon-back" onClick={ this.goBack } />
</div>
<h2 className="screen-title">About Raven</h2>
</div>
<h2 className="screen-title">About Raven</h2>
</div>

<div className="centred welcome vertically-centred">
<div className="app-description">
<h2 className="app-title">Raven</h2>
<div className="app-version">Version {this.state.version}</div>

<div className="centred welcome vertically-centred">
<div className="app-description">
<h2 className="app-title">Raven</h2>
<div className="app-version">Version {this.state.version}</div>
<div className="app-updates">
{ this.renderUpdate(this.state.update) }
</div>
</div>
</div>

<div className="app-copy-info">
<p>Made with <em></em> by Quildreen Motta</p>
<p>Raven is open source under the MIT licence</p>
</div>
</div>

<div className="app-copy-info">
<p>Made with <em></em> by Quildreen Motta</p>
<p>Raven is open source under the MIT licence</p>
<div className="updating-section">
<div className="centred welcome vertically-centred">
<div className="section-heading">
<h2 className="section-info">Raven is updating</h2>
<h3 className="section-subinfo">Sit back and prepare some coffee, this might take a while</h3>
</div>
</div>
</div>
</div>
)
Expand Down
118 changes: 118 additions & 0 deletions source/scripts/updater.sjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright (c) 2014 Quildreen Motta <quildreen@gmail.com>
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files
// (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

var Maybe = require('data.maybe');
var Future = require('data.future');
var fs = require('fs');
var os = require('os');
var path = require('path');
var temp = require('temp');
var { exists, remove } = require('io.filesystem')(fs);
var semver = require('semver');
var http = require('net.http-client');
var request = require('request');
var { move } = require('./utils');

function pipe(a, b) {
return new Future(function(reject, resolve) {
var resolved = false;
var p = a.pipe(b);
p.on('finish', transition(resolve));
p.on('error', transition(reject));

function transition(f){ return function(v) {
if (!resolved) {
resolved = true;
f(v);
}
}}
})
}

var normalise = λ x -> {
var download = Maybe.fromNullable(x.assets.filter(isPackage)[0]);
return {
tag_name: x.tag_name,
draft: x.draft,
prerelease: x.prerelease,
download: download.map(λ x -> ({ url: x.browser_download_url, size: x.size })),
body: x.body
}
}

var isPackage = λ x -> x.name === 'package.nw';
var isRelease = λ x -> x.download.isJust;

var releases = exports.releases = λ url -> $do {
response <- http.get({headers: { 'User-Agent': 'robotlolita/raven' }}, url);
return JSON.parse(response.body).map(normalise).filter(isRelease);
}

var byMostRecentRelease = λ(a, b) -> semver.compare(b.tag_name, a.tag_name);

var sort = λ f xs -> xs.slice().sort(f);

var first = λ xs -> Maybe.fromNullable(xs[0]);

var filter = λ f xs -> xs.filter(f)

var mostRecent = exports.mostRecent = sort(byMostRecentRelease) ->> first;

var moreRecentThan = λ(x, release) -> semver.gt(release.tag_name, x);

var channels = exports.channels = {
stable : λ x -> !x.prerelease,
beta : λ x -> true
}

var hasUpdates = exports.hasUpdates = λ url current channel ->
releases(url) <$> filter(λ x -> channel(x) && moreRecentThan(current, x)) <$> mostRecent;

var tempFile = λ name -> temp.path({ prefix: name, dir: os.tmpDir() });

var execPath = λ[path.dirname(process.execPath)];
var workingDir = λ[process.cwd()];
var findPackageIn = λ x -> $do {
var filename = path.join(x, 'package.nw');
flag <- exists(filename);
if (flag) $do {
Future.of(filename)
} else $do {
Future.rejected(new Error('No package.nw in ' + x));
}
}

var packagePath = λ(_) -> findPackageIn(execPath())
<|> findPackageIn(workingDir())
<|> Future.rejected(new Error("Couldn't find package.nw"));


var doUpdate = exports.doUpdate = λ release -> $do {
var file = tempFile('package');
download <- release.download.cata({ Nothing: Future.rejected, Just: Future.of });
return console.log('Downloading package ' + download.url + ' -> ' + file);
pipe(request(download.url), fs.createWriteStream(file));
return console.log('Getting package path');
target <- packagePath();
return console.log('Moving ' + file + ' -> ' + target);
move(file, target);
return console.log('Update all done');
}
10 changes: 10 additions & 0 deletions source/scripts/utils.sjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var slug = require('to-slug');
var fs = require('fs');
var path = require('path');
var Future = require('data.future');
var mv = require('mv');
var $ = jQuery;

exports.slugify = slug;
Expand Down Expand Up @@ -133,3 +134,12 @@ exports.resource = path.join.bind(path, path.join(__dirname, '../../resources'))
exports.showMessage = function(a) {

};

exports.move = function(a, b) {
return new Future(function(reject, resolve) {
mv(a, b, function(error) {
if (error) reject(error)
else resolve()
})
})
}
Loading

0 comments on commit e17fd59

Please sign in to comment.