Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

The Cardshifter web client is coming along! The game client is a web app written mostly in Angular.js and is hosted at at play.cardshifter.com.

@SimonForsberg (who runs the server) is getting tired of the web people always working tirelessly and updating the code, and asked for a script that can do deployment for him.

Requirements

  • Upload the contents of dist/ to /assets/ and www/ to / through FTP.
  • Post a message to the chat bot Duga using a HTTP request when done.
  • Can be run automatically on Travis CI.

Background

I'm quite new to JavaScript and Node.js land. I chose to still use it for three reasons:

  1. Dependency management already set up with Npm
  2. Project is already JavaScript; easier maintenance
  3. Fun to learn :)

I chose to pass credentials through environment variables because it looks like Travis CI handles encrypted variables well. A person that has the credentials can also set up a simple script to run the deployment with those specific variables.

@skiwi told me about promises after having written the code originally, but it prompted me to convert the code to use them instead of normal callbacks. I think this lead to a great improvement of the readability of the code.

ftp-deploy was acting up when uploading dist/ and www/ separately, which is why the script is now first copying everything to a temporary directory with a structure emulating the server. One problem was that if remoteRoot didn't exist, the upload would fail.

Simon hasn't told me the address of the FTP server yet, which is why I left localhost:2121 in there.

/* Deploy the client to play.cardshifter.com
 *
 * How to use:
 *  1. Build the project and make sure that everything is in order.
 *  2. Set up environment variables (see below).
 *  3. Run `npm run deploy`.
 *  4. Profit!
 *
 * Environment variables used:
 *  - DEPLOY_FTP_USERNAME: Username used to log in through FTP.
 *  - DEPLOY_FTP_PASSWORD: Password used to log in through FTP.
 *  - DEPLOY_DUGA_KEY: Duga API key. If not set the chat bot post is skipped.
 */
'use strict';

var copy = require('recursive-copy');
var FtpDeploy = require('ftp-deploy');
var path = require('path');
var request = require('request-promise');
var temp = require('temp').track();

function ftpConfig(local, remote) {
    return {
        username: process.env.DEPLOY_FTP_USERNAME,
        password: process.env.DEPLOY_FTP_PASSWORD,
        host: "localhost",
        port: 2121,
        localRoot: local,
        remoteRoot: remote
    };
};

var chatBotRequest = {
    apiKey: process.env.DEPLOY_DUGA_KEY,
    roomId: 16134,
    text: "New web client version uploaded to http://play.cardshifter.com/."
};

var chatBotConfig = {
    url: "http://stats.zomis.net/GithubHookSEChatService/bot/jsonPost",
    method: "POST",
    headers: {
        "Content-Type": "application/json"
    }
}

function postToChat(config, botRequest) {
    return new Promise(function(resolve, reject) {
        var json = JSON.stringify(botRequest, ["apiKey", "roomId", "text"]);
        config.headers["Content-Length"] = json.length;
        config.body = json;

        request(config)
        .then(function(body) {
            resolve(body);
        })
    });
}

function setupFiles() {
    // ftp-deploy doesn't handle uploading from multiple directories well
    return new Promise(function(resolve, reject) {
        temp.mkdir("cardshifter-deploy", function (err, tempDir) {
            if (err) {
                reject(err);
            }

            Promise.all([
                copy(path.join(__dirname, "..", "www"), tempDir),
                copy(path.join(__dirname, "..", "dist"), path.join(tempDir, "assets"))
            ])
            .then(function() {
                resolve(tempDir);
            })
            .catch(function(err) {
                reject(err);
            });
        });
    });
}

function deployFtp(config) {
    return new Promise(function(resolve, reject) {
        new FtpDeploy().deploy(config, function(err) {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

setupFiles()
.then(function(dir) {
    var config = ftpConfig(dir, "/");
    console.log("Deploying to ftp://" + config.host + ":" + config.port + "...");
    return deployFtp(config);
})
.then(function() {
    console.log("FTP deployment successful.");
    if (chatBotRequest.apiKey) {
        console.log("Posting message to " + chatBotConfig.url + "...");
        return postToChat(chatBotConfig, chatBotRequest);
    }
})
.then(function(responseBody) {
    if (responseBody) {
        console.log(responseBody);
    }
})
.catch(function(err) {
    console.error("Error: " + err);
});
share|improve this question
1  
Why don't you guys use Amazon S3 for static assets? It's not free but it's cheap enough to essentially be free (something crazy like 0.9c for 10k requests) –  A Red Herring 18 hours ago
1  
One other thing you might look into is gulp. Particularly as it has modules for ftp, and all of the related stuff there. I'm not familiar with Duga though, so I can't help much there. –  Seiyria 13 hours ago
    
Server address? Try play.cardshifter.com ;) –  Simon Forsberg 11 hours ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.