├── vendor
└── .gitkeep
├── tests
├── unit
│ └── .gitkeep
├── dummy
│ ├── public
│ │ ├── .gitkeep
│ │ ├── robots.txt
│ │ └── crossdomain.xml
│ ├── app
│ │ ├── models
│ │ │ └── .gitkeep
│ │ ├── styles
│ │ │ ├── .gitkeep
│ │ │ └── app.css
│ │ ├── templates
│ │ │ └── application.hbs
│ │ ├── pods
│ │ │ └── application
│ │ │ │ └── template.hbs
│ │ ├── router.js
│ │ ├── app.js
│ │ └── index.html
│ ├── .jshintrc
│ └── config
│ │ └── environment.js
├── test-helper.js
├── helpers
│ ├── resolver.js
│ └── start-app.js
├── index.html
└── .jshintrc
├── .watchmanconfig
├── node-tests
├── unit
│ ├── jshint-test.js
│ ├── index-test.js
│ ├── tasks
│ │ ├── deploy-test.js
│ │ ├── read-config-test.js
│ │ └── pipeline-test.js
│ └── models
│ │ └── pipeline-test.js
├── fixtures
│ ├── .env
│ ├── .env.deploy.development
│ ├── config
│ │ ├── deploy-for-addons-config-test-with-alias.js
│ │ ├── deploy-for-addons-config-test-with-aliases.js
│ │ ├── deploy-postbuild.js
│ │ ├── deploy.js
│ │ └── deploy.json
│ └── config-with-defaults
│ │ └── deploy.js
├── helpers
│ ├── expect.js
│ └── fake-progress-bar.js
└── .jshintrc
├── .jshintignore
├── .bowerrc
├── blueprints
├── .jshintrc
└── ember-cli-deploy
│ ├── index.js
│ └── files
│ └── config
│ └── deploy.js
├── config
└── environment.js
├── lib
├── commands
│ ├── index.js
│ ├── activate.js
│ ├── list.js
│ └── deploy.js
├── helpers
│ └── option-value.js
├── tasks
│ ├── deploy.js
│ ├── read-config.js
│ └── pipeline.js
└── models
│ └── pipeline.js
├── .npmignore
├── testem.json
├── .ember-cli
├── CODE_OF_CONDUCT.md
├── .gitignore
├── .travis.yml
├── bower.json
├── ember-cli-build.js
├── .jshintrc
├── .editorconfig
├── LICENSE.md
├── README.md
├── index.js
├── package.json
├── bin
└── changelog
├── UPGRADE_TO_0.5.x.md
├── MIGRATING_FROM_0_0_x_TO_0_4_x.md
├── rfc
└── plugins.md
└── CHANGELOG.md
/vendor/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/public/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/models/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/styles/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["tmp"]
3 | }
4 |
--------------------------------------------------------------------------------
/node-tests/unit/jshint-test.js:
--------------------------------------------------------------------------------
1 | require('mocha-jshint')();
2 |
--------------------------------------------------------------------------------
/tests/dummy/app/styles/app.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | margin: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /tmp
3 | /node_modules
4 | /bower_components
5 |
6 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "analytics": false
4 | }
5 |
--------------------------------------------------------------------------------
/node-tests/fixtures/.env:
--------------------------------------------------------------------------------
1 | OVERRIDDEN='main-dot-env-flavor'
2 | SHARED='shared-key'
3 |
--------------------------------------------------------------------------------
/tests/dummy/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/node-tests/fixtures/.env.deploy.development:
--------------------------------------------------------------------------------
1 | ENVTEST=SUCCESS
2 | OVERRIDDEN='deploy-env-flavor'
3 |
--------------------------------------------------------------------------------
/blueprints/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "console"
4 | ],
5 | "strict": false
6 | }
7 |
--------------------------------------------------------------------------------
/tests/dummy/app/templates/application.hbs:
--------------------------------------------------------------------------------
1 |
Welcome to Ember.js
2 |
3 | {{outlet}}
4 |
--------------------------------------------------------------------------------
/tests/dummy/app/pods/application/template.hbs:
--------------------------------------------------------------------------------
1 | Welcome to Ember.js
2 |
3 | {{outlet}}
4 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(/* environment */) {
4 | return { something: 'test' };
5 | };
6 |
--------------------------------------------------------------------------------
/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import resolver from './helpers/resolver';
2 | import {
3 | setResolver
4 | } from 'ember-qunit';
5 |
6 | setResolver(resolver);
7 |
--------------------------------------------------------------------------------
/lib/commands/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'deploy': require('./deploy'),
3 | 'deploy:activate': require('./activate'),
4 | 'deploy:list': require('./list'),
5 | };
6 |
--------------------------------------------------------------------------------
/node-tests/fixtures/config/deploy-for-addons-config-test-with-alias.js:
--------------------------------------------------------------------------------
1 | module.exports = function(/* environment */) {
2 | return {
3 | plugins: ['foo-plugin:bar-alias']
4 | };
5 | };
6 |
--------------------------------------------------------------------------------
/node-tests/helpers/expect.js:
--------------------------------------------------------------------------------
1 | var chai = require('chai');
2 | var chaiAsPromised = require('chai-as-promised');
3 |
4 | chai.use(chaiAsPromised);
5 |
6 | module.exports = chai.expect;
7 |
--------------------------------------------------------------------------------
/node-tests/fixtures/config/deploy-for-addons-config-test-with-aliases.js:
--------------------------------------------------------------------------------
1 | module.exports = function(/* environment */) {
2 | return {
3 | plugins: ['foo-plugin', 'foo-plugin:bar-alias', 'foo-plugin:doo-alias']
4 | };
5 | };
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bower_components/
2 | tests/
3 | tmp/
4 | dist/
5 |
6 | .bowerrc
7 | .editorconfig
8 | .ember-cli
9 | .travis.yml
10 | .npmignore
11 | **/.gitkeep
12 | bower.json
13 | ember-cli-build.js
14 | Brocfile.js
15 | testem.json
16 |
--------------------------------------------------------------------------------
/testem.json:
--------------------------------------------------------------------------------
1 | {
2 | "framework": "qunit",
3 | "test_page": "tests/index.html?hidepassed",
4 | "disable_watching": true,
5 | "launch_in_ci": [
6 | "PhantomJS"
7 | ],
8 | "launch_in_dev": [
9 | "PhantomJS",
10 | "Chrome"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/tests/dummy/app/router.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import config from './config/environment';
3 |
4 | var Router = Ember.Router.extend({
5 | location: config.locationType
6 | });
7 |
8 | Router.map(function() {
9 | });
10 |
11 | export default Router;
12 |
--------------------------------------------------------------------------------
/node-tests/fixtures/config/deploy-postbuild.js:
--------------------------------------------------------------------------------
1 | module.exports = function(environment) {
2 | var ENV = {};
3 |
4 | if (environment === 'development-postbuild') {
5 | ENV.pipeline = {
6 | activateOnDeploy: true
7 | };
8 | }
9 |
10 | return ENV;
11 | };
12 |
--------------------------------------------------------------------------------
/.ember-cli:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | Ember CLI sends analytics information by default. The data is completely
4 | anonymous, but there are times when you might want to disable this behavior.
5 |
6 | Setting `disableAnalytics` to true will prevent any data from being sent.
7 | */
8 | "disableAnalytics": false
9 | }
10 |
--------------------------------------------------------------------------------
/tests/helpers/resolver.js:
--------------------------------------------------------------------------------
1 | import Resolver from 'ember/resolver';
2 | import config from '../../config/environment';
3 |
4 | var resolver = Resolver.create();
5 |
6 | resolver.namespace = {
7 | modulePrefix: config.modulePrefix,
8 | podModulePrefix: config.podModulePrefix
9 | };
10 |
11 | export default resolver;
12 |
--------------------------------------------------------------------------------
/node-tests/helpers/fake-progress-bar.js:
--------------------------------------------------------------------------------
1 | function FakeProgressBar(template, options) {
2 | this.template = template;
3 | this.total = options.total;
4 | this.ticks = [];
5 | }
6 |
7 | FakeProgressBar.prototype.tick = function(options) {
8 | this.ticks.push(options);
9 | };
10 |
11 | module.exports = FakeProgressBar;
12 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | The Ember team and community are committed to everyone having a safe and inclusive experience.
2 |
3 | **Our Community Guidelines / Code of Conduct can be found here**:
4 |
5 | http://emberjs.com/guidelines/
6 |
7 | For a history of updates, see the page history here:
8 |
9 | https://github.com/emberjs/website/commits/master/source/guidelines.html.erb
10 |
--------------------------------------------------------------------------------
/lib/helpers/option-value.js:
--------------------------------------------------------------------------------
1 | module.exports = function(commandOption, settings, optionKey, defaultValue) {
2 | if (commandOption !== undefined) {
3 | return commandOption;
4 | }
5 | if (settings && settings['ember-cli-deploy'] && settings['ember-cli-deploy'][optionKey] !== undefined) {
6 | return settings['ember-cli-deploy'][optionKey];
7 | }
8 | return defaultValue;
9 | };
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 |
7 | # dependencies
8 | /node_modules
9 | /bower_components
10 |
11 | # misc
12 | /.sass-cache
13 | /connect.lock
14 | /coverage/*
15 | /libpeerconnection.log
16 | npm-debug.log
17 | testem.log
18 | deploy_test.json
19 | .DS_Store
20 | NERD_tree_2
21 | *.tgz
22 | /tmp-delete-excluded-test
23 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: node_js
3 | node_js:
4 | - "0.12"
5 |
6 | sudo: false
7 |
8 | cache:
9 | directories:
10 | - node_modules
11 |
12 | before_install:
13 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH
14 | - "npm config set spin false"
15 | - "npm install -g npm@^2"
16 |
17 | install:
18 | - npm install -g bower
19 | - npm install
20 | - bower install
21 |
22 | script:
23 | - npm test
24 |
--------------------------------------------------------------------------------
/tests/dummy/app/app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Resolver from 'ember/resolver';
3 | import loadInitializers from 'ember/load-initializers';
4 | import config from './config/environment';
5 |
6 | var App;
7 |
8 | Ember.MODEL_FACTORY_INJECTIONS = true;
9 |
10 | App = Ember.Application.extend({
11 | modulePrefix: config.modulePrefix,
12 | podModulePrefix: config.podModulePrefix,
13 | Resolver: Resolver
14 | });
15 |
16 | loadInitializers(App, config.modulePrefix);
17 |
18 | export default App;
19 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-cli-deploy",
3 | "dependencies": {
4 | "ember": "1.13.8",
5 | "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
6 | "ember-cli-test-loader": "ember-cli-test-loader#0.1.3",
7 | "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5",
8 | "ember-qunit": "0.4.9",
9 | "ember-qunit-notifications": "0.0.7",
10 | "ember-resolver": "~0.1.18",
11 | "jquery": "^1.11.3",
12 | "loader.js": "ember-cli/loader.js#3.2.1",
13 | "qunit": "~1.18.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ember-cli-build.js:
--------------------------------------------------------------------------------
1 | /* global require, module */
2 | var EmberApp = require('ember-cli/lib/broccoli/ember-addon');
3 |
4 | module.exports = function(defaults) {
5 | var app = new EmberApp(defaults, {
6 | // Add options here
7 | });
8 |
9 | /*
10 | This build file specifes the options for the dummy test app of this
11 | addon, located in `/tests/dummy`
12 | This build file does *not* influence how the addon or the app using it
13 | behave. You most likely want to be modifying `./index.js` or app's build file
14 | */
15 |
16 | return app.toTree();
17 | };
18 |
--------------------------------------------------------------------------------
/node-tests/fixtures/config/deploy.js:
--------------------------------------------------------------------------------
1 | module.exports = function(environment) {
2 | var ENV = {};
3 |
4 | if (environment === 'development') {
5 | ENV.build = {
6 | environment: 'development'
7 | };
8 |
9 | ENV.s3 = {
10 | bucket: 'shineonyoucrazy',
11 | region: 'us-east-1'
12 | };
13 | }
14 |
15 | if (environment === 'staging') {
16 | ENV.build = {
17 | environment: 'production'
18 | };
19 |
20 | ENV.s3 = {
21 | bucket: 'keepitreal',
22 | region: 'us-east-1'
23 | };
24 | }
25 |
26 | return ENV;
27 | };
28 |
--------------------------------------------------------------------------------
/tests/helpers/start-app.js:
--------------------------------------------------------------------------------
1 | import Ember from 'ember';
2 | import Application from '../../app';
3 | import config from '../../config/environment';
4 |
5 | export default function startApp(attrs) {
6 | var application;
7 |
8 | var attributes = Ember.merge({}, config.APP);
9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
10 |
11 | Ember.run(function() {
12 | application = Application.create(attributes);
13 | application.setupForTesting();
14 | application.injectTestHelpers();
15 | });
16 |
17 | return application;
18 | }
19 |
--------------------------------------------------------------------------------
/blueprints/ember-cli-deploy/index.js:
--------------------------------------------------------------------------------
1 | var chalk = require('chalk');
2 | var green = chalk.green;
3 |
4 | module.exports = {
5 | description: 'Generate config for ember-cli deployments',
6 | normalizeEntityName: function() {
7 | // this prevents an error when the entityName is
8 | // not specified (since that doesn't actually matter
9 | // to us
10 | },
11 | afterInstall: function(options) {
12 | this.ui.write(green('ember-cli-deploy needs plugins to actually do the deployment work.\nSee http://ember-cli.github.io/ember-cli-deploy/docs/v0.5.x/quick-start/\nto learn how to install plugins and see what plugins are available.\n'));
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/tests/dummy/public/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/dummy/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "-Promise"
6 | ],
7 | "browser" : true,
8 | "boss" : true,
9 | "curly": true,
10 | "debug": false,
11 | "devel": true,
12 | "eqeqeq": true,
13 | "evil": true,
14 | "forin": false,
15 | "immed": false,
16 | "laxbreak": false,
17 | "newcap": true,
18 | "noarg": true,
19 | "noempty": false,
20 | "nonew": false,
21 | "nomen": false,
22 | "onevar": false,
23 | "plusplus": false,
24 | "regexp": false,
25 | "undef": true,
26 | "sub": true,
27 | "strict": false,
28 | "white": false,
29 | "eqnull": true,
30 | "esnext": true,
31 | "unused": true
32 | }
33 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "-Promise"
6 | ],
7 | "browser": true,
8 | "boss": true,
9 | "curly": true,
10 | "debug": false,
11 | "devel": true,
12 | "eqeqeq": true,
13 | "evil": true,
14 | "forin": false,
15 | "immed": false,
16 | "laxbreak": false,
17 | "newcap": true,
18 | "noarg": true,
19 | "noempty": false,
20 | "nonew": false,
21 | "nomen": false,
22 | "onevar": false,
23 | "plusplus": false,
24 | "regexp": false,
25 | "undef": true,
26 | "sub": true,
27 | "strict": false,
28 | "white": false,
29 | "eqnull": true,
30 | "esnext": true,
31 | "unused": true,
32 | "node": true
33 | }
34 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [*.js]
17 | indent_style = space
18 | indent_size = 2
19 |
20 | [*.hbs]
21 | insert_final_newline = false
22 | indent_style = space
23 | indent_size = 2
24 |
25 | [*.css]
26 | indent_style = space
27 | indent_size = 2
28 |
29 | [*.html]
30 | indent_style = space
31 | indent_size = 2
32 |
33 | [*.{diff,md}]
34 | trim_trailing_whitespace = false
35 |
--------------------------------------------------------------------------------
/node-tests/fixtures/config/deploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "development": {
3 | "store": {
4 | "host": "localhost",
5 | "port": 6379
6 | },
7 | "assets": {
8 | "accessKeyId": "",
9 | "secretAccessKey": "",
10 | "bucket": ""
11 | }
12 | },
13 |
14 | "staging": {
15 | "store": {
16 | "host": "staging-redis.firstiwaslike.com",
17 | "port": 6379
18 | },
19 | "assets": {
20 | "accessKeyId": "",
21 | "secretAccessKey": "",
22 | "bucket": "",
23 | "prefix": ""
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/dummy/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Ember CLI Deploy
7 |
8 |
9 |
10 | {{content-for 'head'}}
11 |
12 |
13 |
14 |
15 | {{content-for 'head-footer'}}
16 |
17 |
18 | {{content-for 'body'}}
19 |
20 |
21 |
22 |
23 | {{content-for 'body-footer'}}
24 |
25 |
26 |
--------------------------------------------------------------------------------
/node-tests/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "-Promise",
6 | "describe",
7 | "beforeEach",
8 | "afterEach",
9 | "it"
10 | ],
11 | "browser": true,
12 | "boss": true,
13 | "curly": true,
14 | "debug": false,
15 | "devel": true,
16 | "eqeqeq": true,
17 | "evil": true,
18 | "forin": false,
19 | "immed": false,
20 | "laxbreak": false,
21 | "newcap": true,
22 | "noarg": true,
23 | "noempty": false,
24 | "nonew": false,
25 | "nomen": false,
26 | "onevar": false,
27 | "plusplus": false,
28 | "regexp": false,
29 | "undef": true,
30 | "sub": true,
31 | "strict": false,
32 | "white": false,
33 | "eqnull": true,
34 | "esnext": true,
35 | "unused": true,
36 | "node": true,
37 | "expr": true
38 | }
39 |
--------------------------------------------------------------------------------
/blueprints/ember-cli-deploy/files/config/deploy.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 |
3 | module.exports = function(deployTarget) {
4 | var ENV = {
5 | build: {}
6 | // include other plugin configuration that applies to all deploy targets here
7 | };
8 |
9 | if (deployTarget === 'development') {
10 | ENV.build.environment = 'development';
11 | // configure other plugins for development deploy target here
12 | }
13 |
14 | if (deployTarget === 'staging') {
15 | ENV.build.environment = 'production';
16 | // configure other plugins for staging deploy target here
17 | }
18 |
19 | if (deployTarget === 'production') {
20 | ENV.build.environment = 'production';
21 | // configure other plugins for production deploy target here
22 | }
23 |
24 | // Note: if you need to build some configuration asynchronously, you can return
25 | // a promise that resolves with the ENV object instead of returning the
26 | // ENV object synchronously.
27 | return ENV;
28 | };
29 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015
4 |
5 | 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:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | 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.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ember CLI Deploy
2 | [](https://travis-ci.org/ember-cli/ember-cli-deploy) [](https://codeclimate.com/github/ember-cli/ember-cli-deploy)
3 |
4 | Simple, flexible deployment for your Ember CLI app
5 |
6 | ## Installation
7 |
8 | ```
9 | ember install ember-cli-deploy
10 | ```
11 | ## Quick start
12 |
13 | After installation, choose [plugins](http://ember-cli.github.io/ember-cli-deploy/docs/v0.5.x/plugins/) matching your deployment environment, [configure](http://ember-cli.github.io/ember-cli-deploy/docs/v0.5.x/configuration-overview/) your deployment script appropriately and you're ready to [start deploying](http://ember-cli.github.io/ember-cli-deploy/docs/v0.5.x/usage-overview/).
14 |
15 | ## In-depth documentation
16 |
17 | [Visit the Docs site](http://ember-cli.github.io/ember-cli-deploy/)
18 |
19 | ## Contributing
20 |
21 | Clone the repo and run `npm install`. To run tests,
22 |
23 | npm test
24 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy Tests
7 |
8 |
9 |
10 | {{content-for 'head'}}
11 | {{content-for 'test-head'}}
12 |
13 |
14 |
15 |
16 |
17 | {{content-for 'head-footer'}}
18 | {{content-for 'test-head-footer'}}
19 |
20 |
21 |
22 | {{content-for 'body'}}
23 | {{content-for 'test-body'}}
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{content-for 'body-footer'}}
31 | {{content-for 'test-body-footer'}}
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tests/dummy/config/environment.js:
--------------------------------------------------------------------------------
1 | /* jshint node: true */
2 |
3 | module.exports = function(environment) {
4 | var ENV = {
5 | modulePrefix: 'dummy',
6 | environment: environment,
7 | baseURL: '/',
8 | locationType: 'auto',
9 | EmberENV: {
10 | FEATURES: {
11 | // Here you can enable experimental features on an ember canary build
12 | // e.g. 'with-controller': true
13 | }
14 | },
15 |
16 | APP: {
17 | // Here you can pass flags/options to your application instance
18 | // when it is created
19 | }
20 | };
21 |
22 | if (environment === 'development') {
23 | // ENV.APP.LOG_RESOLVER = true;
24 | // ENV.APP.LOG_ACTIVE_GENERATION = true;
25 | // ENV.APP.LOG_TRANSITIONS = true;
26 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
27 | // ENV.APP.LOG_VIEW_LOOKUPS = true;
28 | }
29 |
30 | if (environment === 'test') {
31 | // Testem prefers this...
32 | ENV.baseURL = '/';
33 | ENV.locationType = 'none';
34 |
35 | // keep test console output quieter
36 | ENV.APP.LOG_ACTIVE_GENERATION = false;
37 | ENV.APP.LOG_VIEW_LOOKUPS = false;
38 |
39 | ENV.APP.rootElement = '#ember-testing';
40 | }
41 |
42 | if (environment === 'production') {
43 |
44 | }
45 |
46 | return ENV;
47 | };
48 |
--------------------------------------------------------------------------------
/node-tests/unit/index-test.js:
--------------------------------------------------------------------------------
1 | var assert = require('ember-cli/tests/helpers/assert');
2 |
3 | describe ('ember-cli-deploy', function() {
4 | var subject;
5 | beforeEach(function() {
6 | subject = require('../../index.js');
7 | });
8 | describe('postBuild', function() {
9 | it('requires an app', function() {
10 | assert(!subject.postBuild(), 'returns false');
11 | });
12 |
13 | it('requires a deployTarget', function() {
14 | var context = {
15 | app: {
16 | options: {
17 | emberCLIDeploy: {
18 | runOnPostBuild: null
19 | }
20 | }
21 | }
22 | };
23 |
24 | assert(!subject.postBuild.apply(context));
25 | });
26 |
27 | it('reads the config', function() {
28 | var context = {
29 | project: {
30 | name: function() {return 'test-project';},
31 | root: process.cwd(),
32 | addons: []
33 | },
34 | app: {
35 | options: {
36 | emberCLIDeploy: {
37 | runOnPostBuild: 'production',
38 | configFile: 'node-tests/fixtures/config/deploy.js'
39 | }
40 | }
41 | }
42 | };
43 |
44 | assert(subject.postBuild.apply(context));
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/node-tests/fixtures/config-with-defaults/deploy.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "development": {
3 | "build": {
4 | "environment": "development"
5 | },
6 | "buildPath": "tmp/deploy-dist/",
7 | "indexFiles": null,
8 | "manifestPrefix": "foo",
9 | "tagging": "sha",
10 | "store": {
11 | "host": "localhost",
12 | "manifestSize": 10,
13 | "port": 6379,
14 | "type": "redis"
15 | },
16 | "assets": {
17 | "accessKeyId": "",
18 | "secretAccessKey": "",
19 | "bucket": "",
20 | "exclude": [],
21 | "gzip": true,
22 | "gzipExtensions": ["js", "css", "svg"],
23 | "type": "s3"
24 | }
25 | },
26 |
27 | "staging": {
28 | "build": {
29 | "environment": "production"
30 | },
31 | "buildPath": "tmp/deploy-dist/",
32 | "indexFiles": null,
33 | "manifestPrefix": "foo",
34 | "tagging": "sha",
35 | "store": {
36 | "host": "staging-redis.firstiwaslike.com",
37 | "manifestSize": 10,
38 | "port": 6379,
39 | "type": "redis"
40 | },
41 | "assets": {
42 | "accessKeyId": "",
43 | "secretAccessKey": "",
44 | "bucket": "",
45 | "exclude": [],
46 | "gzip": true,
47 | "prefix": "",
48 | "gzipExtensions": ["js", "css", "svg"],
49 | "type": "s3"
50 | }
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/tests/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "predef": [
3 | "document",
4 | "window",
5 | "location",
6 | "setTimeout",
7 | "$",
8 | "-Promise",
9 | "QUnit",
10 | "define",
11 | "console",
12 | "equal",
13 | "notEqual",
14 | "notStrictEqual",
15 | "test",
16 | "asyncTest",
17 | "testBoth",
18 | "testWithDefault",
19 | "raises",
20 | "throws",
21 | "deepEqual",
22 | "start",
23 | "stop",
24 | "ok",
25 | "strictEqual",
26 | "module",
27 | "moduleFor",
28 | "moduleForComponent",
29 | "moduleForModel",
30 | "process",
31 | "expect",
32 | "visit",
33 | "exists",
34 | "fillIn",
35 | "click",
36 | "keyEvent",
37 | "triggerEvent",
38 | "find",
39 | "findWithAssert",
40 | "wait",
41 | "DS",
42 | "isolatedContainer",
43 | "startApp",
44 | "andThen",
45 | "currentURL",
46 | "currentPath",
47 | "currentRouteName"
48 | ],
49 | "node": false,
50 | "browser": false,
51 | "boss": true,
52 | "curly": false,
53 | "debug": false,
54 | "devel": false,
55 | "eqeqeq": true,
56 | "evil": true,
57 | "forin": false,
58 | "immed": false,
59 | "laxbreak": false,
60 | "newcap": true,
61 | "noarg": true,
62 | "noempty": false,
63 | "nonew": false,
64 | "nomen": false,
65 | "onevar": false,
66 | "plusplus": false,
67 | "regexp": false,
68 | "undef": true,
69 | "sub": true,
70 | "strict": false,
71 | "white": false,
72 | "eqnull": true,
73 | "esnext": true
74 | }
75 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var commands = require('./lib/commands');
3 |
4 | module.exports = {
5 | name: 'ember-cli-deploy',
6 |
7 | includedCommands: function() {
8 | return commands;
9 | },
10 |
11 | blueprintsPath: function() {
12 | return path.join(__dirname, 'blueprints');
13 | },
14 |
15 | postBuild: function(result) {
16 | var _this = this;
17 | if (!this.app) {
18 | // You will need ember-cli >= 1.13 to use ember-cli-deploy's postBuild integration.
19 | // This is because prior to 1.13, `this.app` is not available in the postBuild hook.
20 | return;
21 | }
22 | var options = this.app.options.emberCLIDeploy || {};
23 |
24 | var deployTarget = options.runOnPostBuild;
25 | if (deployTarget) {
26 | var ReadConfigTask = require('./lib/tasks/read-config');
27 | var readConfig = new ReadConfigTask({
28 | project: this.project,
29 | deployTarget: deployTarget,
30 | deployConfigPath: options.configFile
31 | });
32 | return readConfig.run().then(function(config){
33 | var DeployTask = require('./lib/tasks/deploy');
34 | var deploy = new DeployTask({
35 | project: _this.project,
36 | ui: _this.ui,
37 | deployTarget: deployTarget,
38 | config: config,
39 | shouldActivate: options.shouldActivate,
40 | commandOptions: {
41 | buildDir: result.directory
42 | }
43 | });
44 | return deploy.run();
45 | });
46 | }
47 | }
48 | };
49 |
--------------------------------------------------------------------------------
/lib/tasks/deploy.js:
--------------------------------------------------------------------------------
1 | var Task = require('ember-cli/lib/models/task');
2 | var PipelineTask = require('../tasks/pipeline');
3 |
4 | module.exports = Task.extend({
5 | init: function() {
6 | this.commandOptions = this.commandOptions || {};
7 | this.shouldActivate = this.shouldActivate || this._shouldActivate(this.commandOptions);
8 | },
9 |
10 | run: function() {
11 | var pipeline = this._pipeline || new PipelineTask({
12 | project: this.project,
13 | ui: this.ui,
14 | deployTarget: this.deployTarget,
15 | config: this.config,
16 | commandOptions: this.commandOptions,
17 | hooks: this._hooks(this.shouldActivate)
18 | });
19 | return pipeline.run();
20 | },
21 |
22 | _shouldActivate: function(options) {
23 | var pipelineConfig = this.config['pipeline'] || {};
24 | var shouldReferToPipelineConfig = (options.activate === undefined);
25 | return shouldReferToPipelineConfig ? pipelineConfig.activateOnDeploy : options.activate;
26 | },
27 |
28 | _hooks: function(shouldActivate) {
29 | var hooks = ['configure',
30 | 'setup',
31 | 'willDeploy',
32 | 'willBuild', 'build', 'didBuild',
33 | 'willPrepare', 'prepare', 'didPrepare',
34 | 'fetchInitialRevisions',
35 | 'willUpload', 'upload', 'didUpload'];
36 |
37 | if (shouldActivate) {
38 | hooks.push('willActivate', 'activate', 'fetchRevisions', 'didActivate');
39 | } else {
40 | hooks.push('fetchRevisions');
41 | }
42 |
43 | hooks.push('didDeploy', 'teardown');
44 |
45 | return hooks;
46 | }
47 | });
48 |
--------------------------------------------------------------------------------
/lib/commands/activate.js:
--------------------------------------------------------------------------------
1 | var chooseOptionValue = require('../helpers/option-value');
2 |
3 | module.exports = {
4 | name: 'deploy:activate',
5 | description: 'Activates a passed deploy-revision',
6 | works: 'insideProject',
7 |
8 | availableOptions: [
9 | { name: 'revision', type: String, required: true },
10 | { name: 'verbose', type: Boolean },
11 | { name: 'deploy-config-file', type: String, description: '(Default: config/deploy.js)' }
12 | ],
13 |
14 | anonymousOptions: [
15 | ''
16 | ],
17 |
18 | run: function(commandOptions, rawArgs) {
19 | commandOptions.deployTarget = rawArgs.shift();
20 |
21 | this.ui.verbose = chooseOptionValue(commandOptions.verbose, this.settings, 'verbose');
22 | commandOptions.deployConfigFile = chooseOptionValue(commandOptions.deployConfigFile, this.settings, 'deploy-config-file', 'config/deploy.js');
23 |
24 | process.env.DEPLOY_TARGET = commandOptions.deployTarget;
25 |
26 | var ReadConfigTask = require('../tasks/read-config');
27 | var readConfig = new ReadConfigTask({
28 | project: this.project,
29 | deployTarget: commandOptions.deployTarget,
30 | deployConfigFile: commandOptions.deployConfigFile
31 | });
32 | var self = this;
33 | return readConfig.run().then(function(config){
34 | var PipelineTask = require('../tasks/pipeline');
35 | var pipeline = new PipelineTask({
36 | project: self.project,
37 | ui: self.ui,
38 | config: config,
39 | deployTarget: commandOptions.deployTarget,
40 | commandOptions: commandOptions,
41 | hooks: [
42 | 'configure',
43 | 'setup',
44 | 'fetchInitialRevisions',
45 | 'willActivate',
46 | 'activate',
47 | 'fetchRevisions',
48 | 'didActivate',
49 | 'teardown'
50 | ]
51 | });
52 |
53 | return pipeline.run();
54 | });
55 | }
56 | };
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-cli-deploy",
3 | "description": "A deployment pipeline for ember-cli apps",
4 | "version": "0.6.0",
5 | "directories": {
6 | "doc": "doc",
7 | "test": "tests"
8 | },
9 | "scripts": {
10 | "start": "ember server",
11 | "build": "ember build",
12 | "test": "node node_modules/mocha/bin/mocha 'node-tests/**/*-test.js'",
13 | "autotest": "node node_modules/mocha/bin/mocha --watch --reporter spec 'node-tests/**/*-test.js'"
14 | },
15 | "repository": "https://github.com/ember-cli/ember-cli-deploy",
16 | "engines": {
17 | "node": ">= 0.10.0"
18 | },
19 | "author": "",
20 | "license": "MIT",
21 | "devDependencies": {
22 | "chai": "^1.9.2",
23 | "chai-as-promised": "^4.1.1",
24 | "broccoli-asset-rev": "^2.1.2",
25 | "ember-cli": "1.13.8",
26 | "ember-cli-app-version": "0.5.0",
27 | "ember-cli-content-security-policy": "0.4.0",
28 | "ember-cli-dependency-checker": "^1.0.1",
29 | "ember-cli-htmlbars": "0.7.9",
30 | "ember-cli-htmlbars-inline-precompile": "^0.2.0",
31 | "ember-cli-ic-ajax": "0.2.1",
32 | "ember-cli-inject-live-reload": "^1.3.1",
33 | "ember-cli-qunit": "^1.0.0",
34 | "github": "0.2.3",
35 | "mocha": "^2.0.1",
36 | "mocha-jshint": "^2.2.6",
37 | "sinon": "^1.12.1"
38 | },
39 | "keywords": [
40 | "ember-addon",
41 | "ember-cli-deploy"
42 | ],
43 | "ember-addon": {
44 | "configPath": "tests/dummy/config"
45 | },
46 | "dependencies": {
47 | "broccoli": "^0.13.2",
48 | "broccoli-gzip": "^0.2.0",
49 | "chalk": "^0.5.1",
50 | "core-object": "0.0.2",
51 | "ember-cli-babel": "^5.1.3",
52 | "dotenv": "^1.1.0",
53 | "glob": "4.4.2",
54 | "lodash-node": "^2.4.1",
55 | "mkdirp": "^0.5.0",
56 | "ncp": "^2.0.0",
57 | "rimraf": "^2.2.8",
58 | "silent-error": "^1.0.0",
59 | "ember-cli-deploy-progress": "^1.3.0",
60 | "sync-exec": "^0.5.0"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/commands/list.js:
--------------------------------------------------------------------------------
1 | var chooseOptionValue = require('../helpers/option-value');
2 |
3 | module.exports = {
4 | name: 'deploy:list',
5 | description: 'Lists the currently uploaded deploy-revisions',
6 | works: 'insideProject',
7 |
8 | anonymousOptions: [
9 | ''
10 | ],
11 |
12 | availableOptions: [
13 | { name: 'deploy-config-file', type: String, description: '(Default: config/deploy.js)' },
14 | { name: 'verbose', type: Boolean },
15 | { name: 'amount', type: Number, description: '(Default: 10)' }
16 | ],
17 |
18 | run: function(commandOptions, rawArgs) {
19 | commandOptions.deployTarget = rawArgs.shift();
20 |
21 | commandOptions.deployConfigFile = chooseOptionValue(commandOptions.deployConfigFile, this.settings, 'deploy-config-file', 'config/deploy.js');
22 | this.ui.verbose = chooseOptionValue(commandOptions.verbose, this.settings, 'verbose');
23 | commandOptions.amount = chooseOptionValue(commandOptions.amount, this.settings, 'amount', 10);
24 |
25 | process.env.DEPLOY_TARGET = commandOptions.deployTarget;
26 |
27 | var ReadConfigTask = require('../tasks/read-config');
28 | var readConfig = new ReadConfigTask({
29 | project: this.project,
30 | deployTarget: commandOptions.deployTarget,
31 | deployConfigFile: commandOptions.deployConfigFile
32 | });
33 | var self = this;
34 | return readConfig.run().then(function(config){
35 | var PipelineTask = require('../tasks/pipeline');
36 | var pipeline = new PipelineTask({
37 | project: self.project,
38 | ui: self.ui,
39 | config: config,
40 | deployTarget: commandOptions.deployTarget,
41 | commandOptions: commandOptions,
42 | hooks: [
43 | 'configure',
44 | 'setup',
45 | 'fetchRevisions',
46 | 'displayRevisions',
47 | 'teardown'
48 | ]
49 | });
50 |
51 | return pipeline.run();
52 | });
53 | }
54 | };
55 |
--------------------------------------------------------------------------------
/lib/tasks/read-config.js:
--------------------------------------------------------------------------------
1 | var Task = require('ember-cli/lib/models/task');
2 | var Promise = require('ember-cli/lib/ext/promise');
3 | var SilentError = require('silent-error');
4 |
5 | var existsSync = require('fs').existsSync;
6 | var path = require('path');
7 | var dotenv = require('dotenv');
8 |
9 | module.exports = Task.extend({
10 | init: function() {
11 | if (!this.project) {
12 | throw new SilentError('No project passed to read-config task');
13 | }
14 |
15 | if(!this.deployTarget) {
16 | throw new SilentError('No deployTarget passed to read-config task');
17 | }
18 |
19 | this.root = this.project.root;
20 |
21 | this.deployConfigPath = this.deployConfigPath || 'config/deploy.js';
22 |
23 | if (!existsSync(path.join(this.root, this.deployConfigPath))) {
24 | throw new SilentError('Deploy config does not exist at `' + this.deployConfigPath + '`');
25 | }
26 | },
27 |
28 | run: function() {
29 | this._loadDotEnv();
30 | return this._readDeployConfig();
31 | },
32 |
33 | _loadDotEnv: function() {
34 | var root = this.root;
35 |
36 | var deployDotEnvFilename = '.env.deploy.' + this.deployTarget;
37 | var deployDotEnvFilePath = path.join(root, deployDotEnvFilename);
38 |
39 | var dotEnvFilename = '.env';
40 | var dotEnvFilePath = path.join(root, dotEnvFilename);
41 |
42 | // order is important here. vars defined in files loaded first
43 | // will override files loaded after.
44 | var paths = [deployDotEnvFilePath, dotEnvFilePath];
45 | paths.forEach(function(path) {
46 | if (existsSync(path)) {
47 | dotenv.load({
48 | path: path
49 | });
50 | }
51 | });
52 | },
53 |
54 | _readDeployConfig: function() {
55 | var root = this.root;
56 | var deployConfigFn = require(path.resolve(root, this.deployConfigPath));
57 | return Promise.resolve(deployConfigFn(this.deployTarget));
58 | }
59 | });
60 |
--------------------------------------------------------------------------------
/lib/commands/deploy.js:
--------------------------------------------------------------------------------
1 | var chooseOptionValue = require('../helpers/option-value');
2 |
3 | module.exports = {
4 | name: 'deploy',
5 | description: 'Deploys an ember-cli app',
6 | works: 'insideProject',
7 |
8 | anonymousOptions: [
9 | ''
10 | ],
11 |
12 | // note: we can not use `default` from ember-cli because we need to use
13 | // settings from .ember-cli config-file as secondary defaults
14 | availableOptions: [
15 | { name: 'deploy-config-file', type: String, description: '(Default: config/deploy.js)' },
16 | { name: 'verbose', type: Boolean, description: '(Default: false)' },
17 | { name: 'activate', type: Boolean, description: '(Default: false)' },
18 | { name: 'show-progress', type: Boolean, aliases: ['p', 'progress'], description: '(Default: true)'}
19 | ],
20 |
21 | run: function(commandOptions, rawArgs) {
22 | commandOptions.deployTarget = rawArgs.shift();
23 |
24 | commandOptions.deployConfigFile = chooseOptionValue(commandOptions.deployConfigFile, this.settings, 'deploy-config-file', 'config/deploy.js');
25 | commandOptions.activate = chooseOptionValue(commandOptions.activate, this.settings, 'activate');
26 |
27 | this.ui.verbose = chooseOptionValue(commandOptions.verbose, this.settings, 'verbose');
28 | this.ui.showProgress = chooseOptionValue(commandOptions.showProgress, this.settings, 'showProgress', process.stdout.isTTY ? true : false);
29 |
30 | process.env.DEPLOY_TARGET = commandOptions.deployTarget;
31 |
32 | var ReadConfigTask = require('../tasks/read-config');
33 | var readConfig = new ReadConfigTask({
34 | project: this.project,
35 | deployTarget: commandOptions.deployTarget,
36 | deployConfigFile: commandOptions.deployConfigFile
37 | });
38 | var self = this;
39 | return readConfig.run().then(function(config){
40 | var DeployTask = require('../tasks/deploy');
41 | var deploy = new DeployTask({
42 | project: self.project,
43 | ui: self.ui,
44 | config: config,
45 | deployTarget: commandOptions.deployTarget,
46 | commandOptions: commandOptions
47 | });
48 |
49 | return deploy.run();
50 | });
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/bin/changelog:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict';
3 |
4 | /*
5 | * This script generates the template a changelog by comparing a current version
6 | * with master. Run this, copy what's logged into the `CHANGELOG.md` and update
7 | * the top section based on the changes listed in "Community Contributions"
8 | *
9 | * Usage:
10 | *
11 | * bin/changelog
12 | */
13 |
14 | var EOL = require('os').EOL;
15 | var Promise = require('ember-cli/lib/ext/promise');
16 | var GitHubApi = require('github');
17 |
18 | var github = new GitHubApi({version: '3.0.0'});
19 | var compareCommits = Promise.denodeify(github.repos.compareCommits);
20 | var currentVersion = 'v' + require('../package').version;
21 |
22 | compareCommits({
23 | user: 'ember-cli',
24 | repo: 'ember-cli-deploy',
25 | base: currentVersion,
26 | head: 'master'
27 | }).then(function(res) {
28 | return res.commits.map(function(commitInfo) {
29 | return commitInfo.commit.message
30 |
31 | }).filter(function(message) {
32 | return message.indexOf('Merge pull request #') > -1;
33 |
34 | }).map(function(message) {
35 | var numAndAuthor = message.match(/#(\d+) from (.*)\//).slice(1,3);
36 | var title = message.split('\n\n')[1];
37 |
38 | return {
39 | number: +numAndAuthor[0],
40 | author: numAndAuthor[1],
41 | title: title
42 | };
43 |
44 | }).sort(function(a, b) {
45 | return a.number > b.number;
46 | }).map(function(pr) {
47 | var link = '[#' + pr.number + ']' +
48 | '(https://github.com/ember-cli/ember-cli-deploy/pull/' + pr.number + ')';
49 | var title = pr.title;
50 | var author = '[@' + pr.author + ']' +
51 | '(https://github.com/' + pr.author +')';
52 |
53 | return '- ' + link + ' ' + title + ' ' + author;
54 |
55 | }).join('\n');
56 |
57 | }).then(function(contributions) {
58 | var changelog = generateChangelog(contributions);
59 |
60 | console.log(changelog);
61 | }).catch(function(err) {
62 | console.error(err);
63 | })
64 |
65 | function generateChangelog(contributions) {
66 | var header = '#### Community Contributions';
67 | var footer = 'Thank you to all who took the time to contribute!';
68 |
69 | return header + EOL + EOL + contributions + EOL + EOL + footer;
70 | }
71 |
--------------------------------------------------------------------------------
/UPGRADE_TO_0.5.x.md:
--------------------------------------------------------------------------------
1 | ## Overview
2 |
3 | The pipeline approach in 0.5.x requires some modifications to ember-cli-deploy plugins and a few to deploy configuration scripts.
4 |
5 | ## Plugin users
6 |
7 | - Add the following dependencies: `ember-cli-deploy-build` and `ember-cli-deploy-revision-data`
8 | - Change your config/deploy.js to return a function instead of an object, for each `environment` setting:
9 | - `ENV["%INDEX_ADAPTER_NAME%"] = { /* plugin config goes here */ }`
10 | - `ENV["%ASSETS_ADAPTER_NAME%"] = { /* plugin config goes here */ }`
11 |
12 | ## Plugin authors
13 | Depending on what type of adapters you're publishing, you'll have to make different amendments.
14 |
15 | ### For all adapters
16 |
17 | 1. update package.json keywords to include the keyword "ember-cli-deploy-plugin"
18 | 2. `npm install ember-cli-deploy-plugin --save` and subclass the new https://github.com/lukemelia/ember-cli-deploy-plugin for all your adapters.
19 | 3. implement `createDeployPlugin` in index.js and return an extended `ember-cli-deploy-plugin` from there.
20 | 4. listen to hooks to actually upload stuff, most notably upload (index and assets adapter) and activate (index adapter)
21 | 5. instruct your users to update their config (see above)
22 | 6. instruct your users to install ember-cli-deploy-build (see above)
23 | 7. replace console.log statements to this.log to play nicely with the formatting in ember-cli-deploy
24 |
25 | ### For index/store adapters:
26 | 1. instruct your users to install ember-cli-deploy-revision-data (see above)
27 | 2. mind you that the revision key is now under context.revisionData.revisionKey (provided ember-cli-deploy-revision-data is installed) and it doesn't include your project name yet.
28 | ```javascript
29 | _key: function(context) {
30 | var revisionKey = context.commandOptions.revision || context.revisionData.revisionKey.substr(0, 8);
31 | return context.project.name() + ':' + revisionKey;
32 | }
33 | ```
34 | 3. mind you that you won't be passed the contents of index.html to your upload function, instead you will have to read the file using the filesystem package,
35 | ```javascript
36 | var path = require('path');
37 | var fs = require('fs');
38 | var readFile = denodeify(fs.readFile);
39 |
40 | upload: function() {
41 | readFile(path.join(context.distDir, "index.html"))
42 | .then(function(buffer) {
43 | return buffer.toString();
44 | }).then(function(indexContents) {
45 | // do uploady stuff with contents here
46 | });
47 | }
48 | ```
49 |
50 | ### For asset adapters:
51 | 1. if you have a hard reference to "tmp/asset-sync", replace that with context.distDir.
52 |
--------------------------------------------------------------------------------
/node-tests/unit/tasks/deploy-test.js:
--------------------------------------------------------------------------------
1 | var Promise = require('ember-cli/lib/ext/promise');
2 | var DeployTask = require('../../../lib/tasks/deploy');
3 | var expect = require('../../helpers/expect');
4 |
5 | describe('DeployTask', function() {
6 | var mockProject = {addons: []};
7 | var mockPostBuildConfig = {
8 | pipeline: {
9 | activateOnDeploy: true
10 | }
11 | };
12 | var mockDeployConfig = {
13 | build: {
14 | buildEnv: 'development'
15 | }
16 | };
17 | var mockUi = { write: function() {}, writeError: function() {} };
18 |
19 | describe('creating and setting up a new instance', function() {
20 |
21 | describe('detects that shouldActivate', function() {
22 | it('is passed as a value', function() {
23 | var deploy = new DeployTask({
24 | project: mockProject,
25 | ui: mockUi,
26 | deployTarget: 'development-postbuild',
27 | config: mockPostBuildConfig,
28 | shouldActivate: true
29 | });
30 | expect(deploy.shouldActivate).to.eq(true);
31 | });
32 |
33 | it('is specified in the deploy config', function() {
34 | var project = {
35 | name: function() {return 'test-project';},
36 | root: process.cwd(),
37 | addons: []
38 | };
39 | var deploy = new DeployTask({
40 | project: project,
41 | ui: mockUi,
42 | deployTarget: 'development-postbuild',
43 | config: mockPostBuildConfig,
44 | });
45 | expect(deploy.shouldActivate).to.eq(true);
46 | });
47 |
48 | it('is passed as a commandLine option', function() {
49 | var project = {
50 | name: function() {return 'test-project';},
51 | root: process.cwd(),
52 | addons: []
53 | };
54 | var deploy = new DeployTask({
55 | project: project,
56 | ui: mockUi,
57 | deployTarget: 'development-postbuild',
58 | config: mockDeployConfig,
59 | commandOptions: {
60 | activate: true
61 | }
62 | });
63 | expect(deploy.shouldActivate).to.eq(true);
64 | });
65 | });
66 |
67 | describe('executing the deployTask', function() {
68 | it ('executes the pipelineTask', function() {
69 | var pipelineExecuted = false;
70 |
71 | var project = {
72 | name: function() {return 'test-project';},
73 | root: process.cwd(),
74 | addons: [ ]
75 | };
76 |
77 | var task = new DeployTask({
78 | project: project,
79 | ui: mockUi,
80 | deployTarget: 'development',
81 | config: mockDeployConfig,
82 | _pipeline: {
83 | run: function() {
84 | pipelineExecuted = true;
85 | return Promise.resolve();
86 | }
87 | }
88 | });
89 |
90 | return expect(task.run()).to.be.fulfilled
91 | .then(function() {
92 | expect(pipelineExecuted).to.eq(true);
93 | });
94 | });
95 | });
96 |
97 | describe('setting environment variables from .env', function() {
98 | beforeEach(function(){
99 | delete process.env.ENVTEST;
100 | });
101 | });
102 |
103 | });
104 |
105 | });
106 |
--------------------------------------------------------------------------------
/node-tests/unit/tasks/read-config-test.js:
--------------------------------------------------------------------------------
1 | var ReadConfigTask = require('../../../lib/tasks/read-config');
2 | var assert = require('chai').assert;
3 | var path = require('path');
4 |
5 | describe('ReadConfigTask', function() {
6 | describe('#run', function() {
7 | it('reads from the config file', function(done){
8 | var project = {
9 | name: function() {return 'test-project';},
10 | root: process.cwd(),
11 | addons: []
12 | };
13 |
14 | var task = new ReadConfigTask({
15 | project: project,
16 | deployTarget: 'development',
17 | deployConfigPath: 'node-tests/fixtures/config/deploy.js'
18 | });
19 | task.run().then(function(config){
20 | assert.equal(config.build.environment, 'development');
21 | assert.equal(config.s3.bucket, 'shineonyoucrazy');
22 | done();
23 | });
24 | });
25 |
26 | it('accepts an absolute deployConfigPath', function() {
27 | var project = {
28 | name: function() {return 'test-project';},
29 | root: process.cwd(),
30 | addons: []
31 | };
32 |
33 | var fn = function () {
34 | new ReadConfigTask({
35 | project: project,
36 | deployTarget: 'development',
37 | deployConfigPath: path.join(process.cwd(), 'node-tests/fixtures/config/deploy.js')
38 | }).run();
39 | };
40 |
41 | assert.doesNotThrow(fn, /Cannot find module/, 'config file could not be read');
42 | });
43 |
44 | describe('setting environment variables from .env', function() {
45 | var project;
46 | var customDotEnvVars = [
47 | 'OVERRIDDEN',
48 | 'SHARED',
49 | 'ENVTEST'
50 | ];
51 |
52 | beforeEach(function(){
53 | customDotEnvVars.forEach(function(dotEnvVar) {
54 | delete process.env[dotEnvVar];
55 | });
56 |
57 | project = {
58 | name: function() {return 'test-project';},
59 | root: path.join(process.cwd(), 'node-tests/fixtures'),
60 | addons: []
61 | };
62 | });
63 | it('sets the process.env vars if a .env file exists for deploy environment', function() {
64 | assert.isUndefined(process.env.ENVTEST);
65 |
66 | var task = new ReadConfigTask({
67 | project: project,
68 | deployTarget: 'development',
69 | deployConfigPath: 'config/deploy.js'
70 | });
71 | task.run();
72 |
73 | assert.equal(process.env.ENVTEST, 'SUCCESS');
74 | });
75 |
76 | it('sets the process.env vars from main .env file', function() {
77 | assert.isUndefined(process.env.SHARED);
78 |
79 | var task = new ReadConfigTask({
80 | project: project,
81 | deployTarget: 'development',
82 | deployConfigPath: 'config/deploy.js'
83 | });
84 | task.run();
85 |
86 | assert.equal(process.env.SHARED, 'shared-key');
87 | });
88 |
89 | it('overrides vars from main .env file if defined in deploy environment .env file', function() {
90 | assert.isUndefined(process.env.OVERRIDDEN);
91 |
92 | var task = new ReadConfigTask({
93 | project: project,
94 | deployTarget: 'development',
95 | deployConfigPath: 'config/deploy.js'
96 | });
97 | task.run();
98 |
99 | assert.equal(process.env.OVERRIDDEN, 'deploy-env-flavor');
100 | });
101 | });
102 |
103 | });
104 |
105 | });
106 |
--------------------------------------------------------------------------------
/MIGRATING_FROM_0_0_x_TO_0_4_x.md:
--------------------------------------------------------------------------------
1 | ##Overview
2 |
3 | `achambers/ember-cli-deploy`, `LevelBossMike/ember-deploy` and `tedconf/ember-cli-front-end-builds` are coming together to create the offical Ember CLI deployment tool, `ember-cli/ember-cli-deploy`.
4 |
5 | Users upgrading from the `ember-cli-deploy` npm package `<= v0.0.6` to `>= v0.4.0` will need to follow the migration steps below as the core codebase of the package will be changing completely.
6 |
7 | While we are trying our best to maintain backwards compatability for users of `<= v0.0.6`, this will only be temporary and users are strongly urged to migrate ASAP.
8 |
9 | ## Migrate config
10 |
11 | Migrate your `<= v0.0.6` config from this:
12 |
13 | ```javascript
14 | // config/deploy/staging.js
15 |
16 | module.exports = {
17 | assets: {
18 | accessKeyId: process.env.AWS_ACCESS_KEY,
19 | secretAccessKey: process.env.AWS_SECRET,
20 | bucket: 'staging-bucket',
21 | region: 'eu-west-1'
22 | },
23 |
24 | index: {
25 | host: 'staging-redis.example.com',
26 | port: '1234'
27 | }
28 | };
29 | ```
30 |
31 | ```javascript
32 | // config/deploy/production.js
33 |
34 | module.exports = {
35 | assets: {
36 | accessKeyId: process.env.AWS_ACCESS_KEY,
37 | secretAccessKey: process.env.AWS_SECRET,
38 | bucket: 'prod-bucket',
39 | region: 'eu-west-1'
40 | },
41 |
42 | index: {
43 | host: 'production-redis.example.com',
44 | port: '9876',
45 | password: process.env.REDIS_PASSWORD
46 | }
47 | };
48 | ```
49 |
50 | to this:
51 |
52 | ```javascript
53 | // config/deploy.js
54 |
55 | module.exports = {
56 | staging: {
57 | buildEnv: 'staging',
58 | store: {
59 | host: 'staging-redis.example.com',
60 | port: 1234
61 | },
62 | assets: {
63 | accessKeyId: process.env.AWS_ACCESS_KEY,
64 | secretAccessKey: process.env.AWS_SECRET,
65 | bucket: 'staging-bucket'
66 | region: 'eu-west-1'
67 | }
68 | },
69 |
70 | production: {
71 | store: {
72 | host: 'production-redis.example.com',
73 | port: 9876,
74 | password: process.env.REDIS_PASSWORD
75 | },
76 | assets: {
77 | accessKeyId: process.env.AWS_ACCESS_KEY,
78 | secretAccessKey: process.env.AWS_SECRET,
79 | bucket: 'prod-bucket'
80 | region: 'eu-west-1'
81 | }
82 | }
83 | };
84 | ```
85 |
86 | ## Migrate adapters
87 |
88 | Uninstall the now unsupported adapters:
89 |
90 | ```shell
91 | $ npm uninstall ember-cli-deploy-redis-index-adapter --save-dev
92 | ```
93 |
94 | And install the corresponding supported plugins:
95 |
96 | ```shell
97 | $ npm install ember-deploy-redis --save-dev
98 |
99 | $ npm install ember-deploy-s3 --save-dev
100 | ```
101 |
102 | ## Serving of index.html
103 |
104 | Due to the way `v0.4.0` now stores the list of previous revisions, [achambers/fuzzy-wookie](https://github.com/achambers/fuzzy-wookie) is no longer compatible with ember-cli-deploy.
105 |
106 | If you were using [achambers/fuzzy-wookie](https://github.com/achambers/fuzzy-wookie), please migrate to use [philipheinser/ember-lightning](https://github.com/philipheinser/ember-lightning) instead.
107 |
108 | If you wrote your own server to serve the index.html, you will need to modify it in order for it to work with `v0.4.0`. The breaking change is that instead of storing just `sha` in the list of previous revisions,ember-cli-deploy now stores `app-name:sha`. Please make any changes necessary to your server to support this change.
109 |
110 | ## Unsupported commands
111 |
112 | A number of commands became deprecated in `v0.4.0` and will become unsupported in future versions very soon.
113 |
114 | - instead of `ember deploy:index` and `ember deploy:assets`, please use `ember deploy`
115 | - instead of `ember activate`, please use `ember deploy:activate`
116 | - instead of `ember deploy:versions`, please use `ember deploy:list`
117 |
--------------------------------------------------------------------------------
/lib/tasks/pipeline.js:
--------------------------------------------------------------------------------
1 | var Task = require('ember-cli/lib/models/task');
2 | var SilentError = require('silent-error');
3 | var Pipeline = require('../models/pipeline');
4 |
5 | module.exports = Task.extend({
6 | init: function() {
7 | if (!this.project) {
8 | throw new SilentError('No project passed to pipeline task');
9 | }
10 |
11 | if (!this.ui) {
12 | throw new SilentError('No ui passed to pipeline task');
13 | }
14 |
15 | if(!this.deployTarget) {
16 | throw new SilentError('You need to provide a deployTarget: `ember deploy production`');
17 | }
18 |
19 | if(!this.config) {
20 | throw new SilentError('No config passed to pipeline task');
21 | }
22 |
23 | this._pipeline = this.pipeline || new Pipeline(this.hooks, {
24 | ui: this.ui
25 | });
26 |
27 | this.commandOptions = this.commandOptions || {};
28 | },
29 |
30 | setup: function(){
31 | var self = this;
32 | self._installedPlugins = self._discoverInstalledPlugins();
33 |
34 | var plugins;
35 | var configuredPluginList = this.config.plugins;
36 | if (configuredPluginList) {
37 | plugins = self._configuredPlugins(configuredPluginList, self._installedPlugins);
38 | } else {
39 | // no whitelist available, use all autodiscovered plugins
40 | plugins = self._installedPlugins;
41 | }
42 |
43 | var pluginNames = Object.keys(plugins);
44 |
45 | if (pluginNames.length === 0) {
46 | self.ui.writeError("\nWARNING: No plugins installed.\n");
47 | self.ui.writeError("ember-cli-deploy works by registering plugins in its pipeline.");
48 | self.ui.writeError("In order to execute a deployment you must install at least one ember-cli-deploy compatible plugin.\n");
49 | self.ui.writeError("Visit http://ember-cli.github.io/ember-cli-deploy/docs/v0.5.x/plugins/ for a list of supported plugins.\n");
50 | }
51 |
52 | pluginNames.forEach(function(pluginName) {
53 | self._registerPipelineHooks(plugins[pluginName], pluginName);
54 | });
55 | },
56 |
57 | _discoverInstalledPlugins: function(){
58 | var self = this;
59 | var installedPlugins = Object.create(null);
60 | (this.project.addons || []).forEach(function(addon){
61 | self._registerInstalledPlugin(addon, installedPlugins);
62 | });
63 | return installedPlugins;
64 | },
65 |
66 | _registerInstalledPlugin: function(addon, registry) {
67 | var self = this;
68 |
69 | if (this._isDeployPluginPack(addon)) {
70 | addon.addons.forEach(function(nestedAddon){
71 | self._registerInstalledPlugin(nestedAddon, registry);
72 | });
73 | return;
74 | }
75 |
76 | if (!this._isValidDeployPlugin(addon)) { return; }
77 |
78 | registry[this._pluginNameFromAddonName(addon)] = addon;
79 | },
80 |
81 | run: function() {
82 | var pipeline = this._pipeline;
83 | var ui = this.ui;
84 | var project = this.project;
85 | var commandOptions = this.commandOptions;
86 |
87 | this.setup();
88 | var context = {
89 | commandOptions: commandOptions,
90 | config: this.config,
91 | deployTarget: this.deployTarget,
92 | project: project,
93 | ui: ui
94 | };
95 | return pipeline.execute(context);
96 | },
97 |
98 | _configuredPlugins: function(configuredPluginNames, availablePlugins) {
99 | var unavailablePlugins = Object.create(null);
100 | var configuredPlugins = Object.create(null);
101 |
102 | configuredPluginNames.forEach(function(configuredPluginKey) {
103 | var parts = configuredPluginKey.split(':', 2);
104 | var pluginName = parts[0];
105 | var configuredName = parts[1] ? parts[1] : parts[0];
106 |
107 | if (availablePlugins[pluginName]) {
108 | configuredPlugins[configuredName] = availablePlugins[pluginName];
109 | } else {
110 | unavailablePlugins[configuredName] = pluginName;
111 | }
112 | });
113 |
114 | if (Object.keys(unavailablePlugins).length !== 0) {
115 | this.ui.writeError(unavailablePlugins);
116 | var error = new SilentError('plugins configuration references plugins which are not available.');
117 | error.unavailablePlugins = unavailablePlugins;
118 | throw error;
119 | }
120 |
121 | return configuredPlugins;
122 | },
123 |
124 | _pluginNameFromAddonName: function(addon) {
125 | var pluginNameRegex = /^(ember\-cli\-deploy\-)(.*)$/;
126 | return addon.name.match(pluginNameRegex)[2];
127 | },
128 |
129 | _registerPipelineHooks: function(addon, alias) {
130 | var deployPlugin = addon.createDeployPlugin({name: alias});
131 |
132 | this._pipeline.hookNames().forEach(function(hookName) {
133 | var fn = deployPlugin[hookName];
134 | if (typeof fn !== 'function') {
135 | return;
136 | }
137 |
138 | this._pipeline.register(hookName, {
139 | name: deployPlugin.name,
140 | fn: function(context){
141 | if (deployPlugin.beforeHook && typeof deployPlugin.beforeHook === 'function') {
142 | deployPlugin.beforeHook(context);
143 | }
144 | return fn.call(deployPlugin, context);
145 | }
146 | });
147 | }.bind(this));
148 | },
149 |
150 | _isDeployPluginPack: function(addon) {
151 | return this._addonHasKeyword(addon, 'ember-cli-deploy-plugin-pack');
152 | },
153 |
154 | _isValidDeployPlugin: function(addon) {
155 | return this._addonHasKeyword(addon, 'ember-cli-deploy-plugin') && this._addonImplementsDeploymentHooks(addon);
156 | },
157 |
158 | _addonHasKeyword: function(addon, keyword) {
159 | var keywords = addon.pkg.keywords;
160 | return keywords.indexOf(keyword) > -1;
161 | },
162 |
163 | _addonImplementsDeploymentHooks: function(addon) {
164 | return addon.createDeployPlugin && typeof addon.createDeployPlugin === 'function';
165 | }
166 | });
167 |
--------------------------------------------------------------------------------
/lib/models/pipeline.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var Promise = require('ember-cli/lib/ext/promise');
4 | var _ = require('lodash-node');
5 |
6 | var chalk = require('chalk');
7 | var blue = chalk.blue;
8 | var red = chalk.red;
9 |
10 | /* This is a generic implementation of a pipeline with ordered, promise-aware hooks,
11 | * pleasant logging, and failure handling. It should not have any "deployment" domain
12 | * logic or semantics, and is a candidate for extraction to its own npm module.
13 | */
14 | function Pipeline(hookNames, options) {
15 | hookNames = hookNames || [];
16 | options = options || {};
17 |
18 | this._ui = options.ui;
19 |
20 | this._pipelineHooks = hookNames.reduce(function(pipelineHooks, hookName) {
21 | pipelineHooks[hookName] = [];
22 |
23 | return pipelineHooks;
24 | }, { didFail: [] });
25 |
26 | this._progressBarLib = options.progressBarLib || require('ember-cli-deploy-progress');
27 | }
28 |
29 | Pipeline.prototype.register = function(hookName, fn) {
30 | var ui = this._ui;
31 | var pipelineHooks = this._pipelineHooks;
32 |
33 | if (typeof fn === 'function') {
34 | fn = {
35 | name: 'anonymous function',
36 | fn: fn
37 | };
38 | }
39 |
40 | if (pipelineHooks[hookName]) {
41 | if (ui.verbose) {
42 | ui.write(blue('Registering hook -> ' + hookName + '[' + fn.name + ']\n'));
43 | }
44 |
45 | pipelineHooks[hookName].push(fn);
46 | }
47 | };
48 |
49 | Pipeline.prototype.execute = function(context) {
50 | context = context || { };
51 |
52 | var ui = this._ui;
53 | var hooks = this._hooksWithoutDidFail(this.hookNames());
54 | var ProgressBar = this._progressBarLib;
55 | if (ui.verbose) {
56 | ui.write(blue('Executing pipeline\n'));
57 | } else if (ui.showProgress) {
58 | ui.progressBar = new ProgressBar('Deploying [:bar] :percent [plugin: :plugin -> :hook]', {
59 | total: this._hooksCount(this._hooksWithoutConfigure(hooks)),
60 | cursor: process.platform === 'darwin' ? '🚀 ' : '>'
61 | });
62 | }
63 |
64 | return hooks.reduce(this._addHookExecutionPromiseToPipelinePromiseChain.bind(this, ui), Promise.resolve(context))
65 | .then(this._notifyPipelineCompletion.bind(this, ui))
66 | .catch(this._handlePipelineFailure.bind(this, ui, context))
67 | .catch(this._abortPipelineExecution.bind(this, ui));
68 | };
69 |
70 | Pipeline.prototype.hookNames = function() {
71 | return Object.keys(this._pipelineHooks);
72 | };
73 |
74 | Pipeline.prototype._addHookExecutionPromiseToPipelinePromiseChain = function(ui, promise, hookName) {
75 | var self = this;
76 | return promise
77 | .then(this._notifyPipelineHookExecution.bind(this, ui, hookName))
78 | .then(function(context){
79 | try {
80 | return self._executeHook(hookName, context);
81 | } catch(error) {
82 | return Promise.reject(error);
83 | }
84 | });
85 | };
86 |
87 | Pipeline.prototype._hooksWithoutDidFail = function(hooks) {
88 | return hooks.filter(function(hook) {
89 | return hook !== 'didFail';
90 | });
91 | };
92 |
93 | Pipeline.prototype._hooksWithoutConfigure = function(hooks) {
94 | return hooks.filter(function(hook) {
95 | return hook !== 'configure';
96 | });
97 | };
98 |
99 | Pipeline.prototype._hooksCount = function(hooks) {
100 | return hooks.reduce(function(sum, hookName) {
101 | var hookFunctions = this._pipelineHooks[hookName];
102 | return sum + hookFunctions.length;
103 | }.bind(this), 0);
104 | };
105 |
106 | Pipeline.prototype._handlePipelineFailure = function(ui, context, error) {
107 | if (ui.verbose) {
108 | ui.write(red('|\n'));
109 | ui.write(red('+- didFail\n'));
110 | }
111 | ui.write(red(error + '\n' + (error ? error.stack : null)));
112 | return this._executeHook('didFail', context)
113 | .then(Promise.reject.bind(this, error));
114 | };
115 |
116 | Pipeline.prototype._abortPipelineExecution = function(ui/*, error */) {
117 | if (ui.verbose) {
118 | ui.write(blue('|\n'));
119 | }
120 | ui.write(red('Pipeline aborted\n'));
121 | return Promise.reject();
122 | };
123 |
124 | Pipeline.prototype._notifyPipelineCompletion = function(ui) {
125 | if (ui.verbose) {
126 | ui.write(blue('|\n'));
127 | ui.write(blue('Pipeline complete\n'));
128 | }
129 | };
130 |
131 | Pipeline.prototype._notifyPipelineHookExecution = function(ui, hookName, context) {
132 | if (ui.verbose) {
133 | ui.write(blue('|\n'));
134 | ui.write(blue('+- ' + hookName + '\n'));
135 | }
136 | return context;
137 | };
138 |
139 | Pipeline.prototype._executeHook = function(hookName, context) {
140 | var ui = this._ui;
141 | var hookFunctions = this._pipelineHooks[hookName];
142 |
143 | return hookFunctions.reduce(this._addPluginHookExecutionPromiseToHookPromiseChain.bind(this, ui, context, hookName), Promise.resolve(context));
144 | };
145 |
146 | Pipeline.prototype._addPluginHookExecutionPromiseToHookPromiseChain = function(ui, context, hookName, promise, fnObject) {
147 | return promise
148 | .then(this._notifyPipelinePluginHookExecution.bind(this, ui, fnObject, hookName))
149 | .then(this._mergePluginHookResultIntoContext.bind(this, context));
150 | };
151 |
152 | Pipeline.prototype._notifyPipelinePluginHookExecution = function(ui, fnObject, hookName, context) {
153 | if (ui.verbose) {
154 | ui.write(blue('| |\n'));
155 | ui.write(blue('| +- ' + fnObject.name + '\n'));
156 | } else if (ui.showProgress) {
157 | if (hookName !== 'configure') {
158 | ui.progressBar.tick({
159 | hook: hookName,
160 | plugin: fnObject.name
161 | });
162 | }
163 | }
164 |
165 | return fnObject.fn(context);
166 | };
167 |
168 | Pipeline.prototype._mergePluginHookResultIntoContext = function(context,result) {
169 | return _.merge(context, result, function(a, b) {
170 | if (_.isArray(a)) {
171 | return a.concat(b);
172 | }
173 | });
174 | };
175 |
176 | module.exports = Pipeline;
177 |
--------------------------------------------------------------------------------
/node-tests/unit/models/pipeline-test.js:
--------------------------------------------------------------------------------
1 | var Promise = require('ember-cli/lib/ext/promise');
2 | var Pipeline = require('../../../lib/models/pipeline');
3 |
4 | var expect = require('../../helpers/expect');
5 | var FakeProgressBar = require('../../helpers/fake-progress-bar');
6 |
7 | describe ('Pipeline', function() {
8 | describe ('initialization', function() {
9 | it ('initializes the given list of hooks plus the `didFail`-hook', function() {
10 | var subject = new Pipeline(['willDeploy', 'didDeploy']);
11 |
12 | expect(Object.keys(subject._pipelineHooks).length).to.eq(3);
13 | expect(subject._pipelineHooks.willDeploy).to.eql([]);
14 | expect(subject._pipelineHooks.didDeploy).to.eql([]);
15 | expect(subject._pipelineHooks.didFail).to.eql([]);
16 | });
17 | });
18 |
19 | describe ('#register', function() {
20 | it ('registers functions for defined hooks', function() {
21 | var subject = new Pipeline(['willDeploy'], {
22 | ui: {write: function() {}}
23 | });
24 | var fn = function() {};
25 |
26 | subject.register('willDeploy', fn);
27 |
28 | expect(subject._pipelineHooks.willDeploy.length).to.eq(1);
29 | expect(subject._pipelineHooks.willDeploy[0].name).to.eq('anonymous function');
30 | expect(subject._pipelineHooks.willDeploy[0].fn).to.eql(fn);
31 | });
32 |
33 | it ('doesn\'t register functions for hooks not defined', function() {
34 | var subject = new Pipeline(['willDeploy'], {
35 | ui: {write: function() {}}
36 | });
37 | var fn = function() {};
38 |
39 | subject.register('build', fn);
40 |
41 | expect(subject._pipelineHooks.willDeploy.length).to.eq(0);
42 | expect(subject._pipelineHooks.build).to.eq(undefined);
43 | });
44 | });
45 |
46 | describe ('#execute', function() {
47 | it('runs the registered functions', function() {
48 | var subject = new Pipeline(['hook1', 'hook2'], {ui: {write: function() {}}});
49 | var hooksRun = [];
50 |
51 | subject.register('hook1', function() {
52 | hooksRun.push('1');
53 | });
54 |
55 | subject.register('hook2', function() {
56 | hooksRun.push('2');
57 | });
58 | return expect(subject.execute()).to.be.fulfilled
59 | .then(function() {
60 | expect(hooksRun.length).to.eq(2);
61 | expect(hooksRun[0]).to.eq('1');
62 | expect(hooksRun[1]).to.eq('2');
63 | });
64 | });
65 |
66 | it('executes the `didFail`-hook as soon as one of the pipeline hooks rejects', function() {
67 | var subject = new Pipeline(['hook1', 'hook2'], {ui: {write: function() {}}});
68 | var hooksRun = [];
69 |
70 | subject.register('hook1', function() {
71 | hooksRun.push('hook1');
72 | });
73 |
74 | subject.register('hook2', function() {
75 | return Promise.reject();
76 | });
77 |
78 | subject.register('hook3', function() {
79 | hooksRun.push('3');
80 | });
81 |
82 | subject.register('didFail', function() {
83 | hooksRun.push('didFail');
84 | });
85 |
86 | return expect(subject.execute()).to.be.rejected
87 | .then(function() {
88 | expect(hooksRun.length).to.eq(2);
89 | expect(hooksRun[0]).to.eq('hook1');
90 | expect(hooksRun[1]).to.eq('didFail');
91 | });
92 | });
93 |
94 | it('passes the default context object when one isn\'t provided', function() {
95 | var subject = new Pipeline(['hook1'], {ui: {write: function() {}}});
96 | var data = null;
97 |
98 | subject.register('hook1', function(context) {
99 | data = context;
100 | });
101 |
102 | return expect(subject.execute()).to.be.fulfilled
103 | .then(function() {
104 | expect(data).to.deep.equal({});
105 | });
106 | });
107 |
108 | it('passes the provided context object to hooks when provided', function() {
109 | var subject = new Pipeline(['hook1'], {ui: {write: function() {}}});
110 | var data = null;
111 |
112 | subject.register('hook1', function(context) {
113 | data = context;
114 | });
115 |
116 | return expect(subject.execute({deploy: {}})).to.be.fulfilled
117 | .then(function() {
118 | expect(data).to.deep.equal({deploy: {}});
119 | });
120 | });
121 |
122 | it('merges the return value (object) of each hook into the context', function() {
123 | var subject = new Pipeline(['hook1'], {ui: {write: function() {}}});
124 | var finalContext = null;
125 |
126 | subject.register('hook1', function() {
127 | return {age: 47};
128 | });
129 |
130 | subject.register('hook1', function(context) {
131 | finalContext = context;
132 | });
133 |
134 | return expect(subject.execute({name: 'test-context'})).to.be.fulfilled
135 | .then(function() {
136 | expect(finalContext.name).to.equal('test-context');
137 | expect(finalContext.age).to.equal(47);
138 | });
139 | });
140 |
141 | it('merges the return value (promise) of each hook into the context', function() {
142 | var subject = new Pipeline(['hook1'], {ui: {write: function() {}}});
143 | var finalContext = null;
144 |
145 | subject.register('hook1', function() {
146 | return Promise.resolve({age: 47});
147 | });
148 |
149 | subject.register('hook1', function(context) {
150 | finalContext = context;
151 | });
152 |
153 | return expect(subject.execute({name: 'test-context'})).to.be.fulfilled
154 | .then(function() {
155 | expect(finalContext.name).to.equal('test-context');
156 | expect(finalContext.age).to.equal(47);
157 | });
158 | });
159 | });
160 |
161 | describe('#hookNames', function() {
162 | it('returns the names of the registered hooks', function() {
163 | var subject = new Pipeline(['hook1', 'hook2']);
164 |
165 | var result = subject.hookNames();
166 |
167 | expect(result).to.have.members(['hook1', 'hook2', 'didFail']);
168 | });
169 | });
170 |
171 | describe('progressBar', function() {
172 | it('is initalized if showProgress is true', function() {
173 | var ui = {
174 | showProgress: true,
175 | };
176 | var subject = new Pipeline(['hook1'], {
177 | ui: ui,
178 | progressBarLib: FakeProgressBar
179 | });
180 |
181 | subject.execute();
182 |
183 | expect(ui.progressBar).to.be.ok;
184 | });
185 |
186 | it('is not used in --verbose mode', function() {
187 | var ui = {
188 | verbose: true,
189 | showProgress: true,
190 | write: function() {}
191 | };
192 | var subject = new Pipeline(['hook1'], {
193 | ui: ui,
194 | progressBarLib: FakeProgressBar
195 | });
196 |
197 | subject.execute();
198 |
199 | expect(ui.progressBar).to.be.falsy;
200 | });
201 |
202 | it('calculates the total number of registered hooks functions', function() {
203 | var ui = {
204 | showProgress: true,
205 | };
206 | var subject = new Pipeline(['hook1', 'hook2'], {
207 | ui: ui,
208 | showProgress: true,
209 | progressBarLib: FakeProgressBar
210 | });
211 |
212 | subject.register('hook1', function() {});
213 | subject.register('hook1', function() {});
214 | subject.register('hook2', function() {});
215 | subject.execute();
216 |
217 | expect(ui.progressBar.total).to.equal(3);
218 | });
219 |
220 | it('excludes the configure hooks from the total', function() {
221 | var ui = {
222 | showProgress: true,
223 | };
224 | var subject = new Pipeline(['configure', 'hook1'], {
225 | ui: ui,
226 | showProgress: true,
227 | progressBarLib: FakeProgressBar
228 | });
229 |
230 | subject.register('hook1', function() {});
231 | subject.register('hook1', function() {});
232 | subject.register('configure', function() {});
233 | subject.execute();
234 |
235 | expect(ui.progressBar.total).to.equal(2);
236 | });
237 |
238 | it('ticks during execution', function() {
239 | var ui = {
240 | showProgress: true,
241 | };
242 | var subject = new Pipeline(['hook1', 'hook2'], {
243 | ui: ui,
244 | showProgress: true,
245 | progressBarLib: FakeProgressBar
246 | });
247 |
248 | var fn1 = {
249 | fn: function() {},
250 | name: 'fn1'
251 | };
252 | var fn2 = {
253 | fn: function() {},
254 | name: 'fn2'
255 | };
256 |
257 | subject.register('hook1', fn1);
258 | subject.register('hook2', fn2);
259 |
260 | return expect(subject.execute()).to.be.fulfilled
261 | .then(function() {
262 | expect(ui.progressBar.ticks).to.eql(
263 | [{hook: 'hook1', plugin: 'fn1'},
264 | {hook: 'hook2', plugin: 'fn2'}
265 | ]
266 | );
267 | });
268 | });
269 | });
270 | });
271 |
--------------------------------------------------------------------------------
/rfc/plugins.md:
--------------------------------------------------------------------------------
1 | # Plugins for ember-cli-deploy
2 |
3 | This document describes the API related to plugins in the next
4 | version of ember-cli-deploy.
5 |
6 | ### Overview of plugins
7 |
8 | A plugin is an ember-cli addon that hooks into the ember-cli-deploy
9 | pipeline in order to add functionality to a deploy. Example addon
10 | functionality would be uploading assets to S3, writing the index.html to
11 | Redis, or notifying a Slack channel a deploy has completed.
12 |
13 | In general, OSS plugins should focus on doing a specific task. Most
14 | Ember developers with common deployment targets will compose multiple
15 | plugins to fine tune their deployment process. Developers with very
16 | custom needs might create a single private plugin that implements all
17 | aspects of their deployment process within the structure provided by
18 | ember-cli-deploy.
19 |
20 | Because plugins are implemented via addons, they may be included via
21 | node_module dependencies or via in-repo-addons.
22 |
23 | ### Identifying plugins
24 |
25 | Plugins are ember-cli addons. They will also have the keyword
26 | `ember-cli-deploy-plugin`. An example `package.json` for a plugin:
27 |
28 | ```
29 | {
30 | "name": "ember-cli-deploy-example-plugin",
31 | "version": "0.4.0",
32 | // ...
33 | "devDependencies": {
34 | "ember-cli": "0.2.0",
35 | "ember-cli-deploy": "0.4.1"
36 | // ...
37 | },
38 | "keywords": [
39 | "ember-addon",
40 | "ember-cli-deploy-plugin"
41 | ]
42 | }
43 | ```
44 |
45 | By default, any plugin that is installed will be loaded, similar to
46 | ember-cli addons. The plugin order is determined by how ember-cli orders
47 | addons (based on `before:`/`after:` properties). To override this, see
48 | Advanced Plugin Configuration and Ordering below.
49 |
50 | ### Plugins are provided by ember-cli addons
51 |
52 | Plugins are ember-cli addons which implement an `createDeployPlugin()` method
53 | to return an object which implements one or more methods that are called by
54 | ember-cli-deploy. For example:
55 |
56 | ```javascript
57 | module.exports = {
58 | name: 'ember-cli-deploy-example-plugin',
59 | createDeployPlugin: function(options){
60 | return {
61 | name: options.name,
62 | willDeploy: function:(deployment){
63 | // do something during the willDeploy phase of the pipeline
64 | },
65 | didDeploy: function:(deployment){
66 | // do something during the didDeploy phase of the pipeline
67 | },
68 | // etc, see hooks section for a complete list of methods that
69 | // may be implemented by a plugin
70 | }
71 | }
72 | }
73 | ```
74 |
75 | This approach limits the risk of name conflicts at the top-level of the addon.
76 | It also allows for the plugin author to consult the addon instance during
77 | creation of the plugin object to make any contextual decisions necessary.
78 | Finally, it is agnostic with respect to the type of object the plugin is.
79 | It may be a POJO, a subclass of CoreObject, or maybe an ES6 class.
80 |
81 | The `options` argument passed to `createDeployPlugin` will have a `name`
82 | property. Usually, the `name` will be the plugin name sans the `ember-cli-deploy-`
83 | prefix, unless a name has been specified as described in Advanced Plugin
84 | Configuration below.
85 |
86 | ### The `context` object
87 |
88 | For each high-level ember-cli-deploy operation, a `context` object is created.
89 | This object is passed to each hook that is invoked on the plugins. It has a number
90 | of properties that may be of use to a plugin:
91 |
92 | Property | file | info
93 | --- | --- | ---
94 | `ui` | - | The ember-cli UI object that can be used to write to stdout.
95 | `config` | stored in `config/deploy.js` | The configuration portion of `config/deploy.js` for the active environment
96 | `data` | - | Runtime information about the current operation. Plugins can set properties on this object for later use by themselves or another plugin.
97 |
98 | ### Async operations in hooks
99 |
100 | Hook functions can return a promise to block the deployment pipeline.
101 | Since most deployment involves some sort of IO it makes senses that most
102 | plugins will want an async function to complete before continuing to the
103 | next step. If a plugin does not return a promise, then ember-cli-deploy
104 | proceeds immediately.
105 |
106 | If a promise from any of the plugins is rejected then the deployment
107 | pipeline will stop and ember-cli-deploy will exit. Returned promises that are
108 | rejected are treated as unrecoverable errors.
109 |
110 | ### Configuration
111 |
112 | By convention, plugin configuration should be kept in `config/deploy.js` and scoped by
113 | the plugin's name. e.g. for an `ember-cli-deploy-example` plugin, the configuration might look like:
114 |
115 | ```javascript
116 | // config/deploy.js
117 | module.exports = return function(environment) {
118 | return {
119 | "example": {
120 | bucket: "my-app-" + environment,
121 | awsAccessKeyId: process.env.AWS_ACCESS_KEY_ID,
122 | awsSecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
123 | }
124 | };
125 | };
126 | ```
127 |
128 | ### Hooks
129 |
130 | These hooks (part of a typical deployment process) are available for plugins to implement:
131 |
132 | ```
133 | configure: ---> runs before anything happens
134 |
135 | setup: -------> the first hook for every command
136 |
137 | willDeploy: --> runs before anything happens. good opportunity for plugins to validate
138 | configuration or other preconditions
139 |
140 | /-- willBuild confirm environment
141 | /
142 | build --------> builds app assets, documentation, etc.
143 | \
144 | \-- didBuild manipulate index.html, validate assets
145 |
146 | /-- willPrepare confirm deployment info
147 | /
148 | prepare --------> prepare information about the deploy, eg revisonKey, timestamp, commit message
149 | \
150 | \-- didPrepare notify APIS (slack etc)
151 |
152 | /-- willUpload confirm remote servers(S3, Redis, Azure, etc.)
153 | /
154 | upload -------> puts the assets somewhere(S3, Redis, Azure, Rackspace, etc.)
155 | | Note: a plugin that implements upload of the HTML file and
156 | | wants to support version activation should set
157 | | `currentVersion` on the `deployment` object to the ID
158 | | of the newly deployed version.
159 | \
160 | \-- didUpload notify APIs (slack, pusher, etc.), warm cache
161 |
162 | /-- willActivate create backup of assets, notify APIs, uninstall earlier versions
163 | /
164 | activate -------> make a new version live (clear cache, swap Redis values, etc.)
165 | \
166 | \-- didActivate notify APIs, warm cache
167 |
168 | Note: when hooks in the activate series of hooks are called, the plugin can assume the
169 | presence of a `currentVersion` property on the deployment object, that is set to
170 | the ID of the version to be activated.
171 |
172 | didDeploy: --> runs at the end of a full deployment operation.
173 |
174 | teardown: ---> always the last hook being run
175 | ```
176 |
177 | In addition, there are a few more specialized hooks that plugins may implement:
178 |
179 | ```
180 | discoverVersions: --> should return a promise resolving to an array of version objects. Each
181 | version object _must_ have an `id` property. Each version _may_ have one
182 | or more of the following properties:
183 |
184 | `timestamp`: (Date) when the version was created
185 | `revision`: (String) reference of version in SCM
186 | `creator`: (String) email address of developer who deployed the version
187 | `description`: (String) summary of the version
188 |
189 | ```
190 |
191 | ### Hooks by command
192 |
193 | Depending on the command you're running the hooks that are called vary
194 |
195 | #### `ember deploy`
196 | ```
197 | - configure
198 | - setup
199 | - willDeploy
200 | - willBuild, build, didBuild,
201 | - willPrepare, prepare, didPrepare,
202 | - willUpload, upload, didUpload,
203 | - willActivate, activate, didActivate, (only if --activate flag is passed)
204 | - didDeploy,
205 | - teardown
206 | ```
207 |
208 | #### `ember deploy:activate`
209 | ```
210 | - configure
211 | - setup
212 | - willActivate, activate, didActivate
213 | - teardown
214 | ```
215 |
216 | #### `ember deploy:list`
217 | ```
218 | - configure
219 | - setup
220 | - fetchRevisions
221 | - displayRevisions
222 | - teardown
223 | ```
224 |
225 | ### Advanced Plugin Configuration
226 |
227 | As mentioned above, by default, all plugins from installed addons will be loaded, and
228 | ordered based on ember-cli's order of the addons. Developers may have advanced use cases
229 | for specifying the order of plugins, disabling plugins, or configuring a single plugin to
230 | be configured and used twice.
231 |
232 | If you want to opt-into this configuration, you can set the `plugins` property in your `config/deploy.js` file at either the top-level (for global configuration), or under an environment (for per-environment configuration).
233 |
234 | ```
235 | plugins: ["s3-assets", "s3-index", "notify-slack"]
236 | ```
237 |
238 | Any plugins not included in the list will not have their hooks executed.
239 |
240 | #### Aliasing plugins
241 |
242 | To include a plugin twice, alias it using a colon.
243 |
244 | ```
245 | plugins: ["s3-assets:foo-assets", "s3-assets:bar-assets", "s3-index", "notify-slack"]
246 | ```
247 |
248 | The name specified after the colon will be passed as the `name` property
249 | of the `options` argument to the addon's `createDeployPlugin` method. Plugins
250 | should use their name to retrieve configuration values. In this example,
251 | the foo-assets instance of the s3-assets plugin could have different configuration
252 | than the bar-assets instance does.
253 |
254 | ### Another take on configuration
255 |
256 | Convention-over-configuration is of utmost importance. For golden path use cases, including the packages and doing unavoidable configuration (e.g. credentials and connection info) should be all it takes. Of course, it should be possible to wire together plugins in ways beside the golden path, too.
257 |
258 | Here's how we should accomplish this.
259 |
260 | Goals:
261 |
262 | 1) A great out-of-the-box app developer experience for common deployment scenarios.
263 | 2) An API for app developers to define deployment pipeline configuration synchronously or asynchronously.
264 | 3) An API for plugin developers to provide static default configuration values.
265 | 4) An API for plugin developers to provide default configuration values that are derived at run-time from the data produced by plugin running prior to it in the pipeline.
266 | 5) An API for plugin developers to allow ap developers to interact with plugin settings via command line flags.
267 | 6) An API for app developers to specify configuration of a plugin to use data produced by a plugin running prior to it in the pipeline.
268 |
269 | Approach:
270 |
271 | * App developers use `config/deploy.js` to return a function that receives the build environment as a string and returns either a config object or a Promise that fulfills with a config object. The config object has properties corresponding to the name of the plugin (e.g. for ember-cli-deploy-redis, the property is “redis”). Supports goal No. 2 above. (This is implemented in the 0.5.0 WIP already.)
272 |
273 | Examples:
274 |
275 | ```js
276 | // deploy.js (sync)
277 | module.export function(environment){
278 | var ENV = {
279 | redis: {
280 | url: process.env.REDIS_URL
281 | }
282 | }
283 | return ENV;
284 | };
285 |
286 | // deploy.js (async)
287 | module.export function(environment){
288 | var ENV = {
289 | redis: {
290 | }
291 | }
292 | return someAsyncDataRetrieval(environment).then(function(data){
293 | ENV.redis = data.redisUrl;
294 | return ENV;
295 | }
296 | };
297 | ```
298 |
299 | * Plugin developers can implement a `configure` hook that runs at the beginning of pipeline execution (because “configure” is the first step of the pipeline). This hook has read/write access to the config object. It can specify default configuration values, as well as throw an Error in the case that a required configuration property was not provided. Supports goal No. 3 above. (This is implemented in the 0.5.0 WIP already, although we should provide plugins with helper to define defaults and enforce required properties more expressively and more consistently with other plugins.)
300 |
301 | Example:
302 |
303 | ```js
304 | // some-ember-cli-deploy-plugin/index.js
305 | module.exports = {
306 | name: 'ember-cli-deploy-myplugin',
307 |
308 | createDeployPlugin: function(options) {
309 | return {
310 | name: options.name,
311 | configure: function(context) {
312 | var deployment = context.deployment;
313 | var config = deployment.config[this.name] = deployment.config[this.name] || {};
314 | config.filePattern = config.filePattern || “**/*.html”; // provide default
315 |
316 | },
317 | // ...
318 | }
319 | };
320 | ```
321 |
322 | * Plugin developers can also use `configure` hook specify a default configuration property as a function, which will be called at run-time, when a plugin wishes to read and use the configuration value. The function will receive the context and must return a value or throw an Error. The context would allow access to data added to pipeline by previous plugins, as well as flags set on command line. Supports goal No. 4 and No. 5 above. (This is not yet implemented.)
323 |
324 | Example:
325 |
326 | ```js
327 | // some-ember-cli-deploy-plugin/index.js
328 | module.exports = {
329 | name: 'ember-cli-deploy-myplugin',
330 |
331 | createDeployPlugin: function(options) {
332 | return {
333 | name: options.name,
334 | configure: function(context) {
335 | var deployment = context.deployment;
336 | var config = deployment.config[this.name] = deployment.config[this.name] || {};
337 | config.revision = config.revision || function(context){
338 | return context.deployment.revision;
339 | };
340 | // we could also provide a helper for this, e.g.
341 | // config.revision = config.revision || fromPipelineData(context, “revision”);
342 | config.shouldActivate = config.shouldActivate || function(context){
343 | return !!context.flags.activate; // set via `--activate on command line
344 | };
345 | },
346 | // ...
347 | }
348 | };
349 | ```
350 |
351 | * App developers can also use this `function`-style configuration in `config/deploy.js` in order to wire together plugins. The function will receive the context and must return a value or throw an Error. Supports goal No. 6 above. (No additional implementation would be necessary if the above were implemented.)
352 |
353 | Example:
354 |
355 | ```js
356 | // deploy.js
357 | module.export function(environment){
358 | var ENV = {
359 | redis: {
360 | revisionKey: function(context) { return context.deployment.tag; },
361 | forceUpdate: function(context) { return context.flags.force; }
362 | }
363 | }
364 | return ENV;
365 | };
366 | ```
367 |
368 | These approaches all combine to achieve goal No. 1 above.
369 |
370 | ### Plugin packs
371 |
372 | A "plugin pack" is an ember-cli addon with the keyword
373 | "ember-cli-deploy-plugin-pack" and one or more dependent
374 | addons that are ember-cli-deploy-plugins.
375 |
376 | Note that the plugin pack’s dependent addons should be listed as
377 | dependencies in the pack’s package.json, not as devDependencies.
378 |
379 | Plugin packs may also implement a config/deploy.js blueprint that
380 | is auto-executed upon `ember install` of the pack to make
381 | configuration easy for end-developers.
382 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # ember-cli-deploy Changelog
2 |
3 | ## [0.6.0](https://github.com/ember-cli/ember-cli-deploy/tree/0.6.0) (2016-02-26)
4 | [Full Changelog](https://github.com/ember-cli/ember-cli-deploy/compare/v0.5.1...0.6.0)
5 |
6 | This release has no breaking changes from 0.5.1. It introduces a progress bar for deploys run on interactive terminals, improved ability to configure command defaults, and new fetchInitialRevisions & fetchRevisions hooks to the deploy pipeline, which will better enable plugins that want to do changelog/diff notifications.
7 |
8 | We have also introduced new versioned docs for 0.6.x, available now from the version selector on ember-cli-deploy.com in the Docs section.
9 |
10 | Huge thanks to the fine work of the ember-cli-deploy core team, particularly @ghedamat who has been very active this cycle, and to our growing set of contributors and community. It is amazing to the see the ember-cli-deploy plugin ecosystem blossom.
11 |
12 | ### Plugin Authors
13 |
14 | The `fetchRevisions` hook is now be called during the "deploy" and "activate" pipelines. It was previously only called during the "list" pipeline. In addition, a new `fetchInitialRevisions` hook will be called during the "deploy" and "activate" pipelines. See the [0.6.x Pipeline Hooks docs](http://ember-cli.com/ember-cli-deploy/docs/v0.6.x/pipeline-hooks/) for details. If you maintain a plugin that uploads new revisions, you will want to update your plugin to implement the new hook. Here is an example of [updating ember-cli-deploy-redis](https://github.com/ember-cli-deploy/ember-cli-deploy-redis/pull/50).
15 |
16 | You should also update your ember-cli-deploy-plugin dependency to 0.2.2, to ensure your plugin's logging plays nicely with the nifty new progress bar in this ember-cli-deploy release.
17 |
18 | Pull Requests:
19 |
20 | - Move to non-scoped npm package for progress, and newer version. Fixes an issue causing crashes when running at certain narrow terminal widths. [\#366](https://github.com/ember-cli/ember-cli-deploy/pull/366) and [\#367](https://github.com/ember-cli/ember-cli-deploy/pull/367) ([lukemelia](https://github.com/lukemelia))
21 | - Use our fork of progress so that we can make the rocket part of the progress bar [\#364](https://github.com/ember-cli/ember-cli-deploy/pull/364) ([lukemelia](https://github.com/lukemelia))
22 | - Remove unused code [\#359](https://github.com/ember-cli/ember-cli-deploy/pull/359) ([achambers](https://github.com/achambers))
23 | - display progress notification during deploy [\#280](https://github.com/ember-cli/ember-cli-deploy/pull/280) ([ghedamat](https://github.com/ghedamat))
24 | - add `fetchRevisions`hook to deploy and activate pipelines [\#323](https://github.com/ember-cli/ember-cli-deploy/pull/323) ([sethpollack](https://github.com/sethpollack))
25 | - Allow loading .env files + .env.deploy.\ files [\#342](https://github.com/ember-cli/ember-cli-deploy/pull/342) ([blimmer](https://github.com/blimmer))
26 | - read-config task \(fix issue where dotenv files were not loaded in time for deploy tasks\) [\#319](https://github.com/ember-cli/ember-cli-deploy/pull/319) ([lukemelia](https://github.com/lukemelia))
27 | - Remove temporary fix for broccoli-asset-rev [\#316](https://github.com/ember-cli/ember-cli-deploy/pull/316) ([backspace](https://github.com/backspace))
28 |
29 | ## [0.5.1](https://github.com/ember-cli/ember-cli-deploy/tree/0.5.1) (2015-11-09)
30 | [Full Changelog](https://github.com/ember-cli/ember-cli-deploy/compare/v0.5.0...0.5.1)
31 |
32 | - Upgrade ember-cli to 1.13.8. [\#296](https://github.com/ember-cli/ember-cli-deploy/pull/296) ([blimmer](https://github.com/blimmer))
33 | - Require a deploy target to run ember deploy [\#294](https://github.com/ember-cli/ember-cli-deploy/pull/294) ([kiwiupover](https://github.com/kiwiupover))
34 | - Fix typo in "no plugins" warning \(it's/its\) [\#284](https://github.com/ember-cli/ember-cli-deploy/pull/284) ([pgengler](https://github.com/pgengler))
35 |
36 | ## [0.5.0](https://github.com/ember-cli/ember-cli-deploy/tree/v0.5.0) (2015-10-29)
37 | [Full Changelog](https://github.com/ember-cli/ember-cli-deploy/compare/v0.4.3...v0.5.0)
38 |
39 | [BREAKING] This release is the first in the 0.5.0 series and the first using the new deploy pipeline and plugin architecture.
40 |
41 | 0.5.0 harnesses the concept of a pipeline that gives users the ability to flexibly compose plugins to satisfy their
42 | custom deployment needs.
43 |
44 | NOTE: 0.5.0 is a major rewrite and shift from the 0.4.x series and hence introduces a number of breaking changes.
45 |
46 | For more information on the ember-cli-deploy v0.5.0, how to use it and how to write plugins please see the brand new
47 | [documentation site](http://ember-cli-deploy.com).
48 |
49 | ## [0.5.0-beta.4](https://github.com/ember-cli/ember-cli-deploy/tree/0.5.0-beta.4) (2015-10-24)
50 | [Full Changelog](https://github.com/ember-cli/ember-cli-deploy/compare/v0.5.0-beta.3...0.5.0-beta.4)
51 |
52 | **Merged pull requests:**
53 |
54 | - Add a `--verbose` option to commands, make default output quiet, and … [\#266](https://github.com/ember-cli/ember-cli-deploy/pull/266) ([lukemelia](https://github.com/lukemelia))
55 |
56 | ## [0.5.0-beta.3](https://github.com/ember-cli/ember-cli-deploy/tree/0.5.0-beta.3) (2015-10-22)
57 | [Full Changelog](https://github.com/ember-cli/ember-cli-deploy/compare/v0.5.0-beta.2...0.5.0-beta.3)
58 |
59 | This release is expected to be the last beta before 0.5.0. We're happy to welcome
60 | @ghedamat to the core team and are grateful for his many contributions to
61 | documentation this cycle.
62 |
63 | Thanks to everyone who took time to contribute to this release!
64 |
65 | #### Community Contributions
66 |
67 | - add postInstall message [\#256](https://github.com/ember-cli/ember-cli-deploy/pull/256) ([ghedamat](https://github.com/ghedamat))
68 | - Correct typo; commandOptions are a local var [\#255](https://github.com/ember-cli/ember-cli-deploy/pull/255) ([kategengler](https://github.com/kategengler))
69 | - Make --deploy-config-path accept absolute paths [\#253](https://github.com/ember-cli/ember-cli-deploy/pull/253) ([dschmidt](https://github.com/dschmidt))
70 | - Update warning message url when no plugins installed [\#234](https://github.com/ember-cli/ember-cli-deploy/pull/234) ([achambers](https://github.com/achambers))
71 | - Guard postBuild hook [\#232](https://github.com/ember-cli/ember-cli-deploy/pull/232) ([lukemelia](https://github.com/lukemelia))
72 | - Merge 0.4.x [\#228](https://github.com/ember-cli/ember-cli-deploy/pull/228) ([lukemelia](https://github.com/lukemelia))
73 | - Give warning when no plugins are installed [\#225](https://github.com/ember-cli/ember-cli-deploy/pull/225) ([achambers](https://github.com/achambers))
74 | - Add support for postBuildDeployHook [\#222](https://github.com/ember-cli/ember-cli-deploy/pull/222) ([ghedamat](https://github.com/ghedamat))
75 |
76 | ### 0.5.0-beta.2 (June 14, 2015)
77 |
78 | #### Community Contributions
79 |
80 | - [#189](https://github.com/ember-cli/ember-cli-deploy/pull/189) [DOCUMENTATION] Upgrade instructions for plugin authors (and a bit for end users) [@duizendnegen/feature](https://github.com/duizendnegen/feature)
81 | - [#196](https://github.com/ember-cli/ember-cli-deploy/pull/196) [BUGFIX] Fix bug where fingerprint options in deploy.js don't work [@achambers](https://github.com/achambers)
82 | - [#197](https://github.com/ember-cli/ember-cli-deploy/pull/197) Remove unused code and commands deprecated in 0.4.0. [@yapplabs](https://github.com/yapplabs)
83 | - [#206](https://github.com/ember-cli/ember-cli-deploy/pull/206) Remove modifying fingerprint options on `included` hook [@achambers](https://github.com/achambers)
84 | - [#207](https://github.com/ember-cli/ember-cli-deploy/pull/207) Add 'prepare' hooks to pipeline [@achambers](https://github.com/achambers)
85 | - [#208](https://github.com/ember-cli/ember-cli-deploy/pull/208) [DOCUMENTATION] Update RFC with prepare hook [@achambers](https://github.com/achambers)
86 | - [#210](https://github.com/ember-cli/ember-cli-deploy/pull/210) add setup and teardown hooks to all commands [@ghedamat](https://github.com/ghedamat)
87 | - [#212](https://github.com/ember-cli/ember-cli-deploy/pull/212) [DOCUMENTATION] Update description of hooks in rfc [@ghedamat](https://github.com/ghedamat)
88 | - [#213](https://github.com/ember-cli/ember-cli-deploy/pull/213) Change deprecated require 'ember-cli/lib/errors/silent' to 'silent-error' [@dukex](https://github.com/dukex)
89 |
90 | ### 0.4.3 (July 12, 2015)
91 |
92 | This release fixes problems with the silent-error package used by
93 | `ember-cli-deploy` internally, brings improvements for the activate task and
94 | makes it possible to configure the build path that `ember-cli-deploy` uses to
95 | store files before uploading.
96 |
97 | Thanks to everyone who took time to contribute to this release!
98 |
99 | #### Community Contributions
100 |
101 | - [#156](https://github.com/ember-cli/ember-cli-deploy/pull/156) Fix `_materialize` using wrong this context [@jeffhertzler](https://github.com/jeffhertzler)
102 | - [#158](https://github.com/ember-cli/ember-cli-deploy/pull/158) added ember-cli-rest-index adapter to list of adapter [@leojh](https://github.com/leojh)
103 | - [#159](https://github.com/ember-cli/ember-cli-deploy/pull/159) Update video link in README. [@blimmer](https://github.com/blimmer)
104 | - [#161](https://github.com/ember-cli/ember-cli-deploy/pull/161) Remove leading '+' es from code of conduct [@pangratz](https://github.com/pangratz)
105 | - [#164](https://github.com/ember-cli/ember-cli-deploy/pull/164) [#151] read manifestPrefix from config [@pavloo](https://github.com/pavloo)
106 | - [#170](https://github.com/ember-cli/ember-cli-deploy/pull/170) Add ability to configure build paths, defaulting to tmp/deploy-dist/. [@duizendnegen/feature](https://github.com/duizendnegen/feature)
107 | - [#171](https://github.com/ember-cli/ember-cli-deploy/pull/171) Update ember cli 0.2.7 and fix npm warnings [@ghedamat](https://github.com/ghedamat)
108 | - [#175](https://github.com/ember-cli/ember-cli-deploy/pull/175) Manifest prefix for activate task. [@juggy](https://github.com/juggy)
109 | - [#176](https://github.com/ember-cli/ember-cli-deploy/pull/176) Use silent-error NPM Package [@jherdman](https://github.com/jherdman)
110 | - [#178](https://github.com/ember-cli/ember-cli-deploy/pull/178) Use SilentError to log errors when parsing config. [@ember-cli](https://github.com/ember-cli)
111 |
112 | Thank you to all who took the time to contribute!
113 |
114 | ### 0.4.2 (June 14, 2015)
115 |
116 | This release fixes asset upload issues with io.js, adds the possibility for
117 | index adapters to support multiple files and adds a configuration option to
118 | exclude asset files from deployment.
119 |
120 | #### Community Contributions
121 |
122 | - [#140](https://github.com/ember-cli/ember-cli-deploy/pull/140) Link to ember-deploy-couchbase. [@waltznetworks](https://github.com/waltznetworks)
123 | - [#113](https://github.com/ember-cli/ember-cli-deploy/pull/113) Provide better error support for missing environment config [@achambers](https://github.com/achambers)
124 | - [#115](https://github.com/ember-cli/ember-cli-deploy/pull/115) Changed package to be able to run tests on windows. [@Twinkletoes](https://github.com/Twinkletoes)
125 | - [#119](https://github.com/ember-cli/ember-cli-deploy/pull/119) Stub active, list, createTag UnknownAdapter methods [@waltznetworks](https://github.com/waltznetworks)
126 | - [#120](https://github.com/ember-cli/ember-cli-deploy/pull/120) [DOCUMENTATION] Make Sinatra example a bit more secure [@elucid](https://github.com/elucid)
127 | - [#124](https://github.com/ember-cli/ember-cli-deploy/pull/124) Index Adapter support for multiple files [@Ahalogy](https://github.com/Ahalogy)
128 | - [#128](https://github.com/ember-cli/ember-cli-deploy/pull/128) Link to custom adapters section for quick ref [@jayphelps](https://github.com/jayphelps)
129 | - [#129](https://github.com/ember-cli/ember-cli-deploy/pull/129) Make a callout easily actionable [@jorgedavila25](https://github.com/jorgedavila25)
130 | - [#141](https://github.com/ember-cli/ember-cli-deploy/pull/141) Add configuration option to exclude asset files from being deployed. [@yapplabs](https://github.com/yapplabs)
131 | - [#142](https://github.com/ember-cli/ember-cli-deploy/pull/142) Test against stable node versions [@yapplabs](https://github.com/yapplabs)
132 | - [#144](https://github.com/ember-cli/ember-cli-deploy/pull/144) Resolve JSHint error on deploy.js blueprint [@blimmer](https://github.com/blimmer)
133 | - [#146](https://github.com/ember-cli/ember-cli-deploy/pull/146) Make io.js work [@trym](https://github.com/trym)
134 |
135 | Thank you to all who took the time to contribute!
136 |
137 | ### 0.4.1 (March 13, 2015)
138 |
139 | This release mainly revolves round fixing a bug around `child_process` and `execSync` compatability among the nodejs versions and platforms.
140 |
141 | #### Community Contributions
142 |
143 | - [#93](https://github.com/ember-cli/ember-cli-deploy/pull/93) [BUGFIX] execSync compat issue #92 [@joebartels](https://github.com/joebartels)
144 | - [#100](https://github.com/ember-cli/ember-cli-deploy/pull/100) [DOCS] Update config around production-like environments [@Soliah](https://github.com/Soliah)
145 |
146 | ### 0.4.0 (March 07, 2015)
147 |
148 | This release marks the merge of `achambers/ember-cli-deploy`, `LevelBossMike/ember-deploy` and `tedconf/ember-cli-front-end-builds` into the official `ember-cli-deploy`
149 |
150 | If you are upgrading from `achambers/ember-cli-deploy v0.0.6`, please follow these [miragtion steps](https://github.com/ember-cli/ember-cli-deploy/blob/master/MIGRATION_STEPS.md);
151 |
152 | #### Community Contributions
153 |
154 | - [#33](https://github.com/ember-cli/ember-cli-deploy/pull/33) [DOCS] Link to new S3 Index Adapter [@pootsbook](https://github.com/pootsbook)
155 | - [#65](https://github.com/ember-cli/ember-cli-deploy/pull/65) [DOCS] Update CodeClimate batch. [@LevelbossMike](https://github.com/LevelbossMike)
156 | - [#35](https://github.com/ember-cli/ember-cli-deploy/pull/35) [ENHANCEMENT] Match ember-cli's build command aliases by supporting --prod and --dev [@jamesfid](https://github.com/jamesfid)
157 | - [#36](https://github.com/ember-cli/ember-cli-deploy/pull/36) [ENHANCEMENT] Allow custom config file via --deploy-config-file. [@yapplabs](https://github.com/yapplabs)
158 | - [#63](https://github.com/ember-cli/ember-cli-deploy/pull/63) [BUGFIX] Fix regression to the type of object that was being passed to assets adapters as “config”. [@yapplabs](https://github.com/yapplabs)
159 | - [#56](https://github.com/ember-cli/ember-cli-deploy/pull/56) [BREAKING ENHANCEMENT] Deprecated commands no longer needed. [@ember-cli](https://github.com/ember-cli)
160 | - [#40](https://github.com/ember-cli/ember-cli-deploy/pull/40) [BUGFIX] Removed erroneous conflict markers. [@jamesfid](https://github.com/jamesfid)
161 | - [#58](https://github.com/ember-cli/ember-cli-deploy/pull/58) [ENHANCEMENT] Add blueprint to auto generate config/deploy.js [@ember-cli](https://github.com/ember-cli)
162 | - [#57](https://github.com/ember-cli/ember-cli-deploy/pull/57) [DEPRECATION] Deprecate use of deploy.json in favor of config/deploy.js. Closes #51 [@yapplabs](https://github.com/yapplabs)
163 | - [#66](https://github.com/ember-cli/ember-cli-deploy/pull/66) [DOCS] Add note for fingerprint.prepend and staging envs. [@LevelbossMike](https://github.com/LevelbossMike)
164 | - [#74](https://github.com/ember-cli/ember-cli-deploy/pull/74) [BREAKING DEPRECATION] Revert Unsupported Commands back to Deprecated for 0.4.0 release [@danshultz](https://github.com/danshultz)
165 | - [#85](https://github.com/ember-cli/ember-cli-deploy/pull/85) [DEPRECATION] npm post install message for users of v0.0.6 [@achambers](https://github.com/achambers)
166 |
167 | ### 0.3.1 (February 08, 2015)
168 |
169 | - [#32](https://github.com/LevelbossMike/ember-deploy/pull/32) add support for execSync in node >= 0.11 [@kriswill](https://github.com/kriswill)
170 |
171 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
172 |
--------------------------------------------------------------------------------
/node-tests/unit/tasks/pipeline-test.js:
--------------------------------------------------------------------------------
1 | var Promise = require('ember-cli/lib/ext/promise');
2 | var PipelineTask = require('../../../lib/tasks/pipeline');
3 | var Pipeline = require('../../../lib/models/pipeline');
4 | var expect = require('../../helpers/expect');
5 |
6 | describe('PipelineTask', function() {
7 | var mockProject = {addons: [], root: process.cwd()};
8 | var mockConfig = {};
9 | var mockUi;
10 | beforeEach(function(){
11 | mockUi = { write: function() {}, writeError: function() {} };
12 | });
13 |
14 | describe('creating and setting up a new instance', function() {
15 | it ('raises an error if project is not provided', function() {
16 | var fn = function() {
17 | new PipelineTask({});
18 | };
19 |
20 | expect(fn).to.throw('No project passed to pipeline task');
21 | });
22 |
23 | it ('raises an error if ui is not provided', function() {
24 | var fn = function() {
25 | new PipelineTask({
26 | project: mockProject,
27 | config: mockConfig
28 | });
29 | };
30 |
31 | expect(fn).to.throw('No ui passed to pipeline task');
32 | });
33 |
34 | it('raises an error if deployTarget is not provided', function() {
35 | var fn = function() {
36 | new PipelineTask({
37 | project: mockProject,
38 | ui: mockUi,
39 | config: mockConfig
40 | });
41 | };
42 |
43 | expect(fn).to.throw('You need to provide a deployTarget: `ember deploy production`');
44 | });
45 |
46 | it('raises an error if config is not provided', function() {
47 | var fn = function() {
48 | new PipelineTask({
49 | project: mockProject,
50 | ui: mockUi,
51 | deployTarget: 'development'
52 | });
53 | };
54 |
55 | expect(fn).to.throw('No config passed to pipeline task');
56 | });
57 |
58 | describe('registering addons with the pipeline', function() {
59 | it('registers addons with ember-cli-deploy-plugin keyword', function() {
60 | var project = {
61 | name: function() {return 'test-project';},
62 | root: process.cwd(),
63 | addons: [
64 | {
65 | name: 'ember-cli-deploy-test-plugin',
66 | pkg: {
67 | keywords: [
68 | 'ember-cli-deploy-plugin'
69 | ]
70 | },
71 | createDeployPlugin: function() {
72 | return {
73 | name: 'test-plugin',
74 | willDeploy: function() {},
75 | upload: function() {}
76 | };
77 | }
78 | }
79 | ]
80 | };
81 |
82 | var task = new PipelineTask({
83 | project: project,
84 | ui: mockUi,
85 | deployTarget: 'development',
86 | config: mockConfig,
87 | hooks: ['willDeploy', 'upload']
88 | });
89 | task.setup();
90 | var registeredHooks = task._pipeline._pipelineHooks;
91 |
92 | expect(registeredHooks.willDeploy[0].name).to.eq('test-plugin');
93 | expect(registeredHooks.willDeploy[0].fn).to.be.a('function');
94 | expect(registeredHooks.upload[0].name).to.eq('test-plugin');
95 | expect(registeredHooks.upload[0].fn).to.be.a('function');
96 | });
97 |
98 | it('does not register addons missing the ember-cli-deploy-plugin keyword', function() {
99 | var project = {
100 | name: function() {return 'test-project';},
101 | root: process.cwd(),
102 | addons: [
103 | {
104 | name: 'ember-cli-deploy-test-plugin',
105 | pkg: {
106 | keywords: [
107 | 'some-other-plugin'
108 | ]
109 | },
110 | createDeployPlugin: function() {
111 | return {
112 | willDeploy: function() {},
113 | upload: function() {}
114 | };
115 | }
116 | }
117 | ]
118 | };
119 |
120 | var task = new PipelineTask({
121 | project: project,
122 | ui: mockUi,
123 | deployTarget: 'development',
124 | config: mockConfig,
125 | hooks: ['willDeploy', 'upload']
126 | });
127 |
128 | var registeredHooks = task._pipeline._pipelineHooks;
129 |
130 | expect(registeredHooks.willDeploy[0]).to.be.undefined;
131 | });
132 |
133 | it('does not register addons that don\'t implement the createDeployPlugin function', function() {
134 | var project = {
135 | name: function() {return 'test-project';},
136 | root: process.cwd(),
137 | addons: [
138 | {
139 | name: 'ember-cli-deploy-test-plugin',
140 | pkg: {
141 | keywords: [ ]
142 | },
143 | someOtherFunction: function() {
144 | return {
145 | willDeploy: function() {}
146 | };
147 | }
148 | }
149 | ]
150 | };
151 |
152 | var task = new PipelineTask({
153 | project: project,
154 | ui: mockUi,
155 | deployTarget: 'development',
156 | config: mockConfig,
157 | hooks: ['willDeploy', 'upload']
158 | });
159 |
160 | var registeredHooks = task._pipeline._pipelineHooks;
161 |
162 | expect(registeredHooks.willDeploy[0]).to.be.undefined;
163 | });
164 |
165 | it('registers configured addons only, if addons configuration is present', function () {
166 | var project = {
167 | name: function() {return 'test-project';},
168 | root: process.cwd(),
169 | addons: [
170 | {
171 | name: 'ember-cli-deploy-foo-plugin',
172 | pkg: {
173 | keywords: [
174 | 'ember-cli-deploy-plugin'
175 | ]
176 | },
177 | createDeployPlugin: function() {
178 | return {
179 | name: 'foo-plugin',
180 | willDeploy: function() {},
181 | upload: function() {}
182 | };
183 | }
184 | },
185 | {
186 | name: 'ember-cli-deploy-bar-plugin',
187 | pkg: {
188 | keywords: [
189 | 'ember-cli-deploy-plugin'
190 | ]
191 | },
192 | createDeployPlugin: function() {
193 | return {
194 | name: 'bar-plugin',
195 | willDeploy: function() {},
196 | upload: function() {}
197 | };
198 | }
199 | }
200 | ]
201 | };
202 |
203 | var task = new PipelineTask({
204 | project: project,
205 | ui: mockUi,
206 | deployTarget: 'development',
207 | config: { plugins: ['foo-plugin'] },
208 | hooks: ['willDeploy', 'upload']
209 | });
210 | task.setup();
211 | var registeredHooks = task._pipeline._pipelineHooks;
212 |
213 | expect(registeredHooks.willDeploy.length).to.equal(1);
214 | expect(registeredHooks.willDeploy[0].name).to.eq('foo-plugin');
215 | expect(registeredHooks.willDeploy[0].fn).to.be.a('function');
216 | expect(registeredHooks.upload.length).to.equal(1);
217 | expect(registeredHooks.upload[0].name).to.eq('foo-plugin');
218 | expect(registeredHooks.upload[0].fn).to.be.a('function');
219 | });
220 | it('registers dependent plugin addons of a plugin pack addon designated by the ember-cli-deploy-plugin-pack keyword', function() {
221 | var project = {
222 | name: function() {return 'test-project';},
223 | root: process.cwd(),
224 | addons: [
225 | {
226 | name: 'ember-cli-deploy-test-plugin-pack',
227 | pkg: {
228 | keywords: [
229 | 'ember-cli-deploy-plugin-pack'
230 | ]
231 | },
232 | addons: [
233 | {
234 | name: 'ember-cli-deploy-test-plugin',
235 | pkg: {
236 | keywords: [
237 | 'ember-cli-deploy-plugin'
238 | ]
239 | },
240 | createDeployPlugin: function() {
241 | return {
242 | name: 'test-plugin',
243 | willDeploy: function() {},
244 | upload: function() {}
245 | };
246 | }
247 | }
248 | ]
249 | }
250 | ]
251 | };
252 |
253 | var task = new PipelineTask({
254 | project: project,
255 | ui: mockUi,
256 | deployTarget: 'development',
257 | config: mockConfig,
258 | hooks: ['willDeploy', 'upload']
259 | });
260 | task.setup();
261 | var registeredHooks = task._pipeline._pipelineHooks;
262 |
263 | expect(registeredHooks.willDeploy.length).to.eq(1);
264 | expect(registeredHooks.willDeploy[0].name).to.eq('test-plugin');
265 | expect(registeredHooks.willDeploy[0].fn).to.be.a('function');
266 | expect(registeredHooks.upload.length).to.eq(1);
267 | expect(registeredHooks.upload[0].name).to.eq('test-plugin');
268 | expect(registeredHooks.upload[0].fn).to.be.a('function');
269 | });
270 | });
271 | });
272 |
273 | describe('executing the pipeline task', function() {
274 | it ('executes the pipeline, passing in the deployment context', function() {
275 | var pipelineExecuted = false;
276 | var pipelineContext;
277 |
278 | var project = {
279 | name: function() {return 'test-project';},
280 | root: process.cwd(),
281 | addons: [ ]
282 | };
283 |
284 | var task = new PipelineTask({
285 | project: project,
286 | ui: mockUi,
287 | deployTarget: 'development',
288 | config: { build: { environment: 'development' }},
289 | commandOptions: {revision: '123abc'},
290 | hooks: ['willDeploy', 'upload'],
291 | pipeline: {
292 | execute: function(context) {
293 | pipelineExecuted = true;
294 | pipelineContext = context;
295 | return Promise.resolve();
296 | }
297 | }
298 | });
299 |
300 | return expect(task.run()).to.be.fulfilled
301 | .then(function() {
302 | expect(pipelineExecuted).to.be.true;
303 | expect(pipelineContext.ui).to.eq(mockUi);
304 | expect(pipelineContext.project).to.eq(project);
305 | expect(pipelineContext.deployTarget).to.eq('development');
306 | expect(pipelineContext.config.build.environment).to.eq('development');
307 | expect(pipelineContext.commandOptions.revision).to.eq('123abc');
308 | });
309 | });
310 |
311 | it('executes the pipeline, logging at a verbose level', function() {
312 | var logOutput = "";
313 | mockUi = {
314 | verbose: true,
315 | write: function(s) {
316 | logOutput = logOutput + s;
317 | },
318 | writeError: function(s) {
319 | logOutput = logOutput + s + "\n";
320 | }
321 | };
322 | var project = {
323 | name: function() {return 'test-project';},
324 | root: process.cwd(),
325 | addons: [
326 | {
327 | name: 'ember-cli-deploy-test-plugin',
328 | pkg: {
329 | keywords: [
330 | 'ember-cli-deploy-plugin'
331 | ]
332 | },
333 | createDeployPlugin: function() {
334 | return {
335 | name: 'test-plugin',
336 | willDeploy: function() {
337 | },
338 | upload: function() {}
339 | };
340 | }
341 | }
342 | ]
343 | };
344 |
345 | var task = new PipelineTask({
346 | project: project,
347 | ui: mockUi,
348 | deployTarget: 'development',
349 | config: mockConfig,
350 | commandOptions: {revision: '123abc'},
351 | hooks: ['willDeploy', 'upload'],
352 | pipeline: new Pipeline(['willDeploy', 'upload'], {
353 | ui: mockUi
354 | })
355 | });
356 |
357 | task.setup();
358 | return expect(task.run()).to.be.fulfilled.then(function() {
359 | var logLines = logOutput.split("\n");
360 | expect(logLines[ 0]).to.eq("\u001b[34mRegistering hook -> willDeploy[test-plugin]");
361 | expect(logLines[ 1]).to.eq("\u001b[39m\u001b[34mRegistering hook -> upload[test-plugin]");
362 | expect(logLines[ 2]).to.eq("\u001b[39m\u001b[34mRegistering hook -> willDeploy[test-plugin]");
363 | expect(logLines[ 3]).to.eq("\u001b[39m\u001b[34mRegistering hook -> upload[test-plugin]");
364 | expect(logLines[ 4]).to.eq("\u001b[39m\u001b[34mExecuting pipeline");
365 | expect(logLines[ 5]).to.eq("\u001b[39m\u001b[34m|");
366 | expect(logLines[ 6]).to.eq("\u001b[39m\u001b[34m+- willDeploy");
367 | expect(logLines[ 7]).to.eq("\u001b[39m\u001b[34m| |");
368 | expect(logLines[ 8]).to.eq("\u001b[39m\u001b[34m| +- test-plugin");
369 | expect(logLines[ 9]).to.eq("\u001b[39m\u001b[34m| |");
370 | expect(logLines[10]).to.eq("\u001b[39m\u001b[34m| +- test-plugin");
371 | expect(logLines[11]).to.eq("\u001b[39m\u001b[34m|");
372 | expect(logLines[12]).to.eq("\u001b[39m\u001b[34m+- upload");
373 | expect(logLines[13]).to.eq("\u001b[39m\u001b[34m| |");
374 | expect(logLines[14]).to.eq("\u001b[39m\u001b[34m| +- test-plugin");
375 | expect(logLines[15]).to.eq("\u001b[39m\u001b[34m| |");
376 | expect(logLines[16]).to.eq("\u001b[39m\u001b[34m| +- test-plugin");
377 | expect(logLines[17]).to.eq("\u001b[39m\u001b[34m|");
378 | expect(logLines[18]).to.eq("\u001b[39m\u001b[34mPipeline complete");
379 | });
380 | });
381 | });
382 |
383 | describe('plugin aliases are correctly handled', function() {
384 | it('passes correct name to single plugin alias', function () {
385 | var correctAliasUsed = false;
386 | var project = {
387 | name: function() {return 'test-project';},
388 | root: process.cwd(),
389 | addons: [
390 | {
391 | name: 'ember-cli-deploy-foo-plugin',
392 | pkg: {
393 | keywords: [
394 | 'ember-cli-deploy-plugin'
395 | ]
396 | },
397 | createDeployPlugin: function(options) {
398 | correctAliasUsed = (options.name === 'bar-alias');
399 | }
400 | }
401 | ]
402 | };
403 |
404 | var task = new PipelineTask({
405 | project: project,
406 | ui: mockUi,
407 | deployTarget: 'staging',
408 | config: { plugins: ['foo-plugin:bar-alias'] },
409 | pipeline: {
410 | hookNames: function () {
411 | return [];
412 | }
413 | }
414 | });
415 |
416 | task.setup();
417 | expect(correctAliasUsed).to.be.true;
418 | });
419 |
420 | it('passes correct name to multiple instances of the same plugin', function () {
421 | var correctAliasUsed = {
422 | 'foo-plugin': false, // plugin without alias
423 | 'bar-alias': false, // first alias
424 | 'doo-alias': false // second alias
425 | };
426 |
427 | var project = {
428 | name: function() {return 'test-project';},
429 | root: process.cwd(),
430 | addons: [
431 | {
432 | name: 'ember-cli-deploy-foo-plugin',
433 | pkg: {
434 | keywords: [
435 | 'ember-cli-deploy-plugin'
436 | ]
437 | },
438 | createDeployPlugin: function(options) {
439 | correctAliasUsed[options.name] = true;
440 | }
441 | }
442 | ]
443 | };
444 |
445 | var task = new PipelineTask({
446 | project: project,
447 | ui: mockUi,
448 | deployTarget: 'staging',
449 | config: { plugins: ['foo-plugin', 'foo-plugin:bar-alias', 'foo-plugin:doo-alias'] },
450 | pipeline: {
451 | hookNames: function () {
452 | return [];
453 | }
454 | }
455 | });
456 |
457 | task.setup();
458 | expect(correctAliasUsed['foo-plugin']).to.be.true;
459 | expect(correctAliasUsed['bar-alias']).to.be.true;
460 | expect(correctAliasUsed['doo-alias']).to.be.true;
461 | });
462 |
463 | it('throws error on non-existent plugin in whitelist and appends them to err.unavailablePlugins', function () {
464 | var project = {
465 | name: function() {return 'test-project';},
466 | root: process.cwd()
467 | };
468 |
469 | var task = new PipelineTask({
470 | project: project,
471 | ui: mockUi,
472 | deployTarget: 'production',
473 | config: { plugins: ['foo-plugin', 'foo-plugin:bar-alias', 'foo-plugin:doo-alias'] },
474 | deployConfigPath: 'node-tests/fixtures/config/deploy-for-addons-config-test-with-aliases.js',
475 | });
476 |
477 | try {
478 | task.setup();
479 | expect(false).to.be.true;
480 | } catch(err) {
481 | expect(Object.keys(err.unavailablePlugins).length).to.equal(3);
482 | }
483 | });
484 | });
485 | });
486 |
--------------------------------------------------------------------------------