dschmidt / ember-cli-deploy-sentry

An ember-cli-deploy-plugin to upload javascript sourcemaps to Sentry
MIT License
42 stars 51 forks source link

400 error if release exists #17

Closed noslouch closed 8 years ago

noslouch commented 8 years ago

In some cases (I can't isolate it yet), when my build server tries to deploy, it's dying if the release already exists, which is puzzling given the order of operations in the upload method.

I'm trying to isolate, but I thought I'd let you know. See below for output:

cd "./overhaul"
export AWS_ACCESS_KEY_ID="$DEPLOY_AWS_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$DEPLOY_AWS_SECRET_ACCESS_KEY"
export AWS_BUCKET="$DEMO_AWS_BUCKET"
export AWS_REGION="$DEMO_AWS_REGION"
export SSH_TUNNEL_USERNAME="$DEPLOY_SSH_TUNNEL_USERNAME"
export SSH_TUNNEL_HOST="$DEPLOY_SSH_TUNNEL_HOST"
export SSH_TUNNEL_DESTINATION_HOST="$DEMO_REDIS_HOST"
export SSH_TUNNEL_DESTINATION_PORT="$DEMO_REDIS_PORT"
export FINGERPRINT_PREPEND_URL="$DEMO_FINGERPRINT_PREPEND_URL"
export SENTRY_DSN="$DEMO_SENTRY_EMBER_DSN"
export SENTRY_PROJECT="$DEMO_SENTRY_PROJECT"
export SENTRY_EMBER_SOURCEMAPS_KEY="$DEMO_SENTRY_EMBER_SOURCEMAPS_KEY"
./node_modules/ember-cli/bin/ember deploy demo --verbose
Could not start watchman; falling back to NodeWatcher for file system events.
Visit http://ember-cli.com/user-guide/#watchman for more info.
Registering hook -> configure[json-config]
Registering hook -> didBuild[json-config]
Registering hook -> configure[build]
Registering hook -> build[build]
Registering hook -> configure[display-revisions]
Registering hook -> configure[gzip]
Registering hook -> willUpload[gzip]
Registering hook -> configure[s3]
Registering hook -> upload[s3]
Registering hook -> configure[redis]
Registering hook -> fetchInitialRevisions[redis]
Registering hook -> upload[redis]
Registering hook -> willActivate[redis]
Registering hook -> activate[redis]
Registering hook -> fetchRevisions[redis]
Registering hook -> didDeploy[redis]
Registering hook -> configure[manifest]
Registering hook -> willUpload[manifest]
Registering hook -> configure[revision-data]
Registering hook -> prepare[revision-data]
Registering hook -> configure[sentry]
Registering hook -> prepare[sentry]
Registering hook -> upload[sentry]
Registering hook -> didDeploy[sentry]
Registering hook -> configure[ssh-tunnel]
Registering hook -> setup[ssh-tunnel]
Registering hook -> teardown[ssh-tunnel]
Executing pipeline
|
+- configure
|  |
|  +- json-config
|    - validating config
|    - Missing config: `fileInputPattern`, using default: `index.html`
|    - Missing config: `fileOutputPattern`, using default: `index.json`
|    - Missing config: `projectRoot`, using default: `[Function]`
|    - Missing config: `distDir`, using default: `[Function]`
|    - Missing config: `jsonBlueprint`, using default: `[object Object]`
|    - config ok
|  |
|  +- build
|    - validating config
|    - Missing config: `outputPath`, using default: `tmp/deploy-dist`
|    - config ok
|  |
|  +- display-revisions
|    - validating config
|    - Missing config: `amount`, using default: `[Function]`
|    - Missing config: `revisions`, using default: `[Function]`
|    - config ok
|  |
|  +- gzip
|    - validating config
|    - Missing config: `ignorePattern`, using default: `null`
|    - Missing config: `zopfli`, using default: `false`
|    - Missing config: `keep`, using default: `false`
|    - Missing config: `distDir`, using default: `[Function]`
|    - Missing config: `distFiles`, using default: `[Function]`
|    - config ok
|  |
|  +- s3
|    - validating config
|    - Missing config: `filePattern`, using default: `**/*.{js,css,png,gif,ico,jpg,map,xml,txt,svg,swf,eot,ttf,woff,woff2}`
|    - Missing config: `prefix`, using default: ``
|    - Missing config: `acl`, using default: `public-read`
|    - Missing config: `distDir`, using default: `[Function]`
|    - Missing config: `distFiles`, using default: `[Function]`
|    - Missing config: `gzippedFiles`, using default: `[Function]`
|    - Missing config: `manifestPath`, using default: `[Function]`
|    - Missing config: `uploadClient`, using default: `[Function]`
|    - Missing config: `s3Client`, using default: `[Function]`
|    - config ok
|  |
|  +- redis
|    - validating config
|    - Missing config: `host`, using default: `localhost`
|    - Missing config: `port`, using default: `[Function]`
|    - Missing config: `distDir`, using default: `[Function]`
|    - Missing config: `keyPrefix`, using default: `[Function]`
|    - Missing config: `activationSuffix`, using default: `current`
|    - Missing config: `activeContentSuffix`, using default: `current-content`
|    - Missing config: `revisionKey`, using default: `[Function]`
|    - Missing config: `didDeployMessage`, using default: `[Function]`
|    - Missing config: `redisDeployClient`, using default: `[Function]`
|    - Missing config: `maxRecentUploads`, using default: `10`
|    - Missing config: `revisionData`, using default: `[Function]`
|    - config ok
|  |
|  +- manifest
|    - validating config
|    - Missing config: `filePattern`, using default: `**/*.{js,css,png,gif,ico,jpg,map,xml,txt,svg,swf,eot,ttf,woff,woff2}`
|    - Missing config: `manifestPath`, using default: `manifest.txt`
|    - Missing config: `distDir`, using default: `[Function]`
|    - Missing config: `distFiles`, using default: `[Function]`
|    - config ok
|  |
|  +- revision-data
|    - validating config
|    - Missing config: `type`, using default: `file-hash`
|    - Missing config: `filePattern`, using default: `index.html`
|    - Missing config: `versionFile`, using default: `package.json`
|    - Missing config: `distDir`, using default: `[Function]`
|    - Missing config: `distFiles`, using default: `[Function]`
|    - Missing config: `scm`, using default: `[Function]`
|    - config ok
|  |
|  +- sentry
|    - validating config
|    - Missing config: `distDir`, using default: `[Function]`
|    - Missing config: `filePattern`, using default: `/**/*.{js,map}`
|    - Missing config: `revisionKey`, using default: `[Function]`
|    - Missing config: `didDeployMessage`, using default: `[Function]`
|    - config ok
|  |
|  +- ssh-tunnel
|    - validating config
|    - Missing config: `port`, using default: `22`
|    - Missing config: `srcPort`, using default: `[Function]`
|    - Missing config: `tunnelClient`, using default: `[Function]`
|    - config ok
|
+- setup
|  |
|  +- ssh-tunnel
|
+- willDeploy
|
+- willBuild
|
+- build
|  |
|  +- build
|    - building app to `tmp/deploy-dist` using buildEnv `production`...
|    - ✔  assets/img/icons/edit-a7b24b1cfa9cbc1cc05faa0d5fdc8e67.gif
|    - ✔  assets/img/icons/edit-bucket-34e62381612d48c3d5a5185ee5d4b6c5.png
|    - ✔  assets/img/icons/edit-tout-300c373703c9c2857e7164d82dc3f2b4.png
|    - ✔  assets/img/icons/status_draft-65767c0b6785c19a90e01e448181b2a3.png
|    - ✔  assets/img/icons/status_scheduled-ad9fffae77d3f664e7930bc60c17bd76.png
|    - ✔  assets/img/icons/status_submitted-de68fa84d5e6f4b56a046bbe1e899f9e.png
|    - ✔  assets/img/widgets/quote-2e0663645d09000a25cfb18ddaa55d7f.png
|    - ✔  assets/overhaul-09acfbe2b7a6b95eb8c2e89e6c30bf77.map
|    - ✔  assets/overhaul-128b66dd37656379c1b9a61c9dc49855.css
|    - ✔  assets/overhaul-7781c743c7b7af72d9e665bdefb1b310.js
|    - ✔  assets/vendor-628d67d5fa3b6165b149e0ce5e37a472.map
|    - ✔  assets/vendor-bcbf18b65e0fe959a99bab20e790716f.js
|    - ✔  assets/vendor-db1aae18a50d64dc7d57cc30890fa4a9.css
|    - ✔  crossdomain.xml
|    - ✔  index.html
|    - ✔  robots.txt
|    - build ok
|
+- didBuild
|  |
|  +- json-config
|    - generating `tmp/deploy-dist/index.json` from `tmp/deploy-dist/index.html`
|    - generated: `tmp/deploy-dist/index.json`
|    - added `index.json` to `context.distFiles`
|
+- willPrepare
|
+- prepare
|  |
|  +- revision-data
|    - creating revision data using `file-hash`
|    - generated revision data for revision: `ff1d82b79d0f8a4bc5c2747bf765868b`
|  |
|  +- sentry
|
+- didPrepare
|
+- fetchInitialRevisions
|  |
|  +- redis
|    - Listing initial revisions for key: `overhaul:index`
|
+- willUpload
|  |
|  +- gzip
|    - gzipping `**/*.{js,css,ico,map,xml,txt,svg,eot,ttf,woff,woff2}`
|    - ignoring `null`
|    - ✔  crossdomain.xml
|    - ✔  robots.txt
|    - ✔  assets/vendor-db1aae18a50d64dc7d57cc30890fa4a9.css
|    - ✔  assets/overhaul-128b66dd37656379c1b9a61c9dc49855.css
|    - ✔  assets/overhaul-7781c743c7b7af72d9e665bdefb1b310.js
|    - ✔  assets/overhaul-09acfbe2b7a6b95eb8c2e89e6c30bf77.map
|    - ✔  assets/vendor-bcbf18b65e0fe959a99bab20e790716f.js
|    - ✔  assets/vendor-628d67d5fa3b6165b149e0ce5e37a472.map
|    - gzipped 8 files ok
|  |
|  +- manifest
|    - generating manifest at `manifest.txt`
|    - generated manifest including 15 files ok
|
+- upload
|  |
|  +- s3
|    - Using AWS access key id and secret access key from config
|    - preparing to upload to S3 bucket `wnyc.org-demo-static`
|    - Downloading manifest for differential deploy from `manifest.txt`...
|    - Manifest found. Differential deploy will be applied.
|    - ✔  manifest.txt
|    - ✔  assets/overhaul-128b66dd37656379c1b9a61c9dc49855.css
|    - ✔  assets/overhaul-09acfbe2b7a6b95eb8c2e89e6c30bf77.map
|    - ✔  assets/vendor-bcbf18b65e0fe959a99bab20e790716f.js
|    - ✔  assets/vendor-628d67d5fa3b6165b149e0ce5e37a472.map
|    - ✔  assets/overhaul-7781c743c7b7af72d9e665bdefb1b310.js
|    - uploaded 6 files ok
|  |
|  +- redis
|    - Uploading `tmp/deploy-dist/index.json`
|    - Uploaded with key `overhaul:index:ff1d82b79d0f8a4bc5c2747bf765868b`
|  |
|  +- sentry
{ [StatusCodeError: 400 - [object Object]]
  name: 'StatusCodeError',
  statusCode: 400,
  message: '400 - [object Object]',
  error: { detail: 'Release with version already exists' },
  options: 
   { uri: 'https://sentry.wnyc.org/api/0/projects/sentry/www-demo-ember/releases/',
     method: 'POST',
     auth: { user: [redacted] },
     json: true,
     body: { version: 'ff1d82b79d0f8a4bc5c2747bf765868b' },
     resolveWithFullResponse: true,
     callback: undefined,
     simple: true },
  response: 
   { _readableState: 
      { objectMode: false,
        highWaterMark: 16384,
        buffer: [],
        length: 0,
        pipes: null,
        pipesCount: 0,
        flowing: true,
        ended: true,
        endEmitted: true,
        reading: false,
        sync: true,
        needReadable: false,
        emittedReadable: false,
        readableListening: false,
        defaultEncoding: 'utf8',
        ranOut: false,
        awaitDrain: 0,
        readingMore: false,
        decoder: null,
        encoding: null,
        resumeScheduled: false },
     readable: false,
     domain: null,
     _events: 
      { end: [Object],
        close: [Object],
        data: [Function],
        error: [Function] },
     _maxListeners: undefined,
     socket: 
      { _connecting: false,
        _hadError: false,
        _handle: null,
        _parent: null,
        _host: 'sentry.wnyc.org',
        _readableState: [Object],
        readable: false,
        domain: null,
        _events: [Object],
        _maxListeners: 0,
        _writableState: [Object],
        writable: false,
        allowHalfOpen: false,
        destroyed: true,
        bytesRead: 301,
        _bytesDispatched: 298,
        _pendingData: null,
        _pendingEncoding: '',
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        ssl: [Object],
        servername: null,
        npnProtocol: undefined,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        parser: null,
        _httpMessage: [Object],
        read: [Function],
        _consuming: true,
        server: null,
        _requestCert: true,
        _rejectUnauthorized: true,
        _idleNext: null,
        _idlePrev: null,
        _idleTimeout: -1 },
     connection: 
      { _connecting: false,
        _hadError: false,
        _handle: null,
        _parent: null,
        _host: 'sentry.wnyc.org',
        _readableState: [Object],
        readable: false,
        domain: null,
        _events: [Object],
        _maxListeners: 0,
        _writableState: [Object],
        writable: false,
        allowHalfOpen: false,
        destroyed: true,
        bytesRead: 301,
        _bytesDispatched: 298,
        _pendingData: null,
        _pendingEncoding: '',
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        ssl: [Object],
        servername: null,
        npnProtocol: undefined,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        parser: null,
        _httpMessage: [Object],
        read: [Function],
        _consuming: true,
        server: null,
        _requestCert: true,
        _rejectUnauthorized: true,
        _idleNext: null,
        _idlePrev: null,
        _idleTimeout: -1 },
     httpVersionMajor: 1,
     httpVersionMinor: 1,
     httpVersion: '1.1',
     complete: true,
     headers: 
      { allow: 'GET, POST, HEAD, OPTIONS',
        'content-language': 'en',
        'content-type': 'application/json',
        date: 'Fri, 29 Apr 2016 19:32:00 GMT',
        server: 'nginx/1.4.6 (Ubuntu)',
        vary: 'Accept-Language, Cookie',
        'content-length': '49',
        connection: 'Close' },
     rawHeaders: 
      [ 'Allow',
        'GET, POST, HEAD, OPTIONS',
        'Content-Language',
        'en',
        'Content-Type',
        'application/json',
        'Date',
        'Fri, 29 Apr 2016 19:32:00 GMT',
        'Server',
        'nginx/1.4.6 (Ubuntu)',
        'Vary',
        'Accept-Language, Cookie',
        'Content-Length',
        '49',
        'Connection',
        'Close' ],
     trailers: {},
     rawTrailers: [],
     _pendings: [],
     _pendingIndex: 0,
     upgrade: false,
     url: '',
     method: null,
     statusCode: 400,
     statusMessage: 'BAD REQUEST',
     client: 
      { _connecting: false,
        _hadError: false,
        _handle: null,
        _parent: null,
        _host: 'sentry.wnyc.org',
        _readableState: [Object],
        readable: false,
        domain: null,
        _events: [Object],
        _maxListeners: 0,
        _writableState: [Object],
        writable: false,
        allowHalfOpen: false,
        destroyed: true,
        bytesRead: 301,
        _bytesDispatched: 298,
        _pendingData: null,
        _pendingEncoding: '',
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        ssl: [Object],
        servername: null,
        npnProtocol: undefined,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        parser: null,
        _httpMessage: [Object],
        read: [Function],
        _consuming: true,
        server: null,
        _requestCert: true,
        _rejectUnauthorized: true,
        _idleNext: null,
        _idlePrev: null,
        _idleTimeout: -1 },
     _consuming: true,
     _dumped: false,
     req: 
      { domain: null,
        _events: [Object],
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        writable: true,
        _last: true,
        chunkedEncoding: false,
        shouldKeepAlive: false,
        useChunkedEncodingByDefault: true,
        sendDate: false,
        _removedHeader: [Object],
        _hasBody: true,
        _trailer: '',
        finished: true,
        _hangupClose: false,
        _headerSent: true,
        socket: [Object],
        connection: [Object],
        _header: 'POST /api/0/projects/sentry/www-demo-ember/releases/ HTTP/1.1\r\nhost: sentry.wnyc.org\r\nauthorization: Basic [redacted]\r\naccept: application/json\r\ncontent-type: application/json\r\ncontent-length: 46\r\nConnection: close\r\n\r\n',
        _headers: [Object],
        _headerNames: [Object],
        agent: [Object],
        socketPath: undefined,
        method: 'POST',
        path: '/api/0/projects/sentry/www-demo-ember/releases/',
        parser: null,
        res: [Circular] },
     request: 
      { domain: null,
        _events: [Object],
        _maxListeners: undefined,
        uri: [Object],
        method: 'POST',
        body: '{"version":"ff1d82b79d0f8a4bc5c2747bf765868b"}',
        resolveWithFullResponse: true,
        readable: true,
        writable: true,
        explicitMethod: true,
        _qs: [Object],
        _auth: [Object],
        _oauth: [Object],
        _multipart: [Object],
        _redirect: [Object],
        _tunnel: [Object],
        _rp_resolve: [Function],
        _rp_reject: [Function],
        _rp_promise: [Object],
        _rp_callbackOrig: undefined,
        callback: [Function],
        _rp_options: [Object],
        headers: [Object],
        setHeader: [Function],
        hasHeader: [Function],
        getHeader: [Function],
        removeHeader: [Function],
        localAddress: undefined,
        pool: {},
        dests: [],
        __isRequestRequest: true,
        _callback: [Function: RP$callback],
        proxy: null,
        tunnel: true,
        setHost: true,
        originalCookieHeader: undefined,
        _disableCookies: true,
        _jar: undefined,
        port: 443,
        host: 'sentry.wnyc.org',
        path: '/api/0/projects/sentry/www-demo-ember/releases/',
        _json: true,
        httpModule: [Object],
        agentClass: [Object],
        agent: [Object],
        _rp_promise_in_use: true,
        _started: true,
        href: 'https://sentry.wnyc.org/api/0/projects/sentry/www-demo-ember/releases/',
        req: [Object],
        ntick: true,
        response: [Circular],
        originalHost: 'sentry.wnyc.org',
        originalHostHeaderName: 'host',
        responseContent: [Circular],
        _destdata: true,
        _ended: true,
        _callbackCalled: true },
     toJSON: [Function: responseToJSON],
     caseless: { dict: [Object] },
     read: [Function],
     body: { detail: 'Release with version already exists' } } }
|
+- didFail
SilentError: Creating release failed
undefined|
Pipeline aborted

cd "./overhaul"
export AWS_ACCESS_KEY_ID="$DEPLOY_AWS_ACCESS_KEY_ID"
export AWS_SECRET_ACCESS_KEY="$DEPLOY_AWS_SECRET_ACCESS_KEY"
export AWS_BUCKET="$DEMO_AWS_BUCKET"
export AWS_REGION="$DEMO_AWS_REGION"
export SSH_TUNNEL_USERNAME="$DEPLOY_SSH_TUNNEL_USERNAME"
export SSH_TUNNEL_HOST="$DEPLOY_SSH_TUNNEL_HOST"
export SSH_TUNNEL_DESTINATION_HOST="$DEMO_REDIS_HOST"
export SSH_TUNNEL_DESTINATION_PORT="$DEMO_REDIS_PORT"
export FINGERPRINT_PREPEND_URL="$DEMO_FINGERPRINT_PREPEND_URL"
export SENTRY_DSN="$DEMO_SENTRY_EMBER_DSN"
export SENTRY_PROJECT="$DEMO_SENTRY_PROJECT"
export SENTRY_EMBER_SOURCEMAPS_KEY="$DEMO_SENTRY_EMBER_SOURCEMAPS_KEY"
./node_modules/ember-cli/bin/ember deploy demo --verbose
 returned exit code 1

Action failed: ./node_modules/ember-cli/bin/ember deploy demo --verbose
reidab commented 8 years ago

I'd been hitting this bug before and switched to an earlier version of the code on @noslouch's branch (the original 632c426 commit). With that commit, I'm able to repeatedly deploy the same release with no problem. In this final merged version, I'm back to failing deploys with 400: Release with version already exists.

noslouch commented 8 years ago

we changed the default value of replaceFiles to false. Try adding that config to your config/deploy.js with a value of true, so...

sentry: {
  replaceFiles: true,
  // other config
}
reidab commented 8 years ago

I tried adding that config in my testing earlier today and kept hitting the same issue. I'll take a closer look tomorrow and see if I can isolate it further.

dschmidt commented 8 years ago

@noslouch actually we changed it back to true being the default.

Unfotunately I did not see this comment before I released 0.4.0 :-\

noslouch commented 8 years ago

Zurp. Woops yeah, true is the default. What output are you getting when you run with the --verbose flag?

Might want to try again with replaceFiles set to false.

If you have open issues in sentry for the given release, and replaceFiles is true, the process will still bork when this tries to delete the files.

dschmidt commented 8 years ago

It will bork? as in break? Why did we switch to deleting files instead of deleting the release again? I thought the whole point of your PR was to allow reuploading files (by first deleting them) for an already uploaded release with issues assigned

reidab commented 8 years ago

I logged in to my CI server this morning and did a bit more investigation with 0.4.0:

dschmidt commented 8 years ago

@reidab will investigate soon, seem to work for me all the time... what version of sentry are you running? Or are you using getsentry.com directly?

reidab commented 8 years ago

We're using getsentry.com directly. It may be that the real error is related to deleting or uploading new files, but that it's being masked by this catch: https://github.com/dschmidt/ember-cli-deploy-sentry/blob/master/index.js#L92

noslouch commented 8 years ago

Yeah, I had the same thought, that handleExistingRelease is erroring somewhere in the delete/upload part, and it's bubbling up to that higher level catch.

Can you try installing and redeploying with 383beed512f6cb17e29b6084e43d7f2460584979? I added a deeper catch with should log the actual error, instead if allowing it to continue to the createRelease path.

reidab commented 8 years ago

Okay, that definitely clarifies things. 😌 The real error was a 403 coming back from the file deletions. It's probably worth adding a note to the README that the API key requires project:delete in addition to project:write when using replaceFiles.

Thanks!

dschmidt commented 8 years ago

Oh that makes sense, yes!

Mind adding a reasonable error message and sending a PR?