dschmidt/ember-cli-deploy-sentry

400 error if release exists

Closed this issue · 12 comments

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

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.

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
}

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.

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

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

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.

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

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

  • The first deployment of a given version consistently works fine.
  • With replaceFiles: true, I get a 400: Release with version already exists when trying to deploy for a second time. It seems to be attempting to create a duplicate release, despite recognizing that one already exists: https://gist.github.com/reidab/01d8b53a8406acec2f1141155ea2a4cc#file-0-4-0-replacefiles-true
  • With replaceFiles: false, the deploy succeeds
  • I don't seem to be seeing any failures on attempts to delete or reupload files, only on release creation.
  • The same thing happens regardless of the release in question having issues associated with it on Sentry.

@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?

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

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 383beed? I added a deeper catch with should log the actual error, instead if allowing it to continue to the createRelease path.

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!

Oh that makes sense, yes!

Mind adding a reasonable error message and sending a PR?