Instead of integrating your app with deployment platforms like deployHQ, you can use Travis-CI to create your custom deployment process.
In my case, I just need to deploy my front-end static assets up to an FTP server.
Create your deploy script
I use node-ftp to do the ftp task. It is quite a easy-to-use module, the code is much like below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
// deploy.js var Client = require('ftp'); var chalk = require('chalk'); var fs = require('fs'); var path = require('path'); var ENV = process.env; var BUILD_PATH = path.resolve(__dirname, ENV.FTP_BUILD_PATH || 'build'); var TARGET_PATH = ENV.FTP_SERVER_PATH; var USERNAME = ENV.FTP_USERNAME; var PASSWORD = ENV.FTP_PASSWORD; var HOST = ENV.FTP_SERVER_HOST; var PORT = ENV.FTP_SERVER_PORT || 21; var client = new Client(); client.on('greeting', function(msg) { console.log(chalk.green('greeting'), msg); }); client.on('ready', function() { client.list(TARGET_PATH, function(err, serverList) { console.log(chalk.green('get list from server.')); /* * somehow you need to workout what files you are going to upload * you may need to compare with what already exists in the server */ var uploadList = /* your upload list */ var total = uploadList.length; var uploadCount = 0; var errorList = []; uploadList.forEach(function(file) { console.log(chalk.blue('start'), file.local + chalk.grey(' --> ') + file.target); client.put(file.local, file.target, function(err) { uploadCount++; if (err) { console.error(chalk.red('error'), file.local + chalk.grey(' --> ') + file.target); console.error(err.message); throw err; } else { console.info(chalk.green('success'), file.local + chalk.grey(' --> ') + file.target, chalk.grey('( ' + uploadCount + '/' + total + ' )')); } if (uploadCount === total) { client.end(); if (errorList.length === 0) { console.info(chalk.green('All files uploaded!')); } else { console.log(chalk.red('Failed files:')); errorList.forEach(function(file) { console.log(file.local + chalk.grey(' --> ') + file.target); }); throw 'Total Failed: ' + errorList.length; } } }); }); }); }); // connect to localhost:21 as anonymous client.connect({ host: HOST, port: PORT, user: USERNAME, password: PASSWORD, }); |
Notice the environment variables I use to setup the FTP client. You should not hard code this info into the script; it is not secure and not flexible either. We can pass down all the variables from Travis-CI later on.
Config your travis.yml
Travis provides script deployment for you to create custom deployment process.
1 2 3 4 5 6 |
deploy: provider: script script: node deploy.js on: branch: master |
You just need to specify your deploy script and which branch you want to deploy. In my case, i want to exclude the /build
dir from my repo, and I want the building process on the cloud instead of manually building the assets. So the whole config will be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
language: node_js node_js: - node branches: only: - master script: - npm run build deploy: skip_cleanup: true provider: script script: node deploy.js on: branch: master |
Config your environment variables
Open you project in Travis, and go to project settings:
And add all the variables you need to get access in your script:
Push your code and deploy!
Now push some code to your master (bad habit, though, you should use pull request instead) and see if it works.
3 Comments
Hi can you provide and exemple on how to fullfill var uploadList with current directory and subdirectories please ? Thx
Hi Stéphane, since the script is NodeJS, you can just use the variable
__dirname
to get the pathname of the directory of the current file, then you can calculate the “current directory” based on your project structure bypath.resolve(__dirname', './xxxx')
Hi Neekey,
thank you for this great article. I have configured auto deploy for one site based on it.
@Stéphane
Here is example of my function to create uploadList
const listFiles = dir => {
let filesList = [];
const files = fs.readdirSync(dir);
files.map(file => {
const fullPath = path.resolve(dir, file);
const stats = fs.lstatSync(fullPath);
if (stats.isDirectory()) {
filesList = filesList.concat(listFiles(fullPath));
} else {
if (dir.endsWith(BUILD_PATH)) {
filesList.push({
'local': fullPath,
'target': file
});
} else {
const lastSeparator = dir.lastIndexOf(path.sep);
const parentDir = dir.substring(lastSeparator);
const targetPath = ${parentDir}${path.sep}${file}
.replace(/\\/g, '/');
filesList.push({
'local': fullPath,
'target': targetPath
});
}
}
});
return filesList;
};