├── .gitignore ├── package.json ├── README.md ├── sunny.plugin.coffee └── sunny.plugin.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docpad-plugin-sunny", 3 | "version": "3.0.0", 4 | "description": "DocPad plugin which adds the ability to output to Amazon S3, Google Storage or and other service supported by SunnyJS.", 5 | "homepage": "https://github.com/bobobo1618/docpad-plugin-sunny", 6 | "keywords": [ 7 | "docpad", 8 | "docpad-plugin", 9 | "amazon", 10 | "s3", 11 | "sunny", 12 | "google" 13 | ], 14 | "author": "Lucas ", 15 | "maintainers": [ 16 | "Lucas " 17 | ], 18 | "contributors": [ 19 | "Lucas " 20 | ], 21 | "bugs": { 22 | "url": "https://github.com/bobobo1618/docpad-plugin-sunny/issues" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/bobobo1618/docpad-plugin-sunny.git" 27 | }, 28 | "engines": { 29 | "node": ">=0.4.0", 30 | "docpad": "6.x" 31 | }, 32 | "main": "./sunny.plugin.js", 33 | "dependencies": { 34 | "sunny": "0.0.5", 35 | "mime": "1.2.x", 36 | "taskgroup": "~3.2.4" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docpad Sunny 2 | 3 | So this is another one of the only useful things I've published, yay! Actually distributing my projects is new to me. 4 | 5 | Basically, after your [Docpad](https://github.com/docpad/docpad) installation finishes generating the static documents, this plugin is meant to upload them all to Amazon S3 or Google Storage, whichever you select. It uses the apparently awesome library, [Sunny](https://github.com/ryan-roemer/node-sunny). Give Ryan some love. 6 | 7 | As of 2.0.13, the plugin no longer automatically pushes. Instead, there is a command line option to docpad, `deploy-sunny` which deploys to Sunny supported providers for you. 8 | 9 | ## Installation 10 | 11 | In your Docpad site directory: 12 | 13 | - Temporary: `npm install docpad-plugin-sunny` 14 | - Permanent: `npm install --save docpad-plugin-sunny` (should write the dependency to package.json) 15 | 16 | ## Configuration 17 | 18 | Configuration is mainly set in your Docpad configuration file in the plugins section (look [here](http://docpad.org/docs/config/) for more explanation). The relevant shortname is `sunny`. 19 | 20 | The options are: 21 | 22 | - `configFromEnv`: Set this to `true` if you want to load configuration from the environment. 23 | - `envPrefixes`: An array of prefixes to try to load environment variables with. (e.g. ["MYAPP_SUNNY_", "YOURAPP_SUNNY_"]) 24 | - `cloudConfigs`: An array of objects with the following properties: 25 | - `sunny`: Another object holding the variables passed to `sunny.Configuration.fromObj`. It has the following properties: 26 | - `provider`: A string. Can be any provider supported by sunny. At the moment, must be either `aws` or `google`. 27 | - `account`: A string. The account to use to connect. For Amazon this is the access key, for Google, you get this from the Interoperable Access page under Google Storage in [the console](https://console.developers.google.com/dcredirect/) 28 | - `secretKey`: The key to use. For Amazon, this is the AWS secret key, for Google, this is the Secret found on the page mentioned above. 29 | - `ssl`: `true` or `false`. Whether or not to use SSL to connect. 30 | - `authUrl`: A string. The URL of the service to connect to. E.g. `s3-us-west-1.amazonaws.com` 31 | - `container`: A string containing the name of the container to use. 32 | - `acl`: ACL to use for all requests. Set to `false` to tell sunny not to send an x--acl header. Set to send `public-read` by default. 33 | - `retryLimit`: Number of times to retry a request. Set to -1 for infinite. Set to 2 by default. 34 | 35 | An example section from a docpad config: 36 | 37 | ```coffeescript 38 | [...] 39 | port: 8000 40 | enabledPlugins: 41 | sunny: true # Not necessary, just for reference. 42 | site: 43 | [...] 44 | plugins: 45 | sunny: 46 | configFromEnv: true 47 | envPrefixes: ["DOCPAD_SUNNY_", "DOCPAD_", "MY_AWESOME_APP_SUNNY_"] 48 | cloudConfigs: [ 49 | { 50 | sunny: { 51 | provider: 'google' 52 | account: 'GOOGOPSDG76978SDG' 53 | secretKey: 'SD&*G68S&^DG*&6s8SD' 54 | ssl: true 55 | } 56 | container: 'herpderp.com' 57 | acl: 'private' 58 | }, 59 | { 60 | sunny: { 61 | provider: 'aws' 62 | account: 'ADSDG876SDG87S' 63 | authUrl: 's3-some-region.amazonaws.com' 64 | secretKey: 'A(*G&(S97*S^DG(' 65 | ssl: true 66 | } 67 | container: 'meow' 68 | acl: false #Uses the policy already set on S3. 69 | retryLimit: -1 # Retry as long as is necessary until the upload works. 70 | }] 71 | [...] 72 | ``` 73 | 74 | This will read two providers from the file (Google and Amazon). Google is set to private reads and Amazon to use whatever the default is on the bucket. 75 | 76 | It is also set to pick up configuration from variables with the prefixes `DOCPAD_SUNNY_`, `DOCPAD_`, and `MY_AWESOME_APP_SUNNY_` (so `DOCPAD_SUNNY_ACCOUNT` etc.) 77 | 78 | 79 | ### Environment 80 | 81 | There are 4 environment variables per prefix that must be configured and 2 optional, that can be set for SSL. 82 | 83 | If no prefixes are set in the main configuration section, the default is `DOCPAD_SUNNY_` 84 | 85 | Mandatory for it to work: 86 | 87 | - `PROVIDER = aws|google`: The cloud storage provider to use. At the moment only Google and Amazon are supported. 88 | - `ACCOUNT`: The account to use to connect. For Amazon this is the access key, for Google, you get this from the Interoperable Access page under Google Storage in [the console](https://code.google.com/apis/console/) 89 | - `SECRETKEY`: The key to use. For Amazon, this is the AWS secret key, for Google, this is the Secret found on the page mentioned above. 90 | - `CONTAINER`: The container to use. a.k.a. bucket. 91 | 92 | Optional: 93 | 94 | - `SSL = true|false`: Whether or not to use SSL. False by default. 95 | - `ACL`: The default permissions to use. Set to `public-read` by default. Check the Amazon and [Google](https://cloud.google.com/storage/docs/access-control#extension) documentation for details. 96 | - `RETRY_LIMIT`: Number of times to retry uploads. -1 for infinite. 2 by default. 97 | - `AUTHURL`: The URL to use. E.g. `s3.amazonaws.com` 98 | 99 | ## Running 100 | 101 | By default, the plugin won't run unless either `NODE_ENV=production` or `onlyIfProduction` is false in the configuration. So either set `onlyIfProduction` to false, run `export NODE_ENV=production` before running docpad or run `NODE_ENV=production docpad generate`. 102 | 103 | Generated files will be added to the cloud providers whenever Docpad runs the generate hook. 104 | 105 | ## Custom headers 106 | 107 | The plugin actually checks each Docpad file for a piece of metadata named `headers`. If you put this field in, you can set up a list of HTTP headers that will be sent with the corresponding request. You can use it to force a mime type, set cache control etc. 108 | 109 | ## Security 110 | 111 | Since you may wish to use this in an OSS project such as a blog or somesuch or any real application where you want to distribute source but keep all your keys private, there are two options available for configuration: 112 | 113 | - Environment variables (people can't really commit these through Git ;) ) 114 | - A separate configuration file put in .gitignore. 115 | 116 | ## Known bugs 117 | 118 | None! :D 119 | 120 | ## Known solutions to things that aren't quite bugs ;) 121 | 122 | Gettings errors like this: 123 | 124 | `Received error trying to connect to provider: 125 | Error: 126 | TemporaryRedirectPlease re-send this request to the specified temporary endpoint. Continue to use the original request endpoint for future requests.[redacted][redacted][redacted][redacted].s3-ap-southeast-2.amazonaws.com` 127 | 128 | Can be fixed by setting the `authUrl` for the config that's having the problem to the endpoint given in the error. In this case, you could add `authUrl: '[redacted].s3-ap-southeast-2.amazonaws.com'` to your configuration. 129 | 130 | ## Contributors 131 | 132 | - Me 133 | - [nfriedly](https://github.com/nfriedly) 134 | - [Ryan Roemer](https://github.com/ryan-roemer) (wrote Sunny) 135 | 136 | ## License 137 | 138 | Do what you want so long as this repo be credited or referenced somehow, I ask that [Ryan Roemer](https://github.com/ryan-roemer) be credited also, since he wrote SunnyJS which does all the real work here. 139 | -------------------------------------------------------------------------------- /sunny.plugin.coffee: -------------------------------------------------------------------------------- 1 | # Yay requires. 2 | sunny = require 'sunny' 3 | mime = require 'mime' 4 | http = require 'http' 5 | util = require 'util' 6 | {TaskGroup} = require('taskgroup') 7 | 8 | uploadData = (container, path, headers, data, retryLimit, retries, next)-> 9 | # Test for whether to retry the upload. 10 | retries = if retries? then retries else 0 11 | retryLimit = if retryLimit? then retryLimit else 2 12 | doIt = if not (retryLimit) or (retryLimit and (retries <= retryLimit)) or retryLimit is -1 then true else false 13 | 14 | if doIt 15 | if retries 16 | console.log "Retrying upload of #{path} to #{container.name}: Attempts: #{retries}" 17 | 18 | #Open the stream and do the write. 19 | writeStream = container.putBlob path, headers 20 | writeStream.on 'error', (err)-> 21 | console.log "Error uploading #{path} to #{container.name}" 22 | # Recall this function with the retry counter incremented. Yay recursion! 23 | uploadData container, path, headers, data, retryLimit, retries+1, next 24 | writeStream.on 'end', (results, meta)-> 25 | console.log "Uploaded #{path} to #{container.name}" 26 | next() 27 | writeStream.write data 28 | writeStream.end() 29 | else 30 | next("Upload for #{path} to #{container.name} has failed #{retries} times. Giving up.") 31 | 32 | # Does the upload after Sunny has been set up and such. 33 | doUpload = (docpad, container, acl, retryLimit, next)-> 34 | # Seems obvious enough. Sets files to public read in the cloud. 35 | if acl? 36 | if acl is false 37 | cloudHeaders = {} 38 | else 39 | cloudHeaders = {"acl": acl} 40 | else 41 | cloudHeaders = {"acl": 'public-read'} 42 | 43 | tasks = new TaskGroup().once 'complete', (err) -> 44 | return next(err) 45 | 46 | docpad.getFiles(write: true).forEach (file)-> 47 | path = file.attributes.relativeOutPath 48 | 49 | # Gets the correct data from Docpad. 50 | data = file.get('contentRendered') || file.get('content') || (file.getData && file.getData()) || file.getContent() || "" 51 | if !data 52 | return next("No data for file #{path}") 53 | length = data.length 54 | type = mime.lookup path #file.get('contentType') 55 | 56 | headers = { 57 | "Content-Length": length, 58 | "Content-Type": type 59 | } 60 | 61 | # Merge the headers with those Docpad has. 62 | try 63 | if file.get('headers')? #and file.get('headers').length? 64 | for key, value of file.get('headers') 65 | headers[key] = value 66 | catch err 67 | console.log err 68 | console.dir file 69 | 70 | tasks.addTask (complete) -> 71 | uploadData container, path, {headers: headers, cloudHeaders: cloudHeaders}, data, retryLimit, 0, complete 72 | 73 | tasks.run() 74 | 75 | 76 | 77 | handle = (docpad, sunnyConfig, sunnyContainer, defaultACL, retryLimit, next)-> 78 | # Test the configuration and try it. 79 | if sunnyConfig.provider? and sunnyConfig.account? and sunnyConfig.secretKey? and sunnyContainer? 80 | # Get a connection to the provider. 81 | connection = sunny.Configuration.fromObj(sunnyConfig).connection 82 | # Prepare a request to the provider for the container. Checks to make sure the container exists. 83 | containerReq = connection.getContainer sunnyContainer, {validate: true} 84 | 85 | containerReq.on 'error', (err)-> 86 | console.log "Received error trying to connect to provider: \n #{err}" 87 | 88 | containerReq.on 'end', (results, meta)-> 89 | if results # not sure exactly how, but the 'end' can get called more than once with null params on the second call 90 | container = results.container 91 | console.log "Got container #{container.name}." 92 | # Do the upload. 93 | doUpload docpad, container, defaultACL, retryLimit, next 94 | 95 | containerReq.end() 96 | else 97 | next(""" 98 | One of the config variables is missing. Printing config: 99 | #{util.inspect(sunnyConfig)} 100 | Container is #{sunnyContainer} 101 | """) 102 | 103 | handleEnvPrefix = (docpad, prefix, next)-> 104 | sunnyConfig = { 105 | provider: process.env["#{prefix}PROVIDER"], # Cloud provider: (aws|google) 106 | account: process.env["#{prefix}ACCOUNT"], 107 | secretKey: process.env["#{prefix}SECRETKEY"], 108 | ssl: process.env["#{prefix}SSL"] 109 | authUrl: process.env["#{prefix}AUTHURL"] 110 | } 111 | sunnyContainer = process.env["#{prefix}CONTAINER"] 112 | sunnyACL = process.env["#{prefix}ACL"] 113 | sunnyRetryLimit = process.env["#{prefix}RETRY_LIMIT"] 114 | 115 | # Parse the environment variable for ssl. 116 | sunnyConfig.ssl = ((typeof(sunnyConfig.ssl) is 'string') and (sunnyConfig.ssl.toLowerCase() is 'true')) 117 | 118 | handle docpad, sunnyConfig, sunnyContainer, sunnyACL, sunnyRetryLimit, next 119 | 120 | handleEnv = (docpad, config, next)-> 121 | if config.envPrefixes.length > 0 122 | for prefix in config.envPrefixes 123 | handleEnvPrefix docpad, prefix, next 124 | else 125 | handleEnvPrefix docpad, "DOCPAD_SUNNY_", next 126 | 127 | module.exports = (BasePlugin) -> 128 | class docpadSunnyPlugin extends BasePlugin 129 | name: "sunny" 130 | 131 | config: 132 | defaultACL: 'public-read' 133 | onlyIfProduction: true 134 | configFromEnv: false 135 | envPrefixes: [] 136 | cloudConfigs: [ 137 | # { 138 | # sunny:{ 139 | # provider: undefined 140 | # account: undefined 141 | # secretKey: undefined 142 | # ssl: undefined 143 | # authUrl: undefined 144 | # }, 145 | # container: undefined, 146 | # acl: undefined 147 | # retryLimit: undefined 148 | # } 149 | ] 150 | 151 | deployWithSunny: (next)=> 152 | docpad = @docpad 153 | config = @getConfig() 154 | 155 | if config.cloudConfigs.length > 0 or config.configFromEnv 156 | docpad.log 'info', "Found #{config.cloudConfigs.length} configurations in file." 157 | @docpad.generate (err)-> 158 | return next(err) if err 159 | 160 | tasks = new TaskGroup().once 'complete', (err) -> 161 | return next(err) 162 | 163 | if config.configFromEnv 164 | docpad.log 'info', "Grabbing configs from environment." 165 | tasks.addTask (complete) -> 166 | handleEnv docpad, config, complete 167 | 168 | for cloudConfig in config.cloudConfigs 169 | tasks.addTask (complete) -> 170 | handle docpad, cloudConfig.sunny, cloudConfig.container, cloudConfig.acl, cloudConfig.retryLimit, complete 171 | tasks.run() 172 | else 173 | errMsg = 'No configs found' 174 | docpad.log 'warn', errMsg 175 | next(errMsg) 176 | 177 | consoleSetup: (opts)=> 178 | docpad = @docpad 179 | config = @getConfig() 180 | {consoleInterface, commander} = opts 181 | 182 | commander 183 | .command('deploy-sunny') 184 | .description("Deploys your website to any provider allowed by Sunny.") 185 | .action consoleInterface.wrapAction(@deployWithSunny) 186 | 187 | @ 188 | 189 | #writeAfter: (opts, next)-> 190 | # next?() 191 | # if (not @config.onlyIfProduction) or (process.env.NODE_ENV is "production") 192 | # if @config.configFromEnv 193 | # console.log "Sunny plugin getting config from environment..." 194 | # handleEnv @docpad, @config 195 | # 196 | # if @config.cloudConfigs.length > 0 197 | # console.log "Found #{@config.cloudConfigs.length} configurations in config file." 198 | # for cloudConfig in @config.cloudConfigs 199 | # handle @docpad, cloudConfig.sunny, cloudConfig.container, cloudConfig.acl, cloudConfig.retryLimit 200 | -------------------------------------------------------------------------------- /sunny.plugin.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.3 2 | (function() { 3 | var TaskGroup, doUpload, handle, handleEnv, handleEnvPrefix, http, mime, sunny, uploadData, util, 4 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 5 | __hasProp = {}.hasOwnProperty, 6 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 7 | 8 | sunny = require('sunny'); 9 | 10 | mime = require('mime'); 11 | 12 | http = require('http'); 13 | 14 | util = require('util'); 15 | 16 | TaskGroup = require('taskgroup').TaskGroup; 17 | 18 | uploadData = function(container, path, headers, data, retryLimit, retries, next) { 19 | var doIt, writeStream; 20 | retries = retries != null ? retries : 0; 21 | retryLimit = retryLimit != null ? retryLimit : 2; 22 | doIt = !retryLimit || (retryLimit && (retries <= retryLimit)) || retryLimit === -1 ? true : false; 23 | if (doIt) { 24 | if (retries) { 25 | console.log("Retrying upload of " + path + " to " + container.name + ": Attempts: " + retries); 26 | } 27 | writeStream = container.putBlob(path, headers); 28 | writeStream.on('error', function(err) { 29 | console.log("Error uploading " + path + " to " + container.name); 30 | return uploadData(container, path, headers, data, retryLimit, retries + 1, next); 31 | }); 32 | writeStream.on('end', function(results, meta) { 33 | console.log("Uploaded " + path + " to " + container.name); 34 | return next(); 35 | }); 36 | writeStream.write(data); 37 | return writeStream.end(); 38 | } else { 39 | return next("Upload for " + path + " to " + container.name + " has failed " + retries + " times. Giving up."); 40 | } 41 | }; 42 | 43 | doUpload = function(docpad, container, acl, retryLimit, next) { 44 | var cloudHeaders, tasks; 45 | if (acl != null) { 46 | if (acl === false) { 47 | cloudHeaders = {}; 48 | } else { 49 | cloudHeaders = { 50 | "acl": acl 51 | }; 52 | } 53 | } else { 54 | cloudHeaders = { 55 | "acl": 'public-read' 56 | }; 57 | } 58 | tasks = new TaskGroup().once('complete', function(err) { 59 | return next(err); 60 | }); 61 | docpad.getFiles({ 62 | write: true 63 | }).forEach(function(file) { 64 | var data, err, headers, key, length, path, type, value, _ref; 65 | path = file.attributes.relativeOutPath; 66 | data = file.get('contentRendered') || file.get('content') || (file.getData && file.getData()) || file.getContent() || ""; 67 | if (!data) { 68 | return next("No data for file " + path); 69 | } 70 | length = data.length; 71 | type = mime.lookup(path); 72 | headers = { 73 | "Content-Length": length, 74 | "Content-Type": type 75 | }; 76 | try { 77 | if (file.get('headers') != null) { 78 | _ref = file.get('headers'); 79 | for (key in _ref) { 80 | value = _ref[key]; 81 | headers[key] = value; 82 | } 83 | } 84 | } catch (_error) { 85 | err = _error; 86 | console.log(err); 87 | console.dir(file); 88 | } 89 | return tasks.addTask(function(complete) { 90 | return uploadData(container, path, { 91 | headers: headers, 92 | cloudHeaders: cloudHeaders 93 | }, data, retryLimit, 0, complete); 94 | }); 95 | }); 96 | return tasks.run(); 97 | }; 98 | 99 | handle = function(docpad, sunnyConfig, sunnyContainer, defaultACL, retryLimit, next) { 100 | var connection, containerReq; 101 | if ((sunnyConfig.provider != null) && (sunnyConfig.account != null) && (sunnyConfig.secretKey != null) && (sunnyContainer != null)) { 102 | connection = sunny.Configuration.fromObj(sunnyConfig).connection; 103 | containerReq = connection.getContainer(sunnyContainer, { 104 | validate: true 105 | }); 106 | containerReq.on('error', function(err) { 107 | return console.log("Received error trying to connect to provider: \n " + err); 108 | }); 109 | containerReq.on('end', function(results, meta) { 110 | var container; 111 | if (results) { 112 | container = results.container; 113 | console.log("Got container " + container.name + "."); 114 | return doUpload(docpad, container, defaultACL, retryLimit, next); 115 | } 116 | }); 117 | return containerReq.end(); 118 | } else { 119 | return next("One of the config variables is missing. Printing config:\n" + (util.inspect(sunnyConfig)) + "\nContainer is " + sunnyContainer); 120 | } 121 | }; 122 | 123 | handleEnvPrefix = function(docpad, prefix, next) { 124 | var sunnyACL, sunnyConfig, sunnyContainer, sunnyRetryLimit; 125 | sunnyConfig = { 126 | provider: process.env["" + prefix + "PROVIDER"], 127 | account: process.env["" + prefix + "ACCOUNT"], 128 | secretKey: process.env["" + prefix + "SECRETKEY"], 129 | ssl: process.env["" + prefix + "SSL"], 130 | authUrl: process.env["" + prefix + "AUTHURL"] 131 | }; 132 | sunnyContainer = process.env["" + prefix + "CONTAINER"]; 133 | sunnyACL = process.env["" + prefix + "ACL"]; 134 | sunnyRetryLimit = process.env["" + prefix + "RETRY_LIMIT"]; 135 | sunnyConfig.ssl = (typeof sunnyConfig.ssl === 'string') && (sunnyConfig.ssl.toLowerCase() === 'true'); 136 | return handle(docpad, sunnyConfig, sunnyContainer, sunnyACL, sunnyRetryLimit, next); 137 | }; 138 | 139 | handleEnv = function(docpad, config, next) { 140 | var prefix, _i, _len, _ref, _results; 141 | if (config.envPrefixes.length > 0) { 142 | _ref = config.envPrefixes; 143 | _results = []; 144 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 145 | prefix = _ref[_i]; 146 | _results.push(handleEnvPrefix(docpad, prefix, next)); 147 | } 148 | return _results; 149 | } else { 150 | return handleEnvPrefix(docpad, "DOCPAD_SUNNY_", next); 151 | } 152 | }; 153 | 154 | module.exports = function(BasePlugin) { 155 | var docpadSunnyPlugin, _ref; 156 | return docpadSunnyPlugin = (function(_super) { 157 | __extends(docpadSunnyPlugin, _super); 158 | 159 | function docpadSunnyPlugin() { 160 | this.consoleSetup = __bind(this.consoleSetup, this); 161 | this.deployWithSunny = __bind(this.deployWithSunny, this); 162 | _ref = docpadSunnyPlugin.__super__.constructor.apply(this, arguments); 163 | return _ref; 164 | } 165 | 166 | docpadSunnyPlugin.prototype.name = "sunny"; 167 | 168 | docpadSunnyPlugin.prototype.config = { 169 | defaultACL: 'public-read', 170 | onlyIfProduction: true, 171 | configFromEnv: false, 172 | envPrefixes: [], 173 | cloudConfigs: [] 174 | }; 175 | 176 | docpadSunnyPlugin.prototype.deployWithSunny = function(next) { 177 | var config, docpad, errMsg; 178 | docpad = this.docpad; 179 | config = this.getConfig(); 180 | if (config.cloudConfigs.length > 0 || config.configFromEnv) { 181 | docpad.log('info', "Found " + config.cloudConfigs.length + " configurations in file."); 182 | return this.docpad.generate(function(err) { 183 | var cloudConfig, tasks, _i, _len, _ref1; 184 | if (err) { 185 | return next(err); 186 | } 187 | tasks = new TaskGroup().once('complete', function(err) { 188 | return next(err); 189 | }); 190 | if (config.configFromEnv) { 191 | docpad.log('info', "Grabbing configs from environment."); 192 | tasks.addTask(function(complete) { 193 | return handleEnv(docpad, config, complete); 194 | }); 195 | } 196 | _ref1 = config.cloudConfigs; 197 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) { 198 | cloudConfig = _ref1[_i]; 199 | tasks.addTask(function(complete) { 200 | return handle(docpad, cloudConfig.sunny, cloudConfig.container, cloudConfig.acl, cloudConfig.retryLimit, complete); 201 | }); 202 | } 203 | return tasks.run(); 204 | }); 205 | } else { 206 | errMsg = 'No configs found'; 207 | docpad.log('warn', errMsg); 208 | return next(errMsg); 209 | } 210 | }; 211 | 212 | docpadSunnyPlugin.prototype.consoleSetup = function(opts) { 213 | var commander, config, consoleInterface, docpad; 214 | docpad = this.docpad; 215 | config = this.getConfig(); 216 | consoleInterface = opts.consoleInterface, commander = opts.commander; 217 | commander.command('deploy-sunny').description("Deploys your website to any provider allowed by Sunny.").action(consoleInterface.wrapAction(this.deployWithSunny)); 218 | return this; 219 | }; 220 | 221 | return docpadSunnyPlugin; 222 | 223 | })(BasePlugin); 224 | }; 225 | 226 | }).call(this); 227 | --------------------------------------------------------------------------------