diff options
260 files changed, 3952 insertions, 1793 deletions
diff --git a/.flayignore b/.flayignore index 47597025115..e2d0a2e50c5 100644 --- a/.flayignore +++ b/.flayignore @@ -3,3 +3,4 @@ lib/gitlab/sanitizers/svg/whitelist.rb lib/gitlab/diff/position_tracer.rb app/policies/project_policy.rb app/models/concerns/relative_positioning.rb +lib/gitlab/redis/*.rb diff --git a/.gitignore b/.gitignore index 0d6194dd1e5..3baf640a9c3 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ eslint-report.html /config/initializers/smtp_settings.rb /config/initializers/relative_url.rb /config/resque.yml +/config/redis.cache.yml +/config/redis.queues.yml +/config/redis.shared_state.yml /config/unicorn.rb /config/secrets.yml /config/sidekiq.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f7ab0259448..a54b38d284d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -203,69 +203,69 @@ setup-test-env: - public/assets - tmp/tests -rspec-pg 0 20: *rspec-knapsack-pg -rspec-pg 1 20: *rspec-knapsack-pg -rspec-pg 2 20: *rspec-knapsack-pg -rspec-pg 3 20: *rspec-knapsack-pg -rspec-pg 4 20: *rspec-knapsack-pg -rspec-pg 5 20: *rspec-knapsack-pg -rspec-pg 6 20: *rspec-knapsack-pg -rspec-pg 7 20: *rspec-knapsack-pg -rspec-pg 8 20: *rspec-knapsack-pg -rspec-pg 9 20: *rspec-knapsack-pg -rspec-pg 10 20: *rspec-knapsack-pg -rspec-pg 11 20: *rspec-knapsack-pg -rspec-pg 12 20: *rspec-knapsack-pg -rspec-pg 13 20: *rspec-knapsack-pg -rspec-pg 14 20: *rspec-knapsack-pg -rspec-pg 15 20: *rspec-knapsack-pg -rspec-pg 16 20: *rspec-knapsack-pg -rspec-pg 17 20: *rspec-knapsack-pg -rspec-pg 18 20: *rspec-knapsack-pg -rspec-pg 19 20: *rspec-knapsack-pg - -rspec-mysql 0 20: *rspec-knapsack-mysql -rspec-mysql 1 20: *rspec-knapsack-mysql -rspec-mysql 2 20: *rspec-knapsack-mysql -rspec-mysql 3 20: *rspec-knapsack-mysql -rspec-mysql 4 20: *rspec-knapsack-mysql -rspec-mysql 5 20: *rspec-knapsack-mysql -rspec-mysql 6 20: *rspec-knapsack-mysql -rspec-mysql 7 20: *rspec-knapsack-mysql -rspec-mysql 8 20: *rspec-knapsack-mysql -rspec-mysql 9 20: *rspec-knapsack-mysql -rspec-mysql 10 20: *rspec-knapsack-mysql -rspec-mysql 11 20: *rspec-knapsack-mysql -rspec-mysql 12 20: *rspec-knapsack-mysql -rspec-mysql 13 20: *rspec-knapsack-mysql -rspec-mysql 14 20: *rspec-knapsack-mysql -rspec-mysql 15 20: *rspec-knapsack-mysql -rspec-mysql 16 20: *rspec-knapsack-mysql -rspec-mysql 17 20: *rspec-knapsack-mysql -rspec-mysql 18 20: *rspec-knapsack-mysql -rspec-mysql 19 20: *rspec-knapsack-mysql - -spinach-pg 0 10: *spinach-knapsack-pg -spinach-pg 1 10: *spinach-knapsack-pg -spinach-pg 2 10: *spinach-knapsack-pg -spinach-pg 3 10: *spinach-knapsack-pg -spinach-pg 4 10: *spinach-knapsack-pg -spinach-pg 5 10: *spinach-knapsack-pg -spinach-pg 6 10: *spinach-knapsack-pg -spinach-pg 7 10: *spinach-knapsack-pg -spinach-pg 8 10: *spinach-knapsack-pg -spinach-pg 9 10: *spinach-knapsack-pg - -spinach-mysql 0 10: *spinach-knapsack-mysql -spinach-mysql 1 10: *spinach-knapsack-mysql -spinach-mysql 2 10: *spinach-knapsack-mysql -spinach-mysql 3 10: *spinach-knapsack-mysql -spinach-mysql 4 10: *spinach-knapsack-mysql -spinach-mysql 5 10: *spinach-knapsack-mysql -spinach-mysql 6 10: *spinach-knapsack-mysql -spinach-mysql 7 10: *spinach-knapsack-mysql -spinach-mysql 8 10: *spinach-knapsack-mysql -spinach-mysql 9 10: *spinach-knapsack-mysql +rspec-pg 0 25: *rspec-knapsack-pg +rspec-pg 1 25: *rspec-knapsack-pg +rspec-pg 2 25: *rspec-knapsack-pg +rspec-pg 3 25: *rspec-knapsack-pg +rspec-pg 4 25: *rspec-knapsack-pg +rspec-pg 5 25: *rspec-knapsack-pg +rspec-pg 6 25: *rspec-knapsack-pg +rspec-pg 7 25: *rspec-knapsack-pg +rspec-pg 8 25: *rspec-knapsack-pg +rspec-pg 9 25: *rspec-knapsack-pg +rspec-pg 10 25: *rspec-knapsack-pg +rspec-pg 11 25: *rspec-knapsack-pg +rspec-pg 12 25: *rspec-knapsack-pg +rspec-pg 13 25: *rspec-knapsack-pg +rspec-pg 14 25: *rspec-knapsack-pg +rspec-pg 15 25: *rspec-knapsack-pg +rspec-pg 16 25: *rspec-knapsack-pg +rspec-pg 17 25: *rspec-knapsack-pg +rspec-pg 18 25: *rspec-knapsack-pg +rspec-pg 19 25: *rspec-knapsack-pg +rspec-pg 20 25: *rspec-knapsack-pg +rspec-pg 21 25: *rspec-knapsack-pg +rspec-pg 22 25: *rspec-knapsack-pg +rspec-pg 23 25: *rspec-knapsack-pg +rspec-pg 24 25: *rspec-knapsack-pg + +rspec-mysql 0 25: *rspec-knapsack-mysql +rspec-mysql 1 25: *rspec-knapsack-mysql +rspec-mysql 2 25: *rspec-knapsack-mysql +rspec-mysql 3 25: *rspec-knapsack-mysql +rspec-mysql 4 25: *rspec-knapsack-mysql +rspec-mysql 5 25: *rspec-knapsack-mysql +rspec-mysql 6 25: *rspec-knapsack-mysql +rspec-mysql 7 25: *rspec-knapsack-mysql +rspec-mysql 8 25: *rspec-knapsack-mysql +rspec-mysql 9 25: *rspec-knapsack-mysql +rspec-mysql 10 25: *rspec-knapsack-mysql +rspec-mysql 11 25: *rspec-knapsack-mysql +rspec-mysql 12 25: *rspec-knapsack-mysql +rspec-mysql 13 25: *rspec-knapsack-mysql +rspec-mysql 14 25: *rspec-knapsack-mysql +rspec-mysql 15 25: *rspec-knapsack-mysql +rspec-mysql 16 25: *rspec-knapsack-mysql +rspec-mysql 17 25: *rspec-knapsack-mysql +rspec-mysql 18 25: *rspec-knapsack-mysql +rspec-mysql 19 25: *rspec-knapsack-mysql +rspec-mysql 20 25: *rspec-knapsack-mysql +rspec-mysql 21 25: *rspec-knapsack-mysql +rspec-mysql 22 25: *rspec-knapsack-mysql +rspec-mysql 23 25: *rspec-knapsack-mysql +rspec-mysql 24 25: *rspec-knapsack-mysql + +spinach-pg 0 5: *spinach-knapsack-pg +spinach-pg 1 5: *spinach-knapsack-pg +spinach-pg 2 5: *spinach-knapsack-pg +spinach-pg 3 5: *spinach-knapsack-pg +spinach-pg 4 5: *spinach-knapsack-pg + +spinach-mysql 0 5: *spinach-knapsack-mysql +spinach-mysql 1 5: *spinach-knapsack-mysql +spinach-mysql 2 5: *spinach-knapsack-mysql +spinach-mysql 3 5: *spinach-knapsack-mysql +spinach-mysql 4 5: *spinach-knapsack-mysql # Static analysis jobs .ruby-static-analysis: &ruby-static-analysis diff --git a/.scss-lint.yml b/.scss-lint.yml index acf04bf0735..b107cf944d5 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -93,7 +93,7 @@ linters: # The basenames of @imported SCSS partials should not begin with an # underscore and should not include the filename extension. ImportPath: - enabled: false + enabled: true # Avoid using !important in properties. It is usually indicative of a # misunderstanding of CSS specificity and can lead to brittle code. @@ -133,7 +133,7 @@ linters: # Reports when you use an unknown or disabled CSS property # (ignoring vendor-prefixed properties). PropertySpelling: - enabled: false + enabled: true # Configure which units are allowed for property values. PropertyUnits: @@ -176,6 +176,10 @@ linters: # Commas in lists should be followed by a space. SpaceAfterComma: + enabled: true + + # Comment literals should be followed by a space. + SpaceAfterComment: enabled: false # Properties should be formatted with a single space separating the colon @@ -240,7 +244,7 @@ linters: # Do not use parent selector references (&) when they would otherwise # be unnecessary. UnnecessaryParentReference: - enabled: false + enabled: true # URLs should be valid and not contain protocols or domain names. UrlFormat: diff --git a/CHANGELOG.md b/CHANGELOG.md index c15a59d25d4..c181aba0205 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 9.3.6 (2017-07-12) + +- Fix API Scoping. !12300 +- Username and password are no longer stripped from import url on mirror update. !12725 +- Fix issues with non-UTF8 filenames by always fixing the encoding of tree and blob paths. +- Fixed GFM references not being included when updating issues inline. + ## 9.3.5 (2017-07-05) - Remove "Remove from board" button from backlog and closed list. !12430 diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 04a373efe6b..c5523bd09b1 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -0.16.0 +0.17.0 diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index ac14c3dfaa8..c7cb1311a64 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -5.1.1 +5.3.1 diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index 276cbf9e285..4a36342fcab 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -2.3.0 +3.0.0 @@ -250,7 +250,6 @@ gem 'jquery-rails', '~> 4.1.0' gem 'request_store', '~> 1.3' gem 'select2-rails', '~> 3.5.9' gem 'virtus', '~> 1.0.1' -gem 'net-ssh', '~> 3.0.1' gem 'base32', '~> 0.3.0' # Sentry integration @@ -335,7 +334,7 @@ group :development, :test do gem 'rubocop', '~> 0.47.1', require: false gem 'rubocop-rspec', '~> 1.15.0', require: false - gem 'scss_lint', '~> 0.47.0', require: false + gem 'scss_lint', '~> 0.54.0', require: false gem 'haml_lint', '~> 0.21.0', require: false gem 'simplecov', '~> 0.14.0', require: false gem 'flay', '~> 2.8.0', require: false diff --git a/Gemfile.lock b/Gemfile.lock index deaf3ded2f9..c27c06e9c34 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -472,7 +472,6 @@ GEM mustermann (= 0.4.0) mysql2 (0.3.20) net-ldap (0.12.1) - net-ssh (3.0.1) netrc (0.11.0) nokogiri (1.6.8.1) mini_portile2 (~> 2.1.0) @@ -763,9 +762,9 @@ GEM sawyer (0.8.1) addressable (>= 2.3.5, < 2.6) faraday (~> 0.8, < 1.0) - scss_lint (0.47.1) - rake (>= 0.9, < 11) - sass (~> 3.4.15) + scss_lint (0.54.0) + rake (>= 0.9, < 13) + sass (~> 3.4.20) securecompare (1.0.0) seed-fu (2.3.6) activerecord (>= 3.1) @@ -780,7 +779,7 @@ GEM rack shoulda-matchers (2.8.0) activesupport (>= 3.0.0) - sidekiq (5.0.0) + sidekiq (5.0.4) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) @@ -1013,7 +1012,6 @@ DEPENDENCIES minitest (~> 5.7.0) mousetrap-rails (~> 1.4.6) mysql2 (~> 0.3.16) - net-ssh (~> 3.0.1) nokogiri (~> 1.6.7, >= 1.6.7.2) oauth2 (~> 1.4) octokit (~> 4.6.2) @@ -1083,7 +1081,7 @@ DEPENDENCIES rugged (~> 0.25.1.1) sanitize (~> 2.0) sass-rails (~> 5.0.6) - scss_lint (~> 0.47.0) + scss_lint (~> 0.54.0) seed-fu (~> 2.3.5) select2-rails (~> 3.5.9) sentry-raven (~> 2.5.3) diff --git a/app/assets/images/new_nav.png b/app/assets/images/new_nav.png Binary files differindex 8879d26d341..f98ca15d787 100644 --- a/app/assets/images/new_nav.png +++ b/app/assets/images/new_nav.png diff --git a/app/assets/javascripts/blob/notebook/index.js b/app/assets/javascripts/blob/notebook/index.js index 36fe8a7184f..27312d718b0 100644 --- a/app/assets/javascripts/blob/notebook/index.js +++ b/app/assets/javascripts/blob/notebook/index.js @@ -51,8 +51,9 @@ export default () => { methods: { loadFile() { this.$http.get(el.dataset.endpoint) + .then(response => response.json()) .then((res) => { - this.json = res.json(); + this.json = res; this.loading = false; }) .catch((e) => { diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js index b94009ee76b..88b054b76e6 100644 --- a/app/assets/javascripts/boards/boards_bundle.js +++ b/app/assets/javascripts/boards/boards_bundle.js @@ -81,8 +81,9 @@ $(() => { mounted () { Store.disabled = this.disabled; gl.boardService.all() + .then(response => response.json()) .then((resp) => { - resp.json().forEach((board) => { + resp.forEach((board) => { const list = Store.addList(board, this.defaultAvatar); if (list.type === 'closed') { @@ -97,7 +98,8 @@ $(() => { Store.addBlankState(); this.loading = false; - }).catch(() => new Flash('An error occurred. Please try again.')); + }) + .catch(() => new Flash('An error occurred. Please try again.')); }, methods: { updateTokens() { diff --git a/app/assets/javascripts/boards/components/board_blank_state.js b/app/assets/javascripts/boards/components/board_blank_state.js index 870e115bd1a..e7f16899362 100644 --- a/app/assets/javascripts/boards/components/board_blank_state.js +++ b/app/assets/javascripts/boards/components/board_blank_state.js @@ -64,8 +64,9 @@ export default { // Save the labels gl.boardService.generateDefaultLists() - .then((resp) => { - resp.json().forEach((listObj) => { + .then(resp => resp.json()) + .then((data) => { + data.forEach((listObj) => { const list = Store.findList('title', listObj.title); list.id = listObj.id; diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js index 6356c266ee2..1d36519c75c 100644 --- a/app/assets/javascripts/boards/components/modal/index.js +++ b/app/assets/javascripts/boards/components/modal/index.js @@ -88,9 +88,9 @@ gl.issueBoards.IssuesModal = Vue.extend({ return gl.boardService.getBacklog(queryData(this.filter.path, { page: this.page, per: this.perPage, - })).then((res) => { - const data = res.json(); - + })) + .then(resp => resp.json()) + .then((data) => { if (clearIssues) { this.issues = []; } diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index b4b09b3876e..08f7c5ddcd2 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -40,9 +40,8 @@ class List { save () { return gl.boardService.createList(this.label.id) - .then((resp) => { - const data = resp.json(); - + .then(resp => resp.json()) + .then((data) => { this.id = data.id; this.type = data.list_type; this.position = data.position; @@ -91,8 +90,8 @@ class List { } return gl.boardService.getIssuesForList(this.id, data) - .then((resp) => { - const data = resp.json(); + .then(resp => resp.json()) + .then((data) => { this.loading = false; this.issuesSize = data.size; @@ -109,8 +108,8 @@ class List { this.issuesSize += 1; return gl.boardService.newIssue(this.id, issue) - .then((resp) => { - const data = resp.json(); + .then(resp => resp.json()) + .then((data) => { issue.id = data.iid; if (this.issuesSize > 1) { diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js index db9bced2f89..3742507b236 100644 --- a/app/assets/javascripts/boards/services/board_service.js +++ b/app/assets/javascripts/boards/services/board_service.js @@ -23,11 +23,6 @@ class BoardService { url: bulkUpdatePath, }, }); - - Vue.http.interceptors.push((request, next) => { - request.headers['X-CSRF-Token'] = $.rails.csrfToken(); - next(); - }); } all () { diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue index 3c77f14d533..6d31b78b36d 100644 --- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue +++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue @@ -51,11 +51,11 @@ }, methods: { successCallback(resp) { - const response = resp.json(); - - // depending of the endpoint the response can either bring a `pipelines` key or not. - const pipelines = response.pipelines || response; - this.setCommonData(pipelines); + return resp.json().then((response) => { + // depending of the endpoint the response can either bring a `pipelines` key or not. + const pipelines = response.pipelines || response; + this.setCommonData(pipelines); + }); }, }, }; diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js b/app/assets/javascripts/diff_notes/components/resolve_btn.js index 9d51fb53eb2..efb6ced9f46 100644 --- a/app/assets/javascripts/diff_notes/components/resolve_btn.js +++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js @@ -1,4 +1,4 @@ -/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, no-new, max-len */ +/* eslint-disable comma-dangle, object-shorthand, func-names, quote-props, no-else-return, camelcase, max-len */ /* global CommentsStore */ /* global ResolveService */ /* global Flash */ @@ -64,8 +64,6 @@ const ResolveBtn = Vue.extend({ }); }, resolve: function () { - const errorFlashMsg = 'An error occurred when trying to resolve a comment. Please try again.'; - if (!this.canResolve) return; let promise; @@ -79,24 +77,20 @@ const ResolveBtn = Vue.extend({ .resolve(this.noteId); } - promise.then((response) => { - this.loading = false; + promise + .then(resp => resp.json()) + .then((data) => { + this.loading = false; - if (response.status === 200) { - const data = response.json(); const resolved_by = data ? data.resolved_by : null; CommentsStore.update(this.discussionId, this.noteId, !this.isResolved, resolved_by); this.discussion.updateHeadline(data); gl.mrWidget.checkStatus(); - } else { - new Flash(errorFlashMsg); - } - this.updateTooltip(); - }).catch(() => { - new Flash(errorFlashMsg); - }); + this.updateTooltip(); + }) + .catch(() => new Flash('An error occurred when trying to resolve a comment. Please try again.')); } }, mounted: function () { diff --git a/app/assets/javascripts/diff_notes/services/resolve.js b/app/assets/javascripts/diff_notes/services/resolve.js index 807ab11d292..2f063f6fe1f 100644 --- a/app/assets/javascripts/diff_notes/services/resolve.js +++ b/app/assets/javascripts/diff_notes/services/resolve.js @@ -1,4 +1,3 @@ -/* eslint-disable class-methods-use-this, one-var, camelcase, no-new, comma-dangle, no-param-reassign, max-len */ /* global Flash */ /* global CommentsStore */ @@ -32,27 +31,22 @@ class ResolveServiceClass { promise = this.resolveAll(mergeRequestId, discussionId); } - promise.then((response) => { - discussion.loading = false; - - if (response.status === 200) { - const data = response.json(); - const resolved_by = data ? data.resolved_by : null; + promise + .then(resp => resp.json()) + .then((data) => { + discussion.loading = false; + const resolvedBy = data ? data.resolved_by : null; if (isResolved) { discussion.unResolveAllNotes(); } else { - discussion.resolveAllNotes(resolved_by); + discussion.resolveAllNotes(resolvedBy); } gl.mrWidget.checkStatus(); discussion.updateHeadline(data); - } else { - throw new Error('An error occurred when trying to resolve discussion.'); - } - }).catch(() => { - new Flash('An error occurred when trying to resolve a discussion. Please try again.'); - }); + }) + .catch(() => new Flash('An error occurred when trying to resolve a discussion. Please try again.')); } resolveAll(mergeRequestId, discussionId) { @@ -62,7 +56,7 @@ class ResolveServiceClass { return this.discussionResource.save({ mergeRequestId, - discussionId + discussionId, }, {}); } @@ -73,7 +67,7 @@ class ResolveServiceClass { return this.discussionResource.delete({ mergeRequestId, - discussionId + discussionId, }, {}); } } diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js index 25b24fbd6dc..8f4066e3a6e 100644 --- a/app/assets/javascripts/environments/mixins/environments_mixin.js +++ b/app/assets/javascripts/environments/mixins/environments_mixin.js @@ -1,17 +1,15 @@ export default { methods: { saveData(resp) { - const response = { - headers: resp.headers, - body: resp.json(), - }; + const headers = resp.headers; + return resp.json().then((response) => { + this.isLoading = false; - this.isLoading = false; - - this.store.storeAvailableCount(response.body.available_count); - this.store.storeStoppedCount(response.body.stopped_count); - this.store.storeEnvironments(response.body.environments); - this.store.setPagination(response.headers); + this.store.storeAvailableCount(response.available_count); + this.store.storeStoppedCount(response.stopped_count); + this.store.storeEnvironments(response.environments); + this.store.setPagination(headers); + }); }, }, }; diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js index ff601db2aa6..00e1bd94c9c 100644 --- a/app/assets/javascripts/groups/index.js +++ b/app/assets/javascripts/groups/index.js @@ -99,8 +99,10 @@ document.addEventListener('DOMContentLoaded', () => { page: currentPath, }, document.title, currentPath); - this.updateGroups(response.json()); - this.updatePagination(response.headers); + return response.json().then((data) => { + this.updateGroups(data); + this.updatePagination(response.headers); + }); }) .catch(this.handleErrorResponse); }, @@ -114,18 +116,19 @@ document.addEventListener('DOMContentLoaded', () => { }, leaveGroup(group, collection) { this.service.leaveGroup(group.leavePath) + .then(resp => resp.json()) .then((response) => { $.scrollTo(0); this.store.removeGroup(group, collection); // eslint-disable-next-line no-new - new Flash(response.json().notice, 'notice'); + new Flash(response.notice, 'notice'); }) - .catch((response) => { + .catch((error) => { let message = 'An error occurred. Please try again.'; - if (response.status === 403) { + if (error.status === 403) { message = 'Failed to leave the group. Please make sure you are not the only owner'; } diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index 3d5fb7f441c..efae112923d 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -202,10 +202,7 @@ export default { this.poll = new Poll({ resource: this.service, method: 'getData', - successCallback: (res) => { - const data = res.json(); - this.store.updateState(data); - }, + successCallback: res => res.json().then(data => this.store.updateState(data)), errorCallback(err) { throw new Error(err); }, diff --git a/app/assets/javascripts/jobs/job_details_mediator.js b/app/assets/javascripts/jobs/job_details_mediator.js index 063c52fac74..cc014b815c4 100644 --- a/app/assets/javascripts/jobs/job_details_mediator.js +++ b/app/assets/javascripts/jobs/job_details_mediator.js @@ -54,9 +54,8 @@ export default class JobMediator { } successCallback(response) { - const data = response.json(); this.state.isLoading = false; - this.store.storeJob(data); + return response.json().then(data => this.store.storeJob(data)); } errorCallback() { diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 1a68c5bca00..b2c503d1656 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -1270,7 +1270,7 @@ export default class Notes { <div class="timeline-entry-inner"> <div class="timeline-icon"> <a href="/${currentUsername}"> - <img class="avatar s40" src="${currentUserAvatar}"> + <img class="avatar s40" src="${currentUserAvatar}" /> </a> </div> <div class="timeline-content ${discussionClass}"> diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue index 01ae07aad65..5df317a76bf 100644 --- a/app/assets/javascripts/pipelines/components/pipelines.vue +++ b/app/assets/javascripts/pipelines/components/pipelines.vue @@ -129,14 +129,11 @@ }, successCallback(resp) { - const response = { - headers: resp.headers, - body: resp.json(), - }; - - this.store.storeCount(response.body.count); - this.store.storePagination(response.headers); - this.setCommonData(response.body.pipelines); + return resp.json().then((response) => { + this.store.storeCount(response.count); + this.store.storePagination(resp.headers); + this.setCommonData(response.pipelines); + }); }, }, }; diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index 87b2725a045..a4a27247406 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -73,8 +73,9 @@ export default { fetchJobs() { this.$http.get(this.stage.dropdown_path) - .then((response) => { - this.dropdownContent = response.json().html; + .then(response => response.json()) + .then((data) => { + this.dropdownContent = data.html; this.isLoading = false; }) .catch(() => { diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js b/app/assets/javascripts/pipelines/pipeline_details_mediatior.js index 82537ea06f5..385e7430a7d 100644 --- a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js +++ b/app/assets/javascripts/pipelines/pipeline_details_mediatior.js @@ -40,10 +40,10 @@ export default class pipelinesMediator { } successCallback(response) { - const data = response.json(); - - this.state.isLoading = false; - this.store.storePipeline(data); + return response.json().then((data) => { + this.state.isLoading = false; + this.store.storePipeline(data); + }); } errorCallback() { diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js index 5ccfb4ee9c1..721e92221cf 100644 --- a/app/assets/javascripts/sidebar/sidebar_mediator.js +++ b/app/assets/javascripts/sidebar/sidebar_mediator.js @@ -28,8 +28,8 @@ export default class SidebarMediator { fetch() { this.service.get() - .then((response) => { - const data = response.json(); + .then(response => response.json()) + .then((data) => { this.store.setAssigneeData(data); this.store.setTimeTrackingData(data); }) diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js index f8b3fb748ae..8430548903c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js @@ -92,13 +92,13 @@ export default { :class="{'label-truncated has-tooltip': isBranchTitleLong(mr.targetBranch)}" :title="isBranchTitleLong(mr.targetBranch) ? mr.targetBranch : ''" data-placement="bottom"> - <a :href="mr.targetBranchPath">{{mr.targetBranch}}</a> + <a :href="mr.targetBranchTreePath">{{mr.targetBranch}}</a> </span> </strong> <span v-if="shouldShowCommitsBehindText" class="diverged-commits-count"> - ({{mr.divergedCommitsCount}} {{commitsText}} behind) + (<a :href="mr.targetBranchPath">{{mr.divergedCommitsCount}} {{commitsText}} behind</a>) </span> </div> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 69bc1436284..72a13108404 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -48,6 +48,7 @@ export default class MergeRequestStore { this.sourceBranchLink = data.source_branch_with_namespace_link; this.mergeError = data.merge_error; this.targetBranchPath = data.target_branch_commits_path; + this.targetBranchTreePath = data.target_branch_tree_path; this.conflictResolutionPath = data.conflict_resolution_path; this.cancelAutoMergePath = data.cancel_merge_when_pipeline_succeeds_path; this.removeWIPPath = data.remove_wip_path; diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue index 8303c556f64..4e10bbc7408 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/field.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue @@ -44,9 +44,8 @@ text: this.$slots.textarea[0].elm.value, }, ) - .then((res) => { - const data = res.json(); - + .then(resp => resp.json()) + .then((data) => { this.markdownPreviewLoading = false; this.markdownPreview = data.body; diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js index 740930dce5b..7f8e514fda1 100644 --- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js +++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js @@ -14,11 +14,22 @@ Vue.http.interceptors.push((request, next) => { }); }); -// Inject CSRF token so we don't break any tests. +// Inject CSRF token and parse headers. +// New Vue Resource version uses Headers, we are expecting a plain object to render pagination +// and polling. Vue.http.interceptors.push((request, next) => { if ($.rails) { - // eslint-disable-next-line no-param-reassign - request.headers['X-CSRF-Token'] = $.rails.csrfToken(); + request.headers.set('X-CSRF-Token', $.rails.csrfToken()); } - next(); + + next((response) => { + // Headers object has a `forEach` property that iterates through all values. + const headers = {}; + + response.headers.forEach((value, key) => { + headers[key] = value; + }); + // eslint-disable-next-line no-param-reassign + response.headers = headers; + }); }); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 83a8eeaafde..0665622fe4a 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -42,4 +42,4 @@ /* * Styles for JS behaviors. */ -@import "behaviors.scss"; +@import "behaviors"; diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 9dc9f9a9068..6ce331a9129 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -4,49 +4,49 @@ @import 'framework/tw_bootstrap'; @import "framework/layout"; -@import "framework/animations.scss"; -@import "framework/avatar.scss"; -@import "framework/asciidoctor.scss"; -@import "framework/blocks.scss"; -@import "framework/buttons.scss"; -@import "framework/badges.scss"; -@import "framework/calendar.scss"; -@import "framework/callout.scss"; -@import "framework/common.scss"; -@import "framework/dropdowns.scss"; -@import "framework/files.scss"; -@import "framework/filters.scss"; -@import "framework/flash.scss"; -@import "framework/forms.scss"; -@import "framework/gfm.scss"; -@import "framework/header.scss"; -@import "framework/highlight.scss"; -@import "framework/issue_box.scss"; -@import "framework/jquery.scss"; -@import "framework/lists.scss"; -@import "framework/logo.scss"; -@import "framework/markdown_area.scss"; -@import "framework/mobile.scss"; -@import "framework/modal.scss"; -@import "framework/nav.scss"; -@import "framework/pagination.scss"; -@import "framework/panels.scss"; -@import "framework/selects.scss"; -@import "framework/sidebar.scss"; -@import "framework/tables.scss"; -@import "framework/notes.scss"; -@import "framework/timeline.scss"; -@import "framework/typography.scss"; -@import "framework/zen.scss"; +@import "framework/animations"; +@import "framework/avatar"; +@import "framework/asciidoctor"; +@import "framework/blocks"; +@import "framework/buttons"; +@import "framework/badges"; +@import "framework/calendar"; +@import "framework/callout"; +@import "framework/common"; +@import "framework/dropdowns"; +@import "framework/files"; +@import "framework/filters"; +@import "framework/flash"; +@import "framework/forms"; +@import "framework/gfm"; +@import "framework/header"; +@import "framework/highlight"; +@import "framework/issue_box"; +@import "framework/jquery"; +@import "framework/lists"; +@import "framework/logo"; +@import "framework/markdown_area"; +@import "framework/mobile"; +@import "framework/modal"; +@import "framework/nav"; +@import "framework/pagination"; +@import "framework/panels"; +@import "framework/selects"; +@import "framework/sidebar"; +@import "framework/tables"; +@import "framework/notes"; +@import "framework/timeline"; +@import "framework/typography"; +@import "framework/zen"; @import "framework/blank"; -@import "framework/wells.scss"; -@import "framework/page-header.scss"; -@import "framework/awards.scss"; -@import "framework/images.scss"; +@import "framework/wells"; +@import "framework/page-header"; +@import "framework/awards"; +@import "framework/images"; @import "framework/broadcast-messages"; -@import "framework/emojis.scss"; -@import "framework/emoji-sprites.scss"; -@import "framework/icons.scss"; -@import "framework/snippets.scss"; -@import "framework/memory_graph.scss"; -@import "framework/responsive-tables.scss"; +@import "framework/emojis"; +@import "framework/emoji-sprites"; +@import "framework/icons"; +@import "framework/snippets"; +@import "framework/memory_graph"; +@import "framework/responsive-tables"; diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss index 19166757e64..e474839d95c 100644 --- a/app/assets/stylesheets/framework/awards.scss +++ b/app/assets/stylesheets/framework/awards.scss @@ -24,7 +24,7 @@ opacity: 0; transform: scale(.2); transform-origin: 0 -45px; - transition: .3s cubic-bezier(.67,.06,.19,1.44); + transition: .3s cubic-bezier(.67, .06, .19, 1.44); transition-property: transform, opacity; &.is-aligned-right { diff --git a/app/assets/stylesheets/framework/blank.scss b/app/assets/stylesheets/framework/blank.scss index c0224d3bfa9..6bb096fc5bd 100644 --- a/app/assets/stylesheets/framework/blank.scss +++ b/app/assets/stylesheets/framework/blank.scss @@ -1,9 +1,5 @@ .blank-state-parent-container { - display: flex; - .section-container { - display: flex; - flex: 1; padding: 10px; } @@ -13,91 +9,42 @@ padding-bottom: 25px; border: 1px solid $border-color; border-radius: $border-radius-default; - - &.section-ee-trial { - display: flex; - align-items: center; - justify-content: center; - } - } -} - -.blank-state-welcome { - text-align: center; - - .blank-state-text { - margin-bottom: 0; } } .blank-state { padding-top: 20px; padding-bottom: 20px; -} - -.blank-state.ee-trial { - padding: 20px; text-align: center; -} - -.blank-state-no-icon { - padding-top: 40px; - padding-bottom: 40px; -} - -.blank-state-icon { - padding-bottom: 20px; - font-size: 56px; - svg { - display: block; - margin: auto; - } -} - -@media (min-width: $screen-sm-max) { - .section-welcome .blank-state-icon svg { - width: 130%; - } -} - -.blank-state-title { - margin-top: 0; - margin-bottom: 10px; - font-size: 18px; -} - -.blank-state-text { - margin-top: 0; - margin-bottom: $gl-padding; - font-size: 14px; + &.blank-state-welcome { + .blank-state-welcome-title { + font-size: 24px; + } - > strong { - font-weight: 600; + .blank-state-text { + margin-bottom: 0; + } } -} -.blank-state-welcome-title { - font-size: 24px; -} + .blank-state-icon { + padding-bottom: 20px; -@media (max-width: $screen-md-min) { - .blank-state-parent-container { - &, - .section-container { + svg { display: block; + margin: auto; } } - .blank-state { - text-align: center; + .blank-state-title { + margin-top: 0; + margin-bottom: 10px; + font-size: 18px; } - .blank-state-icon { - padding-bottom: 0; - } - - .blank-state-body { - margin-top: 15px; + .blank-state-text { + max-width: $container-text-max-width; + margin: 0 auto $gl-padding; + font-size: 14px; } } diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 00c981f64c5..5e374360359 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -336,11 +336,6 @@ table { text-align: center; } -#nprogress .spinner { - top: 15px !important; - right: 10px !important; -} - .header-with-avatar { h3 { margin: 0; @@ -450,4 +445,3 @@ table { pointer-events: none; opacity: .5; } - diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index f05348ee4e3..7e4e5fd7f1c 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -368,7 +368,7 @@ margin-right: 0.3em; } - & > .value { + > .value { font-weight: 600; } } @@ -467,7 +467,7 @@ -webkit-flex-direction: column; flex-direction: column; - &> span { + > span { white-space: normal; word-break: break-all; } diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss index 6d27d7568cf..71d5949b023 100644 --- a/app/assets/stylesheets/framework/highlight.scss +++ b/app/assets/stylesheets/framework/highlight.scss @@ -61,7 +61,7 @@ &:focus { outline: none; - & i { + i { visibility: visible; } } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 3f032776d82..3405f142428 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -176,6 +176,7 @@ $header-height: 50px; $fixed-layout-width: 1280px; $limited-layout-width: 990px; $limited-layout-width-sm: 790px; +$container-text-max-width: 540px; $gl-avatar-size: 40px; $error-exclamation-point: $red-500; $border-radius-default: 3px; @@ -316,7 +317,7 @@ $badge-color: $gl-text-color-secondary; /* * Award emoji */ -$award-emoji-menu-shadow: rgba(0,0,0,.175); +$award-emoji-menu-shadow: rgba(0, 0, 0, .175); $award-emoji-positive-add-bg: #fed159; $award-emoji-positive-add-lines: #bb9c13; @@ -567,7 +568,7 @@ $kdb-color: #555; $kdb-border: #ccc; $kdb-border-bottom: #bbb; $kdb-shadow: #bbb; -$body-text-shadow: rgba(255,255,255,0.01); +$body-text-shadow: rgba(255, 255, 255, 0.01); /* * UI Dev Kit diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss index 1daa10aef24..578f1902cce 100644 --- a/app/assets/stylesheets/highlight/white.scss +++ b/app/assets/stylesheets/highlight/white.scss @@ -113,7 +113,7 @@ $white-gc-bg: #eaf2f5; border-color: $line-removed-dark; a { - color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%); + color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%); } } @@ -122,7 +122,7 @@ $white-gc-bg: #eaf2f5; border-color: $line-added-dark; a { - color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%); + color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%); } } @@ -163,7 +163,7 @@ $white-gc-bg: #eaf2f5; background-color: $line-removed; &::before { - color: scale-color($line-number-old,$red: -30%, $green: -30%, $blue: -30%); + color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%); } span.idiff { @@ -175,7 +175,7 @@ $white-gc-bg: #eaf2f5; background-color: $line-added; &::before { - color: scale-color($line-number-new,$red: -30%, $green: -30%, $blue: -30%); + color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%); } span.idiff { diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss index 96459fe31cc..07b487cd090 100644 --- a/app/assets/stylesheets/new_sidebar.scss +++ b/app/assets/stylesheets/new_sidebar.scss @@ -2,12 +2,12 @@ @import 'framework/tw_bootstrap_variables'; @import "bootstrap/variables"; -$active-background: rgba(0,0,0,.04); +$active-background: rgba(0, 0, 0, .04); $active-border: $indigo-500; $active-color: $indigo-700; $active-hover-background: $active-background; $active-hover-color: $gl-text-color; -$inactive-badge-background: rgba(0,0,0,.08); +$inactive-badge-background: rgba(0, 0, 0, .08); $hover-background: $indigo-700; $hover-color: $white-light; $inactive-color: $gl-text-color-secondary; diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 23c06eca3c3..0393820dee6 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -6,26 +6,26 @@ @keyframes blinking-dots { 0% { background-color: rgba($white-light, 1); - box-shadow: 12px 0 0 0 rgba($white-light,0.2), - 24px 0 0 0 rgba($white-light,0.2); + box-shadow: 12px 0 0 0 rgba($white-light, 0.2), + 24px 0 0 0 rgba($white-light, 0.2); } 25% { background-color: rgba($white-light, 0.4); - box-shadow: 12px 0 0 0 rgba($white-light,2), - 24px 0 0 0 rgba($white-light,0.2); + box-shadow: 12px 0 0 0 rgba($white-light, 2), + 24px 0 0 0 rgba($white-light, 0.2); } 75% { background-color: rgba($white-light, 0.4); - box-shadow: 12px 0 0 0 rgba($white-light,0.2), - 24px 0 0 0 rgba($white-light,1); + box-shadow: 12px 0 0 0 rgba($white-light, 0.2), + 24px 0 0 0 rgba($white-light, 1); } 100% { background-color: rgba($white-light, 1); - box-shadow: 12px 0 0 0 rgba($white-light,0.2), - 24px 0 0 0 rgba($white-light,0.2); + box-shadow: 12px 0 0 0 rgba($white-light, 0.2), + 24px 0 0 0 rgba($white-light, 0.2); } } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 7adf17dddb8..2db967547dd 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -96,7 +96,7 @@ overflow: visible; } - & > span { + > span { padding-right: 4px; } @@ -125,7 +125,7 @@ .dropdown-menu { margin-top: 11px; - z-index: 200; + z-index: 300; } .ci-action-icon-wrapper { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 303425041df..64a48e226bc 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -250,7 +250,7 @@ ul.notes { } .note-text { - & p:first-child { + p:first-child { display: none; } diff --git a/app/assets/stylesheets/pages/pipeline_schedules.scss b/app/assets/stylesheets/pages/pipeline_schedules.scss index 7f893bcf68b..50e518e754d 100644 --- a/app/assets/stylesheets/pages/pipeline_schedules.scss +++ b/app/assets/stylesheets/pages/pipeline_schedules.scss @@ -96,12 +96,12 @@ } &:last-child { - & .pipeline-variable-row-remove-button { + .pipeline-variable-row-remove-button { display: none; } @media (max-width: $screen-sm-max) { - & .pipeline-variable-value-input { + .pipeline-variable-value-input { margin-right: $pipeline-variable-remove-button-width; } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 7d7c34115f9..46434eab8f3 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -26,7 +26,7 @@ margin-bottom: 5px; } - & > .form-group { + > .form-group { padding-left: 0; } @@ -83,7 +83,7 @@ border: 1px solid $border-color; } - & + .select2 a { + + .select2 a { border-top-left-radius: 0; border-bottom-left-radius: 0; } diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss index de652a79369..d7a9dda3770 100644 --- a/app/assets/stylesheets/pages/todos.scss +++ b/app/assets/stylesheets/pages/todos.scss @@ -81,7 +81,7 @@ .todo-title { display: flex; - & > .title-item { + > .title-item { -webkit-flex: 0 0 auto; flex: 0 0 auto; margin: 0 2px; diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss index 94d0a39f397..45c21c5d274 100644 --- a/app/assets/stylesheets/pages/wiki.scss +++ b/app/assets/stylesheets/pages/wiki.scss @@ -147,13 +147,13 @@ } ul.wiki-pages-list.content-list { - & ul { + ul { list-style: none; margin-left: 0; padding-left: 15px; } - & ul li { + ul li { padding: 5px 0; } } diff --git a/app/controllers/concerns/requires_health_token.rb b/app/controllers/concerns/requires_health_token.rb deleted file mode 100644 index 34ab1a97649..00000000000 --- a/app/controllers/concerns/requires_health_token.rb +++ /dev/null @@ -1,25 +0,0 @@ -module RequiresHealthToken - extend ActiveSupport::Concern - included do - before_action :validate_health_check_access! - end - - private - - def validate_health_check_access! - render_404 unless token_valid? - end - - def token_valid? - token = params[:token].presence || request.headers['TOKEN'] - token.present? && - ActiveSupport::SecurityUtils.variable_size_secure_compare( - token, - current_application_settings.health_check_access_token - ) - end - - def render_404 - render file: Rails.root.join('public', '404'), layout: false, status: '404' - end -end diff --git a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb new file mode 100644 index 00000000000..ad2f4bbc486 --- /dev/null +++ b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb @@ -0,0 +1,33 @@ +module RequiresWhitelistedMonitoringClient + extend ActiveSupport::Concern + included do + before_action :validate_ip_whitelisted_or_valid_token! + end + + private + + def validate_ip_whitelisted_or_valid_token! + render_404 unless client_ip_whitelisted? || valid_token? + end + + def client_ip_whitelisted? + ip_whitelist.any? { |e| e.include?(Gitlab::RequestContext.client_ip) } + end + + def ip_whitelist + @ip_whitelist ||= Settings.monitoring.ip_whitelist.map(&IPAddr.method(:new)) + end + + def valid_token? + token = params[:token].presence || request.headers['TOKEN'] + token.present? && + ActiveSupport::SecurityUtils.variable_size_secure_compare( + token, + current_application_settings.health_check_access_token + ) + end + + def render_404 + render file: Rails.root.join('public', '404'), layout: false, status: '404' + end +end diff --git a/app/controllers/health_check_controller.rb b/app/controllers/health_check_controller.rb index 5d3109b7187..c3d18991fd4 100644 --- a/app/controllers/health_check_controller.rb +++ b/app/controllers/health_check_controller.rb @@ -1,3 +1,3 @@ class HealthCheckController < HealthCheck::HealthCheckController - include RequiresHealthToken + include RequiresWhitelistedMonitoringClient end diff --git a/app/controllers/health_controller.rb b/app/controllers/health_controller.rb index abc832e6ddc..98c2aaa3526 100644 --- a/app/controllers/health_controller.rb +++ b/app/controllers/health_controller.rb @@ -1,10 +1,13 @@ class HealthController < ActionController::Base protect_from_forgery with: :exception - include RequiresHealthToken + include RequiresWhitelistedMonitoringClient CHECKS = [ Gitlab::HealthChecks::DbCheck, - Gitlab::HealthChecks::RedisCheck, + Gitlab::HealthChecks::Redis::RedisCheck, + Gitlab::HealthChecks::Redis::CacheCheck, + Gitlab::HealthChecks::Redis::QueuesCheck, + Gitlab::HealthChecks::Redis::SharedStateCheck, Gitlab::HealthChecks::FsShardsCheck ].freeze diff --git a/app/controllers/metrics_controller.rb b/app/controllers/metrics_controller.rb index 0e9a19c0b6f..37587a52eaf 100644 --- a/app/controllers/metrics_controller.rb +++ b/app/controllers/metrics_controller.rb @@ -1,12 +1,12 @@ class MetricsController < ActionController::Base - include RequiresHealthToken + include RequiresWhitelistedMonitoringClient protect_from_forgery with: :exception before_action :validate_prometheus_metrics def index - render text: metrics_service.metrics_text, content_type: 'text/plain; verssion=0.0.4' + render text: metrics_service.metrics_text, content_type: 'text/plain; version=0.0.4' end private diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index fb0fbb43fb1..c6d23898560 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -149,7 +149,7 @@ module Ci private def cleanup_runner_queue - Gitlab::Redis.with do |redis| + Gitlab::Redis::Queues.with do |redis| redis.del(runner_queue_key) end end diff --git a/app/models/project.rb b/app/models/project.rb index d5760164663..e50818e3dff 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1386,15 +1386,15 @@ class Project < ActiveRecord::Base end def pushes_since_gc - Gitlab::Redis.with { |redis| redis.get(pushes_since_gc_redis_key).to_i } + Gitlab::Redis::SharedState.with { |redis| redis.get(pushes_since_gc_redis_shared_state_key).to_i } end def increment_pushes_since_gc - Gitlab::Redis.with { |redis| redis.incr(pushes_since_gc_redis_key) } + Gitlab::Redis::SharedState.with { |redis| redis.incr(pushes_since_gc_redis_shared_state_key) } end def reset_pushes_since_gc - Gitlab::Redis.with { |redis| redis.del(pushes_since_gc_redis_key) } + Gitlab::Redis::SharedState.with { |redis| redis.del(pushes_since_gc_redis_shared_state_key) } end def route_map_for(commit_sha) @@ -1457,7 +1457,7 @@ class Project < ActiveRecord::Base from && self != from end - def pushes_since_gc_redis_key + def pushes_since_gc_redis_shared_state_key "projects/#{id}/pushes_since_gc" end diff --git a/app/models/user.rb b/app/models/user.rb index 4b01c2f19f0..2d39b1c1c34 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -699,7 +699,7 @@ class User < ActiveRecord::Base end def sanitize_attrs - %w[name username skype linkedin twitter].each do |attr| + %w[username skype linkedin twitter].each do |attr| value = public_send(attr) public_send("#{attr}=", Sanitize.clean(value)) if value.present? end diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb index 6ba1d3165e9..2df84e58575 100644 --- a/app/presenters/merge_request_presenter.rb +++ b/app/presenters/merge_request_presenter.rb @@ -76,6 +76,12 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated end end + def target_branch_tree_path + if target_branch_exists? + project_tree_path(project, target_branch) + end + end + def target_branch_commits_path if target_branch_exists? project_commits_path(project, target_branch) @@ -94,7 +100,7 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated if source_branch_exists? namespace = link_to(namespace, project_path(source_project)) - branch = link_to(branch, project_commits_path(source_project, source_branch)) + branch = link_to(branch, project_tree_path(source_project, source_branch)) end if for_fork? diff --git a/app/serializers/merge_request_entity.rb b/app/serializers/merge_request_entity.rb index 7ec2dbd0efe..7f17f2bf604 100644 --- a/app/serializers/merge_request_entity.rb +++ b/app/serializers/merge_request_entity.rb @@ -97,6 +97,10 @@ class MergeRequestEntity < IssuableEntity presenter(merge_request).target_branch_commits_path end + expose :target_branch_tree_path do |merge_request| + presenter(merge_request).target_branch_tree_path + end + expose :new_blob_path do |merge_request| if can?(current_user, :push_code, merge_request.project) project_new_blob_path(merge_request.project, merge_request.source_branch) diff --git a/app/services/metrics_service.rb b/app/services/metrics_service.rb index d726db4e99b..c92f070601c 100644 --- a/app/services/metrics_service.rb +++ b/app/services/metrics_service.rb @@ -3,7 +3,10 @@ require 'prometheus/client/formats/text' class MetricsService CHECKS = [ Gitlab::HealthChecks::DbCheck, - Gitlab::HealthChecks::RedisCheck, + Gitlab::HealthChecks::Redis::RedisCheck, + Gitlab::HealthChecks::Redis::CacheCheck, + Gitlab::HealthChecks::Redis::QueuesCheck, + Gitlab::HealthChecks::Redis::SharedStateCheck, Gitlab::HealthChecks::FsShardsCheck ].freeze diff --git a/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml b/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml index 0319838bdb4..209afd4aab4 100644 --- a/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml +++ b/app/views/dashboard/projects/_blank_state_admin_welcome.html.haml @@ -1,7 +1,7 @@ -.row.blank-state.clearfix - .col-md-1.col-md-offset-3.blank-state-icon +.blank-state + .blank-state-icon = custom_icon("add_new_user", size: 50) - .col-md-5.blank-state-body + .blank-state-body %h3.blank-state-title Add user %p.blank-state-text @@ -9,10 +9,10 @@ = link_to new_admin_user_path, class: "btn btn-new" do New user -.row.blank-state.clearfix - .col-md-1.col-md-offset-3.blank-state-icon +.blank-state + .blank-state-icon = custom_icon("configure_server", size: 50) - .col-md-5.blank-state-body + .blank-state-body %h3.blank-state-title Configure GitLab %p.blank-state-text @@ -21,10 +21,10 @@ Configure - if current_user.can_create_group? - .row.blank-state.clearfix - .col-md-1.col-md-offset-3.blank-state-icon + .blank-state + .blank-state-icon = custom_icon("add_new_group", size: 50) - .col-md-5.blank-state-body + .blank-state-body %h3.blank-state-title Create a group %p.blank-state-text diff --git a/app/views/dashboard/projects/_blank_state_welcome.html.haml b/app/views/dashboard/projects/_blank_state_welcome.html.haml index a079f0ac1a4..a93a3415ee1 100644 --- a/app/views/dashboard/projects/_blank_state_welcome.html.haml +++ b/app/views/dashboard/projects/_blank_state_welcome.html.haml @@ -1,10 +1,10 @@ - public_project_count = ProjectsFinder.new(current_user: current_user).execute.count - if current_user.can_create_group? - .row.blank-state.clearfix - .col-md-1.col-md-offset-3.blank-state-icon + .blank-state + .blank-state-icon = custom_icon("add_new_group", size: 50) - .col-md-5.blank-state-body + .blank-state-body %h3.blank-state-title Create a group for several dependent projects. %p.blank-state-text @@ -12,10 +12,10 @@ = link_to new_group_path, class: "btn btn-new" do New group -.row.blank-state.clearfix - .col-md-1.col-md-offset-3.blank-state-icon +.blank-state + .blank-state-icon = custom_icon("add_new_project", size: 50) - .col-md-5.blank-state-body + .blank-state-body %h3.blank-state-title Create a project %p.blank-state-text @@ -32,10 +32,10 @@ New project - if public_project_count > 0 - .row.blank-state.clearfix - .col-md-1.col-md-offset-3.blank-state-icon + .blank-state + .blank-state-icon = custom_icon("globe", size: 50) - .col-md-5.blank-state-body + .blank-state-body %h3.blank-state-title Explore public projects %p.blank-state-text diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml index 94af033c1e3..ad3fac6d164 100644 --- a/app/views/dashboard/projects/_zero_authorized_projects.html.haml +++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml @@ -1,6 +1,6 @@ .row.blank-state-parent-container - .section-container - .container.section-body.section-welcome{ class: "#{ 'section-admin-welcome' if current_user.admin? }" } + .section-container.section-welcome{ class: "#{ 'section-admin-welcome' if current_user.admin? }" } + .container.section-body .blank-state.blank-state-welcome %h2.blank-state-welcome-title Welcome to GitLab diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 615dd56afbd..48edbb8c16f 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -525,7 +525,7 @@ %h4 %code .file-holder - - blob = Snippet.new(content: "Wow\nSuch\nFile") + - blob = Snippet.new(content: "Wow\nSuch\nFile").blob .example .file-holder .js-file-title.file-title diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml index 5f1641f4300..b0c1ab7420f 100644 --- a/app/views/layouts/nav/_breadcrumbs.html.haml +++ b/app/views/layouts/nav/_breadcrumbs.html.haml @@ -2,7 +2,7 @@ - hide_top_links = @hide_top_links || false %nav.breadcrumbs{ role: "navigation" } - .breadcrumbs-container{ class: container_class } + .breadcrumbs-container{ class: [container_class, @content_class] } .breadcrumbs-links.js-title-container - unless hide_top_links .title diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml index cc731db3cc1..8838852803b 100644 --- a/app/views/layouts/nav/_new_project_sidebar.html.haml +++ b/app/views/layouts/nav/_new_project_sidebar.html.haml @@ -74,9 +74,9 @@ = nav_link(controller: @project.default_issues_tracker? ? [:issues, :labels, :milestones, :boards] : :issues) do = link_to project_issues_path(@project), title: 'Issues', class: 'shortcuts-issues' do %span - Issues - if @project.default_issues_tracker? %span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count) + Issues %ul.sidebar-sub-level-items - if project_nav_tab?(:issues) && !current_controller?(:merge_requests) @@ -112,8 +112,8 @@ = nav_link(controller: @project.default_issues_tracker? ? :merge_requests : [:merge_requests, :labels, :milestones]) do = link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do %span - Merge Requests %span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count) + Merge Requests - if project_nav_tab? :pipelines = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index b778e8af121..07c83c0a590 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,6 +1,7 @@ - @no_container = true - container_class = !fluid_layout && diff_view == :inline ? 'container-limited' : '' - limited_container_width = fluid_layout ? '' : 'limit-container-width' +- @content_class = limited_container_width - page_title "#{@commit.title} (#{@commit.short_id})", "Commits" - page_description @commit.description = render "projects/commits/head" diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 7b8be58554a..b0b7575f0d1 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -9,8 +9,9 @@ .col-lg-3.profile-settings-sidebar %h4.prepend-top-0 New project - %p - Create or Import your project from popular Git services + - if import_sources_enabled? + %p + Create or Import your project from popular Git services .col-lg-9 = form_for @project, html: { class: 'new_project' } do |f| .row diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml index 05fe80e5fed..c4ee064ac43 100644 --- a/app/views/projects/pipeline_schedules/index.html.haml +++ b/app/views/projects/pipeline_schedules/index.html.haml @@ -12,9 +12,10 @@ - schedule_path_proc = ->(scope) { pipeline_schedules_path(@project, scope: scope) } = render "tabs", schedule_path_proc: schedule_path_proc, all_schedules: @all_schedules, scope: @scope - .nav-controls - = link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do - %span= _('New schedule') + - if can?(current_user, :create_pipeline_schedule, @project) + .nav-controls + = link_to new_project_pipeline_schedule_path(@project), class: 'btn btn-create' do + %span= _('New schedule') - if @schedules.present? %ul.content-list diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index d413c4619be..a73e111ad6d 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- @content_class = "limit-container-width" unless fluid_layout - flash_message_container = show_new_nav? ? :new_global_flash : :flash_message = content_for :meta_tags do diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml index 130980556c1..f727f340bb9 100644 --- a/app/views/projects/tree/show.html.haml +++ b/app/views/projects/tree/show.html.haml @@ -1,4 +1,5 @@ - @no_container = true +- @content_class = "limit-container-width" unless fluid_layout - page_title @path.presence || _("Files"), @ref = content_for :meta_tags do diff --git a/changelogs/unreleased/10085-stop-encoding-user-name.yml b/changelogs/unreleased/10085-stop-encoding-user-name.yml new file mode 100644 index 00000000000..8fab474e047 --- /dev/null +++ b/changelogs/unreleased/10085-stop-encoding-user-name.yml @@ -0,0 +1,4 @@ +--- +title: "Insert user name directly without encoding" +merge_request: 10085 +author: Nathan Neulinger <nneul@neulinger.org> diff --git a/changelogs/unreleased/33580-fix-api-scoping.yml b/changelogs/unreleased/33580-fix-api-scoping.yml deleted file mode 100644 index f4ebb13c082..00000000000 --- a/changelogs/unreleased/33580-fix-api-scoping.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fix API Scoping -merge_request: 12300 -author: diff --git a/changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml b/changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml new file mode 100644 index 00000000000..a08795e1a26 --- /dev/null +++ b/changelogs/unreleased/33949-deprecate-healthcheck-access-token.yml @@ -0,0 +1,4 @@ +--- +title: Deprecate Healthcheck Access Token in favor of IP whitelist +merge_request: +author: diff --git a/changelogs/unreleased/34325-reinstate-is_admin-for-user-api.yml b/changelogs/unreleased/34325-reinstate-is_admin-for-user-api.yml new file mode 100644 index 00000000000..3bed1fbe16e --- /dev/null +++ b/changelogs/unreleased/34325-reinstate-is_admin-for-user-api.yml @@ -0,0 +1,4 @@ +--- +title: Return `is_admin` attribute in the GET /user endpoint for admins +merge_request: 12811 +author: diff --git a/changelogs/unreleased/34534-update-vue-resource.yml b/changelogs/unreleased/34534-update-vue-resource.yml new file mode 100644 index 00000000000..2d0af0c9bfe --- /dev/null +++ b/changelogs/unreleased/34534-update-vue-resource.yml @@ -0,0 +1,4 @@ +--- +title: Updates vue resource and code according to breaking changes +merge_request: +author: diff --git a/changelogs/unreleased/34729-blob.yml b/changelogs/unreleased/34729-blob.yml new file mode 100644 index 00000000000..15a469d3af0 --- /dev/null +++ b/changelogs/unreleased/34729-blob.yml @@ -0,0 +1,4 @@ +--- +title: Fix crash on /help/ui +merge_request: +author: diff --git a/changelogs/unreleased/34858-bump-scss-lint-to-0-54-0.yml b/changelogs/unreleased/34858-bump-scss-lint-to-0-54-0.yml new file mode 100644 index 00000000000..e6cd834aed2 --- /dev/null +++ b/changelogs/unreleased/34858-bump-scss-lint-to-0-54-0.yml @@ -0,0 +1,4 @@ +--- +title: Bump scss-lint to 0.54.0 +merge_request: 12733 +author: Takuya Noguchi diff --git a/changelogs/unreleased/34867-remove-net-ssh-gem.yml b/changelogs/unreleased/34867-remove-net-ssh-gem.yml new file mode 100644 index 00000000000..f5648d62467 --- /dev/null +++ b/changelogs/unreleased/34867-remove-net-ssh-gem.yml @@ -0,0 +1,4 @@ +--- +title: Remove net-ssh gem +merge_request: +author: Takuya Noguchi diff --git a/changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml b/changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml new file mode 100644 index 00000000000..22c9c45bc75 --- /dev/null +++ b/changelogs/unreleased/34907-dont-show-pipeline-schedule-button-for-non-member.yml @@ -0,0 +1,4 @@ +--- +title: Do not show pipeline schedule button for non-member +merge_request: 12757 +author: Takuya Noguchi diff --git a/changelogs/unreleased/34978-remove-public-ci-favicon-ico.yml b/changelogs/unreleased/34978-remove-public-ci-favicon-ico.yml new file mode 100644 index 00000000000..25cc8b5e45f --- /dev/null +++ b/changelogs/unreleased/34978-remove-public-ci-favicon-ico.yml @@ -0,0 +1,4 @@ +--- +title: Remove public/ci/favicon.ico +merge_request: 12803 +author: Takuya Noguchi diff --git a/changelogs/unreleased/dm-encode-tree-and-blob-paths.yml b/changelogs/unreleased/dm-encode-tree-and-blob-paths.yml deleted file mode 100644 index c1a026e1f29..00000000000 --- a/changelogs/unreleased/dm-encode-tree-and-blob-paths.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix issues with non-UTF8 filenames by always fixing the encoding of tree and - blob paths -merge_request: -author: diff --git a/changelogs/unreleased/enable-scss-lint-import-path.yml b/changelogs/unreleased/enable-scss-lint-import-path.yml new file mode 100644 index 00000000000..d158cf5b5f3 --- /dev/null +++ b/changelogs/unreleased/enable-scss-lint-import-path.yml @@ -0,0 +1,4 @@ +--- +title: Enable ImportPath in scss-lint +merge_request: 12749 +author: Takuya Noguchi diff --git a/changelogs/unreleased/enable-scss-lint-property-spelling.yml b/changelogs/unreleased/enable-scss-lint-property-spelling.yml new file mode 100644 index 00000000000..c5a5a4dddb6 --- /dev/null +++ b/changelogs/unreleased/enable-scss-lint-property-spelling.yml @@ -0,0 +1,4 @@ +--- +title: Enable PropertySpelling in scss-lint +merge_request: 12752 +author: Takuya Noguchi diff --git a/changelogs/unreleased/enable-scss-lint-space-after-comma.yml b/changelogs/unreleased/enable-scss-lint-space-after-comma.yml new file mode 100644 index 00000000000..210f34fbb87 --- /dev/null +++ b/changelogs/unreleased/enable-scss-lint-space-after-comma.yml @@ -0,0 +1,4 @@ +--- +title: Enable SpaceAfterComma in scss-lint +merge_request: 12734 +author: Takuya Noguchi diff --git a/changelogs/unreleased/enable-scss-lint-unnecessary-parent-reference.yml b/changelogs/unreleased/enable-scss-lint-unnecessary-parent-reference.yml new file mode 100644 index 00000000000..59d5df56525 --- /dev/null +++ b/changelogs/unreleased/enable-scss-lint-unnecessary-parent-reference.yml @@ -0,0 +1,4 @@ +--- +title: Enable UnnecessaryParentReference in scss-lint +merge_request: 12738 +author: Takuya Noguchi diff --git a/changelogs/unreleased/fix-n-plus-one-in-url-builder.yml b/changelogs/unreleased/fix-n-plus-one-in-url-builder.yml new file mode 100644 index 00000000000..5781316cfd9 --- /dev/null +++ b/changelogs/unreleased/fix-n-plus-one-in-url-builder.yml @@ -0,0 +1,4 @@ +--- +title: Improve issue rendering performance with lots of notes from other users +merge_request: +author: diff --git a/changelogs/unreleased/issue-description-gfm.yml b/changelogs/unreleased/issue-description-gfm.yml deleted file mode 100644 index 4d421bff677..00000000000 --- a/changelogs/unreleased/issue-description-gfm.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Fixed GFM references not being included when updating issues inline -merge_request: -author: diff --git a/changelogs/unreleased/mr-branch-link-use-tree.yml b/changelogs/unreleased/mr-branch-link-use-tree.yml new file mode 100644 index 00000000000..f4c4d9f5082 --- /dev/null +++ b/changelogs/unreleased/mr-branch-link-use-tree.yml @@ -0,0 +1,4 @@ +--- +title: MR branch link now links to tree instead of commits +merge_request: +author: diff --git a/changelogs/unreleased/remove-nprogress-gleaning.yml b/changelogs/unreleased/remove-nprogress-gleaning.yml new file mode 100644 index 00000000000..78e4dc82dd4 --- /dev/null +++ b/changelogs/unreleased/remove-nprogress-gleaning.yml @@ -0,0 +1,4 @@ +--- +title: Remove CSS for nprogress removed +merge_request: 12737 +author: Takuya Noguchi diff --git a/changelogs/unreleased/replace_spinach_spec_browse_files.yml b/changelogs/unreleased/replace_spinach_spec_browse_files.yml new file mode 100644 index 00000000000..7380d39fa9f --- /dev/null +++ b/changelogs/unreleased/replace_spinach_spec_browse_files.yml @@ -0,0 +1,4 @@ +--- +title: Replace 'browse_files.feature' spinach test with an rspec analog +merge_request: 12251 +author: @blackst0ne diff --git a/changelogs/unreleased/sh-add-mr-simple-mode.yml b/changelogs/unreleased/sh-add-mr-simple-mode.yml new file mode 100644 index 00000000000..0033ca28444 --- /dev/null +++ b/changelogs/unreleased/sh-add-mr-simple-mode.yml @@ -0,0 +1,4 @@ +--- +title: Add a simple mode to merge request API +merge_request: +author: diff --git a/changelogs/unreleased/toggle-new-project-import-description.yml b/changelogs/unreleased/toggle-new-project-import-description.yml new file mode 100644 index 00000000000..8f0d09e0540 --- /dev/null +++ b/changelogs/unreleased/toggle-new-project-import-description.yml @@ -0,0 +1,4 @@ +--- +title: Toggle import description with import_sources_enabled +merge_request: 12691 +author: Brianna Kicia
\ No newline at end of file diff --git a/changelogs/unreleased/username-password-stripped-from-import-url-fix.yml b/changelogs/unreleased/username-password-stripped-from-import-url-fix.yml deleted file mode 100644 index 571279d3dc7..00000000000 --- a/changelogs/unreleased/username-password-stripped-from-import-url-fix.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -title: Username and password are no longer stripped from import url on mirror update -merge_request: 12725 -author: diff --git a/config/README.md b/config/README.md index 0a5ea2424e0..2778d0d4f02 100644 --- a/config/README.md +++ b/config/README.md @@ -19,4 +19,132 @@ an ERB file and then loads the resulting YML as its configuration. This file is called `resque.yml` for historical reasons. We are **NOT** using Resque at the moment. It is used to specify Redis configuration -values instead. +values when a single database instance of Redis is desired. + +# Advanced Redis configuration files + +In more advanced configurations of Redis key-value storage, it is desirable +to separate the keys by lifecycle and intended use to ease provisioning and +management of scalable Redis clusters. + +These settings provide routing and other configuration data (such as sentinel, +persistence policies, and other Redis customization) for connections +to Redis single instances, Redis sentinel, and Redis clusters. + +If desired, the routing URL provided by these settings can be used with: +1. Unix Socket + 1. named socket for each Redis instance desired. + 2. `database number` for each Redis instance desired. +2. TCP Socket + 1. `host name` or IP for each Redis instance desired + 2. TCP port number for each Redis instance desired + 3. `database number` for each Redis instance desired + +## Example URL attribute formats for GitLab Redis `.yml` configuration files +* Unix Socket, default Redis database (0) + * `url: unix:/path/to/redis.sock` + * `url: unix:/path/to/redis.sock?db=` +* Unix Socket, Redis database 44 + * `url: unix:/path/to/redis.sock?db=44` + * `url: unix:/path/to/redis.sock?extra=foo&db=44` +* TCP Socket for Redis on localhost, port 6379, database 33 + * `url: redis://:mynewpassword@localhost:6379/33` +* TCP Socket for Redis on remote host `myserver`, port 6379, database 33 + * `url: redis://:mynewpassword@myserver:6379/33` + +## redis.cache.yml + +If configured, `redis.cache.yml` overrides the +`resque.yml` settings to configure the Redis database instance +used for `Rails.cache` and other volatile non-persistent data which enhances +the performance of GitLab. +Settings here can be overridden by the environment variable +`GITLAB_REDIS_CACHE_CONFIG_FILE` which provides +an alternate location for configuration settings. + +The order of precedence for the URL used to connect to the Redis instance +used for `cache` is: +1. URL from a configuration file pointed to by the +`GITLAB_REDIS_CACHE_CONFIG_FILE` environment variable +2. URL from `redis.cache.yml` +3. URL from a configuration file pointed to by the +`GITLAB_REDIS_CONFIG_FILE` environment variable +4. URL from `resque.yml` +5. `redis://localhost:6380` + +The order of precedence for all other configuration settings for `cache` +are selected from only the first of the following files found (if a setting +is not provided in an earlier file, the remainder of the files are not +searched): +1. the configuration file pointed to by the +`GITLAB_REDIS_CACHE_CONFIG_FILE` environment variable +2. the configuration file `redis.cache.yml` +3. the configuration file pointed to by the +`GITLAB_REDIS_CONFIG_FILE` environment variable +4. the configuration file `resque.yml` + +## redis.queues.yml + +If configured, `redis.queues.yml` overrides the +`resque.yml` settings to configure the Redis database instance +used for clients of `::Gitlab::Redis::Queues`. +These queues are intended to be the foundation +of reliable inter-process communication between modules, whether on the same +host node, or within a cluster. The primary clients of the queues are +SideKiq, Mailroom, CI Runner, Workhorse, and push services. Settings here can +be overridden by the environment variable +`GITLAB_REDIS_QUEUES_CONFIG_FILE` which provides an alternate location for +configuration settings. + +The order of precedence for the URL used to connect to the Redis instance +used for `queues` is: +1. URL from a configuration file pointed to by the +`GITLAB_REDIS_QUEUES_CONFIG_FILE` environment variable +2. URL from `redis.queues.yml` +3. URL from a configuration file pointed to by the +`GITLAB_REDIS_CONFIG_FILE` environment variable +4. URL from `resque.yml` +5. `redis://localhost:6381` + +The order of precedence for all other configuration settings for `queues` +are selected from only the first of the following files found (if a setting +is not provided in an earlier file, the remainder of the files are not +searched): +1. the configuration file pointed to by the +`GITLAB_REDIS_QUEUES_CONFIG_FILE` environment variable +2. the configuration file `redis.queues.yml` +3. the configuration file pointed to by the +`GITLAB_REDIS_CONFIG_FILE` environment variable +4. the configuration file `resque.yml` + +## redis.shared_state.yml + +If configured, `redis.shared_state.yml` overrides the +`resque.yml` settings to configure the Redis database instance +used for clients of `::Gitlab::Redis::SharedState` such as session state, +and rate limiting. +Settings here can be overridden by the environment variable +`GITLAB_REDIS_SHARED_STATE_CONFIG_FILE` which provides +an alternate location for configuration settings. + +The order of precedence for the URL used to connect to the Redis instance +used for `shared_state` is: +1. URL from a configuration file pointed to by the +`GITLAB_REDIS_SHARED_STATE_CONFIG_FILE` environment variable +2. URL from `redis.shared_state.yml` +3. URL from a configuration file pointed to by the +`GITLAB_REDIS_CONFIG_FILE` environment variable +4. URL from `resque.yml` +5. `redis://localhost:6382` + +The order of precedence for all other configuration settings for `shared_state` +are selected from only the first of the following files found (if a setting +is not provided in an earlier file, the remainder of the files are not +searched): +1. the configuration file pointed to by the +`GITLAB_REDIS_SHARED_STATE_CONFIG_FILE` environment variable +2. the configuration file `redis.shared_state.yml` +3. the configuration file pointed to by the +`GITLAB_REDIS_CONFIG_FILE` environment variable +4. the configuration file `resque.yml` + diff --git a/config/application.rb b/config/application.rb index 2f4e2624195..1c13cc81270 100644 --- a/config/application.rb +++ b/config/application.rb @@ -6,7 +6,9 @@ Bundler.require(:default, Rails.env) module Gitlab class Application < Rails::Application - require_dependency Rails.root.join('lib/gitlab/redis') + require_dependency Rails.root.join('lib/gitlab/redis/cache') + require_dependency Rails.root.join('lib/gitlab/redis/queues') + require_dependency Rails.root.join('lib/gitlab/redis/shared_state') require_dependency Rails.root.join('lib/gitlab/request_context') # Settings in config/environments/* take precedence over those specified here. @@ -142,15 +144,15 @@ module Gitlab end end - # Use Redis caching across all environments - redis_config_hash = Gitlab::Redis.params - redis_config_hash[:namespace] = Gitlab::Redis::CACHE_NAMESPACE - redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever + # Use caching across all environments + caching_config_hash = Gitlab::Redis::Cache.params + caching_config_hash[:namespace] = Gitlab::Redis::Cache::CACHE_NAMESPACE + caching_config_hash[:expires_in] = 2.weeks # Cache should not grow forever if Sidekiq.server? # threaded context - redis_config_hash[:pool_size] = Sidekiq.options[:concurrency] + 5 - redis_config_hash[:pool_timeout] = 1 + caching_config_hash[:pool_size] = Sidekiq.options[:concurrency] + 5 + caching_config_hash[:pool_timeout] = 1 end - config.cache_store = :redis_store, redis_config_hash + config.cache_store = :redis_store, caching_config_hash config.active_record.raise_in_transactional_callbacks = true diff --git a/config/database.yml.mysql b/config/database.yml.mysql index db1b712d3bc..eb71d3f5fe1 100644 --- a/config/database.yml.mysql +++ b/config/database.yml.mysql @@ -42,3 +42,4 @@ test: &test password: # host: localhost # socket: /tmp/mysql.sock + prepared_statements: false diff --git a/config/database.yml.postgresql b/config/database.yml.postgresql index c517a4c0cb8..4b30982fe82 100644 --- a/config/database.yml.postgresql +++ b/config/database.yml.postgresql @@ -46,3 +46,4 @@ test: &test username: postgres password: # host: localhost + prepared_statements: false diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 221e3d6e03b..d0ab2dab0af 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -539,10 +539,15 @@ production: &base # enabled: true # host: localhost # port: 3808 - prometheus: + + ## Monitoring + # Built in monitoring settings + monitoring: # Time between sampling of unicorn socket metrics, in seconds # unicorn_sampler_interval: 10 - + # IP whitelist to access monitoring endpoints + ip_whitelist: + - 127.0.0.0/8 # # 5. Extra customization diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index fa33e602e93..eb4a1e390a9 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -494,10 +494,11 @@ Settings.webpack.dev_server['host'] ||= 'localhost' Settings.webpack.dev_server['port'] ||= 3808 # -# Prometheus metrics settings +# Monitoring settings # -Settings['prometheus'] ||= Settingslogic.new({}) -Settings.prometheus['unicorn_sampler_interval'] ||= 10 +Settings['monitoring'] ||= Settingslogic.new({}) +Settings.monitoring['ip_whitelist'] ||= ['127.0.0.1/8'] +Settings.monitoring['unicorn_sampler_interval'] ||= 10 # # Testing settings diff --git a/config/initializers/7_redis.rb b/config/initializers/7_redis.rb index ae2ca258df1..af4967521b8 100644 --- a/config/initializers/7_redis.rb +++ b/config/initializers/7_redis.rb @@ -1,3 +1,8 @@ -# Make sure we initialize a Redis connection pool before Sidekiq starts -# multi-threaded execution. -Gitlab::Redis.with { nil } +# Make sure we initialize a Redis connection pool before multi-threaded +# execution starts by +# 1. Sidekiq +# 2. Rails.cache +# 3. HTTP clients +Gitlab::Redis::Cache.with { nil } +Gitlab::Redis::Queues.with { nil } +Gitlab::Redis::SharedState.with { nil } diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb index d56fd7a6cfa..5e34aac6527 100644 --- a/config/initializers/8_metrics.rb +++ b/config/initializers/8_metrics.rb @@ -119,7 +119,7 @@ def instrument_classes(instrumentation) end # rubocop:enable Metrics/AbcSize -Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.prometheus.unicorn_sampler_interval).start +Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_sampler_interval).start Gitlab::Application.configure do |config| # 0 should be Sentry to catch errors in this middleware diff --git a/config/initializers/peek.rb b/config/initializers/peek.rb index 65432caac2a..da8282ec924 100644 --- a/config/initializers/peek.rb +++ b/config/initializers/peek.rb @@ -1,4 +1,4 @@ -Rails.application.config.peek.adapter = :redis, { client: ::Redis.new(Gitlab::Redis.params) } +Rails.application.config.peek.adapter = :redis, { client: ::Redis.new(Gitlab::Redis::Cache.params) } Peek.into Peek::Views::Host Peek.into Peek::Views::PerformanceBar diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 8919f7640fe..e8213ac8ba4 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -19,12 +19,12 @@ cookie_key = if Rails.env.development? if Rails.env.test? Gitlab::Application.config.session_store :cookie_store, key: "_gitlab_session" else - redis_config = Gitlab::Redis.params - redis_config[:namespace] = Gitlab::Redis::SESSION_NAMESPACE + sessions_config = Gitlab::Redis::SharedState.params + sessions_config[:namespace] = Gitlab::Redis::SharedState::SESSION_NAMESPACE Gitlab::Application.config.session_store( :redis_store, # Using the cookie_store would enable session replay attacks. - servers: redis_config, + servers: sessions_config, key: cookie_key, secure: Gitlab.config.gitlab.https, httponly: true, diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 3be4cd797aa..a1cc9655319 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -1,12 +1,12 @@ -# Custom Redis configuration -redis_config_hash = Gitlab::Redis.params -redis_config_hash[:namespace] = Gitlab::Redis::SIDEKIQ_NAMESPACE +# Custom Queues configuration +queues_config_hash = Gitlab::Redis::Queues.params +queues_config_hash[:namespace] = Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE # Default is to retry 25 times with exponential backoff. That's too much. Sidekiq.default_worker_options = { retry: 3 } Sidekiq.configure_server do |config| - config.redis = redis_config_hash + config.redis = queues_config_hash config.server_middleware do |chain| chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS'] @@ -54,7 +54,7 @@ Sidekiq.configure_server do |config| end Sidekiq.configure_client do |config| - config.redis = redis_config_hash + config.redis = queues_config_hash config.client_middleware do |chain| chain.add Gitlab::SidekiqStatus::ClientMiddleware diff --git a/config/mail_room.yml b/config/mail_room.yml index 88d93d4bc6b..c3a5be8d38c 100644 --- a/config/mail_room.yml +++ b/config/mail_room.yml @@ -21,7 +21,7 @@ :delivery_method: sidekiq :delivery_options: :redis_url: <%= config[:redis_url].to_json %> - :namespace: <%= Gitlab::Redis::SIDEKIQ_NAMESPACE %> + :namespace: <%= Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE %> :queue: email_receiver :worker: EmailReceiverWorker <% if config[:sentinels] %> @@ -36,7 +36,7 @@ :arbitration_method: redis :arbitration_options: :redis_url: <%= config[:redis_url].to_json %> - :namespace: <%= Gitlab::Redis::MAILROOM_NAMESPACE %> + :namespace: <%= Gitlab::Redis::Queues::MAILROOM_NAMESPACE %> <% if config[:sentinels] %> :sentinels: <% config[:sentinels].each do |sentinel| %> diff --git a/config/redis.cache.yml.example b/config/redis.cache.yml.example new file mode 100644 index 00000000000..27478f0a93e --- /dev/null +++ b/config/redis.cache.yml.example @@ -0,0 +1,38 @@ +# If you change this file in a Merge Request, please also create +# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests +# +development: + url: redis://localhost:6379/10 + # + # url: redis://localhost:6380 + # sentinels: + # - + # host: localhost + # port: 26380 # point to sentinel, not to redis port + # - + # host: slave2 + # port: 26380 # point to sentinel, not to redis port +test: + url: redis://localhost:6379/10 + # + # url: redis://localhost:6380 +production: + # Redis (single instance) + url: unix:/var/run/redis/redis.cache.sock + ## + # Redis + Sentinel (for HA) + # + # Please read instructions carefully before using it as you may lose data: + # http://redis.io/topics/sentinel + # + # You must specify a list of a few sentinels that will handle client connection + # please read here for more information: https://docs.gitlab.com/ce/administration/high_availability/redis.html + ## + # url: redis://master:6380 + # sentinels: + # - + # host: slave1 + # port: 26380 # point to sentinel, not to redis port + # - + # host: slave2 + # port: 26380 # point to sentinel, not to redis port diff --git a/config/redis.queues.yml.example b/config/redis.queues.yml.example new file mode 100644 index 00000000000..dab1f26b096 --- /dev/null +++ b/config/redis.queues.yml.example @@ -0,0 +1,38 @@ +# If you change this file in a Merge Request, please also create +# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests +# +development: + url: redis://localhost:6379/11 + # + # url: redis://localhost:6381 + # sentinels: + # - + # host: localhost + # port: 26381 # point to sentinel, not to redis port + # - + # host: slave2 + # port: 26381 # point to sentinel, not to redis port +test: + url: redis://localhost:6379/11 + # + # url: redis://localhost:6381 +production: + # Redis (single instance) + url: unix:/var/run/redis/redis.queues.sock + ## + # Redis + Sentinel (for HA) + # + # Please read instructions carefully before using it as you may lose data: + # http://redis.io/topics/sentinel + # + # You must specify a list of a few sentinels that will handle client connection + # please read here for more information: https://docs.gitlab.com/ce/administration/high_availability/redis.html + ## + # url: redis://master:6381 + # sentinels: + # - + # host: slave1 + # port: 26381 # point to sentinel, not to redis port + # - + # host: slave2 + # port: 26381 # point to sentinel, not to redis port diff --git a/config/redis.shared_state.yml.example b/config/redis.shared_state.yml.example new file mode 100644 index 00000000000..9371e3619b7 --- /dev/null +++ b/config/redis.shared_state.yml.example @@ -0,0 +1,38 @@ +# If you change this file in a Merge Request, please also create +# a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests +# +development: + url: redis://localhost:6379/12 + # + # url: redis://localhost:6382 + # sentinels: + # - + # host: localhost + # port: 26382 # point to sentinel, not to redis port + # - + # host: slave2 + # port: 26382 # point to sentinel, not to redis port +test: + url: redis://localhost:6379/12 + # + # url: redis://localhost:6382 +production: + # Redis (single instance) + url: unix:/var/run/redis/redis.shared_state.sock + ## + # Redis + Sentinel (for HA) + # + # Please read instructions carefully before using it as you may lose data: + # http://redis.io/topics/sentinel + # + # You must specify a list of a few sentinels that will handle client connection + # please read here for more information: https://docs.gitlab.com/ce/administration/high_availability/redis.html + ## + # url: redis://master:6382 + # sentinels: + # - + # host: slave1 + # port: 26382 # point to sentinel, not to redis port + # - + # host: slave2 + # port: 26382 # point to sentinel, not to redis port diff --git a/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb b/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb index 397a9a2d28e..cb1b4f1855d 100644 --- a/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb +++ b/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb @@ -56,7 +56,7 @@ class MigrateUserActivitiesToUsersLastActivityOn < ActiveRecord::Migration end def activities(from, to, page: 1) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| redis.zrangebyscore(USER_ACTIVITY_SET_KEY, from.to_i, to.to_i, with_scores: true, limit: limit(page)) @@ -64,7 +64,7 @@ class MigrateUserActivitiesToUsersLastActivityOn < ActiveRecord::Migration end def activities_count(from, to) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| redis.zcount(USER_ACTIVITY_SET_KEY, from.to_i, to.to_i) end end diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md index fe982ea83c2..8b7a515a076 100644 --- a/doc/administration/high_availability/redis_source.md +++ b/doc/administration/high_availability/redis_source.md @@ -4,6 +4,11 @@ This is the documentation for configuring a Highly Available Redis setup when you have installed Redis all by yourself and not using the bundled one that comes with the Omnibus packages. +Note also that you may elect to override all references to +`/home/git/gitlab/config/resque.yml` in accordance with the advanced Redis +settings outlined in +[Configuration Files Documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/README.md). + We cannot stress enough the importance of reading the [Overview section](redis.md#overview) of the Omnibus Redis HA as it provides some invaluable information to the configuration of Redis. Please proceed to diff --git a/doc/administration/operations/cleaning_up_redis_sessions.md b/doc/administration/operations/cleaning_up_redis_sessions.md index 93521e976d5..3a35aff8366 100644 --- a/doc/administration/operations/cleaning_up_redis_sessions.md +++ b/doc/administration/operations/cleaning_up_redis_sessions.md @@ -15,6 +15,12 @@ prefixed with 'session:gitlab:', so they would look like 'session:gitlab:976aa289e2189b17d7ef525a6702ace9'. Below we describe how to remove the keys in the old format. +**Note:** the instructions below must be modified in accordance with your +configuration settings if you have used the advanced Redis +settings outlined in +[Configuration Files Documentation](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/README.md). + + First we define a shell function with the proper Redis connection details. ``` diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 3dc808c196d..c90d95e4dd0 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -25,6 +25,7 @@ Parameters: | `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | | `milestone` | string | no | Return merge requests for a specific milestone | +| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request | | `labels` | string | no | Return merge requests matching a comma separated list of labels | | `created_after` | datetime | no | Return merge requests created after the given time (inclusive) | | `created_before` | datetime | no | Return merge requests created before the given time (inclusive) | diff --git a/doc/api/users.md b/doc/api/users.md index 91170e79645..6e5ec3231c5 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -364,7 +364,7 @@ GET /user Parameters: -- `sudo` (required) - the ID of a user +- `sudo` (optional) - the ID of a user to make the call in their place ``` GET /user diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md index a984bb6c94c..0742b202807 100644 --- a/doc/development/fe_guide/vue.md +++ b/doc/development/fe_guide/vue.md @@ -112,7 +112,50 @@ Vue Resource should only be imported in the service file. Vue.use(VueResource); ``` -### CSRF token +#### Vue-resource gotchas +#### Headers +Headers are being parsed into a plain object in an interceptor. +In Vue-resource 1.x `headers` object was changed into an `Headers` object. In order to not change all old code, an interceptor was added. + +If you need to write a unit test that takes the headers in consideration, you need to include an interceptor to parse the headers after your test interceptor. +You can see an example in `spec/javascripts/environments/environment_spec.js`: + ```javascript + import { headersInterceptor } from './helpers/vue_resource_helper'; + + beforeEach(() => { + Vue.http.interceptors.push(myInterceptor); + Vue.http.interceptors.push(headersInterceptor); + }); + + afterEach(() => { + Vue.http.interceptors = _.without(Vue.http.interceptors, myInterceptor); + Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor); + }); + ``` + +#### `.json()` +When making a request to the server, you will most likely need to access the body of the response. +Use `.json()` to convert. Because `.json()` returns a Promise the follwoing structure should be used: + + ```javascript + service.get('url') + .then(resp => resp.json()) + .then((data) => { + this.store.storeData(data); + }) + .catch(() => new Flash('Something went wrong')); + ``` + +When using `Poll` (`app/assets/javascripts/lib/utils/poll.js`), the `successCallback` needs to handle `.json()` as a Promise: + ```javascript + successCallback: (response) => { + return response.json().then((data) => { + // handle the response + }); + } + ``` + +#### CSRF token We use a Vue Resource interceptor to manage the CSRF token. `app/assets/javascripts/vue_shared/vue_resource_interceptor.js` holds all our common interceptors. Note: You don't need to load `app/assets/javascripts/vue_shared/vue_resource_interceptor.js` @@ -126,13 +169,13 @@ The following example shows an application: // store.js export default class Store { - /** + /** * This is where we will iniatialize the state of our data. * Usually in a small SPA you don't need any options when starting the store. In the case you do * need guarantee it's an Object and it's documented. - * - * @param {Object} options - */ + * + * @param {Object} options + */ constructor(options) { this.options = options; @@ -205,14 +248,14 @@ import Store from 'store'; import Service from 'service'; import TodoComponent from 'todoComponent'; export default { - /** + /** * Although most data belongs in the store, each component it's own state. * We want to show a loading spinner while we are fetching the todos, this state belong * in the component. * * We need to access the store methods through all methods of our component. * We need to access the state of our store. - */ + */ data() { const store = new Store(); @@ -396,42 +439,46 @@ need to test the rendered output. [Vue][vue-test] guide's to unit test show us e [Vue Resource Interceptors][vue-resource-interceptor] allow us to add a interceptor with the response we need: -```javascript - // Mock the service to return data - const interceptor = (request, next) => { - next(request.respondWith(JSON.stringify([{ - title: 'This is a todo', - body: 'This is the text' - }]), { - status: 200, - })); - }; + ```javascript + // Mock the service to return data + const interceptor = (request, next) => { + next(request.respondWith(JSON.stringify([{ + title: 'This is a todo', + body: 'This is the text' + }]), { + status: 200, + })); + }; - beforeEach(() => { - Vue.http.interceptors.push(interceptor); - }); + beforeEach(() => { + Vue.http.interceptors.push(interceptor); + }); - afterEach(() => { - Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor); - }); + afterEach(() => { + Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor); + }); - it('should do something', (done) => { - setTimeout(() => { - // Test received data - done(); - }, 0); - }); -``` + it('should do something', (done) => { + setTimeout(() => { + // Test received data + done(); + }, 0); + }); + ``` + +1. Headers interceptor +Refer to [this section](vue.md#headers) 1. Use `$.mount()` to mount the component + ```javascript - // bad - new Component({ - el: document.createElement('div') - }); +// bad +new Component({ + el: document.createElement('div') +}); - // good - new Component().$mount(); +// good +new Component().$mount(); ``` [vue-docs]: http://vuejs.org/guide/index.html diff --git a/doc/install/installation.md b/doc/install/installation.md index dfa25deb961..5e981b0b3e7 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -294,9 +294,9 @@ sudo usermod -aG redis git ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 9-3-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 9-4-stable gitlab -**Note:** You can change `9-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `9-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It diff --git a/doc/install/requirements.md b/doc/install/requirements.md index a3d676433e6..141df55f6bc 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -69,7 +69,7 @@ so keep in mind that you need at least 4GB available before running GitLab. With less memory GitLab will give strange errors during the reconfigure run and 500 errors during usage. -- 1GB RAM + 3GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the unicorn worker section below for more advice. +- 1GB RAM + 3GB of swap is the absolute minimum but we strongly **advise against** this amount of memory. See the [unicorn worker section below](#unicorn-workers) for more advice. - 2GB RAM + 2GB swap supports up to 100 users but it will be very slow - **4GB RAM** is the **recommended** memory size for all installations and supports up to 100 users - 8GB RAM supports up to 1,000 users diff --git a/doc/update/9.3-to-9.4.md b/doc/update/9.3-to-9.4.md index bbb7f4a8d48..6962d124c80 100644 --- a/doc/update/9.3-to-9.4.md +++ b/doc/update/9.3-to-9.4.md @@ -72,8 +72,8 @@ More information can be found on the [yarn website](https://yarnpkg.com/en/docs/ ### 5. Update Go -NOTE: GitLab 9.4 and higher only supports Go 1.8.3 and dropped support for Go 1.5.x through 1.7.x. Be -sure to upgrade your installation if necessary +NOTE: GitLab 9.2 and higher only supports Go 1.8.3 and dropped support for Go +1.5.x through 1.7.x. Be sure to upgrade your installation if necessary. You can check which version you are running with `go version`. @@ -117,7 +117,7 @@ cd /home/git/gitlab sudo -u git -H git checkout 9-4-stable-ee ``` -### 5. Update gitlab-shell +### 7. Update gitlab-shell ```bash cd /home/git/gitlab-shell @@ -127,11 +127,10 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION) sudo -u git -H bin/compile ``` -### 6. Update gitlab-workhorse +### 8. Update gitlab-workhorse -Install and compile gitlab-workhorse. This requires -[Go 1.8](https://golang.org/dl) which should already be on your system from -GitLab 8.1. GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/). +Install and compile gitlab-workhorse. GitLab-Workhorse uses +[GNU Make](https://www.gnu.org/software/make/). If you are not using Linux you may have to run `gmake` instead of `make` below. @@ -143,7 +142,7 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION) sudo -u git -H make ``` -### 7. Update Gitaly +### 9. Update Gitaly If you have not yet set up Gitaly then follow [Gitaly section of the installation guide](../install/installation.md#install-gitaly). @@ -171,7 +170,29 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION) sudo -u git -H make ``` -### 10. Update configuration files +### 10. Update MySQL permissions + +If you are using MySQL you need to grant the GitLab user the necessary +permissions on the database: + +```bash +mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';" +``` + +If you use MySQL with replication, or just have MySQL configured with binary logging, +you will need to also run the following on all of your MySQL servers: + +```bash +mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;" +``` + +You can make this setting permanent by adding it to your `my.cnf`: + +``` +log_bin_trust_function_creators=1 +``` + +### 11. Update configuration files #### New configuration options for `gitlab.yml` @@ -245,7 +266,7 @@ For Ubuntu 16.04.1 LTS: sudo systemctl daemon-reload ``` -### 11. Install libs, migrations, etc. +### 12. Install libs, migrations, etc. ```bash cd /home/git/gitlab @@ -271,14 +292,14 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production **MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md). -### 12. Start application +### 13. Start application ```bash sudo service gitlab start sudo service nginx restart ``` -### 13. Check application status +### 14. Check application status Check if GitLab and its environment are configured correctly: diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md index a954840b8a6..69a9dfc3500 100644 --- a/doc/user/admin_area/monitoring/health_check.md +++ b/doc/user/admin_area/monitoring/health_check.md @@ -5,6 +5,8 @@ - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and will be deprecated in GitLab 9.1. Read more in the [old behavior](#old-behavior) section. + - [Access token](#access-token) has been deprecated in GitLab 9.4 + in favor of [IP Whitelist](#ip-whitelist) GitLab provides liveness and readiness probes to indicate service health and reachability to required services. These probes report on the status of the @@ -12,7 +14,19 @@ database connection, Redis connection, and access to the filesystem. These endpoints [can be provided to schedulers like Kubernetes][kubernetes] to hold traffic until the system is ready or restart the container as needed. -## Access Token +## IP Whitelist + +To access monitoring resources the client IP needs to be included in the whitelist. +To add or remove hosts or IP ranges from the list you can edit `gitlab.rb` or `gitlab.yml`. + +Example whitelist configuration: +```yaml +monitoring: + ip_whitelist: + - 127.0.0.0/8 # by default only local IPs are allowed to access monitoring resources +``` + +## Access Token (Deprecated) An access token needs to be provided while accessing the probe endpoints. The current accepted token can be found under the **Admin area ➔ Monitoring ➔ Health check** @@ -47,10 +61,10 @@ which will then provide a report of system health in JSON format: ## Using the Endpoint -Once you have the access token, the probes can be accessed: +With default whitelist settings, the probes can be accessed from localhost: -- `https://gitlab.example.com/-/readiness?token=ACCESS_TOKEN` -- `https://gitlab.example.com/-/liveness?token=ACCESS_TOKEN` +- `http://localhost/-/readiness` +- `http://localhost/-/liveness` ## Status @@ -71,8 +85,8 @@ the database connection, the state of the database migrations, and the ability t and access the cache. This endpoint can be provided to uptime monitoring services like [Pingdom][pingdom], [Nagios][nagios-health], and [NewRelic][newrelic-health]. -Once you have the [access token](#access-token), health information can be -retrieved as plain text, JSON, or XML using the `health_check` endpoint: +Once you have the [access token](#access-token) or your client IP is [whitelisted](#ip-whitelist), +health information can be retrieved as plain text, JSON, or XML using the `health_check` endpoint: - `https://gitlab.example.com/health_check?token=ACCESS_TOKEN` - `https://gitlab.example.com/health_check.json?token=ACCESS_TOKEN` diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature deleted file mode 100644 index 59a625056d6..00000000000 --- a/features/project/source/browse_files.feature +++ /dev/null @@ -1,323 +0,0 @@ -Feature: Project Source Browse Files - Background: - Given I sign in as a user - And I own project "Shop" - Given I visit project source page - - Scenario: I browse files from master branch - Then I should see files from repository - - Scenario: I browse files for specific ref - Given I visit project source page for "6d39438" - Then I should see files from repository for "6d39438" - - @javascript - Scenario: I browse file content - Given I click on ".gitignore" file in repo - Then I should see its content - - Scenario: I browse raw file - Given I visit blob file from repo - And I click link "Raw" - Then I should see raw file content - - Scenario: I can create file - Given I click on "New file" link in repo - Then I can see new file page - - Scenario: I can create file when I don't have write access - Given I don't have write access - And I click on "New file" link in repo - Then I should see a notice about a new fork having been created - Then I can see new file page - - @javascript - Scenario: I can create and commit file - Given I click on "New file" link in repo - And I edit code - And I fill the new file name - And I fill the commit message - And I click on "Commit changes" - Then I am redirected to the new file - And I should see its new content - - @javascript - Scenario: I can create and commit file when I don't have write access - Given I don't have write access - And I click on "New file" link in repo - And I edit code - And I fill the new file name - And I fill the commit message - And I click on "Commit changes" - Then I am redirected to the fork's new merge request page - And I can see the new commit message - - @javascript - Scenario: I can create and commit file with new lines at the end of file - Given I click on "New file" link in repo - And I edit code with new lines at end of file - And I fill the new file name - And I fill the commit message - And I click on "Commit changes" - Then I am redirected to the new file - And I click button "Edit" - And I should see its content with new lines preserved at end of file - - @javascript - Scenario: I can create and commit file and specify new branch - Given I click on "New file" link in repo - And I edit code - And I fill the new file name - And I fill the commit message - And I fill the new branch name - And I click on "Commit changes" - Then I am redirected to the new merge request page - When I click on "Changes" tab - And I should see its new content - - @javascript - Scenario: I can upload file and commit - Given I click on "Upload file" link in repo - And I upload a new text file - And I fill the upload file commit message - And I fill the new branch name - And I click on "Upload file" - Then I can see the new commit message - And I am redirected to the new merge request page - When I click on "Changes" tab - Then I can see the new text file - - @javascript - Scenario: I can upload file and commit when I don't have write access - Given I don't have write access - And I click on "Upload file" link in repo - Then I should see a notice about a new fork having been created - When I click on "Upload file" link in repo - And I upload a new text file - And I fill the upload file commit message - And I click on "Upload file" - Then I can see the new commit message - And I am redirected to the fork's new merge request page - When I click on "Changes" tab - Then I can see the new text file - - @javascript - Scenario: I can replace file and commit - Given I click on ".gitignore" file in repo - And I see the ".gitignore" - And I click on "Replace" - And I replace it with a text file - And I fill the replace file commit message - And I click on "Replace file" - Then I can see the new text file - And I can see the replacement commit message - - @javascript - Scenario: I can replace file and commit when I don't have write access - Given I don't have write access - And I click on ".gitignore" file in repo - And I see the ".gitignore" - And I click on "Replace" - Then I should see a Fork/Cancel combo - And I click button "Fork" - Then I should see a notice about a new fork having been created - When I click on "Replace" - And I replace it with a text file - And I fill the replace file commit message - And I click on "Replace file" - And I can see the replacement commit message - And I am redirected to the fork's new merge request page - When I click on "Changes" tab - Then I can see the new text file - - @javascript - Scenario: I can create file with a directory name - Given I click on "New file" link in repo - And I fill the new file name with a new directory - And I edit code - And I fill the commit message - And I click on "Commit changes" - Then I am redirected to the new file with directory - And I should see its new content - - @javascript - Scenario: I can edit file - Given I click on ".gitignore" file in repo - And I click button "Edit" - Then I can edit code - - @javascript - Scenario: I can edit file when I don't have write access - Given I don't have write access - And I click on ".gitignore" file in repo - And I click button "Edit" - Then I should see a Fork/Cancel combo - And I click button "Fork" - Then I should see a notice about a new fork having been created - And I can edit code - - Scenario: If the file is binary the edit link is hidden - Given I visit a binary file in the repo - Then I cannot see the edit button - - @javascript - Scenario: I can edit and commit file - Given I click on ".gitignore" file in repo - And I click button "Edit" - And I edit code - And I fill the commit message - And I click on "Commit changes" - Then I am redirected to the ".gitignore" - And I should see its new content - - @javascript - Scenario: I can edit and commit file when I don't have write access - Given I don't have write access - And I click on ".gitignore" file in repo - And I click button "Edit" - Then I should see a Fork/Cancel combo - And I click button "Fork" - And I edit code - And I fill the commit message - And I click on "Commit changes" - Then I am redirected to the fork's new merge request page - And I can see the new commit message - - @javascript - Scenario: I can edit and commit file to new branch - Given I click on ".gitignore" file in repo - And I click button "Edit" - And I edit code - And I fill the commit message - And I fill the new branch name - And I click on "Commit changes" - Then I am redirected to the new merge request page - Then I click on "Changes" tab - And I should see its new content - - @javascript @wip - Scenario: If I don't change the content of the file I see an error message - Given I click on ".gitignore" file in repo - And I click button "edit" - And I fill the commit message - And I click on "Commit changes" - # Test fails because carriage returns are added to the file. - Then I am on the ".gitignore" edit file page - And I see a commit error message - - @javascript - Scenario: I can create directory in repo - When I click on "New directory" link in repo - And I fill the new directory name - And I fill the commit message - And I fill the new branch name - And I click on "Create directory" - Then I am redirected to the new merge request page - - @javascript - Scenario: I can create directory in repo when I don't have write access - Given I don't have write access - When I click on "New directory" link in repo - Then I should see a notice about a new fork having been created - When I click on "New directory" link in repo - And I fill the new directory name - And I fill the commit message - And I click on "Create directory" - Then I am redirected to the fork's new merge request page - - @javascript - Scenario: I attempt to create an existing directory - When I click on "New directory" link in repo - And I fill an existing directory name - And I fill the commit message - And I click on "Create directory" - Then I see "Unable to create directory" - And I am redirected to the root directory - - @javascript - Scenario: I can see editing preview - Given I click on ".gitignore" file in repo - And I click button "Edit" - And I edit code - And I click link "Diff" - Then I see diff - - @javascript - Scenario: I can delete file and commit - Given I click on ".gitignore" file in repo - And I see the ".gitignore" - And I click on "Delete" - And I fill the commit message - And I click on "Delete file" - Then I am redirected to the files URL - And I don't see the ".gitignore" - - @javascript - Scenario: I can delete file and commit when I don't have write access - Given I don't have write access - And I click on ".gitignore" file in repo - And I see the ".gitignore" - And I click on "Delete" - Then I should see a Fork/Cancel combo - And I click button "Fork" - Then I should see a notice about a new fork having been created - When I click on "Delete" - And I fill the commit message - And I click on "Delete file" - Then I am redirected to the fork's new merge request page - And I can see the new commit message - - Scenario: I can browse directory with Browse Dir - Given I click on files directory - And I click on History link - Then I see Browse dir link - - Scenario: I can browse file with Browse File - Given I click on readme file - And I click on History link - Then I see Browse file link - - Scenario: I can browse code with Browse Code - Given I click on History link - Then I see Browse code link - - # Permalink - - Scenario: I click on the permalink link from a branch ref - Given I click on ".gitignore" file in repo - And I click on Permalink - Then I am redirected to the permalink URL - - Scenario: I don't see the permalink link from a SHA ref - Given I visit project source page for "6d394385cf567f80a8fd85055db1ab4c5295806f" - And I click on ".gitignore" file in repo - Then I don't see the permalink link - - @javascript - Scenario: I browse code with single quotes in the ref - Given I switch ref to 'test' - And I see the ref 'test' has been selected - And I visit the 'test' tree - Then I see the commit data - - @javascript - Scenario: I browse code with a leading dot in the directory - Given I switch ref to fix - And I visit the fix tree - Then I see the commit data for a directory with a leading dot - - Scenario: I browse LFS object - Given I click on "files/lfs/lfs_object.iso" file in repo - Then I should see download link and object size - And I should not see lfs pointer details - And I should see buttons for allowed commands - - @javascript - Scenario: I preview an SVG file - Given I click on "Upload file" link in repo - And I upload a new SVG file - And I fill the upload file commit message - And I fill the new branch name - And I click on "Upload file" - Given I visit the SVG file - Then I can see the new rendered SVG image diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 945f2821d72..47742a164fc 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -314,6 +314,13 @@ module API expose :id end + class MergeRequestSimple < ProjectEntity + expose :title + expose :web_url do |merge_request, options| + Gitlab::UrlBuilder.build(merge_request) + end + end + class MergeRequestBasic < ProjectEntity expose :target_branch, :source_branch expose :upvotes do |merge_request, options| diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 4ad1eef4ff1..ac33b2b801c 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -44,9 +44,14 @@ module API args[:label_name] = args.delete(:labels) merge_requests = MergeRequestsFinder.new(current_user, args).execute - .preload(:notes, :target_project, :author, :assignee, :milestone, :merge_request_diff, :labels) + .reorder(args[:order_by] => args[:sort]) + merge_requests = paginate(merge_requests) + .preload(:target_project) - merge_requests.reorder(args[:order_by] => args[:sort]) + return merge_requests if args[:view] == 'simple' + + merge_requests + .preload(:notes, :author, :assignee, :milestone, :merge_request_diff, :labels) end params :optional_params_ce do @@ -77,15 +82,25 @@ module API optional :labels, type: String, desc: 'Comma-separated list of label names' optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time' optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time' + optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request' use :pagination end get ":id/merge_requests" do authorize! :read_merge_request, user_project merge_requests = find_merge_requests(project_id: user_project.id) - issuable_metadata = issuable_meta_data(merge_requests, 'MergeRequest') - present paginate(merge_requests), with: Entities::MergeRequestBasic, current_user: current_user, project: user_project, issuable_metadata: issuable_metadata + options = { with: Entities::MergeRequestBasic, + current_user: current_user, + project: user_project } + + if params[:view] == 'simple' + options[:with] = Entities::MergeRequestSimple + else + options[:issuable_metadata] = issuable_meta_data(merge_requests, 'MergeRequest') + end + + present merge_requests, options end desc 'Create a merge request' do diff --git a/lib/api/users.rb b/lib/api/users.rb index c469751c31c..81c68ea2658 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -421,7 +421,16 @@ module API success Entities::UserPublic end get do - present current_user, with: sudo? ? Entities::UserWithPrivateDetails : Entities::UserPublic + entity = + if sudo? + Entities::UserWithPrivateDetails + elsif current_user.admin? + Entities::UserWithAdmin + else + Entities::UserPublic + end + + present current_user, with: entity end desc "Get the currently authenticated user's SSH keys" do diff --git a/lib/gitlab/auth/unique_ips_limiter.rb b/lib/gitlab/auth/unique_ips_limiter.rb index bf2239ca150..baa1f802d8a 100644 --- a/lib/gitlab/auth/unique_ips_limiter.rb +++ b/lib/gitlab/auth/unique_ips_limiter.rb @@ -27,7 +27,7 @@ module Gitlab time = Time.now.utc.to_i key = "#{USER_UNIQUE_IPS_PREFIX}:#{user_id}" - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| unique_ips_count = nil redis.multi do |r| r.zadd(key, time, ip) diff --git a/lib/gitlab/cache/ci/project_pipeline_status.rb b/lib/gitlab/cache/ci/project_pipeline_status.rb index 9c2e09943b0..dba37892863 100644 --- a/lib/gitlab/cache/ci/project_pipeline_status.rb +++ b/lib/gitlab/cache/ci/project_pipeline_status.rb @@ -23,7 +23,7 @@ module Gitlab end def self.cached_results_for_projects(projects) - result = Gitlab::Redis.with do |redis| + result = Gitlab::Redis::Cache.with do |redis| redis.multi do projects.each do |project| cache_key = cache_key_for_project(project) @@ -100,19 +100,19 @@ module Gitlab end def load_from_cache - Gitlab::Redis.with do |redis| + Gitlab::Redis::Cache.with do |redis| self.sha, self.status, self.ref = redis.hmget(cache_key, :sha, :status, :ref) end end def store_in_cache - Gitlab::Redis.with do |redis| + Gitlab::Redis::Cache.with do |redis| redis.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref }) end end def delete_from_cache - Gitlab::Redis.with do |redis| + Gitlab::Redis::Cache.with do |redis| redis.del(cache_key) end end @@ -120,7 +120,7 @@ module Gitlab def has_cache? return self.loaded unless self.loaded.nil? - Gitlab::Redis.with do |redis| + Gitlab::Redis::Cache.with do |redis| redis.exists(cache_key) end end diff --git a/lib/gitlab/chat_name_token.rb b/lib/gitlab/chat_name_token.rb index 1b081aa9b1d..e63e5437331 100644 --- a/lib/gitlab/chat_name_token.rb +++ b/lib/gitlab/chat_name_token.rb @@ -12,23 +12,23 @@ module Gitlab end def get - Gitlab::Redis.with do |redis| - data = redis.get(redis_key) + Gitlab::Redis::SharedState.with do |redis| + data = redis.get(redis_shared_state_key) JSON.parse(data, symbolize_names: true) if data end end def store!(params) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| params = params.to_json - redis.set(redis_key, params, ex: EXPIRY_TIME) + redis.set(redis_shared_state_key, params, ex: EXPIRY_TIME) token end end def delete - Gitlab::Redis.with do |redis| - redis.del(redis_key) + Gitlab::Redis::SharedState.with do |redis| + redis.del(redis_shared_state_key) end end @@ -38,7 +38,7 @@ module Gitlab Devise.friendly_token(TOKEN_LENGTH) end - def redis_key + def redis_shared_state_key "gitlab:chat_names:#{token}" end end diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb index 33f8939bc61..1a697396ff1 100644 --- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb +++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb @@ -145,7 +145,7 @@ module Gitlab def track_rename(type, old_path, new_path) key = redis_key_for_type(type) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| redis.lpush(key, [old_path, new_path].to_json) redis.expire(key, 2.weeks.to_i) end @@ -155,7 +155,7 @@ module Gitlab def reverts_for_type(type) key = redis_key_for_type(type) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| failed_reverts = [] while rename_info = redis.lpop(key) diff --git a/lib/gitlab/etag_caching/store.rb b/lib/gitlab/etag_caching/store.rb index 072fcfc65e6..21172ff8d93 100644 --- a/lib/gitlab/etag_caching/store.rb +++ b/lib/gitlab/etag_caching/store.rb @@ -2,17 +2,17 @@ module Gitlab module EtagCaching class Store EXPIRY_TIME = 20.minutes - REDIS_NAMESPACE = 'etag:'.freeze + SHARED_STATE_NAMESPACE = 'etag:'.freeze def get(key) - Gitlab::Redis.with { |redis| redis.get(redis_key(key)) } + Gitlab::Redis::SharedState.with { |redis| redis.get(redis_shared_state_key(key)) } end def touch(key, only_if_missing: false) etag = generate_etag - Gitlab::Redis.with do |redis| - redis.set(redis_key(key), etag, ex: EXPIRY_TIME, nx: only_if_missing) + Gitlab::Redis::SharedState.with do |redis| + redis.set(redis_shared_state_key(key), etag, ex: EXPIRY_TIME, nx: only_if_missing) end etag @@ -24,10 +24,10 @@ module Gitlab SecureRandom.hex end - def redis_key(key) + def redis_shared_state_key(key) raise 'Invalid key' if !Rails.env.production? && !Gitlab::EtagCaching::Router.match(key) - "#{REDIS_NAMESPACE}#{key}" + "#{SHARED_STATE_NAMESPACE}#{key}" end end end diff --git a/lib/gitlab/exclusive_lease.rb b/lib/gitlab/exclusive_lease.rb index a0f46594eb1..3784f6c4947 100644 --- a/lib/gitlab/exclusive_lease.rb +++ b/lib/gitlab/exclusive_lease.rb @@ -26,17 +26,17 @@ module Gitlab EOS def self.cancel(key, uuid) - Gitlab::Redis.with do |redis| - redis.eval(LUA_CANCEL_SCRIPT, keys: [redis_key(key)], argv: [uuid]) + Gitlab::Redis::SharedState.with do |redis| + redis.eval(LUA_CANCEL_SCRIPT, keys: [redis_shared_state_key(key)], argv: [uuid]) end end - def self.redis_key(key) + def self.redis_shared_state_key(key) "gitlab:exclusive_lease:#{key}" end def initialize(key, timeout:) - @redis_key = self.class.redis_key(key) + @redis_shared_state_key = self.class.redis_shared_state_key(key) @timeout = timeout @uuid = SecureRandom.uuid end @@ -45,24 +45,24 @@ module Gitlab # false if the lease is already taken. def try_obtain # Performing a single SET is atomic - Gitlab::Redis.with do |redis| - redis.set(@redis_key, @uuid, nx: true, ex: @timeout) && @uuid + Gitlab::Redis::SharedState.with do |redis| + redis.set(@redis_shared_state_key, @uuid, nx: true, ex: @timeout) && @uuid end end # Try to renew an existing lease. Return lease UUID on success, # false if the lease is taken by a different UUID or inexistent. def renew - Gitlab::Redis.with do |redis| - result = redis.eval(LUA_RENEW_SCRIPT, keys: [@redis_key], argv: [@uuid, @timeout]) + Gitlab::Redis::SharedState.with do |redis| + result = redis.eval(LUA_RENEW_SCRIPT, keys: [@redis_shared_state_key], argv: [@uuid, @timeout]) result == @uuid end end # Returns true if the key for this lease is set. def exists? - Gitlab::Redis.with do |redis| - redis.exists(@redis_key) + Gitlab::Redis::SharedState.with do |redis| + redis.exists(@redis_shared_state_key) end end end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index e51966313d4..26581a3cc1b 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -930,7 +930,7 @@ module Gitlab return unless commit_object && commit_object.type == :COMMIT - gitmodules = gitaly_commit_client.tree_entry(ref, '.gitmodules', Blob::MAX_DATA_DISPLAY_SIZE) + gitmodules = gitaly_commit_client.tree_entry(ref, '.gitmodules', Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE) found_module = GitmodulesParser.new(gitmodules.data).parse[path] found_module && found_module['url'] diff --git a/lib/gitlab/health_checks/redis/cache_check.rb b/lib/gitlab/health_checks/redis/cache_check.rb new file mode 100644 index 00000000000..a28658d42d4 --- /dev/null +++ b/lib/gitlab/health_checks/redis/cache_check.rb @@ -0,0 +1,31 @@ +module Gitlab + module HealthChecks + module Redis + class CacheCheck + extend SimpleAbstractCheck + + class << self + def check_up + check + end + + private + + def metric_prefix + 'redis_cache_ping' + end + + def is_successful?(result) + result == 'PONG' + end + + def check + catch_timeout 10.seconds do + Gitlab::Redis::Cache.with(&:ping) + end + end + end + end + end + end +end diff --git a/lib/gitlab/health_checks/redis/queues_check.rb b/lib/gitlab/health_checks/redis/queues_check.rb new file mode 100644 index 00000000000..f97d50d3947 --- /dev/null +++ b/lib/gitlab/health_checks/redis/queues_check.rb @@ -0,0 +1,31 @@ +module Gitlab + module HealthChecks + module Redis + class QueuesCheck + extend SimpleAbstractCheck + + class << self + def check_up + check + end + + private + + def metric_prefix + 'redis_queues_ping' + end + + def is_successful?(result) + result == 'PONG' + end + + def check + catch_timeout 10.seconds do + Gitlab::Redis::Queues.with(&:ping) + end + end + end + end + end + end +end diff --git a/lib/gitlab/health_checks/redis/redis_check.rb b/lib/gitlab/health_checks/redis/redis_check.rb new file mode 100644 index 00000000000..fe4e3c4a3ab --- /dev/null +++ b/lib/gitlab/health_checks/redis/redis_check.rb @@ -0,0 +1,27 @@ +module Gitlab + module HealthChecks + module Redis + class RedisCheck + extend SimpleAbstractCheck + + class << self + private + + def metric_prefix + 'redis_ping' + end + + def is_successful?(result) + result == 'PONG' + end + + def check + ::Gitlab::HealthChecks::Redis::CacheCheck.check_up && + ::Gitlab::HealthChecks::Redis::QueuesCheck.check_up && + ::Gitlab::HealthChecks::Redis::SharedStateCheck.check_up + end + end + end + end + end +end diff --git a/lib/gitlab/health_checks/redis/shared_state_check.rb b/lib/gitlab/health_checks/redis/shared_state_check.rb new file mode 100644 index 00000000000..e3244392902 --- /dev/null +++ b/lib/gitlab/health_checks/redis/shared_state_check.rb @@ -0,0 +1,31 @@ +module Gitlab + module HealthChecks + module Redis + class SharedStateCheck + extend SimpleAbstractCheck + + class << self + def check_up + check + end + + private + + def metric_prefix + 'redis_shared_state_ping' + end + + def is_successful?(result) + result == 'PONG' + end + + def check + catch_timeout 10.seconds do + Gitlab::Redis::SharedState.with(&:ping) + end + end + end + end + end + end +end diff --git a/lib/gitlab/health_checks/redis_check.rb b/lib/gitlab/health_checks/redis_check.rb deleted file mode 100644 index 57bbe5b3ad0..00000000000 --- a/lib/gitlab/health_checks/redis_check.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Gitlab - module HealthChecks - class RedisCheck - extend SimpleAbstractCheck - - class << self - private - - def metric_prefix - 'redis_ping' - end - - def is_successful?(result) - result == 'PONG' - end - - def check - catch_timeout 10.seconds do - Gitlab::Redis.with(&:ping) - end - end - end - end - end -end diff --git a/lib/gitlab/lfs_token.rb b/lib/gitlab/lfs_token.rb index 5f67e97fa2a..8e57ba831c5 100644 --- a/lib/gitlab/lfs_token.rb +++ b/lib/gitlab/lfs_token.rb @@ -18,10 +18,10 @@ module Gitlab end def token - Gitlab::Redis.with do |redis| - token = redis.get(redis_key) + Gitlab::Redis::SharedState.with do |redis| + token = redis.get(redis_shared_state_key) token ||= Devise.friendly_token(TOKEN_LENGTH) - redis.set(redis_key, token, ex: EXPIRY_TIME) + redis.set(redis_shared_state_key, token, ex: EXPIRY_TIME) token end @@ -41,7 +41,7 @@ module Gitlab private - def redis_key + def redis_shared_state_key "gitlab:lfs_token:#{actor.class.name.underscore}_#{actor.id}" if actor end end diff --git a/lib/gitlab/mail_room.rb b/lib/gitlab/mail_room.rb index 3503fac40e8..9f432673a6e 100644 --- a/lib/gitlab/mail_room.rb +++ b/lib/gitlab/mail_room.rb @@ -1,6 +1,6 @@ require 'yaml' require 'json' -require_relative 'redis' unless defined?(Gitlab::Redis) +require_relative 'redis/queues' unless defined?(Gitlab::Redis::Queues) module Gitlab module MailRoom @@ -34,11 +34,11 @@ module Gitlab config[:idle_timeout] = 60 if config[:idle_timeout].nil? if config[:enabled] && config[:address] - gitlab_redis = Gitlab::Redis.new(rails_env) - config[:redis_url] = gitlab_redis.url + gitlab_redis_queues = Gitlab::Redis::Queues.new(rails_env) + config[:redis_url] = gitlab_redis_queues.url - if gitlab_redis.sentinels? - config[:sentinels] = gitlab_redis.sentinels + if gitlab_redis_queues.sentinels? + config[:sentinels] = gitlab_redis_queues.sentinels end end diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb index b3f453e506d..3f2bbd9f6a6 100644 --- a/lib/gitlab/o_auth/user.rb +++ b/lib/gitlab/o_auth/user.rb @@ -101,14 +101,18 @@ module Gitlab # Look for a corresponding person with same uid in any of the configured LDAP providers Gitlab::LDAP::Config.providers.each do |provider| adapter = Gitlab::LDAP::Adapter.new(provider) - @ldap_person = Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter) - # The `uid` might actually be a DN. Try it next. - @ldap_person ||= Gitlab::LDAP::Person.find_by_dn(auth_hash.uid, adapter) + @ldap_person = find_ldap_person(auth_hash, adapter) break if @ldap_person end @ldap_person end + def find_ldap_person(auth_hash, adapter) + by_uid = Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter) + # The `uid` might actually be a DN. Try it next. + by_uid || Gitlab::LDAP::Person.find_by_dn(auth_hash.uid, adapter) + end + def ldap_config Gitlab::LDAP::Config.new(ldap_person.provider) if ldap_person end diff --git a/lib/gitlab/redis.rb b/lib/gitlab/redis.rb deleted file mode 100644 index bc5370de32a..00000000000 --- a/lib/gitlab/redis.rb +++ /dev/null @@ -1,102 +0,0 @@ -# This file should not have any direct dependency on Rails environment -# please require all dependencies below: -require 'active_support/core_ext/hash/keys' -require 'active_support/core_ext/module/delegation' - -module Gitlab - class Redis - CACHE_NAMESPACE = 'cache:gitlab'.freeze - SESSION_NAMESPACE = 'session:gitlab'.freeze - SIDEKIQ_NAMESPACE = 'resque:gitlab'.freeze - MAILROOM_NAMESPACE = 'mail_room:gitlab'.freeze - DEFAULT_REDIS_URL = 'redis://localhost:6379'.freeze - - class << self - delegate :params, :url, to: :new - - def with - @pool ||= ConnectionPool.new(size: pool_size) { ::Redis.new(params) } - @pool.with { |redis| yield redis } - end - - def pool_size - if Sidekiq.server? - # the pool will be used in a multi-threaded context - Sidekiq.options[:concurrency] + 5 - else - # probably this is a Unicorn process, so single threaded - 5 - end - end - - def _raw_config - return @_raw_config if defined?(@_raw_config) - - begin - @_raw_config = ERB.new(File.read(config_file)).result.freeze - rescue Errno::ENOENT - @_raw_config = false - end - - @_raw_config - end - - def config_file - ENV['GITLAB_REDIS_CONFIG_FILE'] || File.expand_path('../../config/resque.yml', __dir__) - end - end - - def initialize(rails_env = nil) - @rails_env = rails_env || ::Rails.env - end - - def params - redis_store_options - end - - def url - raw_config_hash[:url] - end - - def sentinels - raw_config_hash[:sentinels] - end - - def sentinels? - sentinels && !sentinels.empty? - end - - private - - def redis_store_options - config = raw_config_hash - redis_url = config.delete(:url) - redis_uri = URI.parse(redis_url) - - if redis_uri.scheme == 'unix' - # Redis::Store does not handle Unix sockets well, so let's do it for them - config[:path] = redis_uri.path - config - else - redis_hash = ::Redis::Store::Factory.extract_host_options_from_uri(redis_url) - # order is important here, sentinels must be after the connection keys. - # {url: ..., port: ..., sentinels: [...]} - redis_hash.merge(config) - end - end - - def raw_config_hash - config_data = fetch_config - - if config_data - config_data.is_a?(String) ? { url: config_data } : config_data.deep_symbolize_keys - else - { url: DEFAULT_REDIS_URL } - end - end - - def fetch_config - self.class._raw_config ? YAML.load(self.class._raw_config)[@rails_env] : false - end - end -end diff --git a/lib/gitlab/redis/cache.rb b/lib/gitlab/redis/cache.rb new file mode 100644 index 00000000000..b0da516ff83 --- /dev/null +++ b/lib/gitlab/redis/cache.rb @@ -0,0 +1,34 @@ +# please require all dependencies below: +require_relative 'wrapper' unless defined?(::Gitlab::Redis::Wrapper) + +module Gitlab + module Redis + class Cache < ::Gitlab::Redis::Wrapper + CACHE_NAMESPACE = 'cache:gitlab'.freeze + DEFAULT_REDIS_CACHE_URL = 'redis://localhost:6380'.freeze + REDIS_CACHE_CONFIG_ENV_VAR_NAME = 'GITLAB_REDIS_CACHE_CONFIG_FILE'.freeze + if defined?(::Rails) && ::Rails.root.present? + DEFAULT_REDIS_CACHE_CONFIG_FILE_NAME = ::Rails.root.join('config', 'redis.cache.yml').freeze + end + + class << self + def default_url + DEFAULT_REDIS_CACHE_URL + end + + def config_file_name + # if ENV set for this class, use it even if it points to a file does not exist + file_name = ENV[REDIS_CACHE_CONFIG_ENV_VAR_NAME] + return file_name unless file_name.nil? + + # otherwise, if config files exists for this class, use it + file_name = File.expand_path(DEFAULT_REDIS_CACHE_CONFIG_FILE_NAME, __dir__) + return file_name if File.file?(file_name) + + # this will force use of DEFAULT_REDIS_QUEUES_URL when config file is absent + super + end + end + end + end +end diff --git a/lib/gitlab/redis/queues.rb b/lib/gitlab/redis/queues.rb new file mode 100644 index 00000000000..f9249d05565 --- /dev/null +++ b/lib/gitlab/redis/queues.rb @@ -0,0 +1,35 @@ +# please require all dependencies below: +require_relative 'wrapper' unless defined?(::Gitlab::Redis::Wrapper) + +module Gitlab + module Redis + class Queues < ::Gitlab::Redis::Wrapper + SIDEKIQ_NAMESPACE = 'resque:gitlab'.freeze + MAILROOM_NAMESPACE = 'mail_room:gitlab'.freeze + DEFAULT_REDIS_QUEUES_URL = 'redis://localhost:6381'.freeze + REDIS_QUEUES_CONFIG_ENV_VAR_NAME = 'GITLAB_REDIS_QUEUES_CONFIG_FILE'.freeze + if defined?(::Rails) && ::Rails.root.present? + DEFAULT_REDIS_QUEUES_CONFIG_FILE_NAME = ::Rails.root.join('config', 'redis.queues.yml').freeze + end + + class << self + def default_url + DEFAULT_REDIS_QUEUES_URL + end + + def config_file_name + # if ENV set for this class, use it even if it points to a file does not exist + file_name = ENV[REDIS_QUEUES_CONFIG_ENV_VAR_NAME] + return file_name if file_name + + # otherwise, if config files exists for this class, use it + file_name = File.expand_path(DEFAULT_REDIS_QUEUES_CONFIG_FILE_NAME, __dir__) + return file_name if File.file?(file_name) + + # this will force use of DEFAULT_REDIS_QUEUES_URL when config file is absent + super + end + end + end + end +end diff --git a/lib/gitlab/redis/shared_state.rb b/lib/gitlab/redis/shared_state.rb new file mode 100644 index 00000000000..395dcf082da --- /dev/null +++ b/lib/gitlab/redis/shared_state.rb @@ -0,0 +1,34 @@ +# please require all dependencies below: +require_relative 'wrapper' unless defined?(::Gitlab::Redis::Wrapper) + +module Gitlab + module Redis + class SharedState < ::Gitlab::Redis::Wrapper + SESSION_NAMESPACE = 'session:gitlab'.freeze + DEFAULT_REDIS_SHARED_STATE_URL = 'redis://localhost:6382'.freeze + REDIS_SHARED_STATE_CONFIG_ENV_VAR_NAME = 'GITLAB_REDIS_SHARED_STATE_CONFIG_FILE'.freeze + if defined?(::Rails) && ::Rails.root.present? + DEFAULT_REDIS_SHARED_STATE_CONFIG_FILE_NAME = ::Rails.root.join('config', 'redis.shared_state.yml').freeze + end + + class << self + def default_url + DEFAULT_REDIS_SHARED_STATE_URL + end + + def config_file_name + # if ENV set for this class, use it even if it points to a file does not exist + file_name = ENV[REDIS_SHARED_STATE_CONFIG_ENV_VAR_NAME] + return file_name if file_name + + # otherwise, if config files exists for this class, use it + file_name = File.expand_path(DEFAULT_REDIS_SHARED_STATE_CONFIG_FILE_NAME, __dir__) + return file_name if File.file?(file_name) + + # this will force use of DEFAULT_REDIS_SHARED_STATE_URL when config file is absent + super + end + end + end + end +end diff --git a/lib/gitlab/redis/wrapper.rb b/lib/gitlab/redis/wrapper.rb new file mode 100644 index 00000000000..c43b37dde74 --- /dev/null +++ b/lib/gitlab/redis/wrapper.rb @@ -0,0 +1,135 @@ +# This file should only be used by sub-classes, not directly by any clients of the sub-classes +# please require all dependencies below: +require 'active_support/core_ext/hash/keys' +require 'active_support/core_ext/module/delegation' + +module Gitlab + module Redis + class Wrapper + DEFAULT_REDIS_URL = 'redis://localhost:6379'.freeze + REDIS_CONFIG_ENV_VAR_NAME = 'GITLAB_REDIS_CONFIG_FILE'.freeze + if defined?(::Rails) && ::Rails.root.present? + DEFAULT_REDIS_CONFIG_FILE_NAME = ::Rails.root.join('config', 'resque.yml').freeze + end + + class << self + delegate :params, :url, to: :new + + def with + @pool ||= ConnectionPool.new(size: pool_size) { ::Redis.new(params) } + @pool.with { |redis| yield redis } + end + + def pool_size + # heuristic constant 5 should be a config setting somewhere -- related to CPU count? + size = 5 + if Sidekiq.server? + # the pool will be used in a multi-threaded context + size += Sidekiq.options[:concurrency] + end + size + end + + def _raw_config + return @_raw_config if defined?(@_raw_config) + + @_raw_config = + begin + if filename = config_file_name + ERB.new(File.read(filename)).result.freeze + else + false + end + rescue Errno::ENOENT + false + end + end + + def default_url + DEFAULT_REDIS_URL + end + + def config_file_name + # if ENV set for wrapper class, use it even if it points to a file does not exist + file_name = ENV[REDIS_CONFIG_ENV_VAR_NAME] + return file_name unless file_name.nil? + + # otherwise, if config files exists for wrapper class, use it + file_name = File.expand_path(DEFAULT_REDIS_CONFIG_FILE_NAME, __dir__) + return file_name if File.file?(file_name) + + # nil will force use of DEFAULT_REDIS_URL when config file is absent + nil + end + end + + def initialize(rails_env = nil) + @rails_env = rails_env || ::Rails.env + end + + def params + redis_store_options + end + + def url + raw_config_hash[:url] + end + + def sentinels + raw_config_hash[:sentinels] + end + + def sentinels? + sentinels && !sentinels.empty? + end + + private + + def redis_store_options + config = raw_config_hash + redis_url = config.delete(:url) + redis_uri = URI.parse(redis_url) + + if redis_uri.scheme == 'unix' + # Redis::Store does not handle Unix sockets well, so let's do it for them + config[:path] = redis_uri.path + query = redis_uri.query + unless query.nil? + queries = CGI.parse(redis_uri.query) + db_numbers = queries["db"] if queries.key?("db") + config[:db] = db_numbers[0].to_i if db_numbers.any? + end + config + else + redis_hash = ::Redis::Store::Factory.extract_host_options_from_uri(redis_url) + # order is important here, sentinels must be after the connection keys. + # {url: ..., port: ..., sentinels: [...]} + redis_hash.merge(config) + end + end + + def raw_config_hash + config_data = fetch_config + + if config_data + config_data.is_a?(String) ? { url: config_data } : config_data.deep_symbolize_keys + else + { url: self.class.default_url } + end + end + + def fetch_config + return false unless self.class._raw_config + + yaml = YAML.load(self.class._raw_config) + + # If the file has content but it's invalid YAML, `load` returns false + if yaml + yaml.fetch(@rails_env, false) + else + false + end + end + end + end +end diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb index 35792d2d67f..824e2d7251f 100644 --- a/lib/gitlab/url_builder.rb +++ b/lib/gitlab/url_builder.rb @@ -52,15 +52,13 @@ module Gitlab commit_url(id: object.commit_id, anchor: dom_id(object)) elsif object.for_issue? - issue = Issue.find(object.noteable_id) - issue_url(issue, anchor: dom_id(object)) + issue_url(object.noteable, anchor: dom_id(object)) elsif object.for_merge_request? - merge_request = MergeRequest.find(object.noteable_id) - merge_request_url(merge_request, anchor: dom_id(object)) + merge_request_url(object.noteable, anchor: dom_id(object)) elsif object.for_snippet? - snippet = Snippet.find(object.noteable_id) + snippet = object.noteable if snippet.is_a?(PersonalSnippet) snippet_url(snippet, anchor: dom_id(object)) diff --git a/lib/gitlab/user_activities.rb b/lib/gitlab/user_activities.rb index eb36ab9fded..125488536e1 100644 --- a/lib/gitlab/user_activities.rb +++ b/lib/gitlab/user_activities.rb @@ -6,13 +6,13 @@ module Gitlab BATCH_SIZE = 500 def self.record(key, time = Time.now) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| redis.hset(KEY, key, time.to_i) end end def delete(*keys) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| redis.hdel(KEY, keys) end end @@ -21,7 +21,7 @@ module Gitlab cursor = 0 loop do cursor, pairs = - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| redis.hscan(KEY, cursor, count: BATCH_SIZE) end diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index 4aef23b6aee..916ef365d78 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -62,10 +62,21 @@ module Gitlab end def send_git_blob(repository, blob) - params = { - 'RepoPath' => repository.path_to_repo, - 'BlobId' => blob.id - } + params = if Gitlab::GitalyClient.feature_enabled?(:project_raw_show) + { + 'GitalyServer' => gitaly_server_hash(repository), + 'GetBlobRequest' => { + repository: repository.gitaly_repository.to_h, + oid: blob.id, + limit: -1 + } + } + else + { + 'RepoPath' => repository.path_to_repo, + 'BlobId' => blob.id + } + end [ SEND_DATA_HEADER, @@ -176,7 +187,7 @@ module Gitlab end def set_key_and_notify(key, value, expire: nil, overwrite: true) - Gitlab::Redis.with do |redis| + Gitlab::Redis::Queues.with do |redis| result = redis.set(key, value, ex: expire, nx: !overwrite) if result redis.publish(NOTIFICATION_CHANNEL, "#{key}=#{value}") @@ -192,6 +203,13 @@ module Gitlab def encode(hash) Base64.urlsafe_encode64(JSON.dump(hash)) end + + def gitaly_server_hash(repository) + { + address: Gitlab::GitalyClient.address(repository.project.repository_storage), + token: Gitlab::GitalyClient.token(repository.project.repository_storage) + } + end end end end diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index 125a3d560d6..564aa141952 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -5,12 +5,12 @@ namespace :cache do desc "GitLab | Clear redis cache" task redis: :environment do - Gitlab::Redis.with do |redis| + Gitlab::Redis::Cache.with do |redis| cursor = REDIS_SCAN_START_STOP loop do cursor, keys = redis.scan( cursor, - match: "#{Gitlab::Redis::CACHE_NAMESPACE}*", + match: "#{Gitlab::Redis::Cache::CACHE_NAMESPACE}*", count: REDIS_CLEAR_BATCH_SIZE ) diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po index db4dc9a02da..7406629ef9d 100644 --- a/locale/bg/gitlab.po +++ b/locale/bg/gitlab.po @@ -4,11 +4,11 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-19 15:50-0500\n" +"POT-Creation-Date: 2017-06-28 13:32+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-06-23 04:07-0400\n" +"PO-Revision-Date: 2017-07-05 08:18-0400\n" "Last-Translator: Lyubomir Vasilev <lyubomirv@abv.bg>\n" "Language-Team: Bulgarian (https://translate.zanata.org/project/view/GitLab)\n" "Language: bg\n" @@ -29,6 +29,14 @@ msgstr[1] "%d подавания" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} подаде %{commit_timeago}" +msgid "1 pipeline" +msgid_plural "%d pipelines" +msgstr[0] "1 схема" +msgstr[1] "%d схеми" + +msgid "A collection of graphs regarding Continuous Integration" +msgstr "Набор от графики относно непрекъснатата интеграция" + msgid "About auto deploy" msgstr "Относно автоматичното внедряване" @@ -191,6 +199,9 @@ msgid_plural "Commits" msgstr[0] "Подаване" msgstr[1] "Подавания" +msgid "Commit duration in minutes for last 30 commits" +msgstr "Времетраене на подаванията в минути за последните 30 подавания" + msgid "Commit message" msgstr "Съобщение за подаването" @@ -230,6 +241,13 @@ msgstr "Копиране на идентификатора на подаване msgid "Create New Directory" msgstr "Създаване на нова папка" +msgid "" +"Create a personal access token on your account to pull or push via " +"%{protocol}." +msgstr "" +"Създайте си личен жетон за достъп в профила си, за да можете да изтегляте и " +"изпращате промени чрез %{protocol}." + msgid "Create directory" msgstr "Създаване на папка" @@ -248,6 +266,9 @@ msgstr "Разклоняване" msgid "CreateTag|Tag" msgstr "Етикет" +msgid "CreateTokenToCloneLink|create a personal access token" +msgstr "си създадете личен жетон за достъп" + msgid "Cron Timezone" msgstr "Часова зона за „Cron“" @@ -419,6 +440,15 @@ msgstr "Шаблон за интервала" msgid "Introducing Cycle Analytics" msgstr "Представяме Ви анализа на циклите" +msgid "Jobs for last month" +msgstr "Задачи за последния месец" + +msgid "Jobs for last week" +msgstr "Задачи за последната седмица" + +msgid "Jobs for last year" +msgstr "Задачи за последната година" + msgid "LFSStatus|Disabled" msgstr "Изключено" @@ -584,6 +614,21 @@ msgstr "План за схема" msgid "Pipeline Schedules" msgstr "Планове за схема" +msgid "PipelineCharts|Failed:" +msgstr "Неуспешни:" + +msgid "PipelineCharts|Overall statistics" +msgstr "Обща статистика" + +msgid "PipelineCharts|Success ratio:" +msgstr "Коефициент на успех:" + +msgid "PipelineCharts|Successful:" +msgstr "Успешни:" + +msgid "PipelineCharts|Total:" +msgstr "Общо:" + msgid "PipelineSchedules|Activated" msgstr "Включено" @@ -614,6 +659,18 @@ msgstr "Цел" msgid "PipelineSheduleIntervalPattern|Custom" msgstr "собствен" +msgid "Pipelines" +msgstr "Схеми" + +msgid "Pipelines charts" +msgstr "Графики за схемите" + +msgid "Pipeline|all" +msgstr "всички" + +msgid "Pipeline|success" +msgstr "успешни" + msgid "Pipeline|with stage" msgstr "с етап" diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po index 0ca8dfca266..015d96d6e53 100644 --- a/locale/eo/gitlab.po +++ b/locale/eo/gitlab.po @@ -4,11 +4,11 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-19 15:50-0500\n" +"POT-Creation-Date: 2017-06-28 13:32+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-07-05 02:56-0400\n" +"PO-Revision-Date: 2017-07-05 08:18-0400\n" "Last-Translator: Lyubomir Vasilev <lyubomirv@abv.bg>\n" "Language-Team: Esperanto (https://translate.zanata.org/project/view/GitLab)\n" "Language: eo\n" @@ -29,6 +29,14 @@ msgstr[1] "%d enmetadoj" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} enmetis %{commit_timeago}" +msgid "1 pipeline" +msgid_plural "%d pipelines" +msgstr[0] "1 ĉenstablo" +msgstr[1] "%d ĉenstabloj" + +msgid "A collection of graphs regarding Continuous Integration" +msgstr "Aro da diagramoj pri la seninterrompa integrado" + msgid "About auto deploy" msgstr "Pri la aŭtomata disponigado" @@ -191,6 +199,9 @@ msgid_plural "Commits" msgstr[0] "Enmetado" msgstr[1] "Enmetadoj" +msgid "Commit duration in minutes for last 30 commits" +msgstr "Daŭro de la enmetadoj por la lastaj 30 enmetadoj" + msgid "Commit message" msgstr "Mesaĝo pri la enmetado" @@ -230,6 +241,13 @@ msgstr "Kopii la identigilon de la enmetado" msgid "Create New Directory" msgstr "Krei novan dosierujon" +msgid "" +"Create a personal access token on your account to pull or push via " +"%{protocol}." +msgstr "" +"Kreu propran atingoĵetonon en via konto por ebligi al vi eltiri kaj alpuŝi " +"per %{protocol}." + msgid "Create directory" msgstr "Krei dosierujon" @@ -248,6 +266,9 @@ msgstr "Disbranĉigi" msgid "CreateTag|Tag" msgstr "Etikedo" +msgid "CreateTokenToCloneLink|create a personal access token" +msgstr "kreos propran atingoĵetonon" + msgid "Cron Timezone" msgstr "Horzono por Cron" @@ -420,6 +441,15 @@ msgstr "Intervala ŝablono" msgid "Introducing Cycle Analytics" msgstr "Ni prezentas al vi la ciklan analizon" +msgid "Jobs for last month" +msgstr "Taskoj po la lasta monato" + +msgid "Jobs for last week" +msgstr "Taskoj po la lasta semajno" + +msgid "Jobs for last year" +msgstr "Taskoj po la lasta jaro" + msgid "LFSStatus|Disabled" msgstr "Malŝaltita" @@ -585,6 +615,21 @@ msgstr "Ĉenstabla plano" msgid "Pipeline Schedules" msgstr "Ĉenstablaj planoj" +msgid "PipelineCharts|Failed:" +msgstr "Malsukcesaj:" + +msgid "PipelineCharts|Overall statistics" +msgstr "Ĝenerala statistiko" + +msgid "PipelineCharts|Success ratio:" +msgstr "Proporcio de sukceso:" + +msgid "PipelineCharts|Successful:" +msgstr "Sukcesaj:" + +msgid "PipelineCharts|Total:" +msgstr "Totalo:" + msgid "PipelineSchedules|Activated" msgstr "Ŝaltita" @@ -615,6 +660,18 @@ msgstr "Celo" msgid "PipelineSheduleIntervalPattern|Custom" msgstr "Propra" +msgid "Pipelines" +msgstr "Ĉenstabloj" + +msgid "Pipelines charts" +msgstr "Ĉenstablaj diagramoj" + +msgid "Pipeline|all" +msgstr "ĉiuj" + +msgid "Pipeline|success" +msgstr "sukcesaj" + msgid "Pipeline|with stage" msgstr "kun etapo" diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po index db992021403..c5600721d49 100644 --- a/locale/it/gitlab.po +++ b/locale/it/gitlab.po @@ -4,13 +4,13 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-19 15:50-0500\n" +"POT-Creation-Date: 2017-06-28 13:32+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-07-02 10:32-0400\n" +"PO-Revision-Date: 2017-07-12 05:45-0400\n" "Last-Translator: Paolo Falomo <info@paolofalomo.it>\n" -"Language-Team: Italian\n" +"Language-Team: Italian (https://translate.zanata.org/project/view/GitLab)\n" "Language: it\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" @@ -33,6 +33,14 @@ msgstr[1] "%d commit" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} ha committato %{commit_timeago}" +msgid "1 pipeline" +msgid_plural "%d pipelines" +msgstr[0] "1 pipeline" +msgstr[1] "%d pipeline" + +msgid "A collection of graphs regarding Continuous Integration" +msgstr "Un insieme di grafici riguardo la Continuous Integration" + msgid "About auto deploy" msgstr "Riguardo il rilascio automatico" @@ -196,6 +204,9 @@ msgid_plural "Commits" msgstr[0] "Commit" msgstr[1] "Commits" +msgid "Commit duration in minutes for last 30 commits" +msgstr "Durata del commit (in minuti) per gli ultimi 30 commit" + msgid "Commit message" msgstr "Messaggio del commit" @@ -235,6 +246,13 @@ msgstr "Copia l'SHA del commit negli appunti" msgid "Create New Directory" msgstr "Crea una nuova cartella" +msgid "" +"Create a personal access token on your account to pull or push via " +"%{protocol}." +msgstr "" +"Creare un token di accesso sul tuo account per eseguire pull o push tramite " +"%{protocol}" + msgid "Create directory" msgstr "Crea cartella" @@ -253,6 +271,9 @@ msgstr "Fork" msgid "CreateTag|Tag" msgstr "Tag" +msgid "CreateTokenToCloneLink|create a personal access token" +msgstr "Crea token d'accesso personale" + msgid "Cron Timezone" msgstr "Cron Timezone" @@ -425,6 +446,15 @@ msgstr "Intervallo di Pattern" msgid "Introducing Cycle Analytics" msgstr "Introduzione delle Analisi Cicliche" +msgid "Jobs for last month" +msgstr "Jobs dell'ultimo mese" + +msgid "Jobs for last week" +msgstr "Jobs dell'ultima settimana" + +msgid "Jobs for last year" +msgstr "Jobs dell'ultimo anno" + msgid "LFSStatus|Disabled" msgstr "Disabilitato" @@ -590,6 +620,21 @@ msgstr "Pianificazione Pipeline" msgid "Pipeline Schedules" msgstr "Pianificazione multipla Pipeline" +msgid "PipelineCharts|Failed:" +msgstr "Fallita:" + +msgid "PipelineCharts|Overall statistics" +msgstr "Statistiche riassuntive" + +msgid "PipelineCharts|Success ratio:" +msgstr "Percentuale di successo" + +msgid "PipelineCharts|Successful:" +msgstr "Completata:" + +msgid "PipelineCharts|Total:" +msgstr "Totale:" + msgid "PipelineSchedules|Activated" msgstr "Attivata" @@ -620,6 +665,18 @@ msgstr "Target" msgid "PipelineSheduleIntervalPattern|Custom" msgstr "Personalizzato" +msgid "Pipelines" +msgstr "Pipeline" + +msgid "Pipelines charts" +msgstr "Grafici pipeline" + +msgid "Pipeline|all" +msgstr "tutto" + +msgid "Pipeline|success" +msgstr "successo" + msgid "Pipeline|with stage" msgstr "con stadio" @@ -684,7 +741,7 @@ msgid "ProjectNetworkGraph|Graph" msgstr "Grafico" msgid "Read more" -msgstr "Continua..." +msgstr "Vedi altro" msgid "Readme" msgstr "Leggimi" @@ -751,7 +808,7 @@ msgstr "Seleziona una branch di destinazione" msgid "Set a password on your account to pull or push via %{protocol}." msgstr "" -"Imposta una password sul tuo account per eseguire pull o push tramite " +"Establezca una contraseña en su cuenta para actualizar o enviar a través de " "%{protocol}." msgid "Set up CI" @@ -814,9 +871,9 @@ msgid "" "the issue to a milestone, or add the issue to a list on your Issue Board. " "Begin creating issues to see data for this stage." msgstr "" -"Questo stadio di issue mostra il tempo che ci vuole dal creare un issue " -"all'assegnarli una milestone, o ad aggiungere un issue alla tua board. Crea " -"un issue per vedere questo stadio." +"Lo stadio di Issue mostra il tempo che impiega un issue ad esser correlato " +"ad una Milestone, o ad esser aggiunto ad una tua Lavagna. Inizia la " +"creazione di problemi per visualizzare i dati in questo stadio." msgid "The phase of the development lifecycle." msgstr "Il ciclo vitale della fase di sviluppo." diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po index 2f21aae2899..8a76121e0c1 100644 --- a/locale/zh_CN/gitlab.po +++ b/locale/zh_CN/gitlab.po @@ -4,11 +4,11 @@ msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-19 15:50-0500\n" +"POT-Creation-Date: 2017-06-28 13:32+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-06-27 03:18-0400\n" +"PO-Revision-Date: 2017-07-10 09:58-0400\n" "Last-Translator: Huang Tao <htve@outlook.com>\n" "Language-Team: Chinese (China) (https://translate.zanata.org/project/view/GitLab)\n" "Language: zh-CN\n" @@ -27,6 +27,13 @@ msgstr[0] "%d 次提交" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "由 %{commit_author_link} 提交于 %{commit_timeago}" +msgid "1 pipeline" +msgid_plural "%d pipelines" +msgstr[0] "%d 条流水线" + +msgid "A collection of graphs regarding Continuous Integration" +msgstr "持续集成数据图" + msgid "About auto deploy" msgstr "关于自动部署" @@ -184,6 +191,9 @@ msgid "Commit" msgid_plural "Commits" msgstr[0] "提交" +msgid "Commit duration in minutes for last 30 commits" +msgstr "最近30次提交相应持续集成花费的时间(分钟)" + msgid "Commit message" msgstr "提交信息" @@ -223,6 +233,11 @@ msgstr "复制提交 SHA 的值到剪贴板" msgid "Create New Directory" msgstr "创建新目录" +msgid "" +"Create a personal access token on your account to pull or push via " +"%{protocol}." +msgstr "在帐户上创建个人访问令牌,以通过%{protocol}来拉取或推送。" + msgid "Create directory" msgstr "创建目录" @@ -241,6 +256,9 @@ msgstr "派生" msgid "CreateTag|Tag" msgstr "标签" +msgid "CreateTokenToCloneLink|create a personal access token" +msgstr "创建个人访问令牌" + msgid "Cron Timezone" msgstr "Cron 时区" @@ -405,6 +423,15 @@ msgstr "循环周期" msgid "Introducing Cycle Analytics" msgstr "周期分析简介" +msgid "Jobs for last month" +msgstr "上个月的作业" + +msgid "Jobs for last week" +msgstr "上个星期的作业" + +msgid "Jobs for last year" +msgstr "去年的作业" + msgid "LFSStatus|Disabled" msgstr "停用" @@ -567,6 +594,21 @@ msgstr "流水线计划" msgid "Pipeline Schedules" msgstr "流水线计划" +msgid "PipelineCharts|Failed:" +msgstr "失败:" + +msgid "PipelineCharts|Overall statistics" +msgstr "总体统计数据" + +msgid "PipelineCharts|Success ratio:" +msgstr "成功率:" + +msgid "PipelineCharts|Successful:" +msgstr "成功:" + +msgid "PipelineCharts|Total:" +msgstr "总计:" + msgid "PipelineSchedules|Activated" msgstr "是否启用" @@ -597,6 +639,18 @@ msgstr "目标" msgid "PipelineSheduleIntervalPattern|Custom" msgstr "自定义" +msgid "Pipelines" +msgstr "流水线" + +msgid "Pipelines charts" +msgstr "流水线统计图" + +msgid "Pipeline|all" +msgstr "所有" + +msgid "Pipeline|success" +msgstr "成功" + msgid "Pipeline|with stage" msgstr "于阶段" diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po index afdbd01b7d7..b679ed3f26b 100644 --- a/locale/zh_HK/gitlab.po +++ b/locale/zh_HK/gitlab.po @@ -1,17 +1,15 @@ # Huang Tao <htve@outlook.com>, 2017. #zanata -# Victor Wu <anonymous@domain.com>, 2017. -# Hazel Yang <anonymous@domain.com>, 2017. msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-15 21:59-0500\n" +"POT-Creation-Date: 2017-06-28 13:32+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-06-23 01:23-0400\n" +"PO-Revision-Date: 2017-07-06 11:26-0400\n" "Last-Translator: Huang Tao <htve@outlook.com>\n" -"Language-Team: Chinese (Hong Kong) (https://translate.zanata.org/project/view/GitLab)\n" +"Language-Team: Chinese (Hong Kong SAR China)\n" "Language: zh-HK\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=1; plural=0\n" @@ -28,6 +26,13 @@ msgstr[0] " %d 次提交" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "由 %{commit_author_link} 提交於 %{commit_timeago}" +msgid "1 pipeline" +msgid_plural "%d pipelines" +msgstr[0] "%d 條流水線" + +msgid "A collection of graphs regarding Continuous Integration" +msgstr "相關持續集成的圖像集合" + msgid "About auto deploy" msgstr "關於自動部署" @@ -185,6 +190,9 @@ msgid "Commit" msgid_plural "Commits" msgstr[0] "提交" +msgid "Commit duration in minutes for last 30 commits" +msgstr "最近30次提交花費的時間(分鐘)" + msgid "Commit message" msgstr "提交信息" @@ -224,6 +232,11 @@ msgstr "複製提交 SHA 到剪貼板" msgid "Create New Directory" msgstr "創建新目錄" +msgid "" +"Create a personal access token on your account to pull or push via " +"%{protocol}." +msgstr "在帳戶上創建個人訪問令牌,以通過 %{protocol} 來拉取或推送。" + msgid "Create directory" msgstr "創建目錄" @@ -242,6 +255,9 @@ msgstr "派生" msgid "CreateTag|Tag" msgstr "標籤" +msgid "CreateTokenToCloneLink|create a personal access token" +msgstr "創建個人訪問令牌" + msgid "Cron Timezone" msgstr "Cron 時區" @@ -406,6 +422,15 @@ msgstr "循環週期" msgid "Introducing Cycle Analytics" msgstr "週期分析簡介" +msgid "Jobs for last month" +msgstr "上個月的作業" + +msgid "Jobs for last week" +msgstr "上個星期的作業" + +msgid "Jobs for last year" +msgstr "去年的作業" + msgid "LFSStatus|Disabled" msgstr "停用" @@ -568,6 +593,21 @@ msgstr "流水線計劃" msgid "Pipeline Schedules" msgstr "流水線計劃" +msgid "PipelineCharts|Failed:" +msgstr "失敗:" + +msgid "PipelineCharts|Overall statistics" +msgstr "總體統計" + +msgid "PipelineCharts|Success ratio:" +msgstr "成功率:" + +msgid "PipelineCharts|Successful:" +msgstr "成功:" + +msgid "PipelineCharts|Total:" +msgstr "總計:" + msgid "PipelineSchedules|Activated" msgstr "是否啟用" @@ -598,6 +638,18 @@ msgstr "目標" msgid "PipelineSheduleIntervalPattern|Custom" msgstr "自定義" +msgid "Pipelines" +msgstr "流水線" + +msgid "Pipelines charts" +msgstr "流水線圖表" + +msgid "Pipeline|all" +msgstr "所有" + +msgid "Pipeline|success" +msgstr "成功" + msgid "Pipeline|with stage" msgstr "於階段" diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po index fa0b3b339fa..d5a3c5ea0b9 100644 --- a/locale/zh_TW/gitlab.po +++ b/locale/zh_TW/gitlab.po @@ -1,18 +1,18 @@ # Huang Tao <htve@outlook.com>, 2017. #zanata -# Lin Jen-Shin <anonymous@domain.com>, 2017. # Hazel Yang <anonymous@domain.com>, 2017. # TzeKei Lee <anonymous@domain.com>, 2017. # Jerry Ho <a29988122@gmail.com>, 2017. +# Lin Jen-Shin <godfat@godfat.org>, 2017. #zanata msgid "" msgstr "" "Project-Id-Version: gitlab 1.0.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-19 15:50-0500\n" +"POT-Creation-Date: 2017-06-28 13:32+0200\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2017-06-28 11:13-0400\n" -"Last-Translator: Huang Tao <htve@outlook.com>\n" +"PO-Revision-Date: 2017-07-11 09:10-0400\n" +"Last-Translator: Lin Jen-Shin <godfat@godfat.org>\n" "Language-Team: Chinese (Taiwan) (https://translate.zanata.org/project/view/GitLab)\n" "Language: zh-TW\n" "X-Generator: Zanata 3.9.6\n" @@ -30,6 +30,13 @@ msgstr[0] "%d 個更動 (commit)" msgid "%{commit_author_link} committed %{commit_timeago}" msgstr "%{commit_author_link} 在 %{commit_timeago} 送交" +msgid "1 pipeline" +msgid_plural "%d pipelines" +msgstr[0] "%d 條流水線" + +msgid "A collection of graphs regarding Continuous Integration" +msgstr "持續整合 (CI) 相關的圖表" + msgid "About auto deploy" msgstr "關於自動部署" @@ -187,6 +194,9 @@ msgid "Commit" msgid_plural "Commits" msgstr[0] "更動記錄 (commit) " +msgid "Commit duration in minutes for last 30 commits" +msgstr "最近 30 次更動花費的時間(分鐘)" + msgid "Commit message" msgstr "更動說明 (commit) " @@ -226,6 +236,11 @@ msgstr "複製更動記錄 (commit) 的 SHA 值到剪貼簿" msgid "Create New Directory" msgstr "建立新目錄" +msgid "" +"Create a personal access token on your account to pull or push via " +"%{protocol}." +msgstr "建立個人存取憑證 (access token) 以使用 %{protocol} 來上傳 (push) 或下載 (pull) 。" + msgid "Create directory" msgstr "建立目錄" @@ -244,6 +259,9 @@ msgstr "分支 (fork) " msgid "CreateTag|Tag" msgstr "建立標籤" +msgid "CreateTokenToCloneLink|create a personal access token" +msgstr "建立個人存取憑證 (access token)" + msgid "Cron Timezone" msgstr "Cron 時區" @@ -408,6 +426,15 @@ msgstr "循環週期" msgid "Introducing Cycle Analytics" msgstr "週期分析簡介" +msgid "Jobs for last month" +msgstr "上個月的任務 (job) " + +msgid "Jobs for last week" +msgstr "上個星期的任務 (job) " + +msgid "Jobs for last year" +msgstr "去年的任務 (job) " + msgid "LFSStatus|Disabled" msgstr "停用" @@ -570,6 +597,21 @@ msgstr "流水線 (pipeline) 排程" msgid "Pipeline Schedules" msgstr "流水線 (pipeline) 排程" +msgid "PipelineCharts|Failed:" +msgstr "失敗:" + +msgid "PipelineCharts|Overall statistics" +msgstr "總體統計" + +msgid "PipelineCharts|Success ratio:" +msgstr "成功比率:" + +msgid "PipelineCharts|Successful:" +msgstr "成功:" + +msgid "PipelineCharts|Total:" +msgstr "總計:" + msgid "PipelineSchedules|Activated" msgstr "是否啟用" @@ -600,6 +642,18 @@ msgstr "目標" msgid "PipelineSheduleIntervalPattern|Custom" msgstr "自訂" +msgid "Pipelines" +msgstr "流水線 (pipeline) " + +msgid "Pipelines charts" +msgstr "流水線 (pipeline) 圖表" + +msgid "Pipeline|all" +msgstr "所有" + +msgid "Pipeline|success" +msgstr "成功" + msgid "Pipeline|with stage" msgstr "於階段" diff --git a/package.json b/package.json index 5a997e813f8..fd944531a6a 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "visibilityjs": "^1.2.4", "vue": "^2.2.6", "vue-loader": "^11.3.4", - "vue-resource": "^0.9.3", + "vue-resource": "^1.3.4", "vue-template-compiler": "^2.2.6", "webpack": "^2.6.1", "webpack-bundle-analyzer": "^2.8.2" diff --git a/public/ci/favicon.ico b/public/ci/favicon.ico Binary files differdeleted file mode 100644 index 9663d4d00b9..00000000000 --- a/public/ci/favicon.ico +++ /dev/null diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index 03de59f27ad..68114d149c4 100644 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -12,9 +12,6 @@ fi # gems could not be found under some circumstance. No idea why, hours wasted. retry gem install knapsack fog-aws mime-types -cp config/resque.yml.example config/resque.yml -sed -i 's/localhost/redis/g' config/resque.yml - cp config/gitlab.yml.example config/gitlab.yml # Determine the database by looking at the job name. @@ -37,6 +34,18 @@ else # Assume it's mysql sed -i 's/# host:.*/host: mysql/g' config/database.yml fi +cp config/resque.yml.example config/resque.yml +sed -i 's/localhost/redis/g' config/resque.yml + +cp config/redis.cache.yml.example config/redis.cache.yml +sed -i 's/localhost/redis/g' config/redis.cache.yml + +cp config/redis.queues.yml.example config/redis.queues.yml +sed -i 's/localhost/redis/g' config/redis.queues.yml + +cp config/redis.shared_state.yml.example config/redis.shared_state.yml +sed -i 's/localhost/redis/g' config/redis.shared_state.yml + if [ "$SETUP_DB" != "false" ]; then bundle exec rake db:drop db:create db:schema:load db:migrate diff --git a/spec/config/mail_room_spec.rb b/spec/config/mail_room_spec.rb index 092048a6259..a31e44fa928 100644 --- a/spec/config/mail_room_spec.rb +++ b/spec/config/mail_room_spec.rb @@ -5,12 +5,12 @@ describe 'mail_room.yml' do let(:mailroom_config_path) { 'config/mail_room.yml' } let(:gitlab_config_path) { 'config/mail_room.yml' } - let(:redis_config_path) { 'config/resque.yml' } + let(:queues_config_path) { 'config/redis.queues.yml' } let(:configuration) do vars = { 'MAIL_ROOM_GITLAB_CONFIG_FILE' => absolute_path(gitlab_config_path), - 'GITLAB_REDIS_CONFIG_FILE' => absolute_path(redis_config_path) + 'GITLAB_REDIS_QUEUES_CONFIG_FILE' => absolute_path(queues_config_path) } cmd = "puts ERB.new(File.read(#{absolute_path(mailroom_config_path).inspect})).result" @@ -21,12 +21,12 @@ describe 'mail_room.yml' do end before(:each) do - stub_env('GITLAB_REDIS_CONFIG_FILE', absolute_path(redis_config_path)) - clear_redis_raw_config + stub_env('GITLAB_REDIS_QUEUES_CONFIG_FILE', absolute_path(queues_config_path)) + clear_queues_raw_config end after(:each) do - clear_redis_raw_config + clear_queues_raw_config end context 'when incoming email is disabled' do @@ -39,9 +39,9 @@ describe 'mail_room.yml' do context 'when incoming email is enabled' do let(:gitlab_config_path) { 'spec/fixtures/config/mail_room_enabled.yml' } - let(:redis_config_path) { 'spec/fixtures/config/redis_new_format_host.yml' } + let(:queues_config_path) { 'spec/fixtures/config/redis_queues_new_format_host.yml' } - let(:gitlab_redis) { Gitlab::Redis.new(Rails.env) } + let(:gitlab_redis_queues) { Gitlab::Redis::Queues.new(Rails.env) } it 'contains the intended configuration' do expect(configuration[:mailboxes].length).to eq(1) @@ -56,8 +56,8 @@ describe 'mail_room.yml' do expect(mailbox[:name]).to eq('inbox') expect(mailbox[:idle_timeout]).to eq(60) - redis_url = gitlab_redis.url - sentinels = gitlab_redis.sentinels + redis_url = gitlab_redis_queues.url + sentinels = gitlab_redis_queues.sentinels expect(mailbox[:delivery_options][:redis_url]).to be_present expect(mailbox[:delivery_options][:redis_url]).to eq(redis_url) @@ -73,8 +73,8 @@ describe 'mail_room.yml' do end end - def clear_redis_raw_config - Gitlab::Redis.remove_instance_variable(:@_raw_config) + def clear_queues_raw_config + Gitlab::Redis::Queues.remove_instance_variable(:@_raw_config) rescue NameError # raised if @_raw_config was not set; ignore end diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb index 58c16cc57e6..03da6287774 100644 --- a/spec/controllers/health_check_controller_spec.rb +++ b/spec/controllers/health_check_controller_spec.rb @@ -3,52 +3,79 @@ require 'spec_helper' describe HealthCheckController do include StubENV - let(:token) { current_application_settings.health_check_access_token } let(:json_response) { JSON.parse(response.body) } let(:xml_response) { Hash.from_xml(response.body)['hash'] } + let(:token) { current_application_settings.health_check_access_token } + let(:whitelisted_ip) { '127.0.0.1' } + let(:not_whitelisted_ip) { '127.0.0.2' } before do + allow(Settings.monitoring).to receive(:ip_whitelist).and_return([whitelisted_ip]) stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') end describe 'GET #index' do - context 'when services are up but NO access token' do + context 'when services are up but accessed from outside whitelisted ips' do + before do + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(not_whitelisted_ip) + end + it 'returns a not found page' do get :index + expect(response).to be_not_found end + + context 'when services are accessed with token' do + it 'supports passing the token in the header' do + request.headers['TOKEN'] = token + + get :index + + expect(response).to be_success + expect(response.content_type).to eq 'text/plain' + end + + it 'supports passing the token in query params' do + get :index, token: token + + expect(response).to be_success + expect(response.content_type).to eq 'text/plain' + end + end end - context 'when services are up and an access token is provided' do - it 'supports passing the token in the header' do - request.headers['TOKEN'] = token - get :index - expect(response).to be_success - expect(response.content_type).to eq 'text/plain' + context 'when services are up and accessed from whitelisted ips' do + before do + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(whitelisted_ip) end - it 'supports successful plaintest response' do - get :index, token: token + it 'supports successful plaintext response' do + get :index + expect(response).to be_success expect(response.content_type).to eq 'text/plain' end it 'supports successful json response' do - get :index, token: token, format: :json + get :index, format: :json + expect(response).to be_success expect(response.content_type).to eq 'application/json' expect(json_response['healthy']).to be true end it 'supports successful xml response' do - get :index, token: token, format: :xml + get :index, format: :xml + expect(response).to be_success expect(response.content_type).to eq 'application/xml' expect(xml_response['healthy']).to be true end it 'supports successful responses for specific checks' do - get :index, token: token, checks: 'email', format: :json + get :index, checks: 'email', format: :json + expect(response).to be_success expect(response.content_type).to eq 'application/json' expect(json_response['healthy']).to be true @@ -58,33 +85,29 @@ describe HealthCheckController do context 'when a service is down but NO access token' do it 'returns a not found page' do get :index + expect(response).to be_not_found end end - context 'when a service is down and an access token is provided' do + context 'when a service is down and an endpoint is accessed from whitelisted ip' do before do allow(HealthCheck::Utils).to receive(:process_checks).with(['standard']).and_return('The server is on fire') allow(HealthCheck::Utils).to receive(:process_checks).with(['email']).and_return('Email is on fire') + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(whitelisted_ip) end - it 'supports passing the token in the header' do - request.headers['TOKEN'] = token + it 'supports failure plaintext response' do get :index - expect(response).to have_http_status(500) - expect(response.content_type).to eq 'text/plain' - expect(response.body).to include('The server is on fire') - end - it 'supports failure plaintest response' do - get :index, token: token expect(response).to have_http_status(500) expect(response.content_type).to eq 'text/plain' expect(response.body).to include('The server is on fire') end it 'supports failure json response' do - get :index, token: token, format: :json + get :index, format: :json + expect(response).to have_http_status(500) expect(response.content_type).to eq 'application/json' expect(json_response['healthy']).to be false @@ -92,7 +115,8 @@ describe HealthCheckController do end it 'supports failure xml response' do - get :index, token: token, format: :xml + get :index, format: :xml + expect(response).to have_http_status(500) expect(response.content_type).to eq 'application/xml' expect(xml_response['healthy']).to be false @@ -100,7 +124,8 @@ describe HealthCheckController do end it 'supports failure responses for specific checks' do - get :index, token: token, checks: 'email', format: :json + get :index, checks: 'email', format: :json + expect(response).to have_http_status(500) expect(response.content_type).to eq 'application/json' expect(json_response['healthy']).to be false diff --git a/spec/controllers/health_controller_spec.rb b/spec/controllers/health_controller_spec.rb index e7c19b47a6a..cc389e554ad 100644 --- a/spec/controllers/health_controller_spec.rb +++ b/spec/controllers/health_controller_spec.rb @@ -3,55 +3,120 @@ require 'spec_helper' describe HealthController do include StubENV - let(:token) { current_application_settings.health_check_access_token } let(:json_response) { JSON.parse(response.body) } + let(:token) { current_application_settings.health_check_access_token } + let(:whitelisted_ip) { '127.0.0.1' } + let(:not_whitelisted_ip) { '127.0.0.2' } before do + allow(Settings.monitoring).to receive(:ip_whitelist).and_return([whitelisted_ip]) stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') end describe '#readiness' do - context 'authorization token provided' do - before do - request.headers['TOKEN'] = token - end + shared_context 'endpoint responding with readiness data' do + let(:request_params) { {} } + + subject { get :readiness, request_params } + + it 'responds with readiness checks data' do + subject - it 'returns proper response' do - get :readiness expect(json_response['db_check']['status']).to eq('ok') - expect(json_response['redis_check']['status']).to eq('ok') + expect(json_response['cache_check']['status']).to eq('ok') + expect(json_response['queues_check']['status']).to eq('ok') + expect(json_response['shared_state_check']['status']).to eq('ok') expect(json_response['fs_shards_check']['status']).to eq('ok') expect(json_response['fs_shards_check']['labels']['shard']).to eq('default') end end - context 'without authorization token' do - it 'returns proper response' do + context 'accessed from whitelisted ip' do + before do + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(whitelisted_ip) + end + + it_behaves_like 'endpoint responding with readiness data' + end + + context 'accessed from not whitelisted ip' do + before do + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(not_whitelisted_ip) + end + + it 'responds with resource not found' do get :readiness + expect(response.status).to eq(404) end + + context 'accessed with valid token' do + context 'token passed in request header' do + before do + request.headers['TOKEN'] = token + end + + it_behaves_like 'endpoint responding with readiness data' + end + end + + context 'token passed as URL param' do + it_behaves_like 'endpoint responding with readiness data' do + let(:request_params) { { token: token } } + end + end end end describe '#liveness' do - context 'authorization token provided' do - before do - request.headers['TOKEN'] = token - end + shared_context 'endpoint responding with liveness data' do + subject { get :liveness } + + it 'responds with liveness checks data' do + subject - it 'returns proper response' do - get :liveness expect(json_response['db_check']['status']).to eq('ok') - expect(json_response['redis_check']['status']).to eq('ok') + expect(json_response['cache_check']['status']).to eq('ok') + expect(json_response['queues_check']['status']).to eq('ok') + expect(json_response['shared_state_check']['status']).to eq('ok') expect(json_response['fs_shards_check']['status']).to eq('ok') end end - context 'without authorization token' do - it 'returns proper response' do + context 'accessed from whitelisted ip' do + before do + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(whitelisted_ip) + end + + it_behaves_like 'endpoint responding with liveness data' + end + + context 'accessed from not whitelisted ip' do + before do + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(not_whitelisted_ip) + end + + it 'responds with resource not found' do get :liveness + expect(response.status).to eq(404) end + + context 'accessed with valid token' do + context 'token passed in request header' do + before do + request.headers['TOKEN'] = token + end + + it_behaves_like 'endpoint responding with liveness data' + end + + context 'token passed as URL param' do + it_behaves_like 'endpoint responding with liveness data' do + subject { get :liveness, token: token } + end + end + end end end end diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb index 044c9f179ed..86847c07c09 100644 --- a/spec/controllers/metrics_controller_spec.rb +++ b/spec/controllers/metrics_controller_spec.rb @@ -3,22 +3,22 @@ require 'spec_helper' describe MetricsController do include StubENV - let(:token) { current_application_settings.health_check_access_token } let(:json_response) { JSON.parse(response.body) } let(:metrics_multiproc_dir) { Dir.mktmpdir } + let(:whitelisted_ip) { '127.0.0.1' } + let(:whitelisted_ip_range) { '10.0.0.0/24' } + let(:ip_in_whitelisted_range) { '10.0.0.1' } + let(:not_whitelisted_ip) { '10.0.1.1' } before do stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') stub_env('prometheus_multiproc_dir', metrics_multiproc_dir) allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(true) + allow(Settings.monitoring).to receive(:ip_whitelist).and_return([whitelisted_ip, whitelisted_ip_range]) end describe '#index' do - context 'authorization token provided' do - before do - request.headers['TOKEN'] = token - end - + shared_examples_for 'endpoint providing metrics' do it 'returns DB ping metrics' do get :index @@ -35,6 +35,30 @@ describe MetricsController do expect(response.body).to match(/^redis_ping_latency [0-9\.]+$/) end + it 'returns Caching ping metrics' do + get :index + + expect(response.body).to match(/^redis_cache_ping_timeout 0$/) + expect(response.body).to match(/^redis_cache_ping_success 1$/) + expect(response.body).to match(/^redis_cache_ping_latency [0-9\.]+$/) + end + + it 'returns Queues ping metrics' do + get :index + + expect(response.body).to match(/^redis_queues_ping_timeout 0$/) + expect(response.body).to match(/^redis_queues_ping_success 1$/) + expect(response.body).to match(/^redis_queues_ping_latency [0-9\.]+$/) + end + + it 'returns SharedState ping metrics' do + get :index + + expect(response.body).to match(/^redis_shared_state_ping_timeout 0$/) + expect(response.body).to match(/^redis_shared_state_ping_success 1$/) + expect(response.body).to match(/^redis_shared_state_ping_latency [0-9\.]+$/) + end + it 'returns file system check metrics' do get :index @@ -59,7 +83,27 @@ describe MetricsController do end end - context 'without authorization token' do + context 'accessed from whitelisted ip' do + before do + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(whitelisted_ip) + end + + it_behaves_like 'endpoint providing metrics' + end + + context 'accessed from ip in whitelisted range' do + before do + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(ip_in_whitelisted_range) + end + + it_behaves_like 'endpoint providing metrics' + end + + context 'accessed from not whitelisted ip' do + before do + allow(Gitlab::RequestContext).to receive(:client_ip).and_return(not_whitelisted_ip) + end + it 'returns proper response' do get :index diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index bf922260b2f..2b4e8723b48 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -47,7 +47,7 @@ describe SessionsController do end end - context 'when using valid password', :redis do + context 'when using valid password', :clean_gitlab_redis_shared_state do include UserActivitiesHelpers let(:user) { create(:user) } diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index 285724f4b48..6b666934563 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Navigation bar counter', feature: true, caching: true do +describe 'Navigation bar counter', :use_clean_rails_memory_store_caching, feature: true do let(:user) { create(:user) } let(:project) { create(:empty_project, namespace: user.namespace) } let(:issue) { create(:issue, project: project) } diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 7ca002fc821..bdba22fe9a9 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -61,7 +61,7 @@ feature 'Dashboard Projects' do end end - describe 'with a pipeline', redis: true do + describe "with a pipeline", clean_gitlab_redis_shared_state: true do let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha) } before do diff --git a/spec/features/groups/members/sort_members_spec.rb b/spec/features/groups/members/sort_members_spec.rb index 169827f5d0d..92ff45e0cdc 100644 --- a/spec/features/groups/members/sort_members_spec.rb +++ b/spec/features/groups/members/sort_members_spec.rb @@ -68,7 +68,7 @@ feature 'Groups > Members > Sort members', feature: true do expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending') end - scenario 'sorts by recent sign in', :redis do + scenario 'sorts by recent sign in', :clean_gitlab_redis_shared_state do visit_members_list(sort: :recent_sign_in) expect(first_member).to include(owner.name) @@ -76,7 +76,7 @@ feature 'Groups > Members > Sort members', feature: true do expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in') end - scenario 'sorts by oldest sign in', :redis do + scenario 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do visit_members_list(sort: :oldest_sign_in) expect(first_member).to include(developer.name) diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index a8055b21cee..2a2213b67ed 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -41,7 +41,7 @@ feature 'Login', feature: true do expect(page).to have_content('Your account has been blocked.') end - it 'does not update Devise trackable attributes', :redis do + it 'does not update Devise trackable attributes', :clean_gitlab_redis_shared_state do user = create(:user, :blocked) expect { gitlab_sign_in(user) }.not_to change { user.reload.sign_in_count } @@ -55,7 +55,7 @@ feature 'Login', feature: true do expect(page).to have_content('Invalid Login or password.') end - it 'does not update Devise trackable attributes', :redis do + it 'does not update Devise trackable attributes', :clean_gitlab_redis_shared_state do expect { gitlab_sign_in(User.ghost) }.not_to change { User.ghost.reload.sign_in_count } end end diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb index afb613f034e..dc7236fa120 100644 --- a/spec/features/projects/members/sorting_spec.rb +++ b/spec/features/projects/members/sorting_spec.rb @@ -67,7 +67,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Name, descending') end - scenario 'sorts by recent sign in', :redis do + scenario 'sorts by recent sign in', :clean_gitlab_redis_shared_state do visit_members_list(sort: :recent_sign_in) expect(first_member).to include(master.name) @@ -75,7 +75,7 @@ feature 'Projects > Members > Sorting', feature: true do expect(page).to have_css('.member-sort-dropdown .dropdown-toggle-text', text: 'Recent sign in') end - scenario 'sorts by oldest sign in', :redis do + scenario 'sorts by oldest sign in', :clean_gitlab_redis_shared_state do visit_members_list(sort: :oldest_sign_in) expect(first_member).to include(developer.name) diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb index 992a68b25a5..033ccf06124 100644 --- a/spec/features/projects/pipeline_schedules_spec.rb +++ b/spec/features/projects/pipeline_schedules_spec.rb @@ -9,188 +9,222 @@ feature 'Pipeline Schedules', :feature, js: true do let(:scope) { nil } let!(:user) { create(:user) } - before do - project.add_master(user) - sign_in(user) - end - - describe 'GET /projects/pipeline_schedules' do + context 'logged in as master' do before do - visit_pipelines_schedules + project.add_master(user) + gitlab_sign_in(user) end - describe 'The view' do - it 'displays the required information description' do - page.within('.pipeline-schedule-table-row') do - expect(page).to have_content('pipeline schedule') - expect(find(".next-run-cell time")['data-original-title']) - .to include(pipeline_schedule.real_next_run.strftime('%b %-d, %Y')) - expect(page).to have_link('master') - expect(page).to have_link("##{pipeline.id}") - end + describe 'GET /projects/pipeline_schedules' do + before do + visit_pipelines_schedules end - it 'creates a new scheduled pipeline' do - click_link 'New schedule' + describe 'The view' do + it 'displays the required information description' do + page.within('.pipeline-schedule-table-row') do + expect(page).to have_content('pipeline schedule') + expect(find(".next-run-cell time")['data-original-title']) + .to include(pipeline_schedule.real_next_run.strftime('%b %-d, %Y')) + expect(page).to have_link('master') + expect(page).to have_link("##{pipeline.id}") + end + end - expect(page).to have_content('Schedule a new pipeline') - end + it 'creates a new scheduled pipeline' do + click_link 'New schedule' - it 'changes ownership of the pipeline' do - click_link 'Take ownership' - page.within('.pipeline-schedule-table-row') do - expect(page).not_to have_content('No owner') - expect(page).to have_link('John Doe') + expect(page).to have_content('Schedule a new pipeline') end - end - it 'edits the pipeline' do - page.within('.pipeline-schedule-table-row') do - click_link 'Edit' + it 'changes ownership of the pipeline' do + click_link 'Take ownership' + page.within('.pipeline-schedule-table-row') do + expect(page).not_to have_content('No owner') + expect(page).to have_link('John Doe') + end end - expect(page).to have_content('Edit Pipeline Schedule') + it 'edits the pipeline' do + page.within('.pipeline-schedule-table-row') do + click_link 'Edit' + end + + expect(page).to have_content('Edit Pipeline Schedule') + end + + it 'deletes the pipeline' do + click_link 'Delete' + + expect(page).not_to have_css(".pipeline-schedule-table-row") + end end - it 'deletes the pipeline' do - click_link 'Delete' + context 'when ref is nil' do + before do + pipeline_schedule.update_attribute(:ref, nil) + visit_pipelines_schedules + end - expect(page).not_to have_css(".pipeline-schedule-table-row") + it 'shows a list of the pipeline schedules with empty ref column' do + expect(first('.branch-name-cell').text).to eq('') + end end end - context 'when ref is nil' do + describe 'POST /projects/pipeline_schedules/new' do before do - pipeline_schedule.update_attribute(:ref, nil) - visit_pipelines_schedules + visit_new_pipeline_schedule end - it 'shows a list of the pipeline schedules with empty ref column' do - expect(first('.branch-name-cell').text).to eq('') + it 'sets defaults for timezone and target branch' do + expect(page).to have_button('master') + expect(page).to have_button('UTC') end - end - end - describe 'POST /projects/pipeline_schedules/new' do - before do - visit_new_pipeline_schedule - end + it 'it creates a new scheduled pipeline' do + fill_in_schedule_form + save_pipeline_schedule - it 'sets defaults for timezone and target branch' do - expect(page).to have_button('master') - expect(page).to have_button('UTC') - end + expect(page).to have_content('my fancy description') + end - it 'it creates a new scheduled pipeline' do - fill_in_schedule_form - save_pipeline_schedule + it 'it prevents an invalid form from being submitted' do + save_pipeline_schedule - expect(page).to have_content('my fancy description') + expect(page).to have_content('This field is required') + end end - it 'it prevents an invalid form from being submitted' do - save_pipeline_schedule + describe 'PATCH /projects/pipelines_schedules/:id/edit' do + before do + edit_pipeline_schedule + end - expect(page).to have_content('This field is required') - end - end + it 'it displays existing properties' do + description = find_field('schedule_description').value + expect(description).to eq('pipeline schedule') + expect(page).to have_button('master') + expect(page).to have_button('UTC') + end - describe 'PATCH /projects/pipelines_schedules/:id/edit' do - before do - edit_pipeline_schedule - end + it 'edits the scheduled pipeline' do + fill_in 'schedule_description', with: 'my brand new description' - it 'it displays existing properties' do - description = find_field('schedule_description').value - expect(description).to eq('pipeline schedule') - expect(page).to have_button('master') - expect(page).to have_button('UTC') - end + save_pipeline_schedule - it 'edits the scheduled pipeline' do - fill_in 'schedule_description', with: 'my brand new description' + expect(page).to have_content('my brand new description') + end - save_pipeline_schedule + context 'when ref is nil' do + before do + pipeline_schedule.update_attribute(:ref, nil) + edit_pipeline_schedule + end - expect(page).to have_content('my brand new description') + it 'shows the pipeline schedule with default ref' do + page.within('.js-target-branch-dropdown') do + expect(first('.dropdown-toggle-text').text).to eq('master') + end + end + end end - context 'when ref is nil' do - before do - pipeline_schedule.update_attribute(:ref, nil) - edit_pipeline_schedule + context 'when user creates a new pipeline schedule with variables' do + background do + visit_pipelines_schedules + click_link 'New schedule' + fill_in_schedule_form + all('[name="schedule[variables_attributes][][key]"]')[0].set('AAA') + all('[name="schedule[variables_attributes][][value]"]')[0].set('AAA123') + all('[name="schedule[variables_attributes][][key]"]')[1].set('BBB') + all('[name="schedule[variables_attributes][][value]"]')[1].set('BBB123') + save_pipeline_schedule end - it 'shows the pipeline schedule with default ref' do - page.within('.js-target-branch-dropdown') do - expect(first('.dropdown-toggle-text').text).to eq('master') + scenario 'user sees the new variable in edit window' do + find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click + page.within('.pipeline-variable-list') do + expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-key-input").value).to eq('AAA') + expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-value-input").value).to eq('AAA123') + expect(find(".pipeline-variable-row:nth-child(2) .pipeline-variable-key-input").value).to eq('BBB') + expect(find(".pipeline-variable-row:nth-child(2) .pipeline-variable-value-input").value).to eq('BBB123') end end end - end - context 'when user creates a new pipeline schedule with variables' do - background do - visit_pipelines_schedules - click_link 'New schedule' - fill_in_schedule_form - all('[name="schedule[variables_attributes][][key]"]')[0].set('AAA') - all('[name="schedule[variables_attributes][][value]"]')[0].set('AAA123') - all('[name="schedule[variables_attributes][][key]"]')[1].set('BBB') - all('[name="schedule[variables_attributes][][value]"]')[1].set('BBB123') - save_pipeline_schedule - end + context 'when user edits a variable of a pipeline schedule' do + background do + create(:ci_pipeline_schedule, project: project, owner: user).tap do |pipeline_schedule| + create(:ci_pipeline_schedule_variable, key: 'AAA', value: 'AAA123', pipeline_schedule: pipeline_schedule) + end - scenario 'user sees the new variable in edit window' do - find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click - page.within('.pipeline-variable-list') do - expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-key-input").value).to eq('AAA') - expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-value-input").value).to eq('AAA123') - expect(find(".pipeline-variable-row:nth-child(2) .pipeline-variable-key-input").value).to eq('BBB') - expect(find(".pipeline-variable-row:nth-child(2) .pipeline-variable-value-input").value).to eq('BBB123') + visit_pipelines_schedules + find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click + all('[name="schedule[variables_attributes][][key]"]')[0].set('foo') + all('[name="schedule[variables_attributes][][value]"]')[0].set('bar') + click_button 'Save pipeline schedule' end - end - end - context 'when user edits a variable of a pipeline schedule' do - background do - create(:ci_pipeline_schedule, project: project, owner: user).tap do |pipeline_schedule| - create(:ci_pipeline_schedule_variable, key: 'AAA', value: 'AAA123', pipeline_schedule: pipeline_schedule) + scenario 'user sees the updated variable in edit window' do + find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click + page.within('.pipeline-variable-list') do + expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-key-input").value).to eq('foo') + expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-value-input").value).to eq('bar') + end end - - visit_pipelines_schedules - find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click - all('[name="schedule[variables_attributes][][key]"]')[0].set('foo') - all('[name="schedule[variables_attributes][][value]"]')[0].set('bar') - click_button 'Save pipeline schedule' end - scenario 'user sees the updated variable in edit window' do - find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click - page.within('.pipeline-variable-list') do - expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-key-input").value).to eq('foo') - expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-value-input").value).to eq('bar') + context 'when user removes a variable of a pipeline schedule' do + background do + create(:ci_pipeline_schedule, project: project, owner: user).tap do |pipeline_schedule| + create(:ci_pipeline_schedule_variable, key: 'AAA', value: 'AAA123', pipeline_schedule: pipeline_schedule) + end + + visit_pipelines_schedules + find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click + find('.pipeline-variable-list .pipeline-variable-row-remove-button').click + click_button 'Save pipeline schedule' + end + + scenario 'user does not see the removed variable in edit window' do + find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click + page.within('.pipeline-variable-list') do + expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-key-input").value).to eq('') + expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-value-input").value).to eq('') + end end end end - context 'when user removes a variable of a pipeline schedule' do - background do - create(:ci_pipeline_schedule, project: project, owner: user).tap do |pipeline_schedule| - create(:ci_pipeline_schedule_variable, key: 'AAA', value: 'AAA123', pipeline_schedule: pipeline_schedule) + context 'logged in as non-member' do + before do + gitlab_sign_in(user) + end + + describe 'GET /projects/pipeline_schedules' do + before do + visit_pipelines_schedules end - visit_pipelines_schedules - find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click - find('.pipeline-variable-list .pipeline-variable-row-remove-button').click - click_button 'Save pipeline schedule' + describe 'The view' do + it 'does not show create schedule button' do + expect(page).not_to have_link('New schedule') + end + end end + end + + context 'not logged in' do + describe 'GET /projects/pipeline_schedules' do + before do + visit_pipelines_schedules + end - scenario 'user does not see the removed variable in edit window' do - find(".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']").click - page.within('.pipeline-variable-list') do - expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-key-input").value).to eq('') - expect(find(".pipeline-variable-row:nth-child(1) .pipeline-variable-value-input").value).to eq('') + describe 'The view' do + it 'does not show create schedule button' do + expect(page).not_to have_link('New schedule') + end end end end diff --git a/spec/features/projects/user_browses_files_spec.rb b/spec/features/projects/user_browses_files_spec.rb new file mode 100644 index 00000000000..263a3a29a66 --- /dev/null +++ b/spec/features/projects/user_browses_files_spec.rb @@ -0,0 +1,188 @@ +require 'spec_helper' + +describe 'User browses files' do + include DropzoneHelper + + let(:fork_message) do + "You're not allowed to make changes to this project directly. "\ + "A fork of this project has been created that you can make changes in, so you can submit a merge request." + end + let(:project) { create(:project, name: 'Shop') } + let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } + let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } + let(:tree_path_ref_6d39438) { project_tree_path(project, '6d39438') } + let(:tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + sign_in(user) + end + + context 'when browsing the master branch' do + before do + visit(tree_path_root_ref) + end + + it 'shows files from a repository' do + expect(page).to have_content('VERSION') + expect(page).to have_content('.gitignore') + expect(page).to have_content('LICENSE') + end + + it 'shows the "Browse Directory" link' do + click_link('files') + click_link('History') + + expect(page).to have_link('Browse Directory') + expect(page).not_to have_link('Browse Code') + end + + it 'shows the "Browse File" link' do + page.within('.tree-table') do + click_link('README.md') + end + click_link('History') + + expect(page).to have_link('Browse File') + expect(page).not_to have_link('Browse Files') + end + + it 'shows the "Browse Code" link' do + click_link('History') + + expect(page).to have_link('Browse Files') + expect(page).not_to have_link('Browse Directory') + end + + it 'redirects to the permalink URL' do + click_link('.gitignore') + click_link('Permalink') + + permalink_path = project_blob_path(project, "#{project.repository.commit.sha}/.gitignore") + + expect(current_path).to eq(permalink_path) + end + end + + context 'when browsing a specific ref' do + before do + visit(tree_path_ref_6d39438) + end + + it 'shows files from a repository for "6d39438"' do + expect(current_path).to eq(tree_path_ref_6d39438) + expect(page).to have_content('.gitignore') + expect(page).to have_content('LICENSE') + end + + it 'shows files from a repository with apostroph in its name', js: true do + first('.js-project-refs-dropdown').click + + page.within('.project-refs-form') do + click_link("'test'") + end + + expect(page).to have_selector('.dropdown-toggle-text', text: "'test'") + + visit(project_tree_path(project, "'test'")) + + expect(page).to have_css('.tree-commit-link', visible: true) + expect(page).not_to have_content('Loading commit data...') + end + + it 'shows the code with a leading dot in the directory', js: true do + first('.js-project-refs-dropdown').click + + page.within('.project-refs-form') do + click_link('fix') + end + + visit(project_tree_path(project, 'fix/.testdir')) + + expect(page).to have_css('.tree-commit-link', visible: true) + expect(page).not_to have_content('Loading commit data...') + end + + it 'does not show the permalink link' do + click_link('.gitignore') + + expect(page).not_to have_link('permalink') + end + end + + context 'when browsing a file content' do + before do + visit(tree_path_root_ref) + click_link('.gitignore') + end + + it 'shows a file content', js: true do + wait_for_requests + expect(page).to have_content('*.rbc') + end + end + + context 'when browsing a raw file' do + before do + visit(project_blob_path(project, File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path))) + end + + it 'shows a raw file content' do + click_link('Open raw') + expect(source).to eq('') # Body is filled in by gitlab-workhorse + end + end + + context 'when browsing an LFS object' do + before do + allow_any_instance_of(Project).to receive(:lfs_enabled?).and_return(true) + visit(project_tree_path(project, 'lfs')) + end + + it 'shows an LFS object' do + click_link('files') + click_link('lfs') + click_link('lfs_object.iso') + + expect(page).to have_content('Download (1.5 MB)') + expect(page).not_to have_content('version https://git-lfs.github.com/spec/v1') + expect(page).not_to have_content('oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897') + expect(page).not_to have_content('size 1575078') + + page.within('.content') do + expect(page).to have_content('Delete') + expect(page).to have_content('History') + expect(page).to have_content('Permalink') + expect(page).to have_content('Replace') + expect(page).not_to have_content('Annotate') + expect(page).not_to have_content('Blame') + expect(page).not_to have_content('Edit') + expect(page).to have_link('Download') + end + end + end + + context 'when previewing a file content' do + before do + visit(tree_path_root_ref) + end + + it 'shows a preview of a file content', js: true do + find('.add-to-tree').click + click_link('Upload file') + drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'logo_sample.svg')) + + page.within('#modal-upload-blob') do + fill_in(:commit_message, with: 'New commit message') + end + + fill_in(:branch_name, with: 'new_branch_name', visible: true) + click_button('Upload file') + + visit(project_blob_path(project, 'new_branch_name/logo_sample.svg')) + + expect(page).to have_css('.file-content img') + end + end +end diff --git a/spec/features/projects/user_create_dir_spec.rb b/spec/features/projects/user_create_dir_spec.rb deleted file mode 100644 index 5e302da8a63..00000000000 --- a/spec/features/projects/user_create_dir_spec.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'spec_helper' - -feature 'New directory creation', feature: true, js: true do - given(:user) { create(:user) } - given(:role) { :developer } - given(:project) { create(:project) } - - background do - sign_in(user) - project.team << [user, role] - visit project_tree_path(project, 'master') - open_new_directory_modal - fill_in 'dir_name', with: 'new_directory' - end - - def open_new_directory_modal - first('.add-to-tree').click - click_link 'New directory' - end - - def create_directory - click_button 'Create directory' - end - - context 'with default target branch' do - background do - create_directory - end - - scenario 'creates the directory in the default branch' do - expect(page).to have_content 'master' - expect(page).to have_content 'The directory has been successfully created' - expect(page).to have_content 'new_directory' - end - end - - context 'with a new target branch' do - given(:new_branch_name) { 'new-feature' } - - background do - fill_in :branch_name, with: new_branch_name - create_directory - end - - scenario 'creates the directory in the new branch' do - expect(page).to have_content new_branch_name - expect(page).to have_content 'The directory has been successfully created' - end - - scenario 'redirects to the merge request' do - expect(page).to have_content 'New Merge Request' - expect(page).to have_content "From #{new_branch_name} into master" - expect(page).to have_content 'Add new directory' - expect(current_path).to eq(project_new_merge_request_path(project)) - end - end -end diff --git a/spec/features/projects/user_creates_directory_spec.rb b/spec/features/projects/user_creates_directory_spec.rb new file mode 100644 index 00000000000..635bd4493dd --- /dev/null +++ b/spec/features/projects/user_creates_directory_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +feature 'User creates a directory', js: true do + let(:fork_message) do + "You're not allowed to make changes to this project directly. "\ + "A fork of this project has been created that you can make changes in, so you can submit a merge request." + end + let(:project) { create(:project) } + let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } + let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } + let(:user) { create(:user) } + + before do + project.team << [user, :developer] + sign_in(user) + visit project_tree_path(project, 'master') + end + + context 'with default target branch' do + before do + first('.add-to-tree').click + click_link('New directory') + end + + it 'creates the directory in the default branch' do + fill_in(:dir_name, with: 'new_directory') + click_button('Create directory') + + expect(page).to have_content('master') + expect(page).to have_content('The directory has been successfully created') + expect(page).to have_content('new_directory') + end + + it 'does not create a directory with a name of already existed directory' do + fill_in(:dir_name, with: 'files') + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Create directory') + + expect(page).to have_content('A directory with this name already exists') + expect(current_path).to eq(project_tree_path(project, 'master')) + end + end + + context 'with a new target branch' do + before do + first('.add-to-tree').click + click_link('New directory') + fill_in(:dir_name, with: 'new_directory') + fill_in(:branch_name, with: 'new-feature') + click_button('Create directory') + end + + it 'creates the directory in the new branch and redirect to the merge request' do + expect(page).to have_content('new-feature') + expect(page).to have_content('The directory has been successfully created') + expect(page).to have_content('New Merge Request') + expect(page).to have_content('From new-feature into master') + expect(page).to have_content('Add new directory') + + expect(current_path).to eq(project_new_merge_request_path(project)) + end + end + + context 'when an user does not have write access' do + before do + project2.team << [user, :reporter] + visit(project2_tree_path_root_ref) + end + + it 'creates a directory in a forked project' do + find('.add-to-tree').click + click_link('New directory') + + expect(page).to have_content(fork_message) + + find('.add-to-tree').click + click_link('New directory') + fill_in(:dir_name, with: 'new_directory') + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Create directory') + + fork = user.fork_of(project2) + + expect(current_path).to eq(project_new_merge_request_path(fork)) + end + end +end diff --git a/spec/features/projects/user_creates_files_spec.rb b/spec/features/projects/user_creates_files_spec.rb new file mode 100644 index 00000000000..0c7f1a775c1 --- /dev/null +++ b/spec/features/projects/user_creates_files_spec.rb @@ -0,0 +1,153 @@ +require 'spec_helper' + +describe 'User creates files' do + let(:fork_message) do + "You're not allowed to make changes to this project directly. "\ + "A fork of this project has been created that you can make changes in, so you can submit a merge request." + end + let(:project) { create(:project, name: 'Shop') } + let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } + let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } + let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + sign_in(user) + end + + context 'without commiting a new file' do + context 'when an user has write access' do + before do + visit(project_tree_path_root_ref) + end + + it 'opens new file page' do + find('.add-to-tree').click + click_link('New file') + + expect(page).to have_content('New file') + expect(page).to have_content('Commit message') + end + end + + context 'when an user does not have write access' do + before do + project2.team << [user, :reporter] + visit(project2_tree_path_root_ref) + end + + it 'opens new file page on a forked project' do + find('.add-to-tree').click + click_link('New file') + + expect(page).to have_selector('.file-editor') + expect(page).to have_content(fork_message) + expect(page).to have_content('New file') + expect(page).to have_content('Commit message') + end + end + end + + context 'with commiting a new file' do + context 'when an user has write access' do + before do + visit(project_tree_path_root_ref) + + find('.add-to-tree').click + click_link('New file') + end + + it 'creates and commit a new file', js: true do + expect(page).to have_selector('.file-editor') + + execute_script("ace.edit('editor').setValue('*.rbca')") + fill_in(:file_name, with: 'not_a_file.md') + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Commit changes') + + new_file_path = project_blob_path(project, 'master/not_a_file.md') + + expect(current_path).to eq(new_file_path) + + wait_for_requests + + expect(page).to have_content('*.rbca') + end + + it 'creates and commit a new file with new lines at the end of file', js: true do + execute_script('ace.edit("editor").setValue("Sample\n\n\n")') + fill_in(:file_name, with: 'not_a_file.md') + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Commit changes') + + new_file_path = project_blob_path(project, 'master/not_a_file.md') + + expect(current_path).to eq(new_file_path) + + find('.js-edit-blob').click + + expect(evaluate_script('ace.edit("editor").getValue()')).to eq("Sample\n\n\n") + end + + it 'creates and commit a new file with a directory name', js: true do + fill_in(:file_name, with: 'foo/bar/baz.txt') + + expect(page).to have_selector('.file-editor') + + execute_script("ace.edit('editor').setValue('*.rbca')") + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Commit changes') + + expect(current_path).to eq(project_blob_path(project, 'master/foo/bar/baz.txt')) + + wait_for_requests + + expect(page).to have_content('*.rbca') + end + + it 'creates and commit a new file specifying a new branch', js: true do + expect(page).to have_selector('.file-editor') + + execute_script("ace.edit('editor').setValue('*.rbca')") + fill_in(:file_name, with: 'not_a_file.md') + fill_in(:commit_message, with: 'New commit message', visible: true) + fill_in(:branch_name, with: 'new_branch_name', visible: true) + click_button('Commit changes') + + expect(current_path).to eq(project_new_merge_request_path(project)) + + click_link('Changes') + + wait_for_requests + + expect(page).to have_content('*.rbca') + end + end + + context 'when an user does not have write access' do + before do + project2.team << [user, :reporter] + visit(project2_tree_path_root_ref) + end + + it 'creates and commit new file in forked project', js: true do + find('.add-to-tree').click + click_link('New file') + + expect(page).to have_selector('.file-editor') + + execute_script("ace.edit('editor').setValue('*.rbca')") + + fill_in(:file_name, with: 'not_a_file.md') + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Commit changes') + + fork = user.fork_of(project2) + + expect(current_path).to eq(project_new_merge_request_path(fork)) + expect(page).to have_content('New commit message') + end + end + end +end diff --git a/spec/features/projects/user_deletes_files_spec.rb b/spec/features/projects/user_deletes_files_spec.rb new file mode 100644 index 00000000000..97e60862b4f --- /dev/null +++ b/spec/features/projects/user_deletes_files_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe 'User deletes files' do + let(:fork_message) do + "You're not allowed to make changes to this project directly. "\ + "A fork of this project has been created that you can make changes in, so you can submit a merge request." + end + let(:project) { create(:project, name: 'Shop') } + let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } + let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } + let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } + let(:user) { create(:user) } + + before do + sign_in(user) + end + + context 'when an user has write access' do + before do + project.team << [user, :master] + visit(project_tree_path_root_ref) + end + + it 'deletes the file', js: true do + click_link('.gitignore') + + expect(page).to have_content('.gitignore') + + click_on('Delete') + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Delete file') + + expect(current_path).to eq(project_tree_path(project, 'master')) + expect(page).not_to have_content('.gitignore') + end + end + + context 'when an user does not have write access' do + before do + project2.team << [user, :reporter] + visit(project2_tree_path_root_ref) + end + + it 'deletes the file in a forked project', js: true do + click_link('.gitignore') + + expect(page).to have_content('.gitignore') + + click_on('Delete') + + expect(page).to have_link('Fork') + expect(page).to have_button('Cancel') + + click_link('Fork') + + expect(page).to have_content(fork_message) + + click_on('Delete') + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Delete file') + + fork = user.fork_of(project2) + + expect(current_path).to eq(project_new_merge_request_path(fork)) + expect(page).to have_content('New commit message') + end + end +end diff --git a/spec/features/projects/user_edits_files_spec.rb b/spec/features/projects/user_edits_files_spec.rb new file mode 100644 index 00000000000..eb26f1bc123 --- /dev/null +++ b/spec/features/projects/user_edits_files_spec.rb @@ -0,0 +1,122 @@ +require 'spec_helper' + +describe 'User edits files' do + let(:fork_message) do + "You're not allowed to make changes to this project directly. "\ + "A fork of this project has been created that you can make changes in, so you can submit a merge request." + end + let(:project) { create(:project, name: 'Shop') } + let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } + let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } + let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } + let(:user) { create(:user) } + + before do + sign_in(user) + end + + context 'when an user has write access' do + before do + project.team << [user, :master] + visit(project_tree_path_root_ref) + end + + it 'inserts a content of a file', js: true do + click_link('.gitignore') + find('.js-edit-blob').click + execute_script("ace.edit('editor').setValue('*.rbca')") + + expect(evaluate_script('ace.edit("editor").getValue()')).to eq('*.rbca') + end + + it 'does not show the edit link if a file is binary' do + binary_file = File.join(project.repository.root_ref, 'files/images/logo-black.png') + visit(project_blob_path(project, binary_file)) + + expect(page).not_to have_link('edit') + end + + it 'commits an edited file', js: true do + click_link('.gitignore') + find('.js-edit-blob').click + execute_script("ace.edit('editor').setValue('*.rbca')") + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Commit changes') + + expect(current_path).to eq(project_blob_path(project, 'master/.gitignore')) + + wait_for_requests + + expect(page).to have_content('*.rbca') + end + + it 'commits an edited file to a new branch', js: true do + click_link('.gitignore') + find('.js-edit-blob').click + execute_script("ace.edit('editor').setValue('*.rbca')") + fill_in(:commit_message, with: 'New commit message', visible: true) + fill_in(:branch_name, with: 'new_branch_name', visible: true) + click_button('Commit changes') + + expect(current_path).to eq(project_new_merge_request_path(project)) + + click_link('Changes') + + wait_for_requests + expect(page).to have_content('*.rbca') + end + + it 'shows the diff of an edited file', js: true do + click_link('.gitignore') + find('.js-edit-blob').click + execute_script("ace.edit('editor').setValue('*.rbca')") + click_link('Preview changes') + + expect(page).to have_css('.line_holder.new') + end + end + + context 'when an user does not have write access' do + before do + project2.team << [user, :reporter] + visit(project2_tree_path_root_ref) + end + + it 'inserts a content of a file in a forked project', js: true do + click_link('.gitignore') + find('.js-edit-blob').click + + expect(page).to have_link('Fork') + expect(page).to have_button('Cancel') + + click_link('Fork') + + expect(page).to have_content(fork_message) + + execute_script("ace.edit('editor').setValue('*.rbca')") + + expect(evaluate_script('ace.edit("editor").getValue()')).to eq('*.rbca') + end + + it 'commits an edited file in a forked project', js: true do + click_link('.gitignore') + find('.js-edit-blob').click + + expect(page).to have_link('Fork') + expect(page).to have_button('Cancel') + + click_link('Fork') + execute_script("ace.edit('editor').setValue('*.rbca')") + fill_in(:commit_message, with: 'New commit message', visible: true) + click_button('Commit changes') + + fork = user.fork_of(project2) + + expect(current_path).to eq(project_new_merge_request_path(fork)) + + wait_for_requests + + expect(page).to have_content('New commit message') + end + end +end diff --git a/spec/features/projects/user_replaces_files_spec.rb b/spec/features/projects/user_replaces_files_spec.rb new file mode 100644 index 00000000000..50f2ffc4bbf --- /dev/null +++ b/spec/features/projects/user_replaces_files_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe 'User replaces files' do + include DropzoneHelper + + let(:fork_message) do + "You're not allowed to make changes to this project directly. "\ + "A fork of this project has been created that you can make changes in, so you can submit a merge request." + end + let(:project) { create(:project, name: 'Shop') } + let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } + let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } + let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } + let(:user) { create(:user) } + + before do + sign_in(user) + end + + context 'when an user has write access' do + before do + project.team << [user, :master] + visit(project_tree_path_root_ref) + end + + it 'replaces an existed file with a new one', js: true do + click_link('.gitignore') + + expect(page).to have_content('.gitignore') + + click_on('Replace') + drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) + + page.within('#modal-upload-blob') do + fill_in(:commit_message, with: 'Replacement file commit message') + end + + click_button('Replace file') + + expect(page).to have_content('Lorem ipsum dolor sit amet') + expect(page).to have_content('Sed ut perspiciatis unde omnis') + expect(page).to have_content('Replacement file commit message') + end + end + + context 'when an user does not have write access' do + before do + project2.team << [user, :reporter] + visit(project2_tree_path_root_ref) + end + + it 'replaces an existed file with a new one in a forked project', js: true do + click_link('.gitignore') + + expect(page).to have_content('.gitignore') + + click_on('Replace') + + expect(page).to have_link('Fork') + expect(page).to have_button('Cancel') + + click_link('Fork') + + expect(page).to have_content(fork_message) + + click_on('Replace') + drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) + + page.within('#modal-upload-blob') do + fill_in(:commit_message, with: 'Replacement file commit message') + end + + click_button('Replace file') + + expect(page).to have_content('Replacement file commit message') + + fork = user.fork_of(project2) + + expect(current_path).to eq(project_new_merge_request_path(fork)) + + click_link('Changes') + + expect(page).to have_content('Lorem ipsum dolor sit amet') + expect(page).to have_content('Sed ut perspiciatis unde omnis') + end + end +end diff --git a/spec/features/projects/user_uploads_files_spec.rb b/spec/features/projects/user_uploads_files_spec.rb new file mode 100644 index 00000000000..64a1439badd --- /dev/null +++ b/spec/features/projects/user_uploads_files_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe 'User uploads files' do + include DropzoneHelper + + let(:fork_message) do + "You're not allowed to make changes to this project directly. "\ + "A fork of this project has been created that you can make changes in, so you can submit a merge request." + end + let(:project) { create(:project, name: 'Shop') } + let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } + let(:project_tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) } + let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + sign_in(user) + end + + context 'when an user has write access' do + before do + visit(project_tree_path_root_ref) + end + + it 'uploads and commit a new file', js: true do + find('.add-to-tree').click + click_link('Upload file') + drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) + + page.within('#modal-upload-blob') do + fill_in(:commit_message, with: 'New commit message') + end + + fill_in(:branch_name, with: 'new_branch_name', visible: true) + click_button('Upload file') + + expect(page).to have_content('New commit message') + expect(current_path).to eq(project_new_merge_request_path(project)) + + click_link('Changes') + + expect(page).to have_content('Lorem ipsum dolor sit amet') + expect(page).to have_content('Sed ut perspiciatis unde omnis') + end + end + + context 'when an user does not have write access' do + before do + project2.team << [user, :reporter] + visit(project2_tree_path_root_ref) + end + + it 'uploads and commit a new fileto a forked project', js: true do + find('.add-to-tree').click + click_link('Upload file') + + expect(page).to have_content(fork_message) + + find('.add-to-tree').click + click_link('Upload file') + drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) + + page.within('#modal-upload-blob') do + fill_in(:commit_message, with: 'New commit message') + end + + click_button('Upload file') + + expect(page).to have_content('New commit message') + + fork = user.fork_of(project2) + + expect(current_path).to eq(project_new_merge_request_path(fork)) + + click_link('Changes') + + expect(page).to have_content('Lorem ipsum dolor sit amet') + expect(page).to have_content('Sed ut perspiciatis unde omnis') + end + end +end diff --git a/spec/fixtures/api/schemas/entities/merge_request.json b/spec/fixtures/api/schemas/entities/merge_request.json index b6a59a6cc47..7ffa82fc4bd 100644 --- a/spec/fixtures/api/schemas/entities/merge_request.json +++ b/spec/fixtures/api/schemas/entities/merge_request.json @@ -75,6 +75,7 @@ "additionalProperties": false }, "target_branch_commits_path": { "type": "string" }, + "target_branch_tree_path": { "type": "string" }, "source_branch_path": { "type": "string" }, "conflict_resolution_path": { "type": ["string", "null"] }, "cancel_merge_when_pipeline_succeeds_path": { "type": "string" }, diff --git a/spec/fixtures/api/schemas/public_api/v4/user/admin.json b/spec/fixtures/api/schemas/public_api/v4/user/admin.json new file mode 100644 index 00000000000..f733914fbf8 --- /dev/null +++ b/spec/fixtures/api/schemas/public_api/v4/user/admin.json @@ -0,0 +1,34 @@ +{ + "type": "object", + "required": [ + "id", + "username", + "email", + "name", + "state", + "avatar_url", + "web_url", + "created_at", + "is_admin", + "bio", + "location", + "skype", + "linkedin", + "twitter", + "website_url", + "organization", + "last_sign_in_at", + "confirmed_at", + "color_scheme_id", + "projects_limit", + "current_sign_in_at", + "identities", + "can_create_group", + "can_create_project", + "two_factor_enabled", + "external" + ], + "properties": { + "$ref": "full.json" + } +} diff --git a/spec/fixtures/config/redis_cache_config_with_env.yml b/spec/fixtures/config/redis_cache_config_with_env.yml new file mode 100644 index 00000000000..52fd5a06460 --- /dev/null +++ b/spec/fixtures/config/redis_cache_config_with_env.yml @@ -0,0 +1,2 @@ +test: + url: <%= ENV['TEST_GITLAB_REDIS_CACHE_URL'] %> diff --git a/spec/fixtures/config/redis_cache_new_format_host.yml b/spec/fixtures/config/redis_cache_new_format_host.yml new file mode 100644 index 00000000000..a24f3716391 --- /dev/null +++ b/spec/fixtures/config/redis_cache_new_format_host.yml @@ -0,0 +1,29 @@ +# redis://[:password@]host[:port][/db-number][?option=value] +# more details: http://www.iana.org/assignments/uri-schemes/prov/redis +development: + url: redis://:mynewpassword@localhost:6380/10 + sentinels: + - + host: localhost + port: 26380 # point to sentinel, not to redis port + - + host: slave2 + port: 26380 # point to sentinel, not to redis port +test: + url: redis://:mynewpassword@localhost:6380/10 + sentinels: + - + host: localhost + port: 26380 # point to sentinel, not to redis port + - + host: slave2 + port: 26380 # point to sentinel, not to redis port +production: + url: redis://:mynewpassword@localhost:6380/10 + sentinels: + - + host: slave1 + port: 26380 # point to sentinel, not to redis port + - + host: slave2 + port: 26380 # point to sentinel, not to redis port diff --git a/spec/fixtures/config/redis_cache_new_format_socket.yml b/spec/fixtures/config/redis_cache_new_format_socket.yml new file mode 100644 index 00000000000..3634c550163 --- /dev/null +++ b/spec/fixtures/config/redis_cache_new_format_socket.yml @@ -0,0 +1,6 @@ +development: + url: unix:/path/to/redis.cache.sock +test: + url: unix:/path/to/redis.cache.sock +production: + url: unix:/path/to/redis.cache.sock diff --git a/spec/fixtures/config/redis_cache_old_format_host.yml b/spec/fixtures/config/redis_cache_old_format_host.yml new file mode 100644 index 00000000000..3609dcd022e --- /dev/null +++ b/spec/fixtures/config/redis_cache_old_format_host.yml @@ -0,0 +1,5 @@ +# redis://[:password@]host[:port][/db-number][?option=value] +# more details: http://www.iana.org/assignments/uri-schemes/prov/redis +development: redis://:mypassword@localhost:6380/10 +test: redis://:mypassword@localhost:6380/10 +production: redis://:mypassword@localhost:6380/10 diff --git a/spec/fixtures/config/redis_cache_old_format_socket.yml b/spec/fixtures/config/redis_cache_old_format_socket.yml new file mode 100644 index 00000000000..26fa0eda245 --- /dev/null +++ b/spec/fixtures/config/redis_cache_old_format_socket.yml @@ -0,0 +1,3 @@ +development: unix:/path/to/old/redis.cache.sock +test: unix:/path/to/old/redis.cache.sock +production: unix:/path/to/old/redis.cache.sock diff --git a/spec/fixtures/config/redis_new_format_host.yml b/spec/fixtures/config/redis_new_format_host.yml index 13772677a45..8d134d467e9 100644 --- a/spec/fixtures/config/redis_new_format_host.yml +++ b/spec/fixtures/config/redis_new_format_host.yml @@ -5,25 +5,25 @@ development: sentinels: - host: localhost - port: 26380 # point to sentinel, not to redis port + port: 26379 # point to sentinel, not to redis port - host: slave2 - port: 26381 # point to sentinel, not to redis port + port: 26379 # point to sentinel, not to redis port test: url: redis://:mynewpassword@localhost:6379/99 sentinels: - host: localhost - port: 26380 # point to sentinel, not to redis port + port: 26379 # point to sentinel, not to redis port - host: slave2 - port: 26381 # point to sentinel, not to redis port + port: 26379 # point to sentinel, not to redis port production: url: redis://:mynewpassword@localhost:6379/99 sentinels: - host: slave1 - port: 26380 # point to sentinel, not to redis port + port: 26379 # point to sentinel, not to redis port - host: slave2 - port: 26381 # point to sentinel, not to redis port + port: 26379 # point to sentinel, not to redis port diff --git a/spec/fixtures/config/redis_queues_config_with_env.yml b/spec/fixtures/config/redis_queues_config_with_env.yml new file mode 100644 index 00000000000..d16a9d8a7f8 --- /dev/null +++ b/spec/fixtures/config/redis_queues_config_with_env.yml @@ -0,0 +1,2 @@ +test: + url: <%= ENV['TEST_GITLAB_REDIS_QUEUES_URL'] %> diff --git a/spec/fixtures/config/redis_queues_new_format_host.yml b/spec/fixtures/config/redis_queues_new_format_host.yml new file mode 100644 index 00000000000..1535584d779 --- /dev/null +++ b/spec/fixtures/config/redis_queues_new_format_host.yml @@ -0,0 +1,29 @@ +# redis://[:password@]host[:port][/db-number][?option=value] +# more details: http://www.iana.org/assignments/uri-schemes/prov/redis +development: + url: redis://:mynewpassword@localhost:6381/11 + sentinels: + - + host: localhost + port: 26381 # point to sentinel, not to redis port + - + host: slave2 + port: 26381 # point to sentinel, not to redis port +test: + url: redis://:mynewpassword@localhost:6381/11 + sentinels: + - + host: localhost + port: 26381 # point to sentinel, not to redis port + - + host: slave2 + port: 26381 # point to sentinel, not to redis port +production: + url: redis://:mynewpassword@localhost:6381/11 + sentinels: + - + host: slave1 + port: 26381 # point to sentinel, not to redis port + - + host: slave2 + port: 26381 # point to sentinel, not to redis port diff --git a/spec/fixtures/config/redis_queues_new_format_socket.yml b/spec/fixtures/config/redis_queues_new_format_socket.yml new file mode 100644 index 00000000000..b488d84d022 --- /dev/null +++ b/spec/fixtures/config/redis_queues_new_format_socket.yml @@ -0,0 +1,6 @@ +development: + url: unix:/path/to/redis.queues.sock +test: + url: unix:/path/to/redis.queues.sock +production: + url: unix:/path/to/redis.queues.sock diff --git a/spec/fixtures/config/redis_queues_old_format_host.yml b/spec/fixtures/config/redis_queues_old_format_host.yml new file mode 100644 index 00000000000..6531748a8d7 --- /dev/null +++ b/spec/fixtures/config/redis_queues_old_format_host.yml @@ -0,0 +1,5 @@ +# redis://[:password@]host[:port][/db-number][?option=value] +# more details: http://www.iana.org/assignments/uri-schemes/prov/redis +development: redis://:mypassword@localhost:6381/11 +test: redis://:mypassword@localhost:6381/11 +production: redis://:mypassword@localhost:6381/11 diff --git a/spec/fixtures/config/redis_queues_old_format_socket.yml b/spec/fixtures/config/redis_queues_old_format_socket.yml new file mode 100644 index 00000000000..53f5db72758 --- /dev/null +++ b/spec/fixtures/config/redis_queues_old_format_socket.yml @@ -0,0 +1,3 @@ +development: unix:/path/to/old/redis.queues.sock +test: unix:/path/to/old/redis.queues.sock +production: unix:/path/to/old/redis.queues.sock diff --git a/spec/fixtures/config/redis_shared_state_config_with_env.yml b/spec/fixtures/config/redis_shared_state_config_with_env.yml new file mode 100644 index 00000000000..eab7203d0de --- /dev/null +++ b/spec/fixtures/config/redis_shared_state_config_with_env.yml @@ -0,0 +1,2 @@ +test: + url: <%= ENV['TEST_GITLAB_REDIS_SHARED_STATE_URL'] %> diff --git a/spec/fixtures/config/redis_shared_state_new_format_host.yml b/spec/fixtures/config/redis_shared_state_new_format_host.yml new file mode 100644 index 00000000000..1180b2b4a82 --- /dev/null +++ b/spec/fixtures/config/redis_shared_state_new_format_host.yml @@ -0,0 +1,29 @@ +# redis://[:password@]host[:port][/db-number][?option=value] +# more details: http://www.iana.org/assignments/uri-schemes/prov/redis +development: + url: redis://:mynewpassword@localhost:6382/12 + sentinels: + - + host: localhost + port: 26382 # point to sentinel, not to redis port + - + host: slave2 + port: 26382 # point to sentinel, not to redis port +test: + url: redis://:mynewpassword@localhost:6382/12 + sentinels: + - + host: localhost + port: 26382 # point to sentinel, not to redis port + - + host: slave2 + port: 26382 # point to sentinel, not to redis port +production: + url: redis://:mynewpassword@localhost:6382/12 + sentinels: + - + host: slave1 + port: 26382 # point to sentinel, not to redis port + - + host: slave2 + port: 26382 # point to sentinel, not to redis port diff --git a/spec/fixtures/config/redis_shared_state_new_format_socket.yml b/spec/fixtures/config/redis_shared_state_new_format_socket.yml new file mode 100644 index 00000000000..1b0e699729e --- /dev/null +++ b/spec/fixtures/config/redis_shared_state_new_format_socket.yml @@ -0,0 +1,6 @@ +development: + url: unix:/path/to/redis.shared_state.sock +test: + url: unix:/path/to/redis.shared_state.sock +production: + url: unix:/path/to/redis.shared_state.sock diff --git a/spec/fixtures/config/redis_shared_state_old_format_host.yml b/spec/fixtures/config/redis_shared_state_old_format_host.yml new file mode 100644 index 00000000000..fef5e768c5d --- /dev/null +++ b/spec/fixtures/config/redis_shared_state_old_format_host.yml @@ -0,0 +1,5 @@ +# redis://[:password@]host[:port][/db-number][?option=value] +# more details: http://www.iana.org/assignments/uri-schemes/prov/redis +development: redis://:mypassword@localhost:6382/12 +test: redis://:mypassword@localhost:6382/12 +production: redis://:mypassword@localhost:6382/12 diff --git a/spec/fixtures/config/redis_shared_state_old_format_socket.yml b/spec/fixtures/config/redis_shared_state_old_format_socket.yml new file mode 100644 index 00000000000..4746afbb5ef --- /dev/null +++ b/spec/fixtures/config/redis_shared_state_old_format_socket.yml @@ -0,0 +1,3 @@ +development: unix:/path/to/old/redis.shared_state.sock +test: unix:/path/to/old/redis.shared_state.sock +production: unix:/path/to/old/redis.shared_state.sock diff --git a/spec/helpers/award_emoji_helper_spec.rb b/spec/helpers/award_emoji_helper_spec.rb index 7dfd6a3f6b4..035960ed96e 100644 --- a/spec/helpers/award_emoji_helper_spec.rb +++ b/spec/helpers/award_emoji_helper_spec.rb @@ -40,7 +40,7 @@ describe AwardEmojiHelper do it 'returns correct url' do @project = merge_request.project - expected_url = "/#{@project.namespace.path}/#{@project.path}/merge_requests/#{merge_request.id}/toggle_award_emoji" + expected_url = "/#{@project.namespace.path}/#{@project.path}/merge_requests/#{merge_request.iid}/toggle_award_emoji" expect(helper.toggle_award_url(merge_request)).to eq(expected_url) end @@ -52,7 +52,7 @@ describe AwardEmojiHelper do it 'returns correct url' do @project = issue.project - expected_url = "/#{@project.namespace.path}/#{@project.path}/issues/#{issue.id}/toggle_award_emoji" + expected_url = "/#{@project.namespace.path}/#{@project.path}/issues/#{issue.iid}/toggle_award_emoji" expect(helper.toggle_award_url(issue)).to eq(expected_url) end diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb index d2e918ef014..b423a09873b 100644 --- a/spec/helpers/issuables_helper_spec.rb +++ b/spec/helpers/issuables_helper_spec.rb @@ -60,7 +60,7 @@ describe IssuablesHelper do end end - describe 'counter caching based on issuable type and params', :caching do + describe 'counter caching based on issuable type and params', :use_clean_rails_memory_store_caching do let(:params) do { scope: 'created-by-me', diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 487d9800707..c462d9006ea 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -63,7 +63,7 @@ describe ProjectsHelper do end end - describe "#project_list_cache_key", redis: true do + describe "#project_list_cache_key", clean_gitlab_redis_shared_state: true do let(:project) { create(:project) } it "includes the route" do diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environment_spec.js index 6639a6b5e7b..0c8817a8148 100644 --- a/spec/javascripts/environments/environment_spec.js +++ b/spec/javascripts/environments/environment_spec.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import '~/flash'; import environmentsComponent from '~/environments/components/environment.vue'; import { environment, folder } from './mock_data'; +import { headersInterceptor } from '../helpers/vue_resource_helper'; describe('Environment', () => { preloadFixtures('static/environments/environments.html.raw'); @@ -25,12 +26,14 @@ describe('Environment', () => { beforeEach(() => { Vue.http.interceptors.push(environmentsEmptyResponseInterceptor); + Vue.http.interceptors.push(headersInterceptor); }); afterEach(() => { Vue.http.interceptors = _.without( Vue.http.interceptors, environmentsEmptyResponseInterceptor, ); + Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor); }); it('should render the empty state', (done) => { @@ -54,6 +57,10 @@ describe('Environment', () => { describe('with paginated environments', () => { const environmentsResponseInterceptor = (request, next) => { + next((response) => { + response.headers.set('X-nExt-pAge', '2'); + }); + next(request.respondWith(JSON.stringify({ environments: [environment], stopped_count: 1, @@ -73,6 +80,7 @@ describe('Environment', () => { beforeEach(() => { Vue.http.interceptors.push(environmentsResponseInterceptor); + Vue.http.interceptors.push(headersInterceptor); component = new EnvironmentsComponent({ el: document.querySelector('#environments-list-view'), }); @@ -82,6 +90,7 @@ describe('Environment', () => { Vue.http.interceptors = _.without( Vue.http.interceptors, environmentsResponseInterceptor, ); + Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor); }); it('should render a table with environments', (done) => { diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js index 350078ad5f5..fdaea5c0b0c 100644 --- a/spec/javascripts/environments/folder/environments_folder_view_spec.js +++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import '~/flash'; import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue'; import { environmentsList } from '../mock_data'; +import { headersInterceptor } from '../../helpers/vue_resource_helper'; describe('Environments Folder View', () => { preloadFixtures('static/environments/environments_folder_view.html.raw'); @@ -36,6 +37,8 @@ describe('Environments Folder View', () => { beforeEach(() => { Vue.http.interceptors.push(environmentsResponseInterceptor); + Vue.http.interceptors.push(headersInterceptor); + component = new EnvironmentsFolderViewComponent({ el: document.querySelector('#environments-folder-list-view'), }); @@ -45,6 +48,7 @@ describe('Environments Folder View', () => { Vue.http.interceptors = _.without( Vue.http.interceptors, environmentsResponseInterceptor, ); + Vue.http.interceptors = _.without(Vue.http.interceptors, headersInterceptor); }); it('should render a table with environments', (done) => { diff --git a/spec/javascripts/helpers/vue_resource_helper.js b/spec/javascripts/helpers/vue_resource_helper.js new file mode 100644 index 00000000000..0d1bf5e2e80 --- /dev/null +++ b/spec/javascripts/helpers/vue_resource_helper.js @@ -0,0 +1,11 @@ +// eslint-disable-next-line import/prefer-default-export +export const headersInterceptor = (request, next) => { + next((response) => { + const headers = {}; + response.headers.forEach((value, key) => { + headers[key] = value; + }); + // eslint-disable-next-line no-param-reassign + response.headers = headers; + }); +}; diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index bc13373a27e..81ce18bf2fb 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -3,7 +3,6 @@ import '~/render_math'; import '~/render_gfm'; import issuableApp from '~/issue_show/components/app.vue'; import eventHub from '~/issue_show/event_hub'; -import Poll from '~/lib/utils/poll'; import issueShowData from '../mock_data'; function formatText(text) { @@ -11,16 +10,26 @@ function formatText(text) { } describe('Issuable output', () => { + let requestData = issueShowData.initialRequest; + document.body.innerHTML = '<span id="task_status"></span>'; + const interceptor = (request, next) => { + next(request.respondWith(JSON.stringify(requestData), { + status: 200, + })); + }; + let vm; - beforeEach(() => { + beforeEach((done) => { spyOn(eventHub, '$emit'); - spyOn(Poll.prototype, 'makeRequest'); const IssuableDescriptionComponent = Vue.extend(issuableApp); + requestData = issueShowData.initialRequest; + Vue.http.interceptors.push(interceptor); + vm = new IssuableDescriptionComponent({ propsData: { canUpdate: true, @@ -40,15 +49,17 @@ describe('Issuable output', () => { projectPath: '/', }, }).$mount(); + + setTimeout(done); }); - it('should render a title/description/edited and update title/description/edited on update', (done) => { - vm.poll.options.successCallback({ - json() { - return issueShowData.initialRequest; - }, - }); + afterEach(() => { + Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor); + vm.poll.stop(); + }); + + it('should render a title/description/edited and update title/description/edited on update', (done) => { let editedText; Vue.nextTick() .then(() => { @@ -64,13 +75,10 @@ describe('Issuable output', () => { expect(editedText.querySelector('time')).toBeTruthy(); }) .then(() => { - vm.poll.options.successCallback({ - json() { - return issueShowData.secondRequest; - }, - }); + requestData = issueShowData.secondRequest; + vm.poll.makeRequest(); }) - .then(Vue.nextTick) + .then(() => new Promise(resolve => setTimeout(resolve))) .then(() => { expect(document.querySelector('title').innerText).toContain('2 (#1)'); expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>'); @@ -304,7 +312,7 @@ describe('Issuable output', () => { it('stops polling when deleting', (done) => { spyOn(gl.utils, 'visitUrl'); - spyOn(vm.poll, 'stop'); + spyOn(vm.poll, 'stop').and.callThrough(); spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => { resolve({ json() { @@ -347,23 +355,14 @@ describe('Issuable output', () => { describe('open form', () => { it('shows locked warning if form is open & data is different', (done) => { - vm.poll.options.successCallback({ - json() { - return issueShowData.initialRequest; - }, - }); - Vue.nextTick() .then(() => { vm.openForm(); - vm.poll.options.successCallback({ - json() { - return issueShowData.secondRequest; - }, - }); + requestData = issueShowData.secondRequest; + vm.poll.makeRequest(); }) - .then(Vue.nextTick) + .then(() => new Promise(resolve => setTimeout(resolve))) .then(() => { expect( vm.formState.lockedWarningVisible, diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js index 7f3eea7d2e5..06f89fabf42 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js @@ -54,6 +54,7 @@ describe('MRWidgetHeader', () => { sourceBranch: 'mr-widget-refactor', sourceBranchLink: `<a href="${sourceBranchPath}">mr-widget-refactor</a>`, targetBranchPath: 'foo/bar/commits-path', + targetBranchTreePath: 'foo/bar/tree/path', targetBranch: 'master', isOpen: true, emailPatchesPath: '/mr/email-patches', @@ -69,12 +70,14 @@ describe('MRWidgetHeader', () => { expect(el.classList.contains('mr-source-target')).toBeTruthy(); const sourceBranchLink = el.querySelectorAll('.label-branch')[0]; const targetBranchLink = el.querySelectorAll('.label-branch')[1]; + const commitsCount = el.querySelector('.diverged-commits-count'); expect(sourceBranchLink.textContent).toContain(mr.sourceBranch); expect(targetBranchLink.textContent).toContain(mr.targetBranch); expect(sourceBranchLink.querySelector('a').getAttribute('href')).toEqual(sourceBranchPath); - expect(targetBranchLink.querySelector('a').getAttribute('href')).toEqual(mr.targetBranchPath); - expect(el.querySelector('.diverged-commits-count').textContent).toContain('12 commits behind'); + expect(targetBranchLink.querySelector('a').getAttribute('href')).toEqual(mr.targetBranchTreePath); + expect(commitsCount.textContent).toContain('12 commits behind'); + expect(commitsCount.querySelector('a').getAttribute('href')).toEqual(mr.targetBranchPath); expect(el.textContent).toContain('Check out branch'); expect(el.querySelectorAll('.dropdown li a')[0].getAttribute('href')).toEqual(mr.emailPatchesPath); diff --git a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb index fc72df575be..15b3db0ed3d 100644 --- a/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb +++ b/spec/lib/gitlab/auth/unique_ips_limiter_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Auth::UniqueIpsLimiter, :redis, lib: true do +describe Gitlab::Auth::UniqueIpsLimiter, :clean_gitlab_redis_shared_state, lib: true do include_context 'unique ips sign in limit' let(:user) { create(:user) } diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb index 07db6c3a640..0daf41a7c86 100644 --- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb +++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do +describe Gitlab::Cache::Ci::ProjectPipelineStatus, :clean_gitlab_redis_cache do let!(:project) { create(:project) } let(:pipeline_status) { described_class.new(project) } let(:cache_key) { "projects/#{project.id}/pipeline_status" } @@ -28,8 +28,8 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do expect(project.instance_variable_get('@pipeline_status')).to be_a(described_class) end - describe 'without a status in redis' do - it 'loads the status from a commit when it was not in redis' do + describe 'without a status in redis_cache' do + it 'loads the status from a commit when it was not in redis_cache' do empty_status = { sha: nil, status: nil, ref: nil } fake_pipeline = described_class.new( project_without_status, @@ -48,9 +48,9 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do described_class.load_in_batch_for_projects([project_without_status]) end - it 'only connects to redis twice' do + it 'only connects to redis_cache twice' do # Once to load, once to store in the cache - expect(Gitlab::Redis).to receive(:with).exactly(2).and_call_original + expect(Gitlab::Redis::Cache).to receive(:with).exactly(2).and_call_original described_class.load_in_batch_for_projects([project_without_status]) @@ -58,9 +58,9 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do end end - describe 'when a status was cached in redis' do + describe 'when a status was cached in redis_cache' do before do - Gitlab::Redis.with do |redis| + Gitlab::Redis::Cache.with do |redis| redis.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref }) end @@ -76,8 +76,8 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do expect(pipeline_status.ref).to eq(ref) end - it 'only connects to redis once' do - expect(Gitlab::Redis).to receive(:with).exactly(1).and_call_original + it 'only connects to redis_cache once' do + expect(Gitlab::Redis::Cache).to receive(:with).exactly(1).and_call_original described_class.load_in_batch_for_projects([project]) @@ -94,8 +94,8 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do end describe '.cached_results_for_projects' do - it 'loads a status from redis for all projects' do - Gitlab::Redis.with do |redis| + it 'loads a status from caching for all projects' do + Gitlab::Redis::Cache.with do |redis| redis.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref }) end @@ -183,7 +183,7 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do end end - describe "#load_from_project" do + describe "#load_from_project", :clean_gitlab_redis_cache do let!(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.sha) } it 'reads the status from the pipeline for the commit' do @@ -203,40 +203,40 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do end end - describe "#store_in_cache", :redis do - it "sets the object in redis" do + describe "#store_in_cache", :clean_gitlab_redis_cache do + it "sets the object in caching" do pipeline_status.sha = '123456' pipeline_status.status = 'failed' pipeline_status.store_in_cache - read_sha, read_status = Gitlab::Redis.with { |redis| redis.hmget(cache_key, :sha, :status) } + read_sha, read_status = Gitlab::Redis::Cache.with { |redis| redis.hmget(cache_key, :sha, :status) } expect(read_sha).to eq('123456') expect(read_status).to eq('failed') end end - describe '#store_in_cache_if_needed', :redis do + describe '#store_in_cache_if_needed', :clean_gitlab_redis_cache do it 'stores the state in the cache when the sha is the HEAD of the project' do create(:ci_pipeline, :success, project: project, sha: project.commit.sha) pipeline_status = described_class.load_for_project(project) pipeline_status.store_in_cache_if_needed - sha, status, ref = Gitlab::Redis.with { |redis| redis.hmget(cache_key, :sha, :status, :ref) } + sha, status, ref = Gitlab::Redis::Cache.with { |redis| redis.hmget(cache_key, :sha, :status, :ref) } expect(sha).not_to be_nil expect(status).not_to be_nil expect(ref).not_to be_nil end - it "doesn't store the status in redis when the sha is not the head of the project" do + it "doesn't store the status in redis_cache when the sha is not the head of the project" do other_status = described_class.new( project, pipeline_info: { sha: "123456", status: "failed" } ) other_status.store_in_cache_if_needed - sha, status = Gitlab::Redis.with { |redis| redis.hmget(cache_key, :sha, :status) } + sha, status = Gitlab::Redis::Cache.with { |redis| redis.hmget(cache_key, :sha, :status) } expect(sha).to be_nil expect(status).to be_nil @@ -244,7 +244,7 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do it "deletes the cache if the repository doesn't have a head commit" do empty_project = create(:empty_project) - Gitlab::Redis.with do |redis| + Gitlab::Redis::Cache.with do |redis| redis.mapped_hmset(cache_key, { sha: 'sha', status: 'pending', ref: 'master' }) end @@ -255,7 +255,7 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do }) other_status.store_in_cache_if_needed - sha, status, ref = Gitlab::Redis.with { |redis| redis.hmget("projects/#{empty_project.id}/pipeline_status", :sha, :status, :ref) } + sha, status, ref = Gitlab::Redis::Cache.with { |redis| redis.hmget("projects/#{empty_project.id}/pipeline_status", :sha, :status, :ref) } expect(sha).to be_nil expect(status).to be_nil @@ -263,20 +263,20 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do end end - describe "with a status in redis", :redis do + describe "with a status in caching", :clean_gitlab_redis_cache do let(:status) { 'success' } let(:sha) { '424d1b73bc0d3cb726eb7dc4ce17a4d48552f8c6' } let(:ref) { 'master' } before do - Gitlab::Redis.with do |redis| + Gitlab::Redis::Cache.with do |redis| redis.mapped_hmset(cache_key, { sha: sha, status: status, ref: ref }) end end describe '#load_from_cache' do - it 'reads the status from redis' do + it 'reads the status from redis_cache' do pipeline_status.load_from_cache expect(pipeline_status.sha).to eq(sha) @@ -292,10 +292,10 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do end describe '#delete_from_cache' do - it 'deletes values from redis' do + it 'deletes values from redis_cache' do pipeline_status.delete_from_cache - key_exists = Gitlab::Redis.with { |redis| redis.exists(cache_key) } + key_exists = Gitlab::Redis::Cache.with { |redis| redis.exists(cache_key) } expect(key_exists).to be_falsy end diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb index 8813f129ef5..df7d1b5d27a 100644 --- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb +++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb @@ -236,7 +236,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :trunca subject.track_rename('namespace', 'path/to/namespace', 'path/to/renamed') old_path, new_path = [nil, nil] - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| rename_info = redis.lpop(key) old_path, new_path = JSON.parse(rename_info) end @@ -268,7 +268,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :trunca key = 'rename:FakeRenameReservedPathMigrationV1:project' stored_renames = nil rename_count = 0 - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| stored_renames = redis.lrange(key, 0, 1) rename_count = redis.llen(key) end diff --git a/spec/lib/gitlab/exclusive_lease_spec.rb b/spec/lib/gitlab/exclusive_lease_spec.rb index 81bbd70ffb8..590d6da4113 100644 --- a/spec/lib/gitlab/exclusive_lease_spec.rb +++ b/spec/lib/gitlab/exclusive_lease_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ExclusiveLease, type: :redis do +describe Gitlab::ExclusiveLease, type: :clean_gitlab_redis_shared_state do let(:unique_key) { SecureRandom.hex(10) } describe '#try_obtain' do diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb index 73518656bde..19f45ea1cb2 100644 --- a/spec/lib/gitlab/git/hook_spec.rb +++ b/spec/lib/gitlab/git/hook_spec.rb @@ -2,6 +2,12 @@ require 'spec_helper' require 'fileutils' describe Gitlab::Git::Hook, lib: true do + before do + # We need this because in the spec/spec_helper.rb we define it like this: + # allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_call_original + end + describe "#trigger" do let(:project) { create(:project, :repository) } let(:repo_path) { project.repository.path } diff --git a/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb b/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb new file mode 100644 index 00000000000..3693f52b51b --- /dev/null +++ b/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../simple_check_shared' + +describe Gitlab::HealthChecks::Redis::CacheCheck do + include_examples 'simple_check', 'redis_cache_ping', 'RedisCache', 'PONG' +end diff --git a/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb b/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb new file mode 100644 index 00000000000..c69443d205d --- /dev/null +++ b/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../simple_check_shared' + +describe Gitlab::HealthChecks::Redis::QueuesCheck do + include_examples 'simple_check', 'redis_queues_ping', 'RedisQueues', 'PONG' +end diff --git a/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb b/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb new file mode 100644 index 00000000000..03afc1cd761 --- /dev/null +++ b/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../simple_check_shared' + +describe Gitlab::HealthChecks::Redis::RedisCheck do + include_examples 'simple_check', 'redis_ping', 'Redis', 'PONG' +end diff --git a/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb b/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb new file mode 100644 index 00000000000..b72e152bbe2 --- /dev/null +++ b/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../simple_check_shared' + +describe Gitlab::HealthChecks::Redis::SharedStateCheck do + include_examples 'simple_check', 'redis_shared_state_ping', 'RedisSharedState', 'PONG' +end diff --git a/spec/lib/gitlab/health_checks/redis_check_spec.rb b/spec/lib/gitlab/health_checks/redis_check_spec.rb deleted file mode 100644 index 734cdcb893e..00000000000 --- a/spec/lib/gitlab/health_checks/redis_check_spec.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'spec_helper' -require_relative './simple_check_shared' - -describe Gitlab::HealthChecks::RedisCheck do - include_examples 'simple_check', 'redis_ping', 'Redis', 'PONG' -end diff --git a/spec/lib/gitlab/health_checks/simple_check_shared.rb b/spec/lib/gitlab/health_checks/simple_check_shared.rb index 3f871d66034..1abebeac4dd 100644 --- a/spec/lib/gitlab/health_checks/simple_check_shared.rb +++ b/spec/lib/gitlab/health_checks/simple_check_shared.rb @@ -47,7 +47,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result| allow(described_class).to receive(:check).and_return 'error!' end - it { is_expected.to have_attributes(success: false, message: "unexpected #{check_name} check result: error!") } + it { is_expected.to have_attributes(success: false, message: "unexpected #{described_class.human_name} check result: error!") } end context 'Check is timeouting' do @@ -55,7 +55,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result| allow(described_class).to receive(:check ).and_return Timeout::Error.new end - it { is_expected.to have_attributes(success: false, message: "#{check_name} check timed out") } + it { is_expected.to have_attributes(success: false, message: "#{described_class.human_name} check timed out") } end end diff --git a/spec/lib/gitlab/performance_bar_spec.rb b/spec/lib/gitlab/performance_bar_spec.rb index 8a586bdbf63..b8a2267f1a4 100644 --- a/spec/lib/gitlab/performance_bar_spec.rb +++ b/spec/lib/gitlab/performance_bar_spec.rb @@ -7,7 +7,7 @@ describe Gitlab::PerformanceBar do described_class.enabled?(user) end - it 'caches the allowed user IDs in cache', :caching do + it 'caches the allowed user IDs in cache', :use_clean_rails_memory_store_caching do expect do expect(described_class.enabled?(user)).to be_truthy end.not_to exceed_query_limit(0) diff --git a/spec/lib/gitlab/redis/cache_spec.rb b/spec/lib/gitlab/redis/cache_spec.rb new file mode 100644 index 00000000000..5a4f17cfcf6 --- /dev/null +++ b/spec/lib/gitlab/redis/cache_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Gitlab::Redis::Cache do + let(:config_file_name) { "config/redis.cache.yml" } + let(:environment_config_file_name) { "GITLAB_REDIS_CACHE_CONFIG_FILE" } + let(:config_old_format_socket) { "spec/fixtures/config/redis_cache_old_format_socket.yml" } + let(:config_new_format_socket) { "spec/fixtures/config/redis_cache_new_format_socket.yml" } + let(:old_socket_path) {"/path/to/old/redis.cache.sock" } + let(:new_socket_path) {"/path/to/redis.cache.sock" } + let(:config_old_format_host) { "spec/fixtures/config/redis_cache_old_format_host.yml" } + let(:config_new_format_host) { "spec/fixtures/config/redis_cache_new_format_host.yml" } + let(:redis_port) { 6380 } + let(:redis_database) { 10 } + let(:sentinel_port) { redis_port + 20000 } + let(:config_with_environment_variable_inside) { "spec/fixtures/config/redis_cache_config_with_env.yml"} + let(:config_env_variable_url) {"TEST_GITLAB_REDIS_CACHE_URL"} + let(:class_redis_url) { Gitlab::Redis::Cache::DEFAULT_REDIS_CACHE_URL } + + include_examples "redis_shared_examples" +end diff --git a/spec/lib/gitlab/redis/queues_spec.rb b/spec/lib/gitlab/redis/queues_spec.rb new file mode 100644 index 00000000000..01ca25635a9 --- /dev/null +++ b/spec/lib/gitlab/redis/queues_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Gitlab::Redis::Queues do + let(:config_file_name) { "config/redis.queues.yml" } + let(:environment_config_file_name) { "GITLAB_REDIS_QUEUES_CONFIG_FILE" } + let(:config_old_format_socket) { "spec/fixtures/config/redis_queues_old_format_socket.yml" } + let(:config_new_format_socket) { "spec/fixtures/config/redis_queues_new_format_socket.yml" } + let(:old_socket_path) {"/path/to/old/redis.queues.sock" } + let(:new_socket_path) {"/path/to/redis.queues.sock" } + let(:config_old_format_host) { "spec/fixtures/config/redis_queues_old_format_host.yml" } + let(:config_new_format_host) { "spec/fixtures/config/redis_queues_new_format_host.yml" } + let(:redis_port) { 6381 } + let(:redis_database) { 11 } + let(:sentinel_port) { redis_port + 20000 } + let(:config_with_environment_variable_inside) { "spec/fixtures/config/redis_queues_config_with_env.yml"} + let(:config_env_variable_url) {"TEST_GITLAB_REDIS_QUEUES_URL"} + let(:class_redis_url) { Gitlab::Redis::Queues::DEFAULT_REDIS_QUEUES_URL } + + include_examples "redis_shared_examples" +end diff --git a/spec/lib/gitlab/redis/shared_state_spec.rb b/spec/lib/gitlab/redis/shared_state_spec.rb new file mode 100644 index 00000000000..24b73745dc5 --- /dev/null +++ b/spec/lib/gitlab/redis/shared_state_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Gitlab::Redis::SharedState do + let(:config_file_name) { "config/redis.shared_state.yml" } + let(:environment_config_file_name) { "GITLAB_REDIS_SHARED_STATE_CONFIG_FILE" } + let(:config_old_format_socket) { "spec/fixtures/config/redis_shared_state_old_format_socket.yml" } + let(:config_new_format_socket) { "spec/fixtures/config/redis_shared_state_new_format_socket.yml" } + let(:old_socket_path) {"/path/to/old/redis.shared_state.sock" } + let(:new_socket_path) {"/path/to/redis.shared_state.sock" } + let(:config_old_format_host) { "spec/fixtures/config/redis_shared_state_old_format_host.yml" } + let(:config_new_format_host) { "spec/fixtures/config/redis_shared_state_new_format_host.yml" } + let(:redis_port) { 6382 } + let(:redis_database) { 12 } + let(:sentinel_port) { redis_port + 20000 } + let(:config_with_environment_variable_inside) { "spec/fixtures/config/redis_shared_state_config_with_env.yml"} + let(:config_env_variable_url) {"TEST_GITLAB_REDIS_SHARED_STATE_URL"} + let(:class_redis_url) { Gitlab::Redis::SharedState::DEFAULT_REDIS_SHARED_STATE_URL } + + include_examples "redis_shared_examples" +end diff --git a/spec/lib/gitlab/redis/wrapper_spec.rb b/spec/lib/gitlab/redis/wrapper_spec.rb new file mode 100644 index 00000000000..e1becd0a614 --- /dev/null +++ b/spec/lib/gitlab/redis/wrapper_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Gitlab::Redis::Wrapper do + let(:config_file_name) { "config/resque.yml" } + let(:environment_config_file_name) { "GITLAB_REDIS_CONFIG_FILE" } + let(:config_old_format_socket) { "spec/fixtures/config/redis_old_format_socket.yml" } + let(:config_new_format_socket) { "spec/fixtures/config/redis_new_format_socket.yml" } + let(:old_socket_path) {"/path/to/old/redis.sock" } + let(:new_socket_path) {"/path/to/redis.sock" } + let(:config_old_format_host) { "spec/fixtures/config/redis_old_format_host.yml" } + let(:config_new_format_host) { "spec/fixtures/config/redis_new_format_host.yml" } + let(:redis_port) { 6379 } + let(:redis_database) { 99 } + let(:sentinel_port) { redis_port + 20000 } + let(:config_with_environment_variable_inside) { "spec/fixtures/config/redis_config_with_env.yml"} + let(:config_env_variable_url) {"TEST_GITLAB_REDIS_URL"} + let(:class_redis_url) { Gitlab::Redis::Wrapper::DEFAULT_REDIS_URL } + + include_examples "redis_shared_examples" +end diff --git a/spec/lib/gitlab/sidekiq_status_spec.rb b/spec/lib/gitlab/sidekiq_status_spec.rb index 496e50fbae4..c2e77ef6b6c 100644 --- a/spec/lib/gitlab/sidekiq_status_spec.rb +++ b/spec/lib/gitlab/sidekiq_status_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Gitlab::SidekiqStatus do - describe '.set', :redis do + describe '.set', :clean_gitlab_redis_shared_state do it 'stores the job ID' do described_class.set('123') @@ -14,7 +14,7 @@ describe Gitlab::SidekiqStatus do end end - describe '.unset', :redis do + describe '.unset', :clean_gitlab_redis_shared_state do it 'removes the job ID' do described_class.set('123') described_class.unset('123') @@ -27,7 +27,7 @@ describe Gitlab::SidekiqStatus do end end - describe '.all_completed?', :redis do + describe '.all_completed?', :clean_gitlab_redis_shared_state do it 'returns true if all jobs have been completed' do expect(described_class.all_completed?(%w(123))).to eq(true) end @@ -39,7 +39,7 @@ describe Gitlab::SidekiqStatus do end end - describe '.num_running', :redis do + describe '.num_running', :clean_gitlab_redis_shared_state do it 'returns 0 if all jobs have been completed' do expect(described_class.num_running(%w(123))).to eq(0) end @@ -52,7 +52,7 @@ describe Gitlab::SidekiqStatus do end end - describe '.num_completed', :redis do + describe '.num_completed', :clean_gitlab_redis_shared_state do it 'returns 1 if all jobs have been completed' do expect(described_class.num_completed(%w(123))).to eq(1) end @@ -74,7 +74,7 @@ describe Gitlab::SidekiqStatus do end end - describe 'completed', :redis do + describe 'completed', :clean_gitlab_redis_shared_state do it 'returns the completed job' do expect(described_class.completed_jids(%w(123))).to eq(['123']) end diff --git a/spec/lib/gitlab/user_activities_spec.rb b/spec/lib/gitlab/user_activities_spec.rb index 187d88c8c58..a4ea0ac59e9 100644 --- a/spec/lib/gitlab/user_activities_spec.rb +++ b/spec/lib/gitlab/user_activities_spec.rb @@ -1,27 +1,27 @@ require 'spec_helper' -describe Gitlab::UserActivities, :redis, lib: true do +describe Gitlab::UserActivities, :clean_gitlab_redis_shared_state, lib: true do let(:now) { Time.now } describe '.record' do context 'with no time given' do - it 'uses Time.now and records an activity in Redis' do + it 'uses Time.now and records an activity in SharedState' do Timecop.freeze do now # eager-load now described_class.record(42) end - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]]) end end end context 'with a time given' do - it 'uses the given time and records an activity in Redis' do + it 'uses the given time and records an activity in SharedState' do described_class.record(42, now) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]]) end end @@ -31,30 +31,30 @@ describe Gitlab::UserActivities, :redis, lib: true do describe '.delete' do context 'with a single key' do context 'and key exists' do - it 'removes the pair from Redis' do + it 'removes the pair from SharedState' do described_class.record(42, now) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]]) end subject.delete(42) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []]) end end end context 'and key does not exist' do - it 'removes the pair from Redis' do - Gitlab::Redis.with do |redis| + it 'removes the pair from SharedState' do + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []]) end subject.delete(42) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []]) end end @@ -63,33 +63,33 @@ describe Gitlab::UserActivities, :redis, lib: true do context 'with multiple keys' do context 'and all keys exist' do - it 'removes the pair from Redis' do + it 'removes the pair from SharedState' do described_class.record(41, now) described_class.record(42, now) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['41', now.to_i.to_s], ['42', now.to_i.to_s]]]) end subject.delete(41, 42) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []]) end end end context 'and some keys does not exist' do - it 'removes the existing pair from Redis' do + it 'removes the existing pair from SharedState' do described_class.record(42, now) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', [['42', now.to_i.to_s]]]) end subject.delete(41, 42) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| expect(redis.hscan(described_class::KEY, 0)).to eq(['0', []]) end end diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 493ff3bb5fb..124f66a6e0e 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -276,7 +276,7 @@ describe Gitlab::Workhorse, lib: true do end it 'set and notify' do - expect_any_instance_of(Redis).to receive(:publish) + expect_any_instance_of(::Redis).to receive(:publish) .with(described_class::NOTIFICATION_CHANNEL, "test-key=test-value") subject @@ -310,11 +310,49 @@ describe Gitlab::Workhorse, lib: true do end it 'does not notify' do - expect_any_instance_of(Redis).not_to receive(:publish) + expect_any_instance_of(::Redis).not_to receive(:publish) subject end end end end + + describe '.send_git_blob' do + include FakeBlobHelpers + + let(:blob) { fake_blob } + + subject { described_class.send_git_blob(repository, blob) } + + context 'when Gitaly project_raw_show feature is enabled' do + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) + + expect(key).to eq('Gitlab-Workhorse-Send-Data') + expect(command).to eq('git-blob') + expect(params).to eq({ + 'GitalyServer' => { + address: Gitlab::GitalyClient.address(project.repository_storage), + token: Gitlab::GitalyClient.token(project.repository_storage) + }, + 'GetBlobRequest' => { + repository: repository.gitaly_repository.to_h, + oid: blob.id, + limit: -1 + } + }.deep_stringify_keys) + end + end + + context 'when Gitaly project_raw_show feature is disabled', skip_gitaly_mock: true do + it 'sets the header correctly' do + key, command, params = decode_workhorse_header(subject) + + expect(key).to eq('Gitlab-Workhorse-Send-Data') + expect(command).to eq('git-blob') + expect(params).to eq('RepoPath' => repository.path_to_repo, 'BlobId' => blob.id) + end + end + end end diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb index 4223d2337a8..5b633dd349b 100644 --- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb +++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb @@ -54,7 +54,7 @@ describe MigrateProcessCommitWorkerJobs do end end - describe '#up', :redis do + describe '#up', :clean_gitlab_redis_shared_state do let(:migration) { described_class.new } def job_count @@ -172,7 +172,7 @@ describe MigrateProcessCommitWorkerJobs do end end - describe '#down', :redis do + describe '#down', :clean_gitlab_redis_shared_state do let(:migration) { described_class.new } def job_count diff --git a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb index e3b42b5eac8..063829be546 100644 --- a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb +++ b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb @@ -3,13 +3,13 @@ require 'spec_helper' require Rails.root.join('db', 'post_migrate', '20170324160416_migrate_user_activities_to_users_last_activity_on.rb') -describe MigrateUserActivitiesToUsersLastActivityOn, :redis, :truncate do +describe MigrateUserActivitiesToUsersLastActivityOn, :clean_gitlab_redis_shared_state, :truncate do let(:migration) { described_class.new } let!(:user_active_1) { create(:user) } let!(:user_active_2) { create(:user) } def record_activity(user, time) - Gitlab::Redis.with do |redis| + Gitlab::Redis::SharedState.with do |redis| redis.zadd(described_class::USER_ACTIVITY_SET_KEY, time.to_i, user.username) end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 76ce558eea0..4b9cce28e0e 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -276,14 +276,14 @@ describe Ci::Runner, models: true do it 'sets a new last_update value when it is called the first time' do last_update = runner.ensure_runner_queue_value - expect_value_in_redis.to eq(last_update) + expect_value_in_queues.to eq(last_update) end it 'does not change if it is not expired and called again' do last_update = runner.ensure_runner_queue_value expect(runner.ensure_runner_queue_value).to eq(last_update) - expect_value_in_redis.to eq(last_update) + expect_value_in_queues.to eq(last_update) end context 'updates runner queue after changing editable value' do @@ -294,7 +294,7 @@ describe Ci::Runner, models: true do end it 'sets a new last_update value' do - expect_value_in_redis.not_to eq(last_update) + expect_value_in_queues.not_to eq(last_update) end end @@ -306,12 +306,12 @@ describe Ci::Runner, models: true do end it 'has an old last_update value' do - expect_value_in_redis.to eq(last_update) + expect_value_in_queues.to eq(last_update) end end - def expect_value_in_redis - Gitlab::Redis.with do |redis| + def expect_value_in_queues + Gitlab::Redis::Queues.with do |redis| runner_queue_key = runner.send(:runner_queue_key) expect(redis.get(runner_queue_key)) end @@ -330,7 +330,7 @@ describe Ci::Runner, models: true do end it 'cleans up the queue' do - Gitlab::Redis.with do |redis| + Gitlab::Redis::Queues.with do |redis| expect(redis.get(queue_key)).to be_nil end end diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb index 808247ebfd5..5f9b7e0a367 100644 --- a/spec/models/concerns/reactive_caching_spec.rb +++ b/spec/models/concerns/reactive_caching_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ReactiveCaching, caching: true do +describe ReactiveCaching, :use_clean_rails_memory_store_caching do include ReactiveCachingHelpers class CacheTest diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index 7b1a554d1fb..99190d763f2 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BambooService, models: true, caching: true do +describe BambooService, :use_clean_rails_memory_store_caching, models: true do include ReactiveCachingHelpers let(:bamboo_url) { 'http://gitlab.com/bamboo' } diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index dd529597067..b4ee6691e67 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe BuildkiteService, models: true, caching: true do +describe BuildkiteService, :use_clean_rails_memory_store_caching, models: true do include ReactiveCachingHelpers let(:project) { create(:empty_project) } diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index 1400175427f..c9ac256ff38 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe DroneCiService, models: true, caching: true do +describe DroneCiService, :use_clean_rails_memory_store_caching, models: true do include ReactiveCachingHelpers describe 'associations' do diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 5ba523a478a..b66bb5321ab 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe KubernetesService, models: true, caching: true do +describe KubernetesService, :use_clean_rails_memory_store_caching, models: true do include KubernetesHelpers include ReactiveCachingHelpers diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb index 37f23b1243c..3fb134ec3b7 100644 --- a/spec/models/project_services/prometheus_service_spec.rb +++ b/spec/models/project_services/prometheus_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe PrometheusService, models: true, caching: true do +describe PrometheusService, :use_clean_rails_memory_store_caching, models: true do include PrometheusHelpers include ReactiveCachingHelpers diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index 6b004098510..3f3a74d0f96 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe TeamcityService, models: true, caching: true do +describe TeamcityService, :use_clean_rails_memory_store_caching, models: true do include ReactiveCachingHelpers let(:teamcity_url) { 'http://gitlab.com/teamcity' } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 99bfab70088..c4bc129dd37 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -899,7 +899,7 @@ describe Project, models: true do end end - describe '.cached_count', caching: true do + describe '.cached_count', :use_clean_rails_memory_store_caching do let(:group) { create(:group, :public) } let!(:project1) { create(:empty_project, :public, group: group) } let!(:project2) { create(:empty_project, :public, group: group) } diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index af305e9b234..7635b0868e7 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -561,7 +561,7 @@ describe Repository, models: true do end end - describe "#changelog", caching: true do + describe "#changelog", :use_clean_rails_memory_store_caching do it 'accepts changelog' do expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('changelog')]) @@ -593,7 +593,7 @@ describe Repository, models: true do end end - describe "#license_blob", caching: true do + describe "#license_blob", :use_clean_rails_memory_store_caching do before do repository.delete_file( user, 'LICENSE', message: 'Remove LICENSE', branch_name: 'master') @@ -638,7 +638,7 @@ describe Repository, models: true do end end - describe '#license_key', caching: true do + describe '#license_key', :use_clean_rails_memory_store_caching do before do repository.delete_file(user, 'LICENSE', message: 'Remove LICENSE', branch_name: 'master') @@ -703,7 +703,7 @@ describe Repository, models: true do end end - describe "#gitlab_ci_yml", caching: true do + describe "#gitlab_ci_yml", :use_clean_rails_memory_store_caching do it 'returns valid file' do files = [TestBlob.new('file'), TestBlob.new('.gitlab-ci.yml'), TestBlob.new('copying')] expect(repository.tree).to receive(:blobs).and_return(files) @@ -1611,7 +1611,7 @@ describe Repository, models: true do end end - describe '#contribution_guide', caching: true do + describe '#contribution_guide', :use_clean_rails_memory_store_caching do it 'returns and caches the output' do expect(repository).to receive(:file_on_head) .with(:contributing) @@ -1625,7 +1625,7 @@ describe Repository, models: true do end end - describe '#gitignore', caching: true do + describe '#gitignore', :use_clean_rails_memory_store_caching do it 'returns and caches the output' do expect(repository).to receive(:file_on_head) .with(:gitignore) @@ -1638,7 +1638,7 @@ describe Repository, models: true do end end - describe '#koding_yml', caching: true do + describe '#koding_yml', :use_clean_rails_memory_store_caching do it 'returns and caches the output' do expect(repository).to receive(:file_on_head) .with(:koding) @@ -1651,7 +1651,7 @@ describe Repository, models: true do end end - describe '#readme', caching: true do + describe '#readme', :use_clean_rails_memory_store_caching do context 'with a non-existing repository' do it 'returns nil' do allow(repository).to receive(:tree).with(:head).and_return(nil) @@ -1822,7 +1822,7 @@ describe Repository, models: true do end end - describe '#cache_method_output', caching: true do + describe '#cache_method_output', :use_clean_rails_memory_store_caching do context 'with a non-existing repository' do let(:value) do repository.cache_method_output(:cats, fallback: 10) do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 448555d2190..c70f916a8bd 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -348,7 +348,7 @@ describe User, models: true do end end - describe '#update_tracked_fields!', :redis do + describe '#update_tracked_fields!', :clean_gitlab_redis_shared_state do let(:request) { OpenStruct.new(remote_ip: "127.0.0.1") } let(:user) { create(:user) } @@ -1159,6 +1159,18 @@ describe User, models: true do end end + describe '#sanitize_attrs' do + let(:user) { build(:user, name: 'test & user', skype: 'test&user') } + + it 'encodes HTML entities in the Skype attribute' do + expect { user.sanitize_attrs }.to change { user.skype }.to('test&user') + end + + it 'does not encode HTML entities in the name attribute' do + expect { user.sanitize_attrs }.not_to change { user.name } + end + end + describe '#starred?' do it 'determines if user starred a project' do user = create :user @@ -1684,7 +1696,7 @@ describe User, models: true do end end - describe '#refresh_authorized_projects', redis: true do + describe '#refresh_authorized_projects', clean_gitlab_redis_shared_state: true do let(:project1) { create(:empty_project) } let(:project2) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb index f5a14b1d04d..c1a0313b13c 100644 --- a/spec/presenters/merge_request_presenter_spec.rb +++ b/spec/presenters/merge_request_presenter_spec.rb @@ -332,7 +332,31 @@ describe MergeRequestPresenter do end end - context 'when target branch does not exists' do + context 'when target branch does not exist' do + it 'returns nil' do + allow(resource).to receive(:target_branch_exists?) { false } + + is_expected.to be_nil + end + end + end + + describe '#target_branch_tree_path' do + subject do + described_class.new(resource, current_user: user) + .target_branch_tree_path + end + + context 'when target branch exists' do + it 'returns path' do + allow(resource).to receive(:target_branch_exists?) { true } + + is_expected + .to eq("/#{resource.target_project.full_path}/tree/#{resource.target_branch}") + end + end + + context 'when target branch does not exist' do it 'returns nil' do allow(resource).to receive(:target_branch_exists?) { false } @@ -355,7 +379,7 @@ describe MergeRequestPresenter do end end - context 'when source branch does not exists' do + context 'when source branch does not exist' do it 'returns nil' do allow(resource).to receive(:source_branch_exists?) { false } @@ -363,4 +387,17 @@ describe MergeRequestPresenter do end end end + + describe '#source_branch_with_namespace_link' do + subject do + described_class.new(resource, current_user: user).source_branch_with_namespace_link + end + + it 'returns link' do + allow(resource).to receive(:source_branch_exists?) { true } + + is_expected + .to eq("<a href=\"/#{resource.source_project.full_path}/tree/#{resource.source_branch}\">#{resource.source_branch}</a>") + end + end end diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb index cde4fa888a0..453eb4683a0 100644 --- a/spec/requests/api/internal_spec.rb +++ b/spec/requests/api/internal_spec.rb @@ -168,7 +168,7 @@ describe API::Internal do end end - describe "POST /internal/allowed", :redis do + describe "POST /internal/allowed", :clean_gitlab_redis_shared_state do context "access granted" do before do project.team << [user, :developer] diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 360a82196a8..9098ae6bcda 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -69,6 +69,22 @@ describe API::MergeRequests do expect(json_response.first['merge_commit_sha']).to eq(merge_request_merged.merge_commit_sha) end + it "returns an array of all merge_requests using simple mode" do + get api("/projects/#{project.id}/merge_requests?view=simple", user) + + expect(response).to have_http_status(200) + expect(response).to include_pagination_headers + expect(json_response.last.keys).to match_array(%w(id iid title web_url created_at description project_id state updated_at)) + expect(json_response).to be_an Array + expect(json_response.length).to eq(3) + expect(json_response.last['iid']).to eq(merge_request.iid) + expect(json_response.last['title']).to eq(merge_request.title) + expect(json_response.last).to have_key('web_url') + expect(json_response.first['iid']).to eq(merge_request_merged.iid) + expect(json_response.first['title']).to eq(merge_request_merged.title) + expect(json_response.first).to have_key('web_url') + end + it "returns an array of all merge_requests" do get api("/projects/#{project.id}/merge_requests?state", user) diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index c34b88f0741..877bde3b9a6 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -943,11 +943,11 @@ describe API::Users do expect(response).to have_http_status(403) end - it 'returns initial current user without private token when sudo not defined' do + it 'returns initial current user without private token but with is_admin when sudo not defined' do get api("/user?private_token=#{admin_personal_access_token}") expect(response).to have_http_status(200) - expect(response).to match_response_schema('public_api/v4/user/public') + expect(response).to match_response_schema('public_api/v4/user/admin') expect(json_response['id']).to eq(admin.id) end end @@ -961,11 +961,11 @@ describe API::Users do expect(json_response['id']).to eq(user.id) end - it 'returns initial current user without private token when sudo not defined' do + it 'returns initial current user without private token but with is_admin when sudo not defined' do get api("/user?private_token=#{admin.private_token}") expect(response).to have_http_status(200) - expect(response).to match_response_schema('public_api/v4/user/public') + expect(response).to match_response_schema('public_api/v4/user/admin') expect(json_response['id']).to eq(admin.id) end end @@ -1313,7 +1313,7 @@ describe API::Users do end end - context "user activities", :redis do + context "user activities", :clean_gitlab_redis_shared_state do let!(:old_active_user) { create(:user, last_activity_on: Time.utc(2000, 1, 1)) } let!(:newly_active_user) { create(:user, last_activity_on: 2.days.ago.midday) } diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb index 185679e1a0f..d0443a450a2 100644 --- a/spec/requests/git_http_spec.rb +++ b/spec/requests/git_http_spec.rb @@ -406,7 +406,7 @@ describe 'Git HTTP requests', lib: true do end end - it 'updates the user last activity', :redis do + it 'updates the user last activity', :clean_gitlab_redis_shared_state do expect(user_activity(user)).to be_nil download(path, env) do |response| diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb index 6d1f0b24196..ebba28ba8ce 100644 --- a/spec/requests/openid_connect_spec.rb +++ b/spec/requests/openid_connect_spec.rb @@ -98,7 +98,7 @@ describe 'OpenID Connect requests' do expect(@payload['sub']).to eq hashed_subject end - it 'includes the time of the last authentication', :redis do + it 'includes the time of the last authentication', :clean_gitlab_redis_shared_state do expect(@payload['auth_time']).to eq user.current_sign_in_at.to_i end diff --git a/spec/serializers/merge_request_entity_spec.rb b/spec/serializers/merge_request_entity_spec.rb index d38433c2365..b3d58b2636f 100644 --- a/spec/serializers/merge_request_entity_spec.rb +++ b/spec/serializers/merge_request_entity_spec.rb @@ -47,7 +47,7 @@ describe MergeRequestEntity do :cancel_merge_when_pipeline_succeeds_path, :create_issue_to_resolve_discussions_path, :source_branch_path, :target_branch_commits_path, - :commits_count) + :target_branch_tree_path, :commits_count) end it 'has email_patches_path' do diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb index b06cefe071d..8d067c194cc 100644 --- a/spec/services/event_create_service_spec.rb +++ b/spec/services/event_create_service_spec.rb @@ -113,7 +113,7 @@ describe EventCreateService, services: true do end end - describe '#push', :redis do + describe '#push', :clean_gitlab_redis_shared_state do let(:project) { create(:empty_project) } let(:user) { create(:user) } diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index 8e8816870e1..3f77ed10069 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -527,14 +527,18 @@ describe GitPushService, services: true do let(:housekeeping) { Projects::HousekeepingService.new(project) } before do - # Flush any raw Redis data stored by the housekeeping code. - Gitlab::Redis.with { |conn| conn.flushall } + # Flush any raw key-value data stored by the housekeeping code. + Gitlab::Redis::Cache.with { |conn| conn.flushall } + Gitlab::Redis::Queues.with { |conn| conn.flushall } + Gitlab::Redis::SharedState.with { |conn| conn.flushall } allow(Projects::HousekeepingService).to receive(:new).and_return(housekeeping) end after do - Gitlab::Redis.with { |conn| conn.flushall } + Gitlab::Redis::Cache.with { |conn| conn.flushall } + Gitlab::Redis::Queues.with { |conn| conn.flushall } + Gitlab::Redis::SharedState.with { |conn| conn.flushall } end it 'does not perform housekeeping when not needed' do diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb index 697dc18feb0..b399d3402fd 100644 --- a/spec/services/projects/destroy_service_spec.rb +++ b/spec/services/projects/destroy_service_spec.rb @@ -60,14 +60,14 @@ describe Projects::DestroyService, services: true do before do new_user = create(:user) project.team.add_user(new_user, Gitlab::Access::DEVELOPER) - allow_any_instance_of(Projects::DestroyService).to receive(:flush_caches).and_raise(Redis::CannotConnectError) + allow_any_instance_of(Projects::DestroyService).to receive(:flush_caches).and_raise(::Redis::CannotConnectError) end it 'keeps project team intact upon an error' do Sidekiq::Testing.inline! do begin destroy_project(project, user, {}) - rescue Redis::CannotConnectError + rescue ::Redis::CannotConnectError end end diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index 175a42a32d9..de41cbab14c 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -908,7 +908,7 @@ describe TodoService, services: true do end end - it 'caches the number of todos of a user', :caching do + it 'caches the number of todos of a user', :use_clean_rails_memory_store_caching do create(:todo, :mentioned, user: john_doe, target: issue, project: project) todo = create(:todo, :mentioned, user: john_doe, target: issue, project: project) TodoService.new.mark_todos_as_done([todo], john_doe) diff --git a/spec/services/users/activity_service_spec.rb b/spec/services/users/activity_service_spec.rb index 2e009d4ce1c..e5330d1d3e4 100644 --- a/spec/services/users/activity_service_spec.rb +++ b/spec/services/users/activity_service_spec.rb @@ -7,7 +7,7 @@ describe Users::ActivityService, services: true do subject(:service) { described_class.new(user, 'type') } - describe '#execute', :redis do + describe '#execute', :clean_gitlab_redis_shared_state do context 'when last activity is nil' do before do service.execute diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb index b65cadbb2f5..1c0f55d2965 100644 --- a/spec/services/users/refresh_authorized_projects_service_spec.rb +++ b/spec/services/users/refresh_authorized_projects_service_spec.rb @@ -8,7 +8,7 @@ describe Users::RefreshAuthorizedProjectsService do let(:user) { project.namespace.owner } let(:service) { described_class.new(user) } - describe '#execute', :redis do + describe '#execute', :clean_gitlab_redis_shared_state do it 'refreshes the authorizations using a lease' do expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain) .and_return('foo') diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a497b8613bb..3c142764e28 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -76,6 +76,11 @@ RSpec.configure do |config| TestEnv.cleanup end + config.before(:example) do + # Skip pre-receive hook check so we can use the web editor and merge. + allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil]) + end + config.before(:example, :request_store) do RequestStore.begin! end @@ -91,20 +96,30 @@ RSpec.configure do |config| end end - config.around(:each, :caching) do |example| + config.around(:each, :use_clean_rails_memory_store_caching) do |example| caching_store = Rails.cache - Rails.cache = ActiveSupport::Cache::MemoryStore.new if example.metadata[:caching] + Rails.cache = ActiveSupport::Cache::MemoryStore.new + example.run + Rails.cache = caching_store end - config.around(:each, :redis) do |example| - Gitlab::Redis.with(&:flushall) + config.around(:each, :clean_gitlab_redis_cache) do |example| + Gitlab::Redis::Cache.with(&:flushall) + + example.run + + Gitlab::Redis::Cache.with(&:flushall) + end + + config.around(:each, :clean_gitlab_redis_shared_state) do |example| + Gitlab::Redis::SharedState.with(&:flushall) Sidekiq.redis(&:flushall) example.run - Gitlab::Redis.with(&:flushall) + Gitlab::Redis::SharedState.with(&:flushall) Sidekiq.redis(&:flushall) end diff --git a/spec/support/dropzone_helper.rb b/spec/support/dropzone_helper.rb index 02fdeb08afe..fe72d320fcf 100644 --- a/spec/support/dropzone_helper.rb +++ b/spec/support/dropzone_helper.rb @@ -54,4 +54,23 @@ module DropzoneHelper loop until page.evaluate_script('window._dropzoneComplete === true') end end + + def drop_in_dropzone(file_path) + # Generate a fake input selector + page.execute_script <<-JS + var fakeFileInput = window.$('<input/>').attr( + {id: 'fakeFileInput', type: 'file'} + ).appendTo('body'); + JS + + # Attach the file to the fake input selector with Capybara + attach_file('fakeFileInput', file_path) + + # Add the file to a fileList array and trigger the fake drop event + page.execute_script <<-JS + var fileList = [$('#fakeFileInput')[0].files[0]]; + var e = jQuery.Event('drop', { dataTransfer : { files : fileList } }); + $('.dropzone')[0].dropzone.listeners[0].events.drop(e); + JS + end end diff --git a/spec/lib/gitlab/redis_spec.rb b/spec/support/redis/redis_shared_examples.rb index 593aa5038ad..f9552e41894 100644 --- a/spec/lib/gitlab/redis_spec.rb +++ b/spec/support/redis/redis_shared_examples.rb @@ -1,12 +1,10 @@ -require 'spec_helper' - -describe Gitlab::Redis do +RSpec.shared_examples "redis_shared_examples" do include StubENV - let(:config) { 'config/resque.yml' } + let(:test_redis_url) { "redis://redishost:#{redis_port}"} before(:each) do - stub_env('GITLAB_REDIS_CONFIG_FILE', Rails.root.join(config).to_s) + stub_env(environment_config_file_name, Rails.root.join(config_file_name)) clear_raw_config end @@ -26,46 +24,40 @@ describe Gitlab::Redis do end context 'when url contains unix socket reference' do - let(:config_old) { 'spec/fixtures/config/redis_old_format_socket.yml' } - let(:config_new) { 'spec/fixtures/config/redis_new_format_socket.yml' } - context 'with old format' do - let(:config) { config_old } + let(:config_file_name) { config_old_format_socket } it 'returns path key instead' do - is_expected.to include(path: '/path/to/old/redis.sock') + is_expected.to include(path: old_socket_path) is_expected.not_to have_key(:url) end end context 'with new format' do - let(:config) { config_new } + let(:config_file_name) { config_new_format_socket } it 'returns path key instead' do - is_expected.to include(path: '/path/to/redis.sock') + is_expected.to include(path: new_socket_path) is_expected.not_to have_key(:url) end end end context 'when url is host based' do - let(:config_old) { 'spec/fixtures/config/redis_old_format_host.yml' } - let(:config_new) { 'spec/fixtures/config/redis_new_format_host.yml' } - context 'with old format' do - let(:config) { config_old } + let(:config_file_name) { config_old_format_host } it 'returns hash with host, port, db, and password' do - is_expected.to include(host: 'localhost', password: 'mypassword', port: 6379, db: 99) + is_expected.to include(host: 'localhost', password: 'mypassword', port: redis_port, db: redis_database) is_expected.not_to have_key(:url) end end context 'with new format' do - let(:config) { config_new } + let(:config_file_name) { config_new_format_host } it 'returns hash with host, port, db, and password' do - is_expected.to include(host: 'localhost', password: 'mynewpassword', port: 6379, db: 99) + is_expected.to include(host: 'localhost', password: 'mynewpassword', port: redis_port, db: redis_database) is_expected.not_to have_key(:url) end end @@ -73,30 +65,22 @@ describe Gitlab::Redis do end describe '.url' do - it 'withstands mutation' do - url1 = described_class.url - url2 = described_class.url - url1 << 'foobar' - - expect(url2).not_to end_with('foobar') - end - context 'when yml file with env variable' do - let(:config) { 'spec/fixtures/config/redis_config_with_env.yml' } + let(:config_file_name) { config_with_environment_variable_inside } before do - stub_env('TEST_GITLAB_REDIS_URL', 'redis://redishost:6379') + stub_env(config_env_variable_url, test_redis_url) end it 'reads redis url from env variable' do - expect(described_class.url).to eq 'redis://redishost:6379' + expect(described_class.url).to eq test_redis_url end end end describe '._raw_config' do subject { described_class._raw_config } - let(:config) { '/var/empty/doesnotexist' } + let(:config_file_name) { '/var/empty/doesnotexist' } it 'should be frozen' do expect(subject).to be_frozen @@ -105,6 +89,12 @@ describe Gitlab::Redis do it 'returns false when the file does not exist' do expect(subject).to eq(false) end + + it "returns false when the filename can't be determined" do + expect(described_class).to receive(:config_file_name).and_return(nil) + + expect(subject).to eq(false) + end end describe '.with' do @@ -124,7 +114,7 @@ describe Gitlab::Redis do it 'instantiates a connection pool with size 5' do expect(ConnectionPool).to receive(:new).with(size: 5).and_call_original - described_class.with { |_redis| true } + described_class.with { |_redis_shared_example| true } end end @@ -137,7 +127,7 @@ describe Gitlab::Redis do it 'instantiates a connection pool with a size based on the concurrency of the worker' do expect(ConnectionPool).to receive(:new).with(size: 18 + 5).and_call_original - described_class.with { |_redis| true } + described_class.with { |_redis_shared_example| true } end end end @@ -146,16 +136,16 @@ describe Gitlab::Redis do subject { described_class.new(Rails.env).sentinels } context 'when sentinels are defined' do - let(:config) { 'spec/fixtures/config/redis_new_format_host.yml' } + let(:config_file_name) { config_new_format_host } it 'returns an array of hashes with host and port keys' do - is_expected.to include(host: 'localhost', port: 26380) - is_expected.to include(host: 'slave2', port: 26381) + is_expected.to include(host: 'localhost', port: sentinel_port) + is_expected.to include(host: 'slave2', port: sentinel_port) end end context 'when sentinels are not defined' do - let(:config) { 'spec/fixtures/config/redis_old_format_host.yml' } + let(:config_file_name) { config_old_format_host } it 'returns nil' do is_expected.to be_nil @@ -167,7 +157,7 @@ describe Gitlab::Redis do subject { described_class.new(Rails.env).sentinels? } context 'when sentinels are defined' do - let(:config) { 'spec/fixtures/config/redis_new_format_host.yml' } + let(:config_file_name) { config_new_format_host } it 'returns true' do is_expected.to be_truthy @@ -175,7 +165,7 @@ describe Gitlab::Redis do end context 'when sentinels are not defined' do - let(:config) { 'spec/fixtures/config/redis_old_format_host.yml' } + let(:config_file_name) { config_old_format_host } it 'returns false' do is_expected.to be_falsey @@ -187,12 +177,12 @@ describe Gitlab::Redis do it 'returns default redis url when no config file is present' do expect(subject).to receive(:fetch_config) { false } - expect(subject.send(:raw_config_hash)).to eq(url: Gitlab::Redis::DEFAULT_REDIS_URL) + expect(subject.send(:raw_config_hash)).to eq(url: class_redis_url ) end it 'returns old-style single url config in a hash' do - expect(subject).to receive(:fetch_config) { 'redis://myredis:6379' } - expect(subject.send(:raw_config_hash)).to eq(url: 'redis://myredis:6379') + expect(subject).to receive(:fetch_config) { test_redis_url } + expect(subject.send(:raw_config_hash)).to eq(url: test_redis_url) end end @@ -200,7 +190,13 @@ describe Gitlab::Redis do it 'returns false when no config file is present' do allow(described_class).to receive(:_raw_config) { false } - expect(subject.send(:fetch_config)).to be_falsey + expect(subject.send(:fetch_config)).to eq false + end + + it 'returns false when config file is present but has invalid YAML' do + allow(described_class).to receive(:_raw_config) { "# development: true" } + + expect(subject.send(:fetch_config)).to eq false end end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 0cae5620920..0a194ca4c90 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -206,6 +206,7 @@ module TestEnv # Otherwise they'd be created by the first test, often timing out and # causing a transient test failure def eager_load_driver_server + return unless ENV['CI'] return unless defined?(Capybara) puts "Starting the Capybara driver server..." diff --git a/spec/support/unique_ip_check_shared_examples.rb b/spec/support/unique_ip_check_shared_examples.rb index 1986d202c4a..ff0b47899f5 100644 --- a/spec/support/unique_ip_check_shared_examples.rb +++ b/spec/support/unique_ip_check_shared_examples.rb @@ -1,7 +1,9 @@ shared_context 'unique ips sign in limit' do include StubENV before(:each) do - Gitlab::Redis.with(&:flushall) + Gitlab::Redis::Cache.with(&:flushall) + Gitlab::Redis::Queues.with(&:flushall) + Gitlab::Redis::SharedState.with(&:flushall) end before do diff --git a/spec/workers/schedule_update_user_activity_worker_spec.rb b/spec/workers/schedule_update_user_activity_worker_spec.rb index e583c3203aa..32c59381b01 100644 --- a/spec/workers/schedule_update_user_activity_worker_spec.rb +++ b/spec/workers/schedule_update_user_activity_worker_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe ScheduleUpdateUserActivityWorker, :redis do +describe ScheduleUpdateUserActivityWorker, :clean_gitlab_redis_shared_state do let(:now) { Time.now } before do diff --git a/spec/workers/update_user_activity_worker_spec.rb b/spec/workers/update_user_activity_worker_spec.rb index 43e9511f116..268ca1d81f2 100644 --- a/spec/workers/update_user_activity_worker_spec.rb +++ b/spec/workers/update_user_activity_worker_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe UpdateUserActivityWorker, :redis do +describe UpdateUserActivityWorker, :clean_gitlab_redis_shared_state do let(:user_active_2_days_ago) { create(:user, current_sign_in_at: 10.months.ago) } let(:user_active_yesterday_1) { create(:user) } let(:user_active_yesterday_2) { create(:user) } @@ -25,7 +25,7 @@ describe UpdateUserActivityWorker, :redis do end end - it 'deletes the pairs from Redis' do + it 'deletes the pairs from SharedState' do data.each { |id, time| Gitlab::UserActivities.record(id, time) } subject.perform(data) diff --git a/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml b/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml index 5b6af7be8c4..eeefadaa019 100644 --- a/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/Docker.gitlab-ci.yml @@ -4,10 +4,21 @@ image: docker:latest services: - docker:dind +before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + +build-master: + stage: build + script: + - docker build --pull -t "$CI_REGISTRY_IMAGE" . + - docker push "$CI_REGISTRY_IMAGE" + only: + - master + build: stage: build - before_script: - - docker login -u "$CI_REGISTRY_USER" -p "CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - - docker build --pull -t "$CI_REGISTRY_IMAGE:CI_COMMIT_REF_SLUG" . - - docker push "$CI_REGISTRY_IMAGE:CI_COMMIT_REF_SLUG" + - docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" . + - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" + except: + - master diff --git a/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml index 555a51d35b9..06b0c84e516 100644 --- a/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml @@ -28,7 +28,7 @@ canary: - command canary environment: name: production - url: http://$CI_PROJECT_NAME.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG.$KUBE_DOMAIN when: manual only: - master @@ -39,7 +39,7 @@ production: - command deploy environment: name: production - url: http://$CI_PROJECT_NAME.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG.$KUBE_DOMAIN when: manual only: - master @@ -50,7 +50,7 @@ staging: - command deploy environment: name: staging - url: http://$CI_PROJECT_NAME-staging.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_DOMAIN only: - master @@ -60,7 +60,7 @@ review: - command deploy environment: name: review/$CI_COMMIT_REF_NAME - url: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN on_stop: stop_review only: - branches diff --git a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml index ee830ec2eb0..722934b7981 100644 --- a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml @@ -27,7 +27,7 @@ production: - command deploy environment: name: production - url: http://$CI_PROJECT_NAME.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG.$KUBE_DOMAIN when: manual only: - master @@ -38,7 +38,7 @@ staging: - command deploy environment: name: staging - url: http://$CI_PROJECT_NAME-staging.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_DOMAIN only: - master @@ -48,7 +48,7 @@ review: - command deploy environment: name: review/$CI_COMMIT_REF_NAME - url: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN on_stop: stop_review only: - branches diff --git a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml index 27c9107e0d7..acba718ebe4 100644 --- a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml +++ b/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml @@ -23,38 +23,32 @@ build: production: stage: production - variables: - CI_ENVIRONMENT_URL: http://$CI_PROJECT_NAME.$KUBE_DOMAIN script: - command deploy environment: name: production - url: http://$CI_PROJECT_NAME.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG.$KUBE_DOMAIN when: manual only: - master staging: stage: staging - variables: - CI_ENVIRONMENT_URL: http://$CI_PROJECT_NAME-staging.$KUBE_DOMAIN script: - command deploy environment: name: staging - url: http://$CI_PROJECT_NAME-staging.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_DOMAIN only: - master review: stage: review - variables: - CI_ENVIRONMENT_URL: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN script: - command deploy environment: name: review/$CI_COMMIT_REF_NAME - url: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN + url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN on_stop: stop_review only: - branches diff --git a/vendor/licenses.csv b/vendor/licenses.csv index 5dcac5947a1..5beb3e5e9bf 100644 --- a/vendor/licenses.csv +++ b/vendor/licenses.csv @@ -1,9 +1,9 @@ RedCloth,4.3.2,MIT -abbrev,1.0.9,ISC +abbrev,1.1.0,ISC accepts,1.3.3,MIT ace-rails-ap,4.1.2,MIT -acorn,5.0.3,MIT -acorn-dynamic-import,2.0.1,MIT +acorn,5.1.1,MIT +acorn-dynamic-import,2.0.2,MIT acorn-jsx,3.0.1,MIT actionmailer,4.2.8,MIT actionpack,4.2.8,MIT @@ -16,7 +16,7 @@ acts-as-taggable-on,4.0.0,MIT addressable,2.3.8,Apache 2.0 after,0.8.2,MIT after_commit_queue,1.3.0,MIT -ajv,4.11.2,MIT +ajv,4.11.8,MIT ajv-keywords,1.5.1,MIT akismet,2.0.0,MIT align-text,0.1.4,MIT @@ -26,16 +26,17 @@ amdefine,1.0.1,BSD-3-Clause OR MIT ansi-escapes,1.4.0,MIT ansi-html,0.0.5,"Apache, Version 2.0" ansi-regex,2.1.1,MIT -ansi-styles,2.2.1,MIT +ansi-styles,3.1.0,MIT anymatch,1.3.0,ISC append-transform,0.4.0,MIT -aproba,1.1.0,ISC -are-we-there-yet,1.1.2,ISC +aproba,1.1.2,ISC +are-we-there-yet,1.1.4,ISC arel,6.0.4,MIT argparse,1.0.9,MIT arr-diff,2.0.0,MIT -arr-flatten,1.0.1,MIT +arr-flatten,1.1.0,MIT array-find,1.0.0,MIT +array-find-index,1.0.2,MIT array-flatten,1.1.1,MIT array-slice,0.2.3,MIT array-union,1.0.2,MIT @@ -63,26 +64,27 @@ aws-sign2,0.6.0,Apache 2.0 aws4,1.6.0,MIT axiom-types,0.1.1,MIT babel-code-frame,6.22.0,MIT -babel-core,6.23.1,MIT -babel-generator,6.23.0,MIT -babel-helper-bindify-decorators,6.22.0,MIT -babel-helper-builder-binary-assignment-operator-visitor,6.22.0,MIT -babel-helper-call-delegate,6.22.0,MIT -babel-helper-define-map,6.23.0,MIT -babel-helper-explode-assignable-expression,6.22.0,MIT -babel-helper-explode-class,6.22.0,MIT -babel-helper-function-name,6.23.0,MIT -babel-helper-get-function-arity,6.22.0,MIT -babel-helper-hoist-variables,6.22.0,MIT -babel-helper-optimise-call-expression,6.23.0,MIT -babel-helper-regex,6.22.0,MIT -babel-helper-remap-async-to-generator,6.22.0,MIT -babel-helper-replace-supers,6.23.0,MIT -babel-helpers,6.23.0,MIT -babel-loader,6.2.10,MIT +babel-core,6.25.0,MIT +babel-eslint,7.2.3,MIT +babel-generator,6.25.0,MIT +babel-helper-bindify-decorators,6.24.1,MIT +babel-helper-builder-binary-assignment-operator-visitor,6.24.1,MIT +babel-helper-call-delegate,6.24.1,MIT +babel-helper-define-map,6.24.1,MIT +babel-helper-explode-assignable-expression,6.24.1,MIT +babel-helper-explode-class,6.24.1,MIT +babel-helper-function-name,6.24.1,MIT +babel-helper-get-function-arity,6.24.1,MIT +babel-helper-hoist-variables,6.24.1,MIT +babel-helper-optimise-call-expression,6.24.1,MIT +babel-helper-regex,6.24.1,MIT +babel-helper-remap-async-to-generator,6.24.1,MIT +babel-helper-replace-supers,6.24.1,MIT +babel-helpers,6.24.1,MIT +babel-loader,6.4.1,MIT babel-messages,6.23.0,MIT babel-plugin-check-es2015-constants,6.22.0,MIT -babel-plugin-istanbul,4.0.0,New BSD +babel-plugin-istanbul,4.1.4,New BSD babel-plugin-syntax-async-functions,6.13.0,MIT babel-plugin-syntax-async-generators,6.13.0,MIT babel-plugin-syntax-class-properties,6.13.0,MIT @@ -91,57 +93,57 @@ babel-plugin-syntax-dynamic-import,6.18.0,MIT babel-plugin-syntax-exponentiation-operator,6.13.0,MIT babel-plugin-syntax-object-rest-spread,6.13.0,MIT babel-plugin-syntax-trailing-function-commas,6.22.0,MIT -babel-plugin-transform-async-generator-functions,6.22.0,MIT -babel-plugin-transform-async-to-generator,6.22.0,MIT -babel-plugin-transform-class-properties,6.23.0,MIT -babel-plugin-transform-decorators,6.22.0,MIT -babel-plugin-transform-define,1.2.0,MIT +babel-plugin-transform-async-generator-functions,6.24.1,MIT +babel-plugin-transform-async-to-generator,6.24.1,MIT +babel-plugin-transform-class-properties,6.24.1,MIT +babel-plugin-transform-decorators,6.24.1,MIT +babel-plugin-transform-define,1.3.0,MIT babel-plugin-transform-es2015-arrow-functions,6.22.0,MIT babel-plugin-transform-es2015-block-scoped-functions,6.22.0,MIT -babel-plugin-transform-es2015-block-scoping,6.23.0,MIT -babel-plugin-transform-es2015-classes,6.23.0,MIT -babel-plugin-transform-es2015-computed-properties,6.22.0,MIT +babel-plugin-transform-es2015-block-scoping,6.24.1,MIT +babel-plugin-transform-es2015-classes,6.24.1,MIT +babel-plugin-transform-es2015-computed-properties,6.24.1,MIT babel-plugin-transform-es2015-destructuring,6.23.0,MIT -babel-plugin-transform-es2015-duplicate-keys,6.22.0,MIT +babel-plugin-transform-es2015-duplicate-keys,6.24.1,MIT babel-plugin-transform-es2015-for-of,6.23.0,MIT -babel-plugin-transform-es2015-function-name,6.22.0,MIT +babel-plugin-transform-es2015-function-name,6.24.1,MIT babel-plugin-transform-es2015-literals,6.22.0,MIT -babel-plugin-transform-es2015-modules-amd,6.24.0,MIT -babel-plugin-transform-es2015-modules-commonjs,6.24.0,MIT -babel-plugin-transform-es2015-modules-systemjs,6.23.0,MIT -babel-plugin-transform-es2015-modules-umd,6.24.0,MIT -babel-plugin-transform-es2015-object-super,6.22.0,MIT -babel-plugin-transform-es2015-parameters,6.23.0,MIT -babel-plugin-transform-es2015-shorthand-properties,6.22.0,MIT +babel-plugin-transform-es2015-modules-amd,6.24.1,MIT +babel-plugin-transform-es2015-modules-commonjs,6.24.1,MIT +babel-plugin-transform-es2015-modules-systemjs,6.24.1,MIT +babel-plugin-transform-es2015-modules-umd,6.24.1,MIT +babel-plugin-transform-es2015-object-super,6.24.1,MIT +babel-plugin-transform-es2015-parameters,6.24.1,MIT +babel-plugin-transform-es2015-shorthand-properties,6.24.1,MIT babel-plugin-transform-es2015-spread,6.22.0,MIT -babel-plugin-transform-es2015-sticky-regex,6.22.0,MIT +babel-plugin-transform-es2015-sticky-regex,6.24.1,MIT babel-plugin-transform-es2015-template-literals,6.22.0,MIT babel-plugin-transform-es2015-typeof-symbol,6.23.0,MIT -babel-plugin-transform-es2015-unicode-regex,6.22.0,MIT -babel-plugin-transform-exponentiation-operator,6.22.0,MIT +babel-plugin-transform-es2015-unicode-regex,6.24.1,MIT +babel-plugin-transform-exponentiation-operator,6.24.1,MIT babel-plugin-transform-object-rest-spread,6.23.0,MIT -babel-plugin-transform-regenerator,6.22.0,MIT -babel-plugin-transform-strict-mode,6.22.0,MIT -babel-preset-es2015,6.24.0,MIT -babel-preset-es2016,6.22.0,MIT -babel-preset-es2017,6.22.0,MIT -babel-preset-latest,6.24.0,MIT -babel-preset-stage-2,6.22.0,MIT -babel-preset-stage-3,6.22.0,MIT -babel-register,6.23.0,MIT -babel-runtime,6.22.0,MIT -babel-template,6.23.0,MIT -babel-traverse,6.23.1,MIT -babel-types,6.23.0,MIT +babel-plugin-transform-regenerator,6.24.1,MIT +babel-plugin-transform-strict-mode,6.24.1,MIT +babel-preset-es2015,6.24.1,MIT +babel-preset-es2016,6.24.1,MIT +babel-preset-es2017,6.24.1,MIT +babel-preset-latest,6.24.1,MIT +babel-preset-stage-2,6.24.1,MIT +babel-preset-stage-3,6.24.1,MIT +babel-register,6.24.1,MIT +babel-runtime,6.23.0,MIT +babel-template,6.25.0,MIT +babel-traverse,6.25.0,MIT +babel-types,6.25.0,MIT babosa,1.0.2,MIT -babylon,6.15.0,MIT +babylon,6.17.4,MIT backo2,1.0.2,MIT -balanced-match,0.4.2,MIT +balanced-match,1.0.0,MIT base32,0.3.2,MIT base64-arraybuffer,0.1.5,MIT -base64-js,1.2.0,MIT +base64-js,1.2.1,MIT base64id,1.0.0,MIT -batch,0.5.3,MIT +batch,0.6.1,MIT bcrypt,3.1.11,MIT bcrypt-pbkdf,1.0.1,New BSD better-assert,1.0.2,MIT @@ -150,24 +152,28 @@ binary-extensions,1.8.0,MIT bindata,2.3.5,ruby blob,0.0.4,unknown block-stream,0.0.9,ISC -bluebird,3.4.7,MIT -bn.js,4.11.6,MIT +bluebird,3.5.0,MIT +bn.js,4.11.7,MIT body-parser,1.17.2,MIT +bonjour,3.5.0,MIT boom,2.10.1,New BSD -bootsnap,1.0.0,MIT +bootsnap,1.1.1,MIT bootstrap-sass,3.3.6,MIT -brace-expansion,1.1.6,MIT +bootstrap-sass,3.3.7,MIT +bootstrap_form,2.7.0,MIT +brace-expansion,1.1.8,MIT braces,1.8.5,MIT -brorand,1.0.7,MIT +brorand,1.1.0,MIT browser,2.2.0,MIT browserify-aes,1.0.6,MIT browserify-cipher,1.0.0,MIT browserify-des,1.0.0,MIT browserify-rsa,4.0.1,MIT -browserify-sign,4.0.0,ISC +browserify-sign,4.0.4,ISC browserify-zlib,0.1.4,MIT browserslist,1.7.7,MIT buffer,4.9.1,MIT +buffer-indexof,1.1.0,MIT buffer-shims,1.0.0,MIT buffer-xor,1.0.3,MIT builder,3.2.3,MIT @@ -178,29 +184,30 @@ caller-path,0.1.0,MIT callsite,1.0.0,unknown callsites,0.2.0,MIT camelcase,1.2.1,MIT +camelcase-keys,2.1.0,MIT caniuse-api,1.6.1,MIT -caniuse-db,1.0.30000649,CC-BY-4.0 -carrierwave,1.0.0,MIT -caseless,0.11.0,Apache 2.0 +caniuse-db,1.0.30000699,CC-BY-4.0 +carrierwave,1.1.0,MIT +caseless,0.12.0,Apache 2.0 cause,0.1,MIT center-align,0.1.3,MIT chalk,1.1.3,MIT charlock_holmes,0.7.3,MIT -chokidar,1.6.1,MIT +chokidar,1.7.0,MIT chronic,0.10.2,MIT chronic_duration,0.10.6,MIT chunky_png,1.3.5,MIT -cipher-base,1.0.3,MIT +cipher-base,1.0.4,MIT circular-json,0.3.1,MIT citrus,3.0.2,MIT -clap,1.1.3,MIT +clap,1.2.0,MIT cli-cursor,1.0.2,MIT cli-width,2.1.0,ISC -clipboard,1.6.1,MIT +clipboard,1.7.1,MIT cliui,2.1.0,ISC clone,1.0.2,MIT co,4.6.0,MIT -coa,1.0.1,MIT +coa,1.0.4,MIT code-point-at,1.1.0,MIT coercible,1.0.0,MIT coffee-rails,4.1.1,MIT @@ -214,13 +221,13 @@ colormin,1.1.2,MIT colors,1.1.2,MIT combine-lists,1.0.1,MIT combined-stream,1.0.5,MIT -commander,2.9.0,MIT +commander,2.11.0,MIT commondir,1.0.1,MIT component-bind,1.0.0,unknown component-emitter,1.2.1,MIT component-inherit,0.0.3,unknown -compressible,2.0.9,MIT -compression,1.6.2,MIT +compressible,2.0.10,MIT +compression,1.7.0,MIT compression-webpack-plugin,0.3.2,MIT concat-map,0.0.1,MIT concat-stream,1.6.0,MIT @@ -237,38 +244,40 @@ constants-browserify,1.0.0,MIT contains-path,0.1.0,MIT content-disposition,0.5.2,MIT content-type,1.0.2,MIT -convert-source-map,1.3.0,MIT +convert-source-map,1.5.0,MIT cookie,0.3.1,MIT cookie-signature,1.0.6,MIT core-js,2.4.1,MIT core-util-is,1.0.2,MIT -cosmiconfig,2.1.1,MIT +cosmiconfig,2.1.3,MIT crack,0.4.3,MIT create-ecdh,4.0.0,MIT -create-hash,1.1.2,MIT -create-hmac,1.1.4,MIT +create-hash,1.1.3,MIT +create-hmac,1.1.6,MIT creole,0.5.0,ruby cryptiles,2.0.5,New BSD crypto-browserify,3.11.0,MIT css-color-names,0.0.4,MIT -css-loader,0.28.0,MIT -css-selector-tokenizer,0.7.0,MIT -css_parser,1.4.1,MIT +css-loader,0.28.4,MIT +css-selector-tokenizer,"",unknown +css_parser,1.5.0,MIT cssesc,0.1.0,MIT cssnano,3.10.0,MIT csso,2.3.2,MIT +currently-unhandled,0.4.1,MIT custom-event,1.0.1,MIT -d,0.1.1,MIT -d3,3.5.11,New BSD +d,1.0.0,MIT +d3,3.5.17,New BSD d3_rails,3.5.11,MIT dashdash,1.14.1,MIT date-now,0.1.4,MIT de-indent,1.0.2,MIT -debug,2.6.0,MIT +debug,2.6.8,MIT debugger-ruby_core_source,1.3.8,MIT decamelize,1.2.0,MIT deckar01-task_list,2.0.0,MIT -deep-extend,0.4.1,MIT +deep-equal,1.0.1,MIT +deep-extend,0.4.2,MIT deep-is,0.1.3,MIT default-require-extensions,1.0.0,MIT default_value_for,3.0.2,MIT @@ -276,31 +285,35 @@ defaults,1.0.3,MIT defined,1.0.0,MIT del,2.2.2,MIT delayed-stream,1.0.0,MIT -delegate,3.1.2,MIT +delegate,3.1.3,MIT delegates,1.0.0,MIT depd,1.1.0,MIT des.js,1.0.0,MIT descendants_tracker,0.0.4,MIT destroy,1.0.4,MIT detect-indent,4.0.0,MIT +detect-node,2.0.3,ISC devise,4.2.0,MIT devise-two-factor,3.0.0,MIT di,0.0.1,MIT diff-lcs,1.2.5,"MIT,Perl Artistic v2,GNU GPL v2" diffie-hellman,5.0.2,MIT diffy,3.1.0,MIT -doctrine,1.5.0,BSD -document-register-element,1.3.0,MIT +dns-equal,1.0.0,MIT +dns-packet,1.1.1,MIT +dns-txt,2.0.2,MIT +doctrine,2.0.0,Apache 2.0 +document-register-element,1.5.0,MIT dom-serialize,2.2.1,MIT dom-serializer,0.1.0,MIT domain-browser,1.1.7,MIT domain_name,0.5.20161021,"Simplified BSD,New BSD,Mozilla Public License 2.0" domelementtype,1.3.0,unknown -domhandler,2.3.0,unknown -domutils,1.5.1,unknown +domhandler,2.4.1,Simplified BSD +domutils,1.6.2,Simplified BSD doorkeeper,4.2.0,MIT doorkeeper-openid_connect,1.1.2,MIT -dropzone,4.2.0,MIT +dropzone,4.3.0,MIT dropzonejs-rails,0.7.2,MIT duplexer,0.1.1,MIT duplexify,3.5.0,MIT @@ -308,8 +321,8 @@ ecc-jsbn,0.1.1,MIT editorconfig,0.13.2,MIT ee-first,1.1.1,MIT ejs,2.5.6,Apache 2.0 -electron-to-chromium,1.3.3,ISC -elliptic,6.3.3,MIT +electron-to-chromium,1.3.15,ISC +elliptic,6.4.0,MIT email_reply_trimmer,0.1.6,MIT emoji-unicode-version,0.2.1,MIT emojis-list,2.1.0,MIT @@ -319,44 +332,45 @@ end-of-stream,1.0.0,MIT engine.io,1.8.3,MIT engine.io-client,1.8.3,MIT engine.io-parser,1.3.2,MIT -enhanced-resolve,3.1.0,MIT +enhanced-resolve,3.3.0,MIT ent,2.2.0,MIT entities,1.1.1,BSD-like equalizer,0.0.11,MIT errno,0.1.4,MIT -error-ex,1.3.0,MIT +error-ex,1.3.1,MIT erubis,2.7.0,MIT -es5-ext,0.10.12,MIT -es6-iterator,2.0.0,MIT -es6-map,0.1.4,MIT +es5-ext,0.10.24,MIT +es6-iterator,2.0.1,MIT +es6-map,0.1.5,MIT es6-promise,3.0.2,MIT -es6-set,0.1.4,MIT -es6-symbol,3.1.0,MIT -es6-weak-map,2.0.1,MIT +es6-set,0.1.5,MIT +es6-symbol,3.1.1,MIT +es6-weak-map,2.0.2,MIT escape-html,1.0.3,MIT escape-string-regexp,1.0.5,MIT escape_utils,1.1.1,MIT escodegen,1.8.1,Simplified BSD escope,3.6.0,Simplified BSD -eslint,3.15.0,MIT +eslint,3.19.0,MIT eslint-config-airbnb-base,10.0.1,MIT -eslint-import-resolver-node,0.2.3,MIT -eslint-import-resolver-webpack,0.8.1,MIT -eslint-module-utils,2.0.0,MIT -eslint-plugin-filenames,1.1.0,MIT -eslint-plugin-html,2.0.1,ISC -eslint-plugin-import,2.2.0,MIT -eslint-plugin-jasmine,2.2.0,MIT +eslint-import-resolver-node,0.3.1,MIT +eslint-import-resolver-webpack,0.8.3,MIT +eslint-module-utils,2.1.1,MIT +eslint-plugin-filenames,1.2.0,MIT +eslint-plugin-html,2.0.3,ISC +eslint-plugin-import,2.7.0,MIT +eslint-plugin-jasmine,2.7.1,MIT eslint-plugin-promise,3.5.0,ISC -espree,3.4.0,Simplified BSD -esprima,3.1.3,Simplified BSD -esrecurse,4.1.0,Simplified BSD -estraverse,4.1.1,Simplified BSD +espree,3.4.3,Simplified BSD +esprima,2.7.3,Simplified BSD +esquery,1.0.0,BSD +esrecurse,4.2.0,Simplified BSD +estraverse,4.2.0,Simplified BSD esutils,2.0.2,BSD et-orbi,1.0.3,MIT etag,1.8.0,MIT eve-raphael,0.5.0,Apache 2.0 -event-emitter,0.3.4,MIT +event-emitter,0.3.5,MIT event-stream,3.3.4,MIT eventemitter3,1.2.0,MIT events,1.1.1,MIT @@ -371,13 +385,14 @@ expand-range,1.8.2,MIT exports-loader,0.6.4,MIT express,4.15.3,MIT expression_parser,0.9.0,MIT -extend,3.0.0,MIT +extend,3.0.1,MIT extglob,0.3.2,MIT extlib,0.9.16,MIT extsprintf,1.0.2,MIT -faraday,0.11.0,MIT +faraday,0.12.1,MIT faraday_middleware,0.11.0.1,MIT faraday_middleware-multi_json,0.0.6,MIT +fast-deep-equal,1.0.0,MIT fast-levenshtein,2.0.6,MIT fast_gettext,1.4.0,"MIT,ruby" fastparse,1.1.1,MIT @@ -385,15 +400,15 @@ faye-websocket,0.7.3,MIT ffi,1.9.10,BSD figures,1.7.0,MIT file-entry-cache,2.0.0,MIT -file-loader,0.11.1,MIT -filename-regex,2.0.0,MIT +file-loader,0.11.2,MIT +filename-regex,2.0.1,MIT fileset,2.0.3,MIT filesize,3.3.0,New BSD fill-range,2.2.3,MIT finalhandler,1.0.3,MIT find-cache-dir,0.1.1,MIT find-root,0.1.2,MIT -find-up,2.1.0,MIT +find-up,1.1.2,MIT flat-cache,1.2.2,MIT flatten,1.0.2,MIT flipper,0.10.2,MIT @@ -409,41 +424,43 @@ fog-openstack,0.1.6,MIT fog-rackspace,0.1.1,MIT fog-xml,0.1.3,MIT font-awesome-rails,4.7.0.1,"MIT,SIL Open Font License" -for-in,0.1.6,MIT -for-own,0.1.4,MIT +for-in,1.0.2,MIT +for-own,0.1.5,MIT forever-agent,0.6.1,Apache 2.0 -form-data,2.1.2,MIT +form-data,2.1.4,MIT formatador,0.2.5,MIT forwarded,0.1.0,MIT fresh,0.5.0,MIT from,0.1.7,MIT +fs-access,1.0.1,MIT fs.realpath,1.0.0,ISC -fsevents,,unknown -fstream,1.0.10,ISC +fsevents,1.1.2,MIT +fstream,1.0.11,ISC fstream-ignore,1.0.5,ISC function-bind,1.1.0,MIT -gauge,2.7.2,ISC +gauge,2.7.4,ISC gemnasium-gitlab-service,0.2.6,MIT gemojione,3.0.1,MIT generate-function,2.0.0,MIT generate-object-property,1.2.0,MIT get-caller-file,1.0.2,ISC +get-stdin,4.0.1,MIT get_process_mem,0.2.0,MIT -getpass,0.1.6,MIT +getpass,0.1.7,MIT gettext_i18n_rails,1.8.0,MIT gettext_i18n_rails_js,1.2.0,MIT -gitaly,0.8.0,MIT +gitaly,0.14.0,MIT github-linguist,4.7.6,MIT github-markup,1.4.0,MIT gitlab-flowdock-git-hook,1.0.1,MIT gitlab-grit,2.8.1,MIT gitlab-markup,1.5.1,MIT gitlab_omniauth-ldap,1.2.1,MIT -glob,7.1.1,ISC +glob,7.1.2,ISC glob-base,0.3.0,MIT glob-parent,2.0.0,ISC globalid,0.3.7,MIT -globals,9.14.0,MIT +globals,9.18.0,MIT globby,5.0.0,MIT gollum-grit_adapter,1.0.1,MIT gollum-lib,4.2.1,MIT @@ -455,32 +472,34 @@ google-protobuf,3.2.0.2,New BSD googleauth,0.5.1,Apache 2.0 got,3.3.1,MIT graceful-fs,4.1.11,ISC -graceful-readlink,1.0.1,MIT grape,0.19.1,MIT grape-entity,0.6.0,MIT -grpc,1.2.5,New BSD +grpc,1.4.0,New BSD gzip-size,3.0.0,MIT hamlit,2.6.1,MIT handle-thing,1.2.5,MIT -handlebars,4.0.6,MIT -har-validator,2.0.6,ISC +handlebars,4.0.10,MIT +har-schema,1.0.5,ISC +har-validator,4.2.1,ISC has,1.0.1,MIT has-ansi,2.0.0,MIT has-binary,0.1.7,MIT has-cors,1.1.0,MIT -has-flag,1.0.0,MIT +has-flag,2.0.0,MIT has-unicode,2.0.1,ISC +hash-base,2.0.2,MIT hash-sum,1.0.2,MIT -hash.js,1.0.3,MIT +hash.js,1.1.3,MIT hashie,3.5.5,MIT hashie-forbidden_attributes,0.1.1,MIT hawk,3.1.3,New BSD he,1.1.1,MIT health_check,2.6.0,MIT hipchat,1.5.2,MIT +hmac-drbg,1.0.1,MIT hoek,2.16.3,New BSD home-or-tmp,2.0.0,MIT -hosted-git-info,2.2.0,ISC +hosted-git-info,2.5.0,ISC hpack.js,2.1.6,MIT html-comment-regex,1.1.1,MIT html-entities,1.2.0,MIT @@ -503,12 +522,14 @@ https-browserify,0.0.1,MIT i18n,0.8.1,MIT ice_nine,0.11.2,MIT iconv-lite,0.4.15,MIT -icss-replace-symbols,1.0.2,ISC +icss-replace-symbols,1.1.0,ISC +icss-utils,2.1.0,ISC ieee754,1.1.8,New BSD -ignore,3.2.2,MIT +ignore,3.3.3,MIT ignore-by-default,1.0.1,ISC immediate,3.0.6,MIT imurmurhash,0.1.4,MIT +indent-string,2.1.0,MIT indexes-of,1.0.1,MIT indexof,0.0.1,unknown infinity-agent,2.0.3,MIT @@ -517,25 +538,28 @@ influxdb,0.2.3,MIT inherits,2.0.3,ISC ini,1.3.4,ISC inquirer,0.12.0,MIT -interpret,1.0.1,MIT +internal-ip,1.2.0,MIT +interpret,1.0.3,MIT invariant,2.2.2,New BSD invert-kv,1.0.0,MIT +ip,1.1.5,MIT ipaddr.js,1.3.0,MIT ipaddress,0.8.3,MIT is-absolute,0.2.6,MIT is-absolute-url,2.1.0,MIT is-arrayish,0.2.1,MIT is-binary-path,1.0.1,MIT -is-buffer,1.1.4,MIT +is-buffer,1.1.5,MIT is-builtin-module,1.0.0,MIT -is-dotfile,1.0.2,MIT +is-directory,0.3.1,MIT +is-dotfile,1.0.3,MIT is-equal-shallow,0.1.3,MIT is-extendable,0.1.1,MIT is-extglob,1.0.0,MIT is-finite,1.0.2,MIT is-fullwidth-code-point,1.0.0,MIT is-glob,2.0.1,MIT -is-my-json-valid,2.15.0,MIT +is-my-json-valid,2.16.0,MIT is-npm,1.0.0,MIT is-number,2.1.0,MIT is-path-cwd,1.0.0,MIT @@ -556,56 +580,58 @@ is-utf8,0.2.1,MIT is-windows,0.2.0,MIT isarray,1.0.0,MIT isbinaryfile,3.0.2,MIT -isexe,1.1.2,ISC +isexe,2.0.0,ISC isobject,2.1.0,MIT isstream,0.1.2,MIT istanbul,0.4.5,New BSD -istanbul-api,1.1.1,New BSD -istanbul-lib-coverage,1.0.1,New BSD -istanbul-lib-hook,1.0.0,New BSD -istanbul-lib-instrument,1.4.2,New BSD -istanbul-lib-report,1.0.0-alpha.3,New BSD -istanbul-lib-source-maps,1.1.0,New BSD -istanbul-reports,1.0.1,New BSD -jasmine-core,2.6.3,MIT +istanbul-api,1.1.10,New BSD +istanbul-lib-coverage,1.1.1,New BSD +istanbul-lib-hook,1.0.7,New BSD +istanbul-lib-instrument,1.7.3,New BSD +istanbul-lib-report,1.1.1,New BSD +istanbul-lib-source-maps,1.2.1,New BSD +istanbul-reports,1.1.1,New BSD +jasmine-core,2.6.4,MIT jasmine-jquery,2.1.1,MIT jed,1.1.1,MIT jira-ruby,1.1.2,MIT jodid25519,1.0.2,MIT -jquery,2.2.1,MIT +jquery,2.2.4,MIT jquery-atwho-rails,1.3.2,MIT jquery-rails,4.1.1,MIT -jquery-ujs,1.2.1,MIT +jquery-ujs,1.2.2,MIT js-base64,2.1.9,BSD -js-beautify,1.6.12,MIT -js-cookie,2.1.3,MIT -js-tokens,3.0.1,MIT -js-yaml,3.7.0,MIT -jsbn,0.1.0,BSD +js-beautify,1.6.14,MIT +js-cookie,2.1.4,MIT +js-tokens,3.0.2,MIT +js-yaml,"",unknown +jsbn,0.1.1,MIT jsesc,1.3.0,MIT json,1.8.6,ruby json-jwt,1.7.1,MIT json-loader,0.5.4,MIT json-schema,0.2.3,"AFLv2.1,BSD" +json-schema-traverse,0.3.1,MIT json-stable-stringify,1.0.1,MIT json-stringify-safe,5.0.1,ISC json3,3.3.2,MIT json5,0.5.1,MIT jsonify,0.0.0,Public Domain jsonpointer,4.0.1,MIT -jsprim,1.3.1,MIT +jsprim,1.4.0,MIT jszip,3.1.3,(MIT OR GPL-3.0) jszip-utils,0.0.2,MIT or GPLv3 jwt,1.5.6,MIT kaminari,0.17.0,MIT karma,1.7.0,MIT -karma-coverage-istanbul-reporter,0.2.0,MIT +karma-chrome-launcher,2.2.0,MIT +karma-coverage-istanbul-reporter,0.2.3,MIT karma-jasmine,1.1.0,MIT -karma-mocha-reporter,2.2.2,MIT +karma-mocha-reporter,2.2.3,MIT karma-sourcemap-loader,0.3.7,MIT -karma-webpack,2.0.2,MIT +karma-webpack,2.0.4,MIT kgio,2.10.0,LGPL-2.1+ -kind-of,3.1.0,MIT +kind-of,3.2.2,MIT kubeclient,2.2.0,MIT latest-version,1.0.1,MIT launchy,2.4.3,ISC @@ -617,7 +643,7 @@ lie,3.1.1,MIT little-plugger,1.1.4,MIT load-json-file,1.1.0,MIT loader-runner,2.3.0,MIT -loader-utils,0.2.16,MIT +loader-utils,"",unknown locale,2.1.2,"ruby,LGPLv3+" locate-path,2.0.0,MIT lodash,4.17.4,MIT @@ -631,65 +657,69 @@ lodash._isiterateecall,3.0.9,MIT lodash._topath,3.8.1,MIT lodash.assign,3.2.0,MIT lodash.camelcase,4.3.0,MIT -lodash.capitalize,4.2.1,MIT lodash.cond,4.5.2,MIT -lodash.deburr,4.1.0,MIT lodash.defaults,3.1.2,MIT -lodash.get,4.4.2,MIT +lodash.get,3.7.0,MIT lodash.isarguments,3.1.0,MIT lodash.isarray,3.0.4,MIT -lodash.kebabcase,4.0.1,MIT +lodash.kebabcase,4.1.1,MIT lodash.keys,3.1.2,MIT lodash.memoize,4.1.2,MIT lodash.restparam,3.6.1,MIT -lodash.snakecase,4.0.1,MIT +lodash.snakecase,4.1.1,MIT lodash.uniq,4.5.0,MIT -lodash.words,4.2.0,MIT +lodash.upperfirst,4.3.1,MIT log4js,0.6.38,Apache 2.0 logging,2.2.2,MIT longest,1.0.1,MIT loofah,2.0.3,MIT loose-envify,1.3.1,MIT +loud-rejection,1.6.0,MIT lowercase-keys,1.0.0,MIT lru-cache,3.2.0,ISC macaddress,0.2.8,MIT mail,2.6.5,MIT mail_room,0.9.1,MIT +map-obj,1.0.1,MIT map-stream,0.1.0,unknown marked,0.3.6,MIT -math-expression-evaluator,1.2.16,MIT +math-expression-evaluator,1.2.17,MIT media-typer,0.3.0,MIT memoist,0.15.0,MIT memory-fs,0.4.1,MIT +meow,3.7.0,MIT merge-descriptors,1.0.1,MIT method_source,0.8.2,MIT methods,1.1.2,MIT micromatch,2.3.11,MIT miller-rabin,4.0.0,MIT -mime,1.3.4,MIT +mime,1.3.6,MIT mime-db,1.27.0,MIT +mime-types,2.1.15,MIT mime-types,2.99.3,"MIT,Artistic-2.0,GPL-2.0" mimemagic,0.3.0,MIT mini_portile2,2.1.0,MIT minimalistic-assert,1.0.0,ISC -minimatch,3.0.3,ISC +minimalistic-crypto-utils,1.0.1,MIT +minimatch,3.0.4,ISC minimist,0.0.8,MIT mkdirp,0.5.1,MIT -mmap2,2.2.6,ruby -moment,2.17.1,MIT -mousetrap,1.4.6,Apache 2.0 +mmap2,2.2.7,ruby +moment,2.18.1,MIT +mousetrap,1.6.1,Apache 2.0 mousetrap-rails,1.4.6,"MIT,Apache" -ms,0.7.2,MIT +ms,2.0.0,MIT msgpack,1.1.0,Apache 2.0 multi_json,1.12.1,MIT multi_xml,0.6.0,MIT +multicast-dns,6.1.1,MIT +multicast-dns-service-types,1.1.0,MIT multipart-post,2.0.0,MIT mustermann,0.4.0,MIT mustermann-grape,0.4.0,MIT mute-stream,0.0.5,ISC -mysql2,0.3.20,MIT name-all-modules-plugin,1.0.1,MIT -nan,2.5.1,MIT +nan,2.6.2,MIT natural-compare,1.4.0,MIT negotiator,0.6.1,MIT nested-error-stacks,1.0.2,MIT @@ -697,23 +727,25 @@ net-ldap,0.12.1,MIT net-ssh,3.0.1,MIT netrc,0.11.0,MIT node-ensure,0.0.0,MIT +node-forge,0.6.33,BSD node-libs-browser,2.0.0,MIT -node-pre-gyp,0.6.33,New BSD +node-pre-gyp,0.6.36,New BSD node-zopfli,2.0.2,MIT nodemon,1.11.0,MIT nokogiri,1.6.8.1,MIT -nopt,3.0.6,ISC -normalize-package-data,2.3.5,Simplified BSD -normalize-path,2.0.1,MIT +nopt,4.0.1,ISC +normalize-package-data,2.4.0,Simplified BSD +normalize-path,2.1.1,MIT normalize-range,0.1.2,MIT normalize-url,1.9.1,MIT -npmlog,4.0.2,ISC +npmlog,4.1.2,ISC +null-check,1.0.0,MIT num2fraction,1.2.2,MIT number-is-nan,1.0.1,MIT numerizer,0.1.1,MIT oauth,0.5.1,MIT oauth-sign,0.8.2,Apache 2.0 -oauth2,1.3.1,MIT +oauth2,1.4.0,MIT object-assign,4.1.1,MIT object-component,0.0.3,unknown object.omit,2.0.1,MIT @@ -740,7 +772,7 @@ omniauth-twitter,1.2.1,MIT omniauth_crowd,2.2.3,MIT on-finished,2.3.0,MIT on-headers,1.0.1,MIT -once,1.3.3,ISC +once,1.4.0,ISC onetime,1.1.0,MIT opener,1.4.3,(WTFPL OR MIT) opn,4.0.2,MIT @@ -758,10 +790,11 @@ os-tmpdir,1.0.2,MIT osenv,0.1.4,ISC p-limit,1.1.0,MIT p-locate,2.0.0,MIT +p-map,1.1.1,MIT package-json,1.2.0,MIT pako,1.0.5,(MIT AND Zlib) -paranoia,2.2.0,MIT -parse-asn1,5.0.0,ISC +paranoia,2.3.1,MIT +parse-asn1,5.1.0,ISC parse-glob,3.0.4,MIT parse-json,2.2.0,MIT parsejson,0.0.3,MIT @@ -769,36 +802,35 @@ parseqs,0.0.5,MIT parseuri,0.0.5,MIT parseurl,1.3.1,MIT path-browserify,0.0.0,MIT -path-exists,3.0.0,MIT +path-exists,2.1.0,MIT path-is-absolute,1.0.1,MIT path-is-inside,1.0.2,(WTFPL OR MIT) path-parse,1.0.5,MIT path-to-regexp,0.1.7,MIT path-type,1.1.0,MIT pause-stream,0.0.11,"MIT,Apache2" -pbkdf2,3.0.9,MIT -pdfjs-dist,1.8.252,Apache 2.0 +pbkdf2,3.0.12,MIT +pdfjs-dist,1.8.527,Apache 2.0 peek,1.0.1,MIT peek-gc,0.0.2,MIT peek-host,1.0.0,MIT -peek-mysql2,1.1.0,MIT peek-performance_bar,1.2.1,MIT peek-pg,1.3.0,MIT peek-rblineprof,0.2.0,MIT peek-redis,1.2.0,MIT peek-sidekiq,1.0.3,MIT +performance-now,0.2.0,MIT pg,0.18.4,"BSD,ruby,GPL" pify,2.3.0,MIT -pikaday,1.5.1,"BSD,MIT" +pikaday,1.6.1,(0BSD OR MIT) pinkie,2.0.4,MIT pinkie-promise,2.0.1,MIT pkg-dir,1.0.0,MIT -pkg-up,1.0.0,MIT pluralize,1.2.1,MIT po_to_json,1.0.1,MIT portfinder,1.0.13,MIT posix-spawn,0.3.11,"MIT,LGPL" -postcss,5.2.16,MIT +postcss,5.2.17,MIT postcss-calc,5.3.1,MIT postcss-colormin,2.2.2,MIT postcss-convert-values,2.6.1,MIT @@ -819,10 +851,10 @@ postcss-minify-font-values,1.0.5,MIT postcss-minify-gradients,1.0.5,MIT postcss-minify-params,1.2.2,MIT postcss-minify-selectors,2.1.1,MIT -postcss-modules-extract-imports,1.0.1,ISC -postcss-modules-local-by-default,1.1.1,MIT -postcss-modules-scope,1.0.2,ISC -postcss-modules-values,1.2.2,ISC +postcss-modules-extract-imports,1.1.0,ISC +postcss-modules-local-by-default,1.2.0,MIT +postcss-modules-scope,1.1.0,ISC +postcss-modules-values,1.3.0,ISC postcss-normalize-charset,1.1.1,MIT postcss-normalize-url,3.0.8,MIT postcss-ordered-values,2.2.3,MIT @@ -835,16 +867,16 @@ postcss-unique-selectors,2.0.2,MIT postcss-value-parser,3.3.0,MIT postcss-zindex,2.2.0,MIT prelude-ls,1.1.2,MIT -premailer,1.8.6,New BSD -premailer-rails,1.9.2,MIT +premailer,1.10.4,New BSD +premailer-rails,1.9.7,MIT prepend-http,1.0.4,MIT preserve,0.2.0,MIT prismjs,1.6.0,MIT private,0.1.7,MIT -process,0.11.9,MIT +process,0.11.10,MIT process-nextick-args,1.0.7,MIT progress,1.1.8,MIT -prometheus-client-mmap,0.7.0.beta5,Apache 2.0 +prometheus-client-mmap,0.7.0.beta8,Apache 2.0 proto-list,1.2.4,ISC proxy-addr,1.1.4,MIT prr,0.0.0,MIT @@ -855,8 +887,8 @@ punycode,1.4.1,MIT pyu-ruby-sasl,0.0.3.3,MIT q,1.5.0,MIT qjobs,1.1.5,MIT -qs,6.3.0,New BSD -query-string,4.3.2,MIT +qs,6.4.0,New BSD +query-string,4.3.4,MIT querystring,0.2.0,MIT querystring-es3,0.2.1,MIT querystringify,0.0.4,MIT @@ -872,24 +904,25 @@ rails,4.2.8,MIT rails-deprecated_sanitizer,1.0.3,MIT rails-dom-testing,1.0.8,MIT rails-html-sanitizer,1.0.3,MIT +rails-i18n,4.0.9,MIT railties,4.2.8,MIT -rainbow,2.1.0,MIT -raindrops,0.17.0,LGPL-2.1+ +rainbow,2.2.2,MIT +raindrops,0.18.0,LGPL-2.1+ rake,10.5.0,MIT -randomatic,1.1.6,MIT -randombytes,2.0.3,MIT +randomatic,1.1.7,MIT +randombytes,2.0.5,MIT range-parser,1.2.0,MIT raphael,2.2.7,MIT -raven-js,3.14.0,Simplified BSD +raven-js,3.16.1,Simplified BSD raw-body,2.2.0,MIT raw-loader,0.5.1,MIT -rc,1.1.6,(BSD-2-Clause OR MIT OR Apache-2.0) +rc,1.2.1,(BSD-2-Clause OR MIT OR Apache-2.0) rdoc,4.2.2,ruby react-dev-utils,0.5.2,New BSD read-all-stream,3.1.0,MIT read-pkg,1.1.0,MIT read-pkg-up,1.0.1,MIT -readable-stream,2.2.2,MIT +readable-stream,2.3.3,MIT readdirp,2.1.0,MIT readline2,1.0.1,MIT recaptcha,3.0.0,MIT @@ -897,6 +930,7 @@ rechoir,0.6.2,MIT recursive-open-struct,1.0.0,MIT recursive-readdir,2.1.1,MIT redcarpet,3.4.0,MIT +redent,1.0.0,MIT redis,3.3.3,MIT redis-actionpack,5.0.1,MIT redis-activesupport,5.0.1,MIT @@ -907,33 +941,34 @@ redis-store,1.2.0,MIT reduce-css-calc,1.3.0,MIT reduce-function-call,1.0.2,MIT regenerate,1.3.2,MIT -regenerator-runtime,0.10.1,MIT -regenerator-transform,0.9.8,BSD +regenerator-runtime,0.10.5,MIT +regenerator-transform,0.9.11,BSD regex-cache,0.4.3,MIT -regexpu-core,2.0.0,MIT +regexpu-core,"",unknown registry-url,3.1.0,MIT regjsgen,0.2.0,MIT regjsparser,0.1.5,BSD +remove-trailing-separator,1.0.2,ISC repeat-element,1.1.2,MIT repeat-string,1.6.1,MIT repeating,2.0.1,MIT -request,2.79.0,Apache 2.0 +request,2.81.0,Apache 2.0 request_store,1.3.1,MIT require-directory,2.1.1,MIT require-from-string,1.2.1,MIT require-main-filename,1.0.1,ISC require-uncached,1.0.3,MIT requires-port,1.0.0,MIT -resolve,1.2.0,MIT +resolve,1.3.3,MIT resolve-from,1.0.1,MIT responders,2.3.0,MIT rest-client,2.0.0,MIT restore-cursor,1.0.1,MIT retriable,1.4.1,MIT right-align,0.1.3,MIT -rimraf,2.5.4,ISC +rimraf,2.6.1,ISC rinku,2.0.0,ISC -ripemd160,1.0.1,New BSD +ripemd160,2.0.1,MIT rotp,2.1.2,MIT rouge,2.1.0,MIT rqrcode,0.7.0,MIT @@ -941,40 +976,42 @@ rqrcode-rails3,0.1.7,MIT ruby-fogbugz,0.2.1,MIT ruby-prof,0.16.2,Simplified BSD ruby-saml,1.4.1,MIT -ruby_parser,3.8.4,MIT +ruby_parser,3.9.0,MIT rubyntlm,0.5.2,MIT rubypants,0.2.0,BSD rufus-scheduler,3.4.0,MIT rugged,0.25.1.1,MIT run-async,0.1.0,MIT rx-lite,3.1.2,Apache 2.0 -safe-buffer,5.0.1,MIT +safe-buffer,5.1.1,MIT safe_yaml,1.0.4,MIT sanitize,2.1.0,MIT sass,3.4.22,MIT sass-rails,5.0.6,MIT sawyer,0.8.1,MIT -sax,1.2.2,ISC +sax,1.2.4,ISC +schema-utils,0.3.0,MIT securecompare,1.0.0,MIT seed-fu,2.3.6,MIT select,1.1.2,MIT select-hose,2.0.0,MIT select2,3.5.2-browserify,unknown select2-rails,3.5.9.3,MIT +selfsigned,1.9.1,MIT semver,5.3.0,ISC semver-diff,2.1.0,MIT send,0.15.3,MIT -sentry-raven,2.4.0,Apache 2.0 -serve-index,1.8.0,MIT +sentry-raven,2.5.3,Apache 2.0 +serve-index,1.9.0,MIT serve-static,1.12.3,MIT set-blocking,2.0.0,ISC set-immediate-shim,1.0.1,MIT setimmediate,1.0.5,MIT setprototypeof,1.0.3,ISC settingslogic,2.0.9,MIT -sexp_processor,4.8.0,MIT +sexp_processor,4.9.0,MIT sha.js,2.4.8,MIT -shelljs,0.7.6,New BSD +shelljs,0.7.8,New BSD sidekiq,5.0.0,LGPL sidekiq-cron,0.6.0,MIT sidekiq-limit_fetch,3.4.0,MIT @@ -995,18 +1032,18 @@ sockjs-client,1.0.1,MIT sort-keys,1.1.2,MIT source-list-map,0.1.8,MIT source-map,0.5.6,New BSD -source-map-support,0.4.11,MIT +source-map-support,0.4.15,MIT spdx-correct,1.0.2,Apache 2.0 spdx-expression-parse,1.0.4,(MIT AND CC-BY-3.0) spdx-license-ids,1.2.2,Unlicense -spdy,3.4.4,MIT -spdy-transport,2.0.18,MIT +spdy,3.4.7,MIT +spdy-transport,2.0.20,MIT split,0.3.3,MIT sprintf-js,1.0.3,New BSD sprockets,3.7.1,MIT sprockets-rails,3.2.0,MIT sql.js,0.4.0,MIT -sshpk,1.10.2,MIT +sshpk,1.13.1,MIT state_machines,0.4.0,MIT state_machines-activemodel,0.4.0,MIT state_machines-activerecord,0.4.0,MIT @@ -1014,7 +1051,7 @@ stats-webpack-plugin,0.4.3,MIT statuses,1.3.1,MIT stream-browserify,2.0.1,MIT stream-combiner,0.0.4,MIT -stream-http,2.6.3,MIT +stream-http,2.7.2,MIT stream-shift,1.0.0,MIT strict-uri-encode,1.1.0,MIT string-length,1.0.1,MIT @@ -1024,44 +1061,47 @@ stringex,2.5.2,MIT stringstream,0.0.5,MIT strip-ansi,3.0.1,MIT strip-bom,2.0.0,MIT -strip-json-comments,1.0.4,MIT -supports-color,0.2.0,MIT +strip-indent,1.0.1,MIT +strip-json-comments,2.0.1,MIT +supports-color,4.2.0,MIT svgo,0.7.2,MIT sys-filesystem,1.1.6,Artistic 2.0 table,3.8.3,New BSD tapable,0.2.6,MIT tar,2.2.1,ISC -tar-pack,3.3.0,Simplified BSD +tar-pack,3.4.0,Simplified BSD temple,0.7.7,MIT -test-exclude,4.0.0,ISC +test-exclude,4.1.1,ISC text,1.3.1,MIT text-table,0.2.0,MIT thor,0.19.4,MIT thread_safe,0.3.6,Apache 2.0 three,0.84.0,MIT three-orbit-controls,82.1.0,MIT -three-stl-loader,1.0.4,MIT +three-stl-loader,1.0.5,MIT through,2.3.8,MIT +thunky,0.1.0,unknown tilt,2.0.6,MIT timeago.js,2.0.5,MIT timed-out,2.0.0,MIT timers-browserify,2.0.2,MIT timfel-krb5-auth,0.8.3,LGPL -tiny-emitter,1.1.0,MIT +tiny-emitter,2.0.1,MIT tmp,0.0.31,MIT to-array,0.1.4,MIT to-arraybuffer,1.0.1,MIT -to-fast-properties,1.0.2,MIT +to-fast-properties,1.0.3,MIT toml-rb,0.3.15,MIT tool,0.2.3,MIT touch,1.0.0,ISC tough-cookie,2.3.2,New BSD traverse,0.6.6,MIT +trim-newlines,1.0.0,MIT trim-right,1.0.1,MIT truncato,0.7.8,MIT tryit,1.0.3,MIT tty-browserify,0.0.0,MIT -tunnel-agent,0.4.3,Apache 2.0 +tunnel-agent,0.6.0,Apache 2.0 tweetnacl,0.14.5,Unlicense type-check,0.3.2,MIT type-is,1.6.15,MIT @@ -1069,7 +1109,7 @@ typedarray,0.0.6,MIT tzinfo,1.2.2,MIT u2f,0.2.1,MIT uglifier,2.7.2,MIT -uglify-js,2.8.27,Simplified BSD +uglify-js,2.8.29,Simplified BSD uglify-to-browserify,1.0.2,MIT uid-number,0.0.6,ISC ultron,1.1.0,MIT @@ -1087,15 +1127,15 @@ uniqs,2.0.0,MIT unpipe,1.0.0,MIT update-notifier,0.5.0,Simplified BSD url,0.11.0,MIT -url-loader,0.5.8,MIT +url-loader,0.5.9,MIT url-parse,1.0.5,MIT url_safe_base64,0.2.2,MIT user-home,2.0.0,MIT -useragent,2.1.13,MIT +useragent,2.2.0,MIT util,0.10.3,MIT util-deprecate,1.0.2,MIT utils-merge,1.0.0,MIT -uuid,3.0.1,MIT +uuid,3.1.0,MIT validate-npm-package-license,3.0.1,Apache 2.0 validates_hostname,1.0.6,MIT vary,1.1.1,MIT @@ -1107,36 +1147,36 @@ visibilityjs,1.2.4,MIT vm-browserify,0.0.4,MIT vmstat,2.3.0,MIT void-elements,2.0.1,MIT -vue,2.2.6,MIT -vue-hot-reload-api,2.0.11,MIT +vue,2.3.4,MIT +vue-hot-reload-api,2.1.0,MIT vue-loader,11.3.4,MIT vue-resource,0.9.3,MIT vue-style-loader,2.0.5,MIT -vue-template-compiler,2.2.6,MIT -vue-template-es2015-compiler,1.5.1,MIT +vue-template-compiler,2.3.4,MIT +vue-template-es2015-compiler,1.5.3,MIT warden,1.2.6,MIT watchpack,1.3.1,MIT wbuf,1.7.2,MIT webpack,2.6.1,MIT webpack-bundle-analyzer,2.8.2,MIT -webpack-dev-middleware,1.10.0,MIT -webpack-dev-server,2.4.2,MIT +webpack-dev-middleware,1.11.0,MIT +webpack-dev-server,2.5.1,MIT webpack-rails,0.9.10,MIT -webpack-sources,0.1.4,MIT +webpack-sources,0.1.5,MIT websocket-driver,0.6.5,MIT websocket-extensions,0.1.1,MIT whet.extend,0.9.9,MIT -which,1.2.12,ISC +which,1.2.14,ISC which-module,1.0.0,ISC -wide-align,1.1.0,ISC +wide-align,1.1.2,ISC wikicloth,0.8.1,MIT window-size,0.1.0,MIT wordwrap,0.0.2,MIT/X11 -worker-loader,0.8.0,MIT +worker-loader,0.8.1,MIT wrap-ansi,2.1.0,MIT wrappy,1.0.2,ISC write,0.2.1,MIT -write-file-atomic,1.3.1,ISC +write-file-atomic,1.3.4,ISC ws,2.3.1,MIT wtf-8,1.0.0,MIT xdg-basedir,2.0.0,MIT diff --git a/yarn.lock b/yarn.lock index b04eebe60af..98da6a984d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1575,6 +1575,12 @@ deckar01-task_list@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/deckar01-task_list/-/deckar01-task_list-2.0.0.tgz#7f7a595430d21b3036ed5dfbf97d6b65de18e2c9" +decompress-response@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + dependencies: + mimic-response "^1.0.0" + deep-extend@~0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" @@ -1712,6 +1718,10 @@ dropzone@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-4.2.0.tgz#fbe7acbb9918e0706489072ef663effeef8a79f3" +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + duplexer@^0.1.1, duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -2445,6 +2455,10 @@ get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + getpass@^0.1.1: version "0.1.6" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" @@ -2521,6 +2535,25 @@ got@^3.2.0: read-all-stream "^3.0.0" timed-out "^2.0.0" +got@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + graceful-fs@^4.1.11, graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -2578,6 +2611,16 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" +has-symbol-support-x@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.3.0.tgz#588bd6927eaa0e296afae24160659167fc2be4f8" + +has-to-string-tag-x@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.3.0.tgz#78e3d98c3c0ec9413e970eb8d766249a1e13058f" + dependencies: + has-symbol-support-x "^1.3.0" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -2902,6 +2945,10 @@ is-number@^2.0.2, is-number@^2.1.0: dependencies: kind-of "^3.0.2" +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -2918,7 +2965,7 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" -is-plain-obj@^1.0.0: +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" @@ -2950,6 +2997,10 @@ is-resolvable@^1.0.0: dependencies: tryit "^1.0.1" +is-retry-allowed@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + is-stream@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -3087,6 +3138,13 @@ istanbul@^0.4.5: which "^1.1.1" wordwrap "^1.0.0" +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + jasmine-core@^2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.6.3.tgz#45072950e4a42b1e322fe55c001100a465d77815" @@ -3633,6 +3691,10 @@ mime@1.3.4, mime@1.3.x, mime@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" +mimic-response@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" + minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" @@ -3981,6 +4043,14 @@ osenv@^0.1.0: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + p-limit@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" @@ -3991,6 +4061,12 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +p-timeout@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.0.tgz#9820f99434c5817868b4f34809ee5291660d5b6c" + dependencies: + p-finally "^1.0.0" + package-json@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0" @@ -4419,7 +4495,7 @@ prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" -prepend-http@^1.0.0: +prepend-http@^1.0.0, prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" @@ -5384,6 +5460,10 @@ timed-out@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a" +timed-out@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + timers-browserify@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" @@ -5545,6 +5625,12 @@ url-loader@^0.5.8: loader-utils "^1.0.2" mime "1.3.x" +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + dependencies: + prepend-http "^1.0.1" + url-parse@1.0.x: version "1.0.5" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" @@ -5559,6 +5645,10 @@ url-parse@^1.0.1, url-parse@^1.1.1: querystringify "0.0.x" requires-port "1.0.x" +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" @@ -5657,9 +5747,11 @@ vue-loader@^11.3.4: vue-style-loader "^2.0.0" vue-template-es2015-compiler "^1.2.2" -vue-resource@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-0.9.3.tgz#ab46e1c44ea219142dcc28ae4043b3b04c80959d" +vue-resource@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.3.4.tgz#9fc0bdf6a2f5cab430129fc99d347b3deae7b099" + dependencies: + got "^7.0.0" vue-style-loader@^2.0.0: version "2.0.5" |