Changeset 464


Ignore:
Timestamp:
06/23/13 21:46:11 (12 years ago)
Author:
hendrikvanantwerpen
Message:

Working deployment to Heroku.

Location:
Dev/trunk
Files:
1 added
10 edited
1 moved

Legend:

Unmodified
Added
Removed
  • Dev/trunk/Gruntfile.js

    r463 r464  
    1 /***************************************
    2  * This Gruntfile is organized with two phases,
    3  * compile and build. Compile will lint HTML,
    4  * JavaScript and compile LESS. The build phase
    5  * creates an optimized stand-alone build that
    6  * can be distributed.
     1/** Gruntfile - control build and deploy tasks
    72 */
    83
    94module.exports = function(grunt) {
    105
     6    var srcDir   = 'src/';
    117    var buildDir = 'build/';
    12     var srcDir   = 'src/';
     8    var developmentDir = buildDir+'development/';
     9    var productionDir = buildDir+'production/';
    1310
    1411    grunt.initConfig({
    1512        clean: {
    16             build: {
    17                 src: [buildDir]
     13            development: {
     14                src: [developmentDir]
     15            },
     16            production: {
     17                src: [productionDir]
    1818            }
    1919        },
     
    2727                    cwd: srcDir,
    2828                    src: ['client/qed-client/**/*.coffee', 'server/**/*.coffee'],
    29                     dest: buildDir,
     29                    dest: developmentDir,
    3030                    ext: '.js'
    3131                }]
     
    3333        },
    3434        copy: {
     35            'client-deps': {
     36                files: [{
     37                    expand: true,
     38                    cwd: srcDir,
     39                    src: ['client/dojo/**', 'client/dijit/**', 'client/dojox/**', 'client/util/**'],
     40                    dest: developmentDir
     41                }]
     42            },
     43            'server-deps': {
     44                files: [{
     45                    expand: true,
     46                    cwd: srcDir,
     47                    src: ['node_modules/**'],
     48                    dest: developmentDir
     49                }]
     50            },
    3551            compile: {
    3652                files: [{
    37                     src: [srcDir+'**', '!**/*.coffee' ],
    38                     dest: buildDir
     53                    expand: true,
     54                    cwd: srcDir,
     55                    src: ['**', '!**/*.coffee', '!**/*.less', '!node_modules/**', '!client/dojo/**', '!client/dijit/**', '!client/dojox/**', '!client/util/**' ],
     56                    dest: developmentDir
     57                }]
     58            },
     59            deploy: {
     60                files: [{
     61                    expand: true,
     62                    cwd: developmentDir,
     63                    src: ['**', '!client/*/**' ],
     64                    dest: productionDir
    3965                }]
    4066            }
    4167        },
    4268        dojo: {
    43             options: {
    44                 dojo: srcDir+'client/dojo/dojo.js'
    45             },
    46             build: {
     69            deploy: {
    4770                options: {
    48                     profile: srcDir+'client/client.profile.js',
    49                     releaseDir: buildDir
     71                    dojo: developmentDir+'client/dojo/dojo.js',
     72                    profile: developmentDir+'client/client.profile.js',
     73                    releaseDir: '../../../'+productionDir
    5074                }
    5175            }
     
    85109                    cwd: srcDir,
    86110                    src: ['client/qed-client/css/qed.less'],
    87                     dest: buildDir,
     111                    dest: developmentDir,
    88112                    ext: '.css'
    89113                }]
     
    97121    grunt.loadNpmTasks('grunt-contrib-jshint');
    98122    grunt.loadNpmTasks('grunt-contrib-less');
    99     grunt.loadNpmTasks('grunt-curl');
    100123    grunt.loadNpmTasks('grunt-dojo');
    101124    grunt.loadNpmTasks('grunt-htmlhint');
    102     grunt.loadNpmTasks('grunt-zip');
    103125
     126    // development tasks
     127    grunt.registerTask('install-deps', ['copy:server-deps', 'copy:client-deps']);
    104128    grunt.registerTask('compile', ['less:compile', 'jshint:compile', 'htmlhint:compile', 'coffee:compile', 'copy:compile']);
    105     grunt.registerTask('build', ['clean:build', 'compile', 'dojo:build']);
    106     grunt.registerTask('deploy', []);
     129    grunt.registerTask('build', ['clean:development', 'install-deps', 'compile']);
     130
     131    // production tasks
     132    grunt.registerTask('deploy', ['clean:production', 'copy:deploy', 'dojo:deploy']);
     133
     134    // default task is quick compile
    107135    grunt.registerTask('default', ['compile']);
    108136
  • Dev/trunk/README.md

    r463 r464  
    1 ===
    2 QED
    3 ===
     1# QED
    42
    5 Install
    6 -------
     3This document describes how to develop this code base and the source
     4code organisation.
    75
    8 - Install CouchDB
    9 - Install Node.js
    10 -
     6## Development, Install and Deploy
    117
     8Source files are located in `src`. Build and deployment is managed
     9with _Grunt_, a node based task runner.
     10
     11First make sure _node_, _npm_ and _couchdb_ are installed. Then
     12install all dependencies by running `npm install`. To run _grunt_,
     13you'll have to install the commandline client with `npm install -g
     14grunt-cli`.
     15
     16From source to a deployable version goes in few steps.
     17 1. Develop in `src`.
     18 1. Create a development version with `grunt build`. This will put a
     19    complete uncompressed version in `build/development`.
     20 1. Run the development version with `node server/standalone.js` or
     21    `foreman start`. For the last option, you have to have the Heroku
     22    chain installed.
     23 1. When editing more files, you can skip copying the fixed
     24    dependencies and only process our own source files by running
     25    `grunt compile`. If dependencies change (e.g. you installed node
     26    modules) or you have the feeling something is weird, just do a
     27    clean build by running `grunt build` again. Don't forget to
     28    restart the server.
     29 1. When you feel your changes are production ready, create a
     30    production version by running `grunt deploy`. This will do a
     31    _Dojo_ build on the client, creating compressed CSS and
     32    Javascript.
     33 1. You can test this version with the same commands as the
     34    development version, but now from the `build/production`
     35    directory. You can distribute the content is this directory as a
     36    release.
     37 1. If you want to deploy to Heroku, do the following:
     38    ```sh
     39    $ git clone git@heroku.com:quod-erat.git build/quod-erat
     40    $ cd build/quod-erat
     41    $ rm -rf *
     42    cp -r ../production/* .
     43    git add .
     44    git commit -a -m "Something informative goes here..."
     45    git push
     46    ```
    1247
    1348File organisation
    1449-----------------
     50
     51 * _/_ Project root
     52   * _build/_ Builds go here
     53     * _development/_ Uncompressed build
     54     * _production/_ Minified build
     55   * _docs/_ Project documentation, design documents etc
     56   * _src/_ Source code
     57     * _client/_ Dojo web client
     58     * _server/_ Node/Express server
     59     * _Procfile_ Heroku process description
     60
     61```
    1562client/
    1663  The web application for QED.
     
    66113      Contains general widgets that are used in our application but do
    67114      not depend on our model.
    68   rx/
    69     Rx.JS reactive framework for Javascript
    70     rx.dojo.js
    71       Custom file to integrate Rx with Dojo.
    72115  util/
    73116    Dojo utils (e.g. build infrastructure)
     117```
  • Dev/trunk/package.json

    r463 r464  
    1212    "grunt-htmlhint": "~0.4.0",
    1313    "grunt-contrib-clean": "~0.4.1",
    14     "grunt-zip": "~0.9.0",
    15     "grunt-curl": "~1.1.0",
    16     "grunt-contrib-coffee": "~0.7.0"
     14    "grunt-contrib-coffee": "~0.7.0",
     15    "underscore": "~1.4.4"
    1716  }
    1817}
  • Dev/trunk/src/.jshintrc

    r443 r464  
    1111    "unused": false,
    1212    "strict": false,
     13    "laxcomma": true,
    1314
    1415    "loopfunc": true,
  • Dev/trunk/src/client/qed-client/store/request.js

    r463 r464  
    1010        var dfd = new Deferred();
    1111        if ( authenticated ) {
    12             request(url,options).response.then(function(response){
    13                 console.log(response);
    14             }, function(error){
     12            var req = request(url,options);
     13
     14            // forward successfull response
     15            req.then(function(data){
     16                dfd.resolve(data);
     17            });
     18           
     19            req.response.then(function(response){
     20                // if we are recovering, launch other requests
     21            }, function(error) {
    1522                if ( error.response.status === 401 ) {
    1623                    queue.push({
  • Dev/trunk/src/package.json

    r463 r464  
    44  "description": "QED Server",
    55  "dependencies": {
    6     "simple-http-proxy": "~0.5.2",
    76    "express": "~3.2.3",
    87    "underscore": "~1.4.4",
    98    "passport": "~0.1.17",
    10     "passport-local": "~0.1.6"
     9    "passport-local": "~0.1.6",
     10    "q": "~0.9.6",
     11    "q-io": "~1.9.1"
    1112  },
    1213  "engines": {
  • Dev/trunk/src/server/app.js

    r463 r464  
    44var fs = require("fs");
    55var path = require("path");
    6 var proxy = require("simple-http-proxy");
     6var proxy = require("./simple-http-proxy");
    77var _ = require("underscore");
    88
     
    5858    // url to login (might work on others as well?)
    5959    // you should then have a session to work with
    60     app.post('/api/login', passport.authenticate('local'));
     60    app.post('/api/login');
    6161
    6262    // forward to couch
    63     app.use('/data/couch', passport.authenticate('local'), proxy(settings.couchDbURL));
     63    app.use('/data/couch', proxy(settings.couchDbURL));
    6464
    6565    return app;
  • Dev/trunk/src/server/config/config-couchdb.js

    r460 r464  
    11var q = require('q');
    2 var http = require('q-io/http');
    3 var fs = require('q-io/fs');
     2var HTTP = require('q-io/http');
     3var URL = require('url');
    44var _ = require('underscore');
     5var util = require('util');
    56
    6 var dbURL = "http://localhost:5984/";
    77var designDocs = require('./couchdb-design-docs');
    88
    9 function stringifyFunctions(value) {
    10     if ( value === null ) {
    11         return null;
    12     } else if ( _.isArray(value) ) {
    13         return value;
    14     } else if ( _.isFunction(value) ) {
    15         return value.toString();
    16     } else if ( _.isObject(value) ) {
    17         value = _.clone(value);
    18         _.each(value, function(propValue, propName){
    19             value[propName] = stringifyFunctions(propValue);
     9module.exports = function(couchDbURL) {
     10
     11    function stringifyFunctions(value) {
     12        if ( value === null ) {
     13            return null;
     14        } else if ( _.isArray(value) ) {
     15            return value;
     16        } else if ( _.isFunction(value) ) {
     17            return value.toString();
     18        } else if ( _.isObject(value) ) {
     19            value = _.clone(value);
     20            _.each(value, function(propValue, propName){
     21                value[propName] = stringifyFunctions(propValue);
     22            });
     23            return value;
     24        } else {
     25            return value;
     26        }
     27    }
     28
     29    function request(method,path,content) {
     30        var url = couchDbURL+path;
     31        var parsedURL = URL.parse(url);
     32        var options = {
     33            url: url,
     34            method: method,
     35            headers: {
     36                'content-type': 'application/json; charset=utf-8',
     37                'accept': 'application/json'
     38            },
     39            body: {
     40                forEach: function(callback) {
     41                    callback(JSON.stringify(content || {}));
     42                }
     43            }
     44        };
     45        // because q-io doesn't support auth properly, we have to
     46        // build the f*ing wheel again.
     47        if ( parsedURL.auth ) {
     48            var auth = new Buffer(parsedURL.auth).toString("base64");
     49            options.headers.authorization = 'Basic '+auth;
     50        }
     51        return HTTP.request(options)
     52        .then(function(res){
     53            return res.body.read().then(function(content){
     54                return JSON.parse(content.toString() || "{}");
     55            });
     56        },function(res){
     57            return res.body.read().then(function(error){
     58                console.warn(error); // q.all doesn't do errors, so let's show them here
     59                return JSON.parse(error.toString() || "{}");
     60            });
    2061        });
    21         return value;
    22     } else {
    23         return value;
    2462    }
    25 }
    2663
    27 function request(method,path,content) {
    28     var url = dbURL+path;
    29     return http.request({
    30         url: url,
    31         method: method,
    32         headers: {
    33             'content-type': 'application/json; charset=utf-8',
    34             'accept': 'application/json'
    35         },
    36         body: {
    37             forEach: function(callback) {
    38                 callback(JSON.stringify(content || {}));
    39             }
     64    console.log("Configuring CouchDB for QED");
     65    console.log("Checking CouchDB version");
     66    return request('GET','')
     67    .then(function(res){
     68        if (res.version !== "1.2.0" ) {
     69            console.log("Found "+res.version+", only tested with CouchDB 1.2.0");
     70        } else {
     71            console.log("CouchDB 1.2.0 found");
    4072        }
    4173    }).then(function(res){
    42         return res.body.read().then(function(content){
    43             return JSON.parse(content);
     74        console.log("Checking database 'qed'");
     75        return request('GET','qed')
     76        .then(function(res){
     77            console.log("Database 'qed' found.");
     78        },function(err){
     79            console.log("Creating database 'qed'");
     80            return request('PUT','qed');
    4481        });
    45     },function(res){
    46         return res.body.read().then(function(error){
    47             return JSON.parse(error);
    48         });
     82    }).then(function(){
     83        console.log("Putting documents in database.");
     84        designDocs = stringifyFunctions(designDocs);
     85        return q.all(_.map(designDocs, function(doc,docUrl){
     86            var configAction = doc.__configAction;
     87            delete doc.__configAction;
     88            switch (configAction) {
     89                case "ignore":
     90                    console.log(docUrl+" ignored.");
     91                break;
     92                case "update":
     93                    console.log(docUrl+" updating.");
     94                    return request('GET',docUrl)
     95                    .then(function(oldDoc){
     96                        _.extend(oldDoc,doc);
     97                        return request('PUT',docUrl,oldDoc);
     98                    },function(){
     99                        return request('PUT',docUrl,doc);
     100                    });
     101                break;
     102                case "replace":
     103                default:
     104                    console.log(docUrl+" replacing.");
     105                    return request('GET',docUrl)
     106                    .then(function(oldDoc){
     107                        _.extend(doc,_.pick(oldDoc,'_id','_rev'));
     108                        return request('PUT',docUrl,doc);
     109                    },function(){
     110                        return request('PUT',docUrl,doc);
     111                    });
     112                break;
     113            }
     114        }));
     115    }).then(function(){
     116        console.log("Done!");
     117    },function(err){
     118        console.error("ERROR",err,err.stack);
    49119    });
    50 }
    51120
    52 console.log("Configuring CouchDB for QED");
    53 console.log("Checking CouchDB version");
    54 request('GET','')
    55 .then(function(res){
    56     if (res.version !== "1.2.0" ) {
    57         console.log("Found "+res.version+", only tested with CouchDB 1.2.0");
    58     } else {
    59         console.log("CouchDB 1.2.0 found");
    60     }
    61     console.log("Checking database 'qed'");
    62 }).then(function(res){
    63     return request('GET','qed')
    64     .then(function(res){
    65         console.log("Database 'qed' found.");
    66     },function(err){
    67         console.log("Creating database 'qed'");
    68         return request('PUT','qed');
    69     });
    70 }).then(function(){
    71     console.log("Putting documents in database.");
    72     designDocs = stringifyFunctions(designDocs);
    73     return q.all(_.map(designDocs, function(doc,docUrl){
    74         var configAction = doc.__configAction;
    75         delete doc.__configAction;
    76         switch (configAction) {
    77             case "ignore":
    78                 console.log(docUrl+" ignored.");
    79             case "update":
    80                 console.log(docUrl+" updating.");
    81                 return request('GET',docUrl)
    82                 .then(function(oldDoc){
    83                     _.extend(oldDoc,doc);
    84                     return request('PUT',docUrl,oldDoc);
    85                 },function(){
    86                     return request('PUT',docUrl,doc);
    87                 });
    88             case "replace":
    89             default:
    90                 console.log(docUrl+" replacing.");
    91                 return request('GET',docUrl)
    92                 .then(function(oldDoc){
    93                     _.extend(doc,_.pick(oldDoc,'_id','_rev'));
    94                     return request('PUT',docUrl,doc);
    95                 },function(){
    96                     return request('PUT',docUrl,doc);
    97                 });
    98         }
    99     }));
    100 }).then(function(){
    101     console.log("Done!");
    102 },function(err){
    103     console.error("ERROR",err);
    104 });
     121};
  • Dev/trunk/src/server/config/couchdb-design-docs.js

    r455 r464  
    11module.exports = {
     2
    23    "_users/qed_admin": {
    34        __configAction: "ignore",
     
    89        type: "user"
    910    },
     11
    1012    "qed/_security": {
    1113        __configAction: "ignore",
     
    1921        }
    2022    },
     23
    2124    "qed/_design/default": {
    2225        __configAction: "replace",
     
    3437        }
    3538    },
     39
    3640    "qed/_design/questions": {
    3741        __configAction: "replace",
     
    8185        }
    8286    },
     87
    8388    "qed/_design/surveys": {
    8489        __configAction: "replace",
     
    100105        }
    101106    },
     107
    102108    "qed/_design/responses": {
    103109        __configAction: "replace",
     
    113119        }
    114120    }
     121
    115122};
  • Dev/trunk/src/server/heroku.js

    r463 r464  
    1 var app = require('./app').App({
    2     couchDbURL: 'http://localhost:5984/qed'
     1var port = process.env.PORT || 5000;
     2var couchDbURL = (process.env.CLOUDANT_URL || 'http://localhost:5984')+'/';
     3
     4var configCouch = require('./config/config-couchdb');
     5
     6configCouch(couchDbURL)
     7.then(function(){
     8    var app = require('./app').App({
     9        couchDbURL: couchDbURL+'qed'
     10    });
     11
     12    app.listen(port, function() {
     13        console.log('Listening on port',port);
     14    });
    315});
    4 
    5 var port = process.env.PORT || 5000;
    6 app.listen(port, function() {
    7     console.log('Listening on port',port);
    8 });
  • Dev/trunk/src/server/standalone.js

    r463 r464  
    22var https = require("https");
    33var os = require("os");
    4 var path = require("path");
    54var _ = require("underscore");
    65
     
    109
    1110var httpsOptions = {
    12     key: fs.readFileSync(path.resolve('../qed-server.key')),
    13     cert: fs.readFileSync(path.resolve('../qed-server.pem'))
     11    key: fs.readFileSync(__dirname+'/../qed-server.key'),
     12    cert: fs.readFileSync(__dirname+'/../qed-server.pem')
    1413};
    1514
Note: See TracChangeset for help on using the changeset viewer.