├── .bowerrc ├── .editorconfig ├── .ember-cli ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── .watchmanconfig ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── addon ├── .gitkeep └── services │ └── raven.js ├── app ├── .gitkeep └── services │ └── raven.js ├── bower.json ├── circle.yml ├── config ├── ember-try.js └── environment.js ├── index.js ├── package-lock.json ├── package.json ├── testem.json ├── tests ├── .jshintrc ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ └── components │ │ │ └── .gitkeep │ ├── config │ │ └── environment.js │ └── public │ │ ├── crossdomain.xml │ │ └── robots.txt ├── helpers │ ├── resolver.js │ └── start-app.js ├── index.html ├── runner.js ├── test-helper.js └── unit │ ├── .gitkeep │ └── index-nodetest.js └── vendor └── .gitkeep /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "analytics": false 4 | } 5 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | env: 13 | - EMBER_TRY_SCENARIO=default 14 | - EMBER_TRY_SCENARIO=ember-release 15 | - EMBER_TRY_SCENARIO=ember-beta 16 | - EMBER_TRY_SCENARIO=ember-canary 17 | 18 | matrix: 19 | fast_finish: true 20 | allow_failures: 21 | - env: EMBER_TRY_SCENARIO=ember-canary 22 | 23 | before_install: 24 | - export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH 25 | - "npm config set spin false" 26 | - "npm install -g npm@^2" 27 | 28 | install: 29 | - npm install -g bower 30 | - npm install 31 | - bower install 32 | 33 | script: 34 | - ember try $EMBER_TRY_SCENARIO test 35 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ember-cli-deploy-sentry Changelog 2 | 3 | ### 0.7.0 4 | - Add ability to associate a commit with a release [#62] 5 | - Make it obvious where to get the keys for hosted accounts [#59] 6 | - Update mock-fs to use more recent versions of Node.js [#55] 7 | - Upload on didPrepare hook to fix gzip issues [#52] 8 | 9 | ### 0.5.5 10 | - Remove jQuery usages [#45] 11 | 12 | ### 0.5.4 13 | - Remove sentryApiKey from required config params [#43] 14 | - Add strictSSL option for request [#43] 15 | 16 | ### 0.5.3 17 | - Remove to prevent IE10 from breaking [#40] 18 | 19 | ### 0.5.2 20 | - Fix deprecation warning for ember-cli/lib/ext/promise [#35] 21 | - Add config documentation [#33] 22 | 23 | ### 0.5.1 24 | - Remove documentation for `didDeployMessage` config option as its gone 25 | 26 | ### 0.5.0 27 | Thanks to @kmiyashiro, @Turbo87 and especially @duizendnegen for making this release happen! 28 | 29 | - Add bearer token authentication [#21] 30 | - Support ember-cli-deploy-plugin 0.2.9 [#27] 31 | 32 | ### 0.4.0 33 | 34 | - Gracefully handle reuploading sourcemaps for releases with assigned issues 35 | - Several logging improvements 36 | - Fix releaseMetaName not being respected by raven service 37 | - Several Documentation fixes 38 | 39 | ### 0.3.1 40 | 41 | - Fix possibly broken tarball 42 | 43 | ### 0.3.0 44 | 45 | - Rate limit uploaded sourcemaps 46 | - Update mock-fs dependency to support newer versions of node 47 | - Use prepare hook instead of didBuild hook to catch revision data 48 | - Fix windows issues by using form-data library directly 49 | 50 | ### 0.2.1 51 | 52 | - Fix logging 53 | 54 | ### 0.2.0 55 | 56 | - Add service for usage with ember-cli-sentry 57 | 58 | ### 0.1.0 59 | 60 | - Initial release. 61 | -------------------------------------------------------------------------------- /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-sentry [![Circle CI](https://circleci.com/gh/dschmidt/ember-cli-deploy-sentry/tree/master.svg?style=shield)](https://circleci.com/gh/dschmidt/ember-cli-deploy-sentry/tree/master) 2 | 3 | > An ember-cli-deploy-plugin to upload javascript sourcemaps to [Sentry][1]. 4 | 5 | [![](https://ember-cli-deploy.github.io/ember-cli-deploy-version-badges/plugins/ember-cli-deploy-sentry.svg)](http://ember-cli-deploy.github.io/ember-cli-deploy-version-badges/) 6 | 7 | ## What is an ember-cli-deploy plugin? 8 | 9 | A plugin is an addon that can be executed as a part of the ember-cli-deploy pipeline. A plugin will implement one or more of the ember-cli-deploy's pipeline hooks. 10 | 11 | For more information on what plugins are and how they work, please refer to the [Plugin Documentation][10]. 12 | 13 | ## Quick Start 14 | To get up and running quickly, do the following: 15 | 16 | - Ensure [ember-cli-deploy-build][11] is installed and configured. 17 | 18 | - Install this plugin 19 | 20 | ```bash 21 | $ ember install ember-cli-deploy-sentry 22 | ``` 23 | 24 | - For hosted accounts, generate your bearer/api key here: https://sentry.io/api/ 25 | - Place the following configuration into `config/deploy.js` 26 | 27 | ```javascript 28 | ENV.sentry = { 29 | // the URL or CDN your js assets are served from 30 | publicUrl: 'https://your.awesome.site', 31 | // the sentry install you're using, https://sentry.io for hosted accounts 32 | sentryUrl: 'https://sentry.your.awesome.site', 33 | sentryOrganizationSlug: 'AwesomeOrg', 34 | sentryProjectSlug: 'AwesomeProject', 35 | 36 | // One of: 37 | sentryApiKey: 'awesomeApiKey', 38 | // or 39 | sentryBearerApiKey: 'awesomeApiKey' 40 | } 41 | ``` 42 | - Integrate [raven-js][2] in your page 43 | 44 | Install [ember-cli-sentry](https://github.com/damiencaselli/ember-cli-sentry) but import the raven service from `ember-cli-deploy-sentry/services/raven`, 45 | which will automatically handle setting up the release version for you. Sentry needs this to find the correct sourcemap for an error that occurs. 46 | 47 | If you don't want to use `ember-cli-sentry` but set [raven-js][2] up manually see [Manual integration with raven-js](#manual-integration-with-raven-js). 48 | 49 | 50 | - Build sourcemaps in production environment 51 | 52 | `ember-cli` builds sourcemaps only in development environment by default. In order to build them always, 53 | just add the following to your `EmberApp` options. 54 | ``` 55 | sourcemaps: { 56 | enabled: true, 57 | extensions: ['js'] 58 | } 59 | ``` 60 | 61 | See also: [ember-cli documentation](http://www.ember-cli.com/user-guide/#source-map) 62 | 63 | 64 | - Run the pipeline 65 | 66 | ```bash 67 | $ ember deploy 68 | ``` 69 | 70 | ## Installation 71 | Run the following command in your terminal: 72 | 73 | ```bash 74 | ember install ember-cli-deploy-sentry 75 | ``` 76 | 77 | For general information on how to setup [Sentry][1] and [raven-js][2] you probably want to check out the official [Sentry Documentation][3] especially on [Sourcemaps][4]. 78 | 79 | ## ember-cli-deploy Hooks Implemented 80 | 81 | For detailed information on what plugin hooks are and how they work, please refer to the [Plugin Documentation][10]. 82 | 83 | - `configure` 84 | - `upload` 85 | - `didDeploy` 86 | 87 | ## Configuration Options 88 | 89 | For detailed information on how configuration of plugins works, please refer to the [Plugin Documentation][10]. 90 | 91 | ### publicUrl 92 | 93 | The public url to the root of where your assets are stored. For instance, if your assets are stored on Cloudfront, it would be `https://xxxx.cloudfront.net`. 94 | 95 | *Required* 96 | 97 | ### sentryUrl 98 | 99 | The url of the sentry installation that `ember-cli-deploy-sentry` shall upload sourcemaps and javascript files to. 100 | If you are deploying in your local network, keep in mind you might need to use the local hostname/IP address. 101 | 102 | *Required* 103 | 104 | ### sentryOrganizationSlug 105 | 106 | The slug of the organization you want to upload sourcemaps for. 107 | You can specify this in organization settings in sentry. 108 | 109 | *Required* 110 | 111 | ### sentryProjectSlug 112 | 113 | The slug of the project you want to upload sourcemaps for. 114 | You can specify this in project settings in sentry. 115 | 116 | *Required* 117 | 118 | ### sentryApiKey _or_ sentryBearerApiKey 119 | 120 | Either an HTTP Basic Auth username, or a bearer token. If you are uploading to the current Sentry API, use the latter. Use the former if you are using an older API. 121 | 122 | You can create the api key in your organization settings. Make sure it has the `project:write` privilege. 123 | 124 | *Required* 125 | 126 | ### distDir 127 | 128 | The root directory that all files matching the `filePattern` will be uploaded from. By default, this option will use the `distDir` property of the deployment context. 129 | 130 | *Default:* `context.distDir` 131 | 132 | ### filePattern 133 | 134 | `minimatch` expression that is used to determine which files should be uploaded from the `distDir`. 135 | 136 | *Default:* `/**/*.{js,map}` 137 | 138 | ### revisionKey 139 | 140 | The revision string that is used to create releases in sentry. 141 | 142 | *Default:* 143 | ```javascript 144 | revisionKey: function(context) { 145 | return context.revisionData && context.revisionData.revisionKey; 146 | } 147 | ``` 148 | 149 | ### revisionCommits 150 | 151 | An array of revision commits allows us to associate commits with this Sentry release. See the [Sentry docs here](https://docs.sentry.io/workflow/releases/?platform=browser#using-the-api). 152 | 153 | *Default:* 154 | ```javascript 155 | revisionCommits: undefined 156 | ``` 157 | 158 | *Examples:* 159 | ```javascript 160 | revisionCommits: function(context) { 161 | return [{ 162 | repository:"owner-name/repo-name", // required 163 | id:"2da95dfb052f477380608d59d32b4ab9" // required 164 | }] 165 | } 166 | ``` 167 | 168 | ### enableRevisionTagging 169 | 170 | Enable adding a meta tag with the current revisionKey into the head of your `index.html`. 171 | 172 | *Default* true 173 | 174 | ### replaceFiles 175 | 176 | At deploy-time, the plugin will check your Sentry instance for an existing release under the current `revisionKey`. If a release is found and this is set to `true`, all existing files for the matching release will be deleted before the current build's files are uploaded to Sentry. If this is set to `false`, the files on Sentry will remain untouched and the just-built files will not be uploaded. 177 | 178 | *Default* true 179 | 180 | ## Prerequisites 181 | 182 | The following properties are expected to be present on the deployment `context` object: 183 | 184 | - `distDir` (provided by [ember-cli-deploy-build][11]) 185 | - `revisionData.revisionKey` (provided by [ember-cli-deploy-revision-data][12]) 186 | 187 | 188 | ## Manual integration with raven-js 189 | 190 | By default a meta tag with the key name `sentry:revision` is inserted in your index.html: 191 | ```html 192 | 193 | 194 | ``` 195 | 196 | When you setup [raven-js][2] you can retrieve it like this: 197 | 198 | ```javascript 199 | Raven.config({ 200 | release: document.querySelector("meta[name='sentry:revision']").content 201 | }); 202 | ``` 203 | 204 | If you only want to use the sourcemap upload functionality of `ember-cli-deploy-sentry`, you can disable automatic meta tag insertion completely by setting [enableRevisionTagging](#enableRevisionTagging) to `false`. 205 | 206 | 207 | Last but not least make sure to setup proper exception catching like [this](https://github.com/getsentry/raven-js/blob/master/plugins/ember.js). 208 | 209 | ## Running Tests 210 | 211 | - `npm test` 212 | 213 | ## TODO 214 | 215 | - use `context.distFiles` from [ember-cli-deploy-build][11] instead globbing distDir again? 216 | - automatically setup raven-js? If you want this, let me know. 217 | - add revision tagging file pattern 218 | - make meta name configurable and document `service.releaseMetaName` 219 | 220 | ### State 221 | 222 | It works. We use it in production at [Hatchet](https://hatchet.is). 223 | 224 | 225 | [1]: https://getsentry.com "Sentry" 226 | [2]: https://github.com/getsentry/raven-js "raven-js" 227 | [3]: https://docs.getsentry.com/on-premise/clients/javascript/ "Sentry Documentation for Javascript clients" 228 | [4]: https://docs.getsentry.com/on-premise/clients/javascript/sourcemaps/ "Sentry Documentation for Javascript Sourcemaps" 229 | 230 | [10]: http://ember-cli.github.io/ember-cli-deploy/plugins "Plugin Documentation" 231 | [11]: https://github.com/zapnito/ember-cli-deploy-build "ember-cli-deploy-build" 232 | [12]: https://github.com/zapnito/ember-cli-deploy-revision-data "ember-cli-deploy-revision-data" 233 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/addon/.gitkeep -------------------------------------------------------------------------------- /addon/services/raven.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import RavenService from 'ember-cli-sentry/services/raven'; 3 | 4 | export default RavenService.extend({ 5 | releaseMetaName: 'sentry:revision', 6 | release: Ember.computed('releaseMetaName', { 7 | get: function() { 8 | return document.querySelector(`meta[name='${this.get('releaseMetaName')}']`).content; 9 | } 10 | }) 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/app/.gitkeep -------------------------------------------------------------------------------- /app/services/raven.js: -------------------------------------------------------------------------------- 1 | import RavenService from 'ember-cli-deply-sentry/services/raven'; 2 | 3 | export default RavenService; 4 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-deploy-sentry", 3 | "dependencies": { 4 | "ember": "1.13.3", 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-data": "1.13.5", 8 | "ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5", 9 | "ember-qunit": "0.4.1", 10 | "ember-qunit-notifications": "0.0.7", 11 | "ember-resolver": "~0.1.18", 12 | "jquery": "^1.11.1", 13 | "loader.js": "ember-cli/loader.js#3.2.0", 14 | "qunit": "~1.17.1" 15 | } 16 | } -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 0.12.0 4 | 5 | dependencies: 6 | pre: 7 | - npm install -g bower 8 | override: 9 | - npm i 10 | - bower i 11 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | scenarios: [ 3 | { 4 | name: 'default', 5 | dependencies: { } 6 | }, 7 | { 8 | name: 'ember-release', 9 | dependencies: { 10 | 'ember': 'components/ember#release' 11 | }, 12 | resolutions: { 13 | 'ember': 'release' 14 | } 15 | }, 16 | { 17 | name: 'ember-beta', 18 | dependencies: { 19 | 'ember': 'components/ember#beta' 20 | }, 21 | resolutions: { 22 | 'ember': 'beta' 23 | } 24 | }, 25 | { 26 | name: 'ember-canary', 27 | dependencies: { 28 | 'ember': 'components/ember#canary' 29 | }, 30 | resolutions: { 31 | 'ember': 'canary' 32 | } 33 | } 34 | ] 35 | }; 36 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(/* environment, appConfig */) { 4 | return { }; 5 | }; 6 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 'use strict'; 3 | 4 | var RSVP = require('rsvp'); 5 | var DeployPluginBase = require('ember-cli-deploy-plugin'); 6 | var SilentError = require('silent-error'); 7 | var glob = require("glob"); 8 | var urljoin_ = require("url-join"); 9 | var request = require('request-promise'); 10 | var path = require('path'); 11 | var fs = require('fs'); 12 | var throat = require('throat'); 13 | var parse = require('parse-link-header'); 14 | 15 | var urljoin = function(...args) { 16 | return urljoin_(...args).split('\\').join('/'); 17 | } 18 | 19 | module.exports = { 20 | name: 'ember-cli-deploy-sentry', 21 | 22 | contentFor: function(type/*, config*/) { 23 | if (type === 'head-footer') { 24 | return ''; 25 | } 26 | }, 27 | 28 | createDeployPlugin: function(options) { 29 | var DeployPlugin = DeployPluginBase.extend({ 30 | name: options.name, 31 | defaultConfig: { 32 | distDir: function(context) { 33 | return context.distDir; 34 | }, 35 | filePattern: '/**/*.{js,map}', 36 | revisionKey: function(context) { 37 | return context.revisionData && context.revisionData.revisionKey; 38 | }, 39 | revisionCommits: undefined, 40 | enableRevisionTagging: true, 41 | replaceFiles: true, 42 | strictSSL: true, 43 | }, 44 | requiredConfig: ['publicUrl', 'sentryUrl', 'sentryOrganizationSlug', 'sentryProjectSlug', 'revisionKey'], 45 | 46 | prepare: function(context) { 47 | var isEnabled = this.readConfig('enableRevisionTagging'); 48 | if(!isEnabled) { 49 | return; 50 | } 51 | 52 | var revisionKey = this.readConfig('revisionKey'); 53 | if(!revisionKey) { 54 | return new SilentError("Could not find revision key to fingerprint Sentry revision with."); 55 | } 56 | 57 | // TODO instead of plainly reading index.html, minimatch 58 | // getConfig('revision patterns') on context.distFiles 59 | var indexPath = path.join(context.distDir, "index.html"); 60 | var index = fs.readFileSync(indexPath, 'utf8'); 61 | index = index.replace('', 62 | ''); 63 | fs.writeFileSync(indexPath, index); 64 | }, 65 | 66 | /** 67 | * Upload the sourcemaps to Sentry 68 | * 69 | * We intentionally use the semantically not quite correct `didPrepare` hook instead of `upload` to work around 70 | * an issue with ember-cli-deploy-gzip (and other compression plugins like -brotli or -compress), where gzipped 71 | * sourcemaps are uploaded that Sentry is not able to decompress automatically. By using a hook before `willUpload` 72 | * we will upload the still uncompressed files. 73 | * 74 | * See https://github.com/dschmidt/ember-cli-deploy-sentry/issues/26 and https://github.com/getsentry/sentry/issues/4566 75 | */ 76 | didPrepare: function(/* context */) { 77 | this.sentrySettings = { 78 | url: this.readConfig('sentryUrl'), 79 | publicUrl: this.readConfig('publicUrl'), 80 | organizationSlug: this.readConfig('sentryOrganizationSlug'), 81 | projectSlug: this.readConfig('sentryProjectSlug'), 82 | apiKey: this.readConfig('sentryApiKey'), 83 | bearerApiKey: this.readConfig('sentryBearerApiKey'), 84 | release: this.readConfig('revisionKey'), 85 | commits: this.readConfig('revisionCommits'), 86 | }; 87 | this.baseUrl = urljoin(this.sentrySettings.url, '/api/0/projects/', this.sentrySettings.organizationSlug, this.sentrySettings.projectSlug, '/releases/'); 88 | this.releaseUrl = urljoin(this.baseUrl, this.sentrySettings.release, '/'); 89 | 90 | if(!this.sentrySettings.release) { 91 | throw new SilentError('revisionKey setting is not available, either provide it manually or make sure the ember-cli-deploy-revision-data plugin is loaded'); 92 | } 93 | 94 | return this.doesReleaseExist(this.releaseUrl) 95 | .then(this.handleExistingRelease.bind(this)) 96 | .catch(this.createRelease.bind(this)); 97 | }, 98 | 99 | generateAuth: function() { 100 | var apiKey = this.sentrySettings.apiKey; 101 | var bearerApiKey = this.sentrySettings.bearerApiKey; 102 | if (bearerApiKey !== undefined) { 103 | return { bearer: bearerApiKey }; 104 | } 105 | return { user: apiKey }; 106 | }, 107 | 108 | doesReleaseExist: function(releaseUrl) { 109 | return request({ 110 | uri: releaseUrl, 111 | auth: this.generateAuth(), 112 | json: true, 113 | strictSSL: this.readConfig('strictSSL'), 114 | }); 115 | }, 116 | handleExistingRelease: function handleExistingRelease(response) { 117 | this.log('Release ' + response.version + ' exists.', {verbose: true}); 118 | this.log('Retrieving release files.', {verbose: true}); 119 | return this._getReleaseFiles().then(function(response) { 120 | if (this.readConfig('replaceFiles')) { 121 | this.log('Replacing files.', {verbose: true}); 122 | return RSVP.all(response.map(this._deleteFile, this)) 123 | .then(this._doUpload.bind(this)) 124 | .then(this._logFiles.bind(this, response)); 125 | } else { 126 | this.log('Leaving files alone.', {verbose: true}); 127 | return this._logFiles(response); 128 | } 129 | }.bind(this)); 130 | }, 131 | createRelease: function createRelease(error) { 132 | if (error.statusCode === 404) { 133 | this.log('Release does not exist. Creating.', {verbose: true}); 134 | } else if (error.statusCode === 400) { 135 | this.log('Bad Request. Not Continuing'); 136 | return RSVP.resolve(error.message); 137 | } 138 | 139 | var body = { 140 | version: this.sentrySettings.release 141 | }; 142 | if (this.sentrySettings.commits) { 143 | body.commits = this.sentrySettings.commits; 144 | } 145 | 146 | return request({ 147 | uri: this.baseUrl, 148 | method: 'POST', 149 | auth: this.generateAuth(), 150 | json: true, 151 | body: body, 152 | resolveWithFullResponse: true, 153 | strictSSL: this.readConfig('strictSSL'), 154 | }) 155 | .then(this._doUpload.bind(this)) 156 | .then(this._logFiles.bind(this)) 157 | .catch(function(err){ 158 | console.error(err); 159 | throw new SilentError('Creating release failed'); 160 | }); 161 | }, 162 | _doUpload: function doUpload() { 163 | return this._getFilesToUpload() 164 | .then(this._uploadFileList.bind(this)); 165 | }, 166 | _getFilesToUpload: function getFilesToUpload() { 167 | this.log('Generating file list for upload', {verbose: true}); 168 | var dir = this.readConfig('distDir'); 169 | var filePattern = this.readConfig('filePattern'); 170 | var pattern = path.join(dir, filePattern); 171 | return new RSVP.Promise(function(resolve, reject) { 172 | // options is optional 173 | glob(pattern, function (err, files) { 174 | if(err) { 175 | reject(err); 176 | } else { 177 | resolve(files); 178 | } 179 | }); 180 | }).then(function(files) { 181 | return files.map(function(file) { 182 | return path.relative(dir, file); 183 | }); 184 | }); 185 | }, 186 | _uploadFileList: function uploadFileList(files) { 187 | this.log('Beginning upload.', {verbose: true}); 188 | return RSVP.all(files.map(throat(5, this._uploadFile.bind(this)))) 189 | .then(this._getReleaseFiles.bind(this)); 190 | }, 191 | _uploadFile: function uploadFile(filePath) { 192 | var distDir = this.readConfig('distDir'); 193 | var fileName = path.join(distDir, filePath); 194 | 195 | var formData = { 196 | name: urljoin(this.sentrySettings.publicUrl, filePath), 197 | file: fs.createReadStream(fileName), 198 | }; 199 | 200 | return request({ 201 | uri: urljoin(this.releaseUrl, 'files/'), 202 | method: 'POST', 203 | auth: this.generateAuth(), 204 | formData: formData, 205 | strictSSL: this.readConfig('strictSSL'), 206 | }); 207 | }, 208 | _getReleaseFiles: function getReleaseFiles(options = {}) { 209 | return request({ 210 | uri: options.url || urljoin(this.releaseUrl, 'files/'), 211 | auth: this.generateAuth(), 212 | json: true, 213 | resolveWithFullResponse: true, 214 | strictSSL: this.readConfig('strictSSL'), 215 | }).then((response) => { 216 | var links = parse(response.headers.link); 217 | 218 | if (!links.next || links.next.results === 'false') { 219 | return response.body; 220 | } 221 | 222 | return this._getReleaseFiles({url: links.next.url}).then((results) => { 223 | return results.concat(response.body); 224 | }); 225 | }); 226 | }, 227 | _deleteFile: function deleteFile(file) { 228 | this.log('Deleting ' + file.name, {verbose: true}); 229 | return request({ 230 | uri: urljoin(this.releaseUrl, 'files/', file.id, '/'), 231 | method: 'DELETE', 232 | auth: this.generateAuth(), 233 | strictSSL: this.readConfig('strictSSL'), 234 | }); 235 | }, 236 | _logFiles: function logFiles(response) { 237 | this.log('Files known to sentry for this release', { verbose: true }); 238 | response.forEach(function(file) { this.log('✔ ' + file.name, { verbose: true }); }, this); 239 | }, 240 | 241 | didDeploy: function(/* context */){ 242 | var deployMessage = "Uploaded sourcemaps to sentry release: " 243 | + this.readConfig('sentryUrl') 244 | + '/' 245 | + this.readConfig('sentryOrganizationSlug') 246 | + '/' 247 | + this.readConfig('sentryProjectSlug') 248 | + '/releases/' 249 | + this.readConfig('revisionKey') 250 | + '/'; 251 | 252 | if (this.readConfig('revisionCommits')) { 253 | deployMessage += '\n\t' + 'Commits ' + this.readConfig('revisionCommits').map(commit => commit.id).join(', ') + ' associated with this release'; 254 | } 255 | 256 | this.log(deployMessage); 257 | } 258 | }); 259 | return new DeployPlugin(); 260 | } 261 | }; 262 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-deploy-sentry", 3 | "version": "0.7.0", 4 | "description": "Ember CLI Deploy plugin to upload javascript sourcemaps to sentry.", 5 | "directories": { 6 | "doc": "doc", 7 | "test": "tests" 8 | }, 9 | "scripts": { 10 | "start": "ember server", 11 | "build": "ember build", 12 | "test": "node tests/runner.js" 13 | }, 14 | "repository": "dschmidt/ember-cli-deploy-sentry", 15 | "engines": { 16 | "node": ">= 10.20.1" 17 | }, 18 | "author": "Dominik Schmidt ", 19 | "contributors": [ 20 | "Pepijn Schoen " 21 | ], 22 | "license": "MIT", 23 | "devDependencies": { 24 | "broccoli-asset-rev": "^2.0.2", 25 | "chai": "^2.2.0", 26 | "chai-as-promised": "^5.0.0", 27 | "ember-cli": "1.13.1", 28 | "ember-cli-app-version": "0.4.0", 29 | "ember-cli-content-security-policy": "0.4.0", 30 | "ember-cli-dependency-checker": "^1.0.0", 31 | "ember-cli-htmlbars": "0.7.9", 32 | "ember-cli-htmlbars-inline-precompile": "^0.1.1", 33 | "ember-cli-ic-ajax": "0.2.1", 34 | "ember-cli-inject-live-reload": "^1.3.0", 35 | "ember-cli-qunit": "0.3.15", 36 | "ember-cli-release": "0.2.3", 37 | "ember-cli-uglify": "^1.0.1", 38 | "ember-data": "1.13.5", 39 | "ember-disable-prototype-extensions": "^1.0.0", 40 | "ember-disable-proxy-controllers": "^1.0.0", 41 | "ember-export-application-global": "^1.0.2", 42 | "ember-try": "0.0.6", 43 | "mocha": "^2.2.4", 44 | "mock-fs": "^4.7.0", 45 | "rsvp": "^3.1.0" 46 | }, 47 | "keywords": [ 48 | "ember-addon", 49 | "ember-cli-deploy-plugin" 50 | ], 51 | "dependencies": { 52 | "ember-cli-babel": "^6.6.0", 53 | "ember-cli-deploy-plugin": "^0.2.0", 54 | "glob": "^5.0.14", 55 | "parse-link-header": "^1.0.1", 56 | "request-promise": "^4.2.5", 57 | "silent-error": "^1.0.0", 58 | "throat": "^2.0.0", 59 | "url-join": "0.0.1" 60 | }, 61 | "ember-addon": { 62 | "configPath": "tests/dummy/config", 63 | "after": [ 64 | "ember-cli-deploy-revision-data", 65 | "ember-cli-sentry" 66 | ] 67 | }, 68 | "volta": { 69 | "node": "10.20.1" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /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/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "location", 6 | "setTimeout", 7 | "$", 8 | "-Promise", 9 | "define", 10 | "console", 11 | "visit", 12 | "exists", 13 | "fillIn", 14 | "click", 15 | "keyEvent", 16 | "triggerEvent", 17 | "find", 18 | "findWithAssert", 19 | "wait", 20 | "DS", 21 | "andThen", 22 | "currentURL", 23 | "currentPath", 24 | "currentRouteName" 25 | ], 26 | "node": false, 27 | "browser": false, 28 | "boss": true, 29 | "curly": true, 30 | "debug": false, 31 | "devel": false, 32 | "eqeqeq": true, 33 | "evil": true, 34 | "forin": false, 35 | "immed": false, 36 | "laxbreak": false, 37 | "newcap": true, 38 | "noarg": true, 39 | "noempty": false, 40 | "nonew": false, 41 | "nomen": false, 42 | "onevar": false, 43 | "plusplus": false, 44 | "regexp": false, 45 | "undef": true, 46 | "sub": true, 47 | "strict": false, 48 | "white": false, 49 | "eqnull": true, 50 | "esnext": true, 51 | "unused": true 52 | } 53 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 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 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/tests/dummy/app/styles/app.css -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Welcome to Ember.js

2 | 3 | {{outlet}} -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/runner.js: -------------------------------------------------------------------------------- 1 | /*jshint globalstrict: true*/ 2 | 'use strict'; 3 | 4 | var glob = require('glob'); 5 | var Mocha = require('mocha'); 6 | 7 | var mocha = new Mocha({ 8 | reporter: 'spec' 9 | }); 10 | 11 | var arg = process.argv[2]; 12 | var root = 'tests/'; 13 | 14 | function addFiles(mocha, files) { 15 | glob.sync(root + files).forEach(mocha.addFile.bind(mocha)); 16 | } 17 | 18 | addFiles(mocha, '/**/*-nodetest.js'); 19 | 20 | if (arg === 'all') { 21 | addFiles(mocha, '/**/*-nodetest-slow.js'); 22 | } 23 | 24 | mocha.run(function(failures) { 25 | process.on('exit', function() { 26 | process.exit(failures); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | 6 | setResolver(resolver); 7 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/index-nodetest.js: -------------------------------------------------------------------------------- 1 | /*jshint globalstrict: true*/ 2 | 'use strict'; 3 | 4 | var RSVP = require('ember-cli/lib/ext/promise'); 5 | 6 | var assert = require('ember-cli/tests/helpers/assert'); 7 | 8 | var mockFs = require('mock-fs'); 9 | var fs = require('fs'); 10 | 11 | describe('deploySentry plugin', function() { 12 | var subject, mockUi, context; 13 | 14 | before(function() { 15 | subject = require('../../index'); 16 | }); 17 | 18 | beforeEach(function() { 19 | mockUi = { 20 | verbose: true, 21 | messages: [], 22 | write: function() { }, 23 | writeLine: function(message) { 24 | this.messages.push(message); 25 | } 26 | }; 27 | 28 | context = { 29 | distFiles: ['app.css', 'app.js'], 30 | ui: mockUi, 31 | config: { 32 | deploySentry: { 33 | publicUrl: 'http://example.org', 34 | sentryUrl: 'http://example.org', 35 | sentryOrganizationSlug: 'slug', 36 | sentryProjectSlug: 'slug', 37 | sentryApiKey: 'api-key', 38 | revisionKey: 'abcdef' 39 | } 40 | } 41 | }; 42 | }); 43 | 44 | it('has a name', function() { 45 | var plugin = subject.createDeployPlugin({ 46 | name: 'test-plugin' 47 | }); 48 | 49 | assert.equal(plugin.name, 'test-plugin'); 50 | }); 51 | 52 | it('implements the correct deployment hooks', function() { 53 | var plugin = subject.createDeployPlugin({ 54 | name: 'test-plugin' 55 | }); 56 | 57 | assert.equal(typeof plugin.configure, 'function'); 58 | assert.equal(typeof plugin.prepare, 'function'); 59 | assert.equal(typeof plugin.didPrepare, 'function'); 60 | assert.equal(typeof plugin.didDeploy, 'function'); 61 | }); 62 | 63 | describe('configure hook', function() { 64 | it('does not throw if config is ok', function() { 65 | var plugin = subject.createDeployPlugin({ 66 | name: 'deploySentry' 67 | }); 68 | plugin.beforeHook(context); 69 | plugin.configure(context); 70 | assert.ok(true); // it didn't throw 71 | }); 72 | 73 | it('throws if config is not valid', function() { 74 | var plugin = subject.createDeployPlugin({ 75 | name: 'deploySentry' 76 | }); 77 | 78 | context.config = { deploySentry: {} }; 79 | 80 | plugin.beforeHook(context); 81 | assert.throws(function(){ 82 | plugin.configure(context); 83 | }); 84 | }); 85 | 86 | describe('without providing config', function () { 87 | var plugin; 88 | 89 | beforeEach(function() { 90 | plugin = subject.createDeployPlugin({ 91 | name: 'deploySentry' 92 | }); 93 | }); 94 | 95 | it('warns about missing required config', function() { 96 | context.config = { deploySentry: {} }; 97 | 98 | plugin.beforeHook(context); 99 | assert.throws(function(error){ 100 | plugin.configure(context); 101 | }); 102 | var messages = mockUi.messages.reduce(function(previous, current) { 103 | if (/- Missing required config:\s.*/.test(current)) { 104 | previous.push(current); 105 | } 106 | 107 | return previous; 108 | }, []); 109 | 110 | assert.equal(messages.length, 1); // doesn't log all failures, just first one 111 | }); 112 | 113 | it('warns about missing optional config', function() { 114 | plugin.beforeHook(context); 115 | plugin.configure(context); 116 | var messages = mockUi.messages.reduce(function(previous, current) { 117 | if (/- Missing config:\s.*, using default:\s/.test(current)) { 118 | previous.push(current); 119 | } 120 | 121 | return previous; 122 | }, []); 123 | 124 | assert.equal(messages.length, 6); 125 | }); 126 | 127 | it('adds default config to the config object', function() { 128 | plugin.beforeHook(context); 129 | plugin.configure(context); 130 | assert.isDefined(context.config.deploySentry.distDir); 131 | assert.isDefined(context.config.deploySentry.filePattern); 132 | assert.isDefined(context.config.deploySentry.enableRevisionTagging); 133 | }); 134 | }); 135 | 136 | describe('with optional config supplied', function () { 137 | var plugin; 138 | 139 | beforeEach(function() { 140 | plugin = subject.createDeployPlugin({ 141 | name: 'deploySentry' 142 | }); 143 | context.config.deploySentry["distDir"] = "dist/dir"; 144 | context.config.deploySentry["filePattern"] = "/**/*.{js,map}"; 145 | context.config.deploySentry["enableRevisionTagging"] = false; 146 | context.config.deploySentry["replaceFiles"] = true; 147 | context.config.deploySentry["strictSSL"] = true; 148 | context.config.deploySentry["revisionCommits"] = [{ 149 | "repository":"owner-org/repo-name", 150 | "id":"4c9c05d912292cb1b3e63c7947505cf19366c078" 151 | }]; 152 | }); 153 | 154 | it('does not warn about missing optional config', function() { 155 | plugin.beforeHook(context); 156 | plugin.configure(context); 157 | var messages = mockUi.messages.reduce(function(previous, current) { 158 | if (/- Missing config:\s.*, using default:\s/.test(current)) { 159 | previous.push(current); 160 | } 161 | 162 | return previous; 163 | }, []); 164 | 165 | assert.equal(messages.length, 0); 166 | }); 167 | }); 168 | }); 169 | 170 | describe('contentFor hook', function() { 171 | it('is defined', function() { 172 | assert.equal(typeof subject.contentFor, 'function'); 173 | }); 174 | it('returns content for head-footer', function() { 175 | assert.equal(subject.contentFor('head-footer'), ''); 176 | }); 177 | it('does not return content for other types', function() { 178 | assert.notEqual(subject.contentFor('head-barter'), ''); 179 | }) 180 | }); 181 | 182 | describe('prepare hook', function() { 183 | var plugin, fileSystem, indexFile; 184 | beforeEach(function() { 185 | plugin = subject.createDeployPlugin({ 186 | name: 'deploySentry' 187 | }); 188 | indexFile = mockFs.file({ 189 | content: '' 190 | }); 191 | fileSystem = { 192 | '/path/to/fake/dir': { 193 | 'index.html': indexFile 194 | } 195 | }; 196 | mockFs(fileSystem); 197 | context.distDir = '/path/to/fake/dir'; 198 | context.revisionKey = 'abc123'; 199 | }); 200 | afterEach(function() { 201 | mockFs.restore(); 202 | }); 203 | 204 | it('does not fill in revision data when disabled', function() { 205 | context.config.deploySentry.enableRevisionTagging = false; 206 | 207 | plugin.beforeHook(context); 208 | plugin.configure(context); 209 | plugin.prepare(context); 210 | var result = fs.readFileSync('/path/to/fake/dir/index.html', 'utf8'); 211 | assert.notEqual(result.indexOf(''), -1); 212 | }); 213 | 214 | it('fills in revision data in the meta-tag', function() { 215 | plugin.beforeHook(context); 216 | plugin.configure(context); 217 | plugin.prepare(context); 218 | var result = fs.readFileSync('/path/to/fake/dir/index.html', 'utf8'); 219 | assert.notEqual(result.indexOf(''), -1); 220 | }); 221 | }); 222 | 223 | describe('upload hook', function() { 224 | // possibly mock Sentry out here 225 | }); 226 | }); 227 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dschmidt/ember-cli-deploy-sentry/13de676f015ca727166d6ba1e6de92950ba5f015/vendor/.gitkeep --------------------------------------------------------------------------------