Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
zonak committed Oct 22, 2012
0 parents commit 6253555
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
.DS_Store
Thumbs.db
node_modules
.ftppass
2 changes: 2 additions & 0 deletions .npmignore
@@ -0,0 +1,2 @@
.npmignore
.ftppass
22 changes: 22 additions & 0 deletions LICENSE-MIT
@@ -0,0 +1,22 @@
Copyright (c) 2012 Damon Oehlman <damon.oehlman@sidelab.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.
58 changes: 58 additions & 0 deletions README.md
@@ -0,0 +1,58 @@
# grunt-ftp-deploy

This is a [grunt](https://github.com/gruntjs/grunt) task for code deployment over the _ftp_ protocol.

These days _git_ is not only our goto code management tool but in many cases our deployment tool as well. But there are many cases where _git_ is not really fit for deployment:

- we deploy to servers with only _ftp_ access
- the production code is a result of a build process producing files that we do not necessarily track with _git_

This is why a _grunt_ task like this would be very useful.

For simplicity purposes this task avoids deleting any files and it is not trying to do any size or time stamp comparison. It simply transfers all the files (and folder structure) from your dev location to a location on your server.

## Usage

To use this task you will need to include the following configuration in your _grunt_ file:

```json
ftp-deploy: {
build: {
auth: {
host: 'server.com',
port: 21,
user: 'myUserName'
},
src: 'build',
dest: '/path/to/destination/folder'
}
}
```

and load the task:

```javascript
grunt.loadNpmTasks('grunt-ftp-deploy');
```

The parameters in our configuration are:

- **host** - the name or the IP address of the server we are deploying to
- **port** - the port that the _ftp_ service is running on
- **user** - the username we authenticate ourselves with
- **src** - the source location, the local folder that we are transferring to the server
- **dest** - the destination location, the folder on the server we are deploying to

## Password management

There are two ways we can provide the password for the _ftp_ authentication:

- as an optional argument to the task
- stored in a text file named `.ftppass`

The first method takes precedence over the second one.

## Dependencies

This task is built by taking advantage of the great work of Sergi Mansilla and his [jsftp](https://github.com/sergi/jsftp) _node.js_ module.

33 changes: 33 additions & 0 deletions package.json
@@ -0,0 +1,33 @@
{
"name": "grunt-ftp-deploy",
"description": "Deployment over FTP",
"version": "0.0.1",
"homepage": "https://github.com/zonak/grunt-ftp-deploy",
"author": {
"name": "Zoran Nakev",
"email": "zoran.nakev@gmail.com"
},
"repository": {
"type": "git",
"url": "git://github.com/zonak/grunt-ftp-deploy.git"
},
"bugs": {
"url": "https://github.com/zonak/grunt-ftp-deploy/issues"
},
"licenses": [
{
"type": "MIT",
"url": "https://github.com/zonak/grunt-ftp-deploy/blob/master/LICENSE-MIT"
}
],
"engines": {
"node": "*"
},
"dependencies": {
"jsftp": "0.4.x",
"grunt": "0.3.x"
},
"keywords": [
"gruntplugin"
]
}
175 changes: 175 additions & 0 deletions tasks/ftp-deploy.js
@@ -0,0 +1,175 @@
//
// Grunt Task File
// ---------------
//
// Task: FTP Deploy
// Description: Deploy code over FTP
// Dependencies: jsftp
//

module.exports = function(grunt) {

var async = grunt.util.async;
var log = grunt.log;
var _ = grunt.util._;
var fs = require('fs');
var path = require('path');
var Ftp = require('jsftp');

var toTransfer;
var ftp;
var localRoot;
var remoteRoot;
var currPath;
var user;
var pass;

// A method for parsing the source location and storing the information into a suitably formated object
function dirParseSync(startDir, result) {
var files;
var i;
var tmpPath;
var currFile;

// initialize the `result` object if it is the first iteration
if (result === undefined) {
result = {'/': []};
}

// check if `startDir` is a valid location
if (!fs.existsSync(startDir)) {
grunt.warn(startDir + 'is not an existing location');
}

// iterate throught the contents of the `startDir` location of the current iteration
files = fs.readdirSync(startDir);
for (i = 0; i < files.length; i++) {
currFile = fs.lstatSync(startDir + path.sep + files[i]);
if (currFile.isDirectory()) {
tmpPath = path.relative(localRoot, startDir + path.sep + files[i]);
if (!_.has(result, tmpPath)) {
result[tmpPath] = [];
}
dirParseSync(startDir + path.sep + files[i], result);
} else {
tmpPath = path.relative(localRoot, startDir);
if (!tmpPath.length) {
tmpPath = '/';
}
result[tmpPath].push(files[i]);
}
}

return result;
}

// A method for changing the remote working directory and creating one if it doesn't already exist
function ftpCwd(inPath, cb) {
ftp.raw.cwd(inPath, function(err) {
if(err){
ftp.raw.mkd(inPath, function(err) {
if(err) {
log.error('Error creating new remote folder ' + inPath + ' --> ' + err);
cb(err);
} else {
log.ok('New remote folder created ' + inPath.yellow);
ftpCwd(inPath, cb);
}
});
} else {
cb(null);
}
});
}

// A method for uploading a single file
function ftpPut(inFilename, cb) {
ftp.put(inFilename, grunt.file.read(localRoot + path.sep + currPath + path.sep + inFilename), function(err) {
if(err) {
log.error('Cannot upload file: ' + inFilename + ' --> ' + err);
cb(err);
} else {
log.ok('Uploaded file: ' + inFilename.green + ' to: ' + currPath.yellow);
cb(null);
}
});
}

// A method that processes a location - changes to a fodler and uploads all respective files
function ftpProcessLocation (inPath, cb) {
if (!toTransfer[inPath]) {
cb(new Error('Data for ' + inPath + ' not found'));
}

ftpCwd(remoteRoot + path.sep + inPath, function (err) {
var files;

if (err) {
grunt.warn('Could not switch to remote folder!');
}

currPath = inPath;
files = toTransfer[inPath];

async.forEach(files, ftpPut, function (err) {
if (err) {
grunt.warn('Failed uploading files!');
}
cb(null);
});
});
}

// The main grunt task
grunt.registerMultiTask('ftp-deploy', 'Deploy code over FTP', function() {
var done = this.async();

// Init
ftp = new Ftp({
host: this.data.auth.host,
port: this.data.auth.port
});
localRoot = this.file.src[0];
remoteRoot = this.file.dest;
user = this.data.auth.user;
pass = null;
ftp.useList = true;
toTransfer = dirParseSync(localRoot);

// If there is a password provided as an argument
if (this.args.length) {
pass = this.args[0];
} else if (fs.existsSync('.ftppass')) { // If there is a `.ftppass` file found in the root of the project get the password from there
pass = grunt.file.read('.ftppass');
}

// Checking if we have all the necessary credentilas before we proceed
if (user == null || pass == null) {
grunt.warn('Username or Password not found!');
}

// Authentication and main processing of files
ftp.auth(user, pass, function(err) {
var locations = _.keys(toTransfer);
if (err) {
grunt.warn('Authentication ' + err);
}

// Iterating through all location from the `localRoot` in parallel
async.forEachSeries(locations, ftpProcessLocation, function() {
ftp.raw.quit(function(err) {
if (err) {
log.error(err);
} else {
log.ok('FTP upload done!');
}
done();
});
});
});

if (grunt.errors) {
return false;
}
});
};

0 comments on commit 6253555

Please sign in to comment.