Commit 87a6364f authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'master' into 4310-security-reports

* master: (135 commits)
  Merge branch '42361-workaround' into 'master'
  Update CHANGELOG.md for 10.4.1
  Update CHANGELOG-EE.md for 10.4.1-ee
  Add details on how to disable GitLab to the DR documentation
  Avoid array indices to fixtures in JS specs
  Work around a bug in DatabaseCleaner when using the deletion strategy on MySQL
  Use the DatabaseCleaner 'deletion' strategy instead of 'truncation'
  Fix a bug in spec/ee/spec/lib/gitlab/geo/file_transfer_spec.rb
  updating geo docs to show Geo HA as GA in 10.4
  Add CHANGELOG
  Fix OPENSSH_EXPECTED_COMMAND in the geo:check rake task
  Moves more mr widget components into vue files Adds i18n Adds better test coverage
  [EE] Remove one Spinach job and add one RSpec job
  Use limit for search count queries
  Execute group hooks after-commit when moving an issue
  Geo: sync .gitattributes to info/attributes in secondary nodes
  Add changelog entry
  Update tests to handle swapping of items
  Fix issue reordering for swapping of items
  EE port of groups-webpack-bundle
  ...
parents 0e6098e8 d3061333
...@@ -347,69 +347,69 @@ setup-test-env: ...@@ -347,69 +347,69 @@ setup-test-env:
rspec-pg geo: *rspec-metadata-pg-geo rspec-pg geo: *rspec-metadata-pg-geo
rspec-pg 0 26: *rspec-metadata-pg rspec-pg 0 27: *rspec-metadata-pg
rspec-pg 1 26: *rspec-metadata-pg rspec-pg 1 27: *rspec-metadata-pg
rspec-pg 2 26: *rspec-metadata-pg rspec-pg 2 27: *rspec-metadata-pg
rspec-pg 3 26: *rspec-metadata-pg rspec-pg 3 27: *rspec-metadata-pg
rspec-pg 4 26: *rspec-metadata-pg rspec-pg 4 27: *rspec-metadata-pg
rspec-pg 5 26: *rspec-metadata-pg rspec-pg 5 27: *rspec-metadata-pg
rspec-pg 6 26: *rspec-metadata-pg rspec-pg 6 27: *rspec-metadata-pg
rspec-pg 7 26: *rspec-metadata-pg rspec-pg 7 27: *rspec-metadata-pg
rspec-pg 8 26: *rspec-metadata-pg rspec-pg 8 27: *rspec-metadata-pg
rspec-pg 9 26: *rspec-metadata-pg rspec-pg 9 27: *rspec-metadata-pg
rspec-pg 10 26: *rspec-metadata-pg rspec-pg 10 27: *rspec-metadata-pg
rspec-pg 11 26: *rspec-metadata-pg rspec-pg 11 27: *rspec-metadata-pg
rspec-pg 12 26: *rspec-metadata-pg rspec-pg 12 27: *rspec-metadata-pg
rspec-pg 13 26: *rspec-metadata-pg rspec-pg 13 27: *rspec-metadata-pg
rspec-pg 14 26: *rspec-metadata-pg rspec-pg 14 27: *rspec-metadata-pg
rspec-pg 15 26: *rspec-metadata-pg rspec-pg 15 27: *rspec-metadata-pg
rspec-pg 16 26: *rspec-metadata-pg rspec-pg 16 27: *rspec-metadata-pg
rspec-pg 17 26: *rspec-metadata-pg rspec-pg 17 27: *rspec-metadata-pg
rspec-pg 18 26: *rspec-metadata-pg rspec-pg 18 27: *rspec-metadata-pg
rspec-pg 19 26: *rspec-metadata-pg rspec-pg 19 27: *rspec-metadata-pg
rspec-pg 20 26: *rspec-metadata-pg rspec-pg 20 27: *rspec-metadata-pg
rspec-pg 21 26: *rspec-metadata-pg rspec-pg 21 27: *rspec-metadata-pg
rspec-pg 22 26: *rspec-metadata-pg rspec-pg 22 27: *rspec-metadata-pg
rspec-pg 23 26: *rspec-metadata-pg rspec-pg 23 27: *rspec-metadata-pg
rspec-pg 24 26: *rspec-metadata-pg rspec-pg 24 27: *rspec-metadata-pg
rspec-pg 25 26: *rspec-metadata-pg rspec-pg 25 27: *rspec-metadata-pg
rspec-pg 26 27: *rspec-metadata-pg
rspec-mysql 0 26: *rspec-metadata-mysql
rspec-mysql 1 26: *rspec-metadata-mysql rspec-mysql 0 27: *rspec-metadata-mysql
rspec-mysql 2 26: *rspec-metadata-mysql rspec-mysql 1 27: *rspec-metadata-mysql
rspec-mysql 3 26: *rspec-metadata-mysql rspec-mysql 2 27: *rspec-metadata-mysql
rspec-mysql 4 26: *rspec-metadata-mysql rspec-mysql 3 27: *rspec-metadata-mysql
rspec-mysql 5 26: *rspec-metadata-mysql rspec-mysql 4 27: *rspec-metadata-mysql
rspec-mysql 6 26: *rspec-metadata-mysql rspec-mysql 5 27: *rspec-metadata-mysql
rspec-mysql 7 26: *rspec-metadata-mysql rspec-mysql 6 27: *rspec-metadata-mysql
rspec-mysql 8 26: *rspec-metadata-mysql rspec-mysql 7 27: *rspec-metadata-mysql
rspec-mysql 9 26: *rspec-metadata-mysql rspec-mysql 8 27: *rspec-metadata-mysql
rspec-mysql 10 26: *rspec-metadata-mysql rspec-mysql 9 27: *rspec-metadata-mysql
rspec-mysql 11 26: *rspec-metadata-mysql rspec-mysql 10 27: *rspec-metadata-mysql
rspec-mysql 12 26: *rspec-metadata-mysql rspec-mysql 11 27: *rspec-metadata-mysql
rspec-mysql 13 26: *rspec-metadata-mysql rspec-mysql 12 27: *rspec-metadata-mysql
rspec-mysql 14 26: *rspec-metadata-mysql rspec-mysql 13 27: *rspec-metadata-mysql
rspec-mysql 15 26: *rspec-metadata-mysql rspec-mysql 14 27: *rspec-metadata-mysql
rspec-mysql 16 26: *rspec-metadata-mysql rspec-mysql 15 27: *rspec-metadata-mysql
rspec-mysql 17 26: *rspec-metadata-mysql rspec-mysql 16 27: *rspec-metadata-mysql
rspec-mysql 18 26: *rspec-metadata-mysql rspec-mysql 17 27: *rspec-metadata-mysql
rspec-mysql 19 26: *rspec-metadata-mysql rspec-mysql 18 27: *rspec-metadata-mysql
rspec-mysql 20 26: *rspec-metadata-mysql rspec-mysql 19 27: *rspec-metadata-mysql
rspec-mysql 21 26: *rspec-metadata-mysql rspec-mysql 20 27: *rspec-metadata-mysql
rspec-mysql 22 26: *rspec-metadata-mysql rspec-mysql 21 27: *rspec-metadata-mysql
rspec-mysql 23 26: *rspec-metadata-mysql rspec-mysql 22 27: *rspec-metadata-mysql
rspec-mysql 24 26: *rspec-metadata-mysql rspec-mysql 23 27: *rspec-metadata-mysql
rspec-mysql 25 26: *rspec-metadata-mysql rspec-mysql 24 27: *rspec-metadata-mysql
rspec-mysql 25 27: *rspec-metadata-mysql
spinach-pg 0 4: *spinach-metadata-pg rspec-mysql 26 27: *rspec-metadata-mysql
spinach-pg 1 4: *spinach-metadata-pg
spinach-pg 2 4: *spinach-metadata-pg spinach-pg 0 3: *spinach-metadata-pg
spinach-pg 3 4: *spinach-metadata-pg spinach-pg 1 3: *spinach-metadata-pg
spinach-pg 2 3: *spinach-metadata-pg
spinach-mysql 0 4: *spinach-metadata-mysql
spinach-mysql 1 4: *spinach-metadata-mysql spinach-mysql 0 3: *spinach-metadata-mysql
spinach-mysql 2 4: *spinach-metadata-mysql spinach-mysql 1 3: *spinach-metadata-mysql
spinach-mysql 3 4: *spinach-metadata-mysql spinach-mysql 2 3: *spinach-metadata-mysql
# Static analysis jobs # Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis .ruby-static-analysis: &ruby-static-analysis
......
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
## 10.4.1 (2018-01-24)
### Fixed (1 change)
- Fix failed LDAP logins when sync_ssh_keys is included in config.
## 10.4.0 (2018-01-22) ## 10.4.0 (2018-01-22)
### Security (2 changes) ### Security (2 changes)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -422,7 +422,7 @@ group :ed25519 do ...@@ -422,7 +422,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.73.0', require: 'gitaly' gem 'gitaly-proto', '~> 0.74.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false gem 'toml-rb', '~> 0.3.15', require: false
......
...@@ -309,7 +309,7 @@ GEM ...@@ -309,7 +309,7 @@ GEM
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly-proto (0.73.0) gitaly-proto (0.74.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -1091,7 +1091,7 @@ DEPENDENCIES ...@@ -1091,7 +1091,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0) gettext_i18n_rails_js (~> 1.2.0)
gitaly-proto (~> 0.73.0) gitaly-proto (~> 0.74.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
......
...@@ -85,7 +85,8 @@ These types of merge requests for the upcoming release need special consideratio ...@@ -85,7 +85,8 @@ These types of merge requests for the upcoming release need special consideratio
and a dedicated team with front-end, back-end, and UX. and a dedicated team with front-end, back-end, and UX.
* **Small features**: any other feature request. * **Small features**: any other feature request.
**Large features** must be with a maintainer **by the 1st**. This means that: It is strongly recommended that **large features** be with a maintainer **by the
1st**. This means that:
* There is a merge request (even if it's WIP). * There is a merge request (even if it's WIP).
* The person (or people, if it needs a frontend and backend maintainer) who will * The person (or people, if it needs a frontend and backend maintainer) who will
...@@ -100,14 +101,37 @@ The maintainer can also choose to assign a reviewer to perform an initial ...@@ -100,14 +101,37 @@ The maintainer can also choose to assign a reviewer to perform an initial
review, but this way the maintainer is unlikely to be surprised by receiving an review, but this way the maintainer is unlikely to be surprised by receiving an
MR later in the cycle. MR later in the cycle.
**Small features** must be with a reviewer (not necessarily maintainer) **by the It is strongly recommended that **small features** be with a reviewer (not
3rd**. necessarily a maintainer) **by the 3rd**.
Most merge requests from the community do not have a specific release Most merge requests from the community do not have a specific release
target. However, if one does and falls into either of the above categories, it's target. However, if one does and falls into either of the above categories, it's
the reviewer's responsibility to manage the above communication and assignment the reviewer's responsibility to manage the above communication and assignment
on behalf of the community member. on behalf of the community member.
#### What happens if these deadlines are missed?
If a small or large feature is _not_ with a maintainer or reviewer by the
recommended date, this does _not_ mean that maintainers or reviewers will refuse
to review or merge it, or that the feature will definitely not make it in before
the feature freeze.
However, with every day that passes without review, it will become more likely
that the feature will slip, because maintainers and reviewers may not have
enough time to do a thorough review, and developers may not have enough time to
adequately address any feedback that may come back.
A maintainer or reviewer may also determine that it will not be possible to
finish the current scope of the feature in time, but that it is possible to
reduce the scope so that something can still ship this month, with the remaining
scope moving to the next release. The sooner this decision is made, in
conversation with the Product Manager and developer, the more time there is to
extract that which is now out of scope, and to finish that which remains in scope.
For these reasons, it is strongly recommended to follow the guidelines above,
to maximize the chances of your feature making it in before the feature freeze,
and to prevent any last minute surprises.
### On the 7th ### On the 7th
Merge requests should still be complete, following the Merge requests should still be complete, following the
......
10.4.0-pre 10.5.0-pre
<svg xmlns="http://www.w3.org/2000/svg" width="430" height="200" viewBox="0 0 430 200"><g fill="none" fill-rule="evenodd"><g transform="translate(138 65)"><path fill="#E5E5E5" fill-rule="nonzero" d="M35 70a2 2 0 1 1 0-4c2.542 0 5.042-.305 7.463-.904a2 2 0 1 1 .96 3.884A35.075 35.075 0 0 1 35 70zm18.21-5.105a2 2 0 1 1-2.083-3.414 31.143 31.143 0 0 0 5.896-4.664 2 2 0 1 1 2.842 2.815 35.143 35.143 0 0 1-6.654 5.263zM66.106 51.06a2 2 0 0 1-3.552-1.838 30.77 30.77 0 0 0 2.612-7.042 2 2 0 1 1 3.892.922 34.77 34.77 0 0 1-2.952 7.958zm3.816-18.433a2 2 0 1 1-3.991.268 30.873 30.873 0 0 0-1.407-7.38 2 2 0 0 1 3.808-1.223 34.873 34.873 0 0 1 1.59 8.335zm-6.346-17.842a2 2 0 0 1-3.264 2.312 31.188 31.188 0 0 0-5.054-5.564 2 2 0 0 1 2.615-3.027 35.188 35.188 0 0 1 5.703 6.279zM48.895 2.867a2 2 0 0 1-1.59 3.67 30.758 30.758 0 0 0-7.206-2.12 2 2 0 1 1 .653-3.946 34.758 34.758 0 0 1 8.143 2.396zM30.263.318a2 2 0 0 1 .537 3.964c-2.505.339-4.94.98-7.266 1.907a2 2 0 1 1-1.48-3.716A34.774 34.774 0 0 1 30.263.318zM12.907 7.853a2 2 0 0 1 2.527 3.1 31.196 31.196 0 0 0-5.213 5.416 2 2 0 0 1-3.196-2.406 35.196 35.196 0 0 1 5.882-6.11zM1.99 23.343a2 2 0 0 1 3.772 1.331 30.82 30.82 0 0 0-1.619 7.337 2 2 0 1 1-3.982-.38 34.82 34.82 0 0 1 1.829-8.289zM.719 42.086a2 2 0 1 1 3.917-.806 30.757 30.757 0 0 0 2.4 7.118 2 2 0 1 1-3.605 1.73 34.757 34.757 0 0 1-2.713-8.042zM9.393 58.86a2 2 0 0 1 2.926-2.728 31.167 31.167 0 0 0 5.751 4.841 2 2 0 1 1-2.187 3.349 35.167 35.167 0 0 1-6.49-5.462zm16.245 9.873a2 2 0 1 1 1.067-3.855 30.979 30.979 0 0 0 7.434 1.11 2 2 0 1 1-.11 3.998 34.979 34.979 0 0 1-8.391-1.253z"/><circle cx="35" cy="35" r="16" stroke="#E1DBF1" stroke-width="4"/><path fill="#6B4FBB" d="M37 33h5a2 2 0 1 1 0 4h-7a2 2 0 0 1-2-2v-8a2 2 0 1 1 4 0v6z"/></g><g transform="translate(247 30)"><rect width="116" height="135" y="5" fill="#F9F9F9" rx="10"/><rect width="116" height="134" x="5" fill="#FFF" stroke="#EEE" stroke-width="4" stroke-linecap="round" rx="10"/><g transform="translate(23 23)"><rect width="16" height="4" fill="#E1DBF1" rx="2"/><rect width="16" height="4" x="32" y="12" fill="#E1DBF1" rx="2"/><rect width="16" height="4" x="44" fill="#EEE" rx="2"/><rect width="16" height="4" x="12" y="24" fill="#E1DBF1" rx="2"/><rect width="16" height="4" x="64" y="36" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="20" fill="#FEE1D3" rx="2"/><rect width="8" height="4" x="32" y="36" fill="#FC6D26" rx="2"/><rect width="8" height="4" x="52" y="12" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="64" fill="#FEF0E8" rx="2"/><rect width="12" height="4" x="16" y="48" fill="#E1DBF1" rx="2"/><rect width="8" height="4" x="44" y="36" fill="#FC6D26" rx="2"/><rect width="4" height="4" x="56" y="36" fill="#E1DBF1" rx="2"/><rect width="4" height="4" x="64" y="60" fill="#E1DBF1" rx="2"/><rect width="4" height="4" x="72" y="60" fill="#FC6D26" rx="2"/><rect width="8" height="4" x="32" fill="#FC6D26" rx="2"/><rect width="28" height="4" y="36" fill="#EEE" rx="2"/><rect width="28" height="4" x="44" y="48" fill="#EEE" rx="2"/><rect width="28" height="4" x="32" y="60" fill="#EFEDF8" rx="2"/><rect width="28" height="4" y="12" fill="#6B4FBB" rx="2"/><rect width="28" height="4" x="32" y="24" fill="#C3B8E3" rx="2"/><rect width="8" height="4" y="24" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="32" y="48" fill="#6B4FBB" rx="2"/><rect width="12" height="4" y="48" fill="#FC6D26" rx="2"/><rect width="12" height="4" y="60" fill="#FEF0E8" rx="2"/><rect width="12" height="4" x="16" y="60" fill="#FEF0E8" rx="2"/></g><g transform="translate(23 95)"><rect width="16" height="4" fill="#EFEDF8" rx="2"/><rect width="16" height="4" x="18" y="12" fill="#FC6D26" rx="2"/><rect width="16" height="4" x="44" fill="#6B4FBB" rx="2"/><rect width="8" height="4" x="20" fill="#FEE1D3" rx="2"/><rect width="8" height="4" x="38" y="12" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="64" fill="#FEF0E8" rx="2"/><rect width="8" height="4" x="32" fill="#FC6D26" rx="2"/><rect width="14" height="4" y="12" fill="#EEE" rx="2"/></g></g><path fill="#FC6D26" fill-rule="nonzero" d="M81 119c-10.493 0-19-8.507-19-19s8.507-19 19-19 19 8.507 19 19-8.507 19-19 19zm0-4c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15-8.284 0-15 6.716-15 15 0 8.284 6.716 15 15 15zm-5-20a2 2 0 0 1 2 2v6a2 2 0 1 1-4 0v-6a2 2 0 0 1 2-2zm10 0a2 2 0 0 1 2 2v6a2 2 0 1 1-4 0v-6a2 2 0 0 1 2-2z"/><path fill="#E5E5E5" fill-rule="nonzero" d="M108 102c0-1.105.887-2 1.998-2h4.004c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4.004a1.994 1.994 0 0 1-1.998-2zm14 0c0-1.105.887-2 1.998-2h4.004c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4.004a1.994 1.994 0 0 1-1.998-2zm93 0c0-1.105.887-2 1.998-2h4.004c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4.004a1.994 1.994 0 0 1-1.998-2zm14 0c0-1.105.887-2 1.998-2h4.004c1.103 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4.004a1.994 1.994 0 0 1-1.998-2z"/></g></svg>
\ No newline at end of file
...@@ -299,6 +299,13 @@ const gfmRules = { ...@@ -299,6 +299,13 @@ const gfmRules = {
export class CopyAsGFM { export class CopyAsGFM {
constructor() { constructor() {
// iOS currently does not support clipboardData.setData(). This bug should
// be fixed in iOS 12, but for now we'll disable this for all iOS browsers
// ref: https://trac.webkit.org/changeset/222228/webkit
const userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || '';
const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent);
if (isIOS) return;
$(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); }); $(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); }); $(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
$(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM); $(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
......
...@@ -87,6 +87,7 @@ ...@@ -87,6 +87,7 @@
<div v-else-if="hasKeys"> <div v-else-if="hasKeys">
<keys-panel <keys-panel
title="Enabled deploy keys for this project" title="Enabled deploy keys for this project"
class="qa-project-deploy-keys"
:keys="keys.enabled_keys" :keys="keys.enabled_keys"
:store="store" :store="store"
:endpoint="endpoint" :endpoint="endpoint"
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
import Milestone from './milestone';
import notificationsDropdown from './notifications_dropdown'; import notificationsDropdown from './notifications_dropdown';
import LineHighlighter from './line_highlighter'; import LineHighlighter from './line_highlighter';
import MergeRequest from './merge_request'; import MergeRequest from './merge_request';
import initCompareAutocomplete from './compare_autocomplete';
import Sidebar from './right_sidebar';
import Flash from './flash'; import Flash from './flash';
import BlobViewer from './blob/viewer/index'; import BlobViewer from './blob/viewer/index';
import GfmAutoComplete from './gfm_auto_complete'; import GfmAutoComplete from './gfm_auto_complete';
...@@ -17,13 +14,13 @@ import { convertPermissionToBoolean } from './lib/utils/common_utils'; ...@@ -17,13 +14,13 @@ import { convertPermissionToBoolean } from './lib/utils/common_utils';
import GlFieldErrors from './gl_field_errors'; import GlFieldErrors from './gl_field_errors';
import Shortcuts from './shortcuts'; import Shortcuts from './shortcuts';
import ShortcutsIssuable from './shortcuts_issuable'; import ShortcutsIssuable from './shortcuts_issuable';
import U2FAuthenticate from './u2f/authenticate';
import Diff from './diff'; import Diff from './diff';
import SearchAutocomplete from './search_autocomplete'; import SearchAutocomplete from './search_autocomplete';
// EE-only // EE-only
import UsersSelect from './users_select'; import UsersSelect from './users_select';
import UserCallout from './user_callout'; import UserCallout from './user_callout';
import initCompareAutocomplete from './compare_autocomplete';
import initGeoInfoModal from 'ee/init_geo_info_modal'; // eslint-disable-line import/first import initGeoInfoModal from 'ee/init_geo_info_modal'; // eslint-disable-line import/first
import initGroupAnalytics from 'ee/init_group_analytics'; // eslint-disable-line import/first import initGroupAnalytics from 'ee/init_group_analytics'; // eslint-disable-line import/first
import initPathLocks from 'ee/path_locks'; // eslint-disable-line import/first import initPathLocks from 'ee/path_locks'; // eslint-disable-line import/first
...@@ -92,6 +89,11 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line ...@@ -92,6 +89,11 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
.catch(fail); .catch(fail);
shortcut_handler = true; shortcut_handler = true;
break; break;
case 'projects:environments:metrics':
import('./pages/projects/environments/metrics')
.then(callDefault)
.catch(fail);
break;
case 'projects:merge_requests:index': case 'projects:merge_requests:index':
import('./pages/projects/merge_requests/index') import('./pages/projects/merge_requests/index')
.then(callDefault) .then(callDefault)
...@@ -116,10 +118,15 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line ...@@ -116,10 +118,15 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
.catch(fail); .catch(fail);
break; break;
case 'projects:milestones:show': case 'projects:milestones:show':
import('./pages/projects/milestones/show')
.then(callDefault)
.catch(fail);
new UserCallout(); new UserCallout();
break;
case 'groups:milestones:show': case 'groups:milestones:show':
new Milestone(); import('./pages/groups/milestones/show')
new Sidebar(); .then(callDefault)
.catch(fail);
break; break;
case 'dashboard:milestones:show': case 'dashboard:milestones:show':
import('./pages/dashboard/milestones/show') import('./pages/dashboard/milestones/show')
...@@ -590,6 +597,10 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line ...@@ -590,6 +597,10 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
.then(callDefault) .then(callDefault)
.catch(fail); .catch(fail);
break; break;
case 'dashboard:groups:index':
import('./pages/dashboard/groups/index')
.then(callDefault)
.catch(fail);
case 'admin:licenses:new': case 'admin:licenses:new':
import(/* webpackChunkName: "admin_licenses" */ 'ee/pages/admin/licenses/new').then(m => m.default()).catch(fail); import(/* webpackChunkName: "admin_licenses" */ 'ee/pages/admin/licenses/new').then(m => m.default()).catch(fail);
break; break;
...@@ -602,18 +613,15 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line ...@@ -602,18 +613,15 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
} }
switch (path[0]) { switch (path[0]) {
case 'sessions': case 'sessions':
import('./pages/sessions')
.then(callDefault)
.catch(fail);
break;
case 'omniauth_callbacks': case 'omniauth_callbacks':
if (!gon.u2f) break; import('./pages/omniauth_callbacks')
const u2fAuthenticate = new U2FAuthenticate( .then(callDefault)
$('#js-authenticate-u2f'), .catch(fail);
'#js-login-u2f-form', break;
gon.u2f,
document.querySelector('#js-login-2fa-device'),
document.querySelector('.js-2fa-form'),
);
u2fAuthenticate.start();
// needed in rspec
gl.u2fAuthenticate = u2fAuthenticate;
case 'admin': case 'admin':
import('./pages/admin') import('./pages/admin')
.then(callDefault) .then(callDefault)
...@@ -672,10 +680,6 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line ...@@ -672,10 +680,6 @@ import initLDAPGroupsSelect from 'ee/ldap_groups_select'; // eslint-disable-line
break; break;
} }
break; break;
case 'dashboard':
case 'root':
new UserCallout();
break;
case 'profiles': case 'profiles':
import('./pages/profiles/index/') import('./pages/profiles/index/')
.then(callDefault) .then(callDefault)
......
...@@ -118,14 +118,14 @@ export const showSubLevelItems = (el) => { ...@@ -118,14 +118,14 @@ export const showSubLevelItems = (el) => {
moveSubItemsToPosition(el, subItems); moveSubItemsToPosition(el, subItems);
}; };
export const mouseEnterTopItems = (el) => { export const mouseEnterTopItems = (el, timeout = getHideSubItemsInterval()) => {
clearTimeout(timeoutId); clearTimeout(timeoutId);
timeoutId = setTimeout(() => { timeoutId = setTimeout(() => {
if (currentOpenMenu) hideMenu(currentOpenMenu); if (currentOpenMenu) hideMenu(currentOpenMenu);
showSubLevelItems(el); showSubLevelItems(el);
}, getHideSubItemsInterval()); }, timeout);
}; };
export const mouseLeaveTopItem = (el) => { export const mouseLeaveTopItem = (el) => {
......
...@@ -10,7 +10,7 @@ import groupItemComponent from './components/group_item.vue'; ...@@ -10,7 +10,7 @@ import groupItemComponent from './components/group_item.vue';
Vue.use(Translate); Vue.use(Translate);
document.addEventListener('DOMContentLoaded', () => { export default () => {
const el = document.getElementById('js-groups-tree'); const el = document.getElementById('js-groups-tree');
// Don't do anything if element doesn't exist (No groups) // Don't do anything if element doesn't exist (No groups)
...@@ -71,4 +71,4 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -71,4 +71,4 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
}, },
}); });
}); };
import Vue from 'vue'; import Vue from 'vue';
import VueResource from 'vue-resource'; import '../../vue_shared/vue_resource_interceptor';
Vue.use(VueResource);
export default class GroupsService { export default class GroupsService {
constructor(endpoint) { constructor(endpoint) {
......
...@@ -103,33 +103,18 @@ export default { ...@@ -103,33 +103,18 @@ export default {
toggleAddRelatedIssuesForm() { toggleAddRelatedIssuesForm() {
eventHub.$emit('toggleAddRelatedIssuesForm'); eventHub.$emit('toggleAddRelatedIssuesForm');
}, },
getBeforeAfterId(newIndex, lastIndex) { getBeforeAfterId(itemEl) {
let beforeId = null; const prevItemEl = itemEl.previousElementSibling;
let afterId = null; const nextItemEl = itemEl.nextElementSibling;
if (newIndex === 0) {
// newIndex is 0, item was moved to top => send only afterId
afterId = this.relatedIssues[newIndex].epic_issue_id;
} else if (newIndex === lastIndex) {
// newIndex is lastIndex, item was moved to bottom => send only beforeId
beforeId = this.relatedIssues[newIndex].epic_issue_id;
} else {
// leave default
beforeId = this.relatedIssues[newIndex - 1].epic_issue_id;
afterId = this.relatedIssues[newIndex].epic_issue_id;
}
return { return {
beforeId, beforeId: prevItemEl && parseInt(prevItemEl.dataset.epicIssueId, 0),
afterId, afterId: nextItemEl && parseInt(nextItemEl.dataset.epicIssueId, 0),
}; };
}, },
reordered(event) { reordered(event) {
this.removeDraggingCursor(); this.removeDraggingCursor();
const { const { beforeId, afterId } = this.getBeforeAfterId(event.item);
beforeId,
afterId,
} = this.getBeforeAfterId(event.newIndex, this.relatedIssues.length - 1);
this.$emit('saveReorder', { this.$emit('saveReorder', {
issueId: parseInt(event.item.dataset.key, 10), issueId: parseInt(event.item.dataset.key, 10),
...@@ -240,6 +225,7 @@ issue-count-badge-add-button btn btn-sm btn-default" ...@@ -240,6 +225,7 @@ issue-count-badge-add-button btn btn-sm btn-default"
card: canReorder card: canReorder
}" }"
:data-key="issue.id" :data-key="issue.id"
:data-epic-issue-id="issue.epic_issue_id"
> >
<issue-item <issue-item
event-namespace="relatedIssue" event-namespace="relatedIssue"
......
import Vue from 'vue'; import Vue from 'vue';
import Dashboard from './components/dashboard.vue'; import Dashboard from './components/dashboard.vue';
document.addEventListener('DOMContentLoaded', () => new Vue({ export default () => new Vue({
el: '#prometheus-graphs', el: '#prometheus-graphs',
render: createElement => createElement(Dashboard), render: createElement => createElement(Dashboard),
})); });
import initGroupsList from '../../../../groups';
export default () => {
initGroupsList();
};
import GroupsList from '~/groups_list'; import GroupsList from '~/groups_list';
import Landing from '~/landing'; import Landing from '~/landing';
import initGroupsList from '../../../groups';
export default function () { export default function () {
new GroupsList(); // eslint-disable-line no-new new GroupsList(); // eslint-disable-line no-new
initGroupsList();
const landingElement = document.querySelector('.js-explore-groups-landing'); const landingElement = document.querySelector('.js-explore-groups-landing');
if (!landingElement) return; if (!landingElement) return;
const exploreGroupsLanding = new Landing( const exploreGroupsLanding = new Landing(
......
import Activities from '~/activities'; import Activities from '~/activities';
export default new Activities(); export default () => new Activities();
import Labels from '~/labels'; import Labels from '~/labels';
export default new Labels(); export default () => new Labels();
import Labels from '~/labels'; import Labels from '~/labels';
export default new Labels(); export default () => new Labels();
import initMilestonesShow from '~/pages/init_milestones_show';
export default initMilestonesShow;
...@@ -5,6 +5,7 @@ import notificationsDropdown from '~/notifications_dropdown'; ...@@ -5,6 +5,7 @@ import notificationsDropdown from '~/notifications_dropdown';
import NotificationsForm from '~/notifications_form'; import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list'; import ProjectsList from '~/projects_list';
import ShortcutsNavigation from '~/shortcuts_navigation'; import ShortcutsNavigation from '~/shortcuts_navigation';
import initGroupsList from '../../../groups';
export default () => { export default () => {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup'); const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
...@@ -16,4 +17,6 @@ export default () => { ...@@ -16,4 +17,6 @@ export default () => {
if (newGroupChildWrapper) { if (newGroupChildWrapper) {
new NewGroupChild(newGroupChildWrapper); new NewGroupChild(newGroupChildWrapper);
} }
initGroupsList();
}; };
/* eslint-disable no-new */
import Milestone from '~/milestone';
import Sidebar from '~/right_sidebar';
export default () => {
new Milestone();
new Sidebar();
};
import initU2F from '../../shared/sessions/u2f';
export default () => {
initU2F();
};
import monitoringBundle from '~/monitoring/monitoring_bundle';
export default monitoringBundle;
import initMilestonesShow from '~/pages/init_milestones_show';
export default initMilestonesShow;
...@@ -210,7 +210,7 @@ ...@@ -210,7 +210,7 @@
</div> </div>
<span class="help-block">{{ visibilityLevelDescription }}</span> <span class="help-block">{{ visibilityLevelDescription }}</span>
<label <label
v-if="visibilityLevel !== visibilityOptions.PUBLIC" v-if="visibilityLevel !== visibilityOptions.PRIVATE"
class="request-access" class="request-access"
> >
<input <input
......
import initU2F from '../../shared/sessions/u2f';
export default () => {
initU2F();
};
import U2FAuthenticate from '../../u2f/authenticate';
export default () => {
if (!gon.u2f) return;
const u2fAuthenticate = new U2FAuthenticate(
$('#js-authenticate-u2f'),
'#js-login-u2f-form',
gon.u2f,
document.querySelector('#js-login-2fa-device'),
document.querySelector('.js-2fa-form'),
);
u2fAuthenticate.start();
// needed in rspec
gl.u2fAuthenticate = u2fAuthenticate;
};
...@@ -32,8 +32,8 @@ export default class IssuableTemplateSelector extends TemplateSelector { ...@@ -32,8 +32,8 @@ export default class IssuableTemplateSelector extends TemplateSelector {
this.startLoadingSpinner(); this.startLoadingSpinner();
Api.issueTemplate(this.namespacePath, this.projectPath, query.name, this.issuableType, (err, currentTemplate) => { Api.issueTemplate(this.namespacePath, this.projectPath, query.name, this.issuableType, (err, currentTemplate) => {
this.currentTemplate = currentTemplate; this.currentTemplate = currentTemplate;
if (err) return; // Error handled by global AJAX error handler
this.stopLoadingSpinner(); this.stopLoadingSpinner();
if (err) return; // Error handled by global AJAX error handler
this.setInputValueToTemplateContent(); this.setInputValueToTemplateContent();
}); });
return; return;
......
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetChecking',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body media">
<status-icon status="loading" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold">
Checking ability to merge automatically
</span>
</div>
</div>
`,
};
<script>
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetChecking',
components: {
statusIcon,
},
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon
status="loading"
:show-disabled-button="true"
/>
<div class="media-body space-children">
<span class="bold">
{{ s__("mrWidget|Checking ability to merge automatically") }}
</span>
</div>
</div>
</template>
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetClosed',
props: {
mr: { type: Object, required: true },
},
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
statusIcon,
},
template: `
<div class="mr-widget-body media">
<status-icon status="warning" />
<div class="media-body">
<mr-widget-author-and-time
actionText="Closed by"
:author="mr.metrics.closedBy"
:dateTitle="mr.metrics.closedAt"
:dateReadable="mr.metrics.readableClosedAt"
/>
<section class="mr-info-list">
<p>
The changes were not merged into
<a
:href="mr.targetBranchPath"
class="label-branch">
{{mr.targetBranch}}</a>
</p>
</section>
</div>
</div>
`,
};
<script>
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetClosed',
components: {
mrWidgetAuthorTime,
statusIcon,
},
props: {
/* TODO: This is providing all store and service down when it
only needs metrics and targetBranch */
mr: {
type: Object,
required: true,
default: () => ({}),
},
},
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon
status="warning"
/>
<div class="media-body">
<mr-widget-author-time
:action-text="s__('mrWidget|Closed by')"
:author="mr.metrics.closedBy"
:date-title="mr.metrics.closedAt"
:date-readable="mr.metrics.readableClosedAt"
/>
<section class="mr-info-list">
<p>
{{ s__("mrWidget|The changes were not merged into") }}
<a
:href="mr.targetBranchPath"
class="label-branch"
>
{{ mr.targetBranch }}
</a>
</p>
</section>
</div>
</div>
</template>
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetConflicts',
props: {
mr: { type: Object, required: true },
},
components: {
statusIcon,
},
template: `
<div class="mr-widget-body media">
<status-icon
status="warning"
:show-disabled-button="true" />
<div class="media-body space-children">
<span
v-if="mr.shouldBeRebased"
class="bold">
Fast-forward merge is not possible.
To merge this request, first rebase locally.
</span>
<template v-else>
<span class="bold">
There are merge conflicts<span v-if="!mr.canMerge">.</span>
<span v-if="!mr.canMerge">
Resolve these conflicts or ask someone with write access to this repository to merge it locally
</span>
</span>
<a
v-if="mr.canMerge && mr.conflictResolutionPath"
:href="mr.conflictResolutionPath"
class="js-resolve-conflicts-button btn btn-default btn-xs">
Resolve conflicts
</a>
<a
v-if="mr.canMerge"
class="js-merge-locally-button btn btn-default btn-xs"
data-toggle="modal"
href="#modal_merge_info">
Merge locally
</a>
</template>
</div>
</div>
`,
};
<script>
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetConflicts',
components: {
statusIcon,
},
props: {
/* TODO: This is providing all store and service down when it
only needs a few props */
mr: {
type: Object,
required: true,
default: () => ({}),
},
},
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon
status="warning"
:show-disabled-button="true"
/>
<div class="media-body space-children">
<span
v-if="mr.shouldBeRebased"
class="bold"
>
{{ s__(`mrWidget|Fast-forward merge is not possible.
To merge this request, first rebase locally.`) }}
</span>
<template v-else>
<span class="bold">
{{ s__("mrWidget|There are merge conflicts") }}<span v-if="!mr.canMerge">.</span>
<span v-if="!mr.canMerge">
{{ s__(`mrWidget|Resolve these conflicts or ask someone
with write access to this repository to merge it locally`) }}
</span>
</span>
<a
v-if="mr.canMerge && mr.conflictResolutionPath"
:href="mr.conflictResolutionPath"
class="js-resolve-conflicts-button btn btn-default btn-xs"
>
{{ s__("mrWidget|Resolve conflicts") }}
</a>
<button
v-if="mr.canMerge"
class="js-merge-locally-button btn btn-default btn-xs"
data-toggle="modal"
data-target="#modal_merge_info"
>
{{ s__("mrWidget|Merge locally") }}
</button>
</template>
</div>
</div>
</template>
...@@ -18,11 +18,11 @@ export { default as WidgetDeployment } from './components/mr_widget_deployment'; ...@@ -18,11 +18,11 @@ export { default as WidgetDeployment } from './components/mr_widget_deployment';
export { default as WidgetRelatedLinks } from './components/mr_widget_related_links'; export { default as WidgetRelatedLinks } from './components/mr_widget_related_links';
export { default as MergedState } from './components/states/mr_widget_merged'; export { default as MergedState } from './components/states/mr_widget_merged';
export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge'; export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge';
export { default as ClosedState } from './components/states/mr_widget_closed'; export { default as ClosedState } from './components/states/mr_widget_closed.vue';
export { default as MergingState } from './components/states/mr_widget_merging'; export { default as MergingState } from './components/states/mr_widget_merging';
export { default as WipState } from './components/states/mr_widget_wip'; export { default as WipState } from './components/states/mr_widget_wip';
export { default as ArchivedState } from './components/states/mr_widget_archived.vue'; export { default as ArchivedState } from './components/states/mr_widget_archived.vue';
export { default as ConflictsState } from './components/states/mr_widget_conflicts'; export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge'; export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge';
export { default as MissingBranchState } from './components/states/mr_widget_missing_branch'; export { default as MissingBranchState } from './components/states/mr_widget_missing_branch';
export { default as NotAllowedState } from './components/states/mr_widget_not_allowed'; export { default as NotAllowedState } from './components/states/mr_widget_not_allowed';
...@@ -34,7 +34,7 @@ export { default as PipelineFailedState } from './components/states/mr_widget_pi ...@@ -34,7 +34,7 @@ export { default as PipelineFailedState } from './components/states/mr_widget_pi
export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds'; export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds';
export { default as RebaseState } from './components/states/mr_widget_rebase.vue'; export { default as RebaseState } from './components/states/mr_widget_rebase.vue';
export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue'; export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue';
export { default as CheckingState } from './components/states/mr_widget_checking'; export { default as CheckingState } from './components/states/mr_widget_checking.vue';
export { default as MRWidgetStore } from 'ee/vue_merge_request_widget/stores/mr_widget_store'; export { default as MRWidgetStore } from 'ee/vue_merge_request_widget/stores/mr_widget_store';
export { default as MRWidgetService } from 'ee/vue_merge_request_widget/services/mr_widget_service'; export { default as MRWidgetService } from 'ee/vue_merge_request_widget/services/mr_widget_service';
export { default as eventHub } from './event_hub'; export { default as eventHub } from './event_hub';
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
width: 100%; width: 100%;
} }
$image-widths: 250 306 394; $image-widths: 250 306 394 430;
@each $width in $image-widths { @each $width in $image-widths {
&.svg-#{$width} { &.svg-#{$width} {
img, img,
......
...@@ -2,7 +2,11 @@ module GroupTree ...@@ -2,7 +2,11 @@ module GroupTree
# rubocop:disable Gitlab/ModuleWithInstanceVariables # rubocop:disable Gitlab/ModuleWithInstanceVariables
def render_group_tree(groups) def render_group_tree(groups)
@groups = if params[:filter].present? @groups = if params[:filter].present?
Gitlab::GroupHierarchy.new(groups.search(params[:filter])) # We find the ancestors by ID of the search results here.
# Otherwise the ancestors would also have filters applied,
# which would cause them not to be preloaded.
group_ids = groups.search(params[:filter]).select(:id)
Gitlab::GroupHierarchy.new(Group.where(id: group_ids))
.base_and_ancestors .base_and_ancestors
else else
# Only show root groups if no parent-id is given # Only show root groups if no parent-id is given
......
...@@ -27,12 +27,16 @@ class GroupDescendantsFinder ...@@ -27,12 +27,16 @@ class GroupDescendantsFinder
end end
def execute def execute
# The children array might be extended with the ancestors of projects when # The children array might be extended with the ancestors of projects and
# filtering. In that case, take the maximum so the array does not get limited # subgroups when filtering. In that case, take the maximum so the array does
# Otherwise, allow paginating through all results # not get limited otherwise, allow paginating through all results.
# #
all_required_elements = children all_required_elements = children
all_required_elements |= ancestors_for_projects if params[:filter] if params[:filter]
all_required_elements |= ancestors_of_filtered_subgroups
all_required_elements |= ancestors_of_filtered_projects
end
total_count = [all_required_elements.size, paginator.total_count].max total_count = [all_required_elements.size, paginator.total_count].max
Kaminari.paginate_array(all_required_elements, total_count: total_count) Kaminari.paginate_array(all_required_elements, total_count: total_count)
...@@ -49,8 +53,11 @@ class GroupDescendantsFinder ...@@ -49,8 +53,11 @@ class GroupDescendantsFinder
end end
def paginator def paginator
@paginator ||= Gitlab::MultiCollectionPaginator.new(subgroups, projects, @paginator ||= Gitlab::MultiCollectionPaginator.new(
per_page: params[:per_page]) subgroups,
projects.with_route,
per_page: params[:per_page]
)
end end
def direct_child_groups def direct_child_groups
...@@ -94,15 +101,21 @@ class GroupDescendantsFinder ...@@ -94,15 +101,21 @@ class GroupDescendantsFinder
# #
# So when searching 'project', on the 'subgroup' page we want to preload # So when searching 'project', on the 'subgroup' page we want to preload
# 'nested-group' but not 'subgroup' or 'root' # 'nested-group' but not 'subgroup' or 'root'
def ancestors_for_groups(base_for_ancestors) def ancestors_of_groups(base_for_ancestors)
Gitlab::GroupHierarchy.new(base_for_ancestors) group_ids = base_for_ancestors.except(:select, :sort).select(:id)
Gitlab::GroupHierarchy.new(Group.where(id: group_ids))
.base_and_ancestors(upto: parent_group.id) .base_and_ancestors(upto: parent_group.id)
end end
def ancestors_for_projects def ancestors_of_filtered_projects
projects_to_load_ancestors_of = projects.where.not(namespace: parent_group) projects_to_load_ancestors_of = projects.where.not(namespace: parent_group)
groups_to_load_ancestors_of = Group.where(id: projects_to_load_ancestors_of.select(:namespace_id)) groups_to_load_ancestors_of = Group.where(id: projects_to_load_ancestors_of.select(:namespace_id))
ancestors_for_groups(groups_to_load_ancestors_of) ancestors_of_groups(groups_to_load_ancestors_of)
.with_selects_for_list(archived: params[:archived])
end
def ancestors_of_filtered_subgroups
ancestors_of_groups(subgroups)
.with_selects_for_list(archived: params[:archived]) .with_selects_for_list(archived: params[:archived])
end end
...@@ -112,7 +125,7 @@ class GroupDescendantsFinder ...@@ -112,7 +125,7 @@ class GroupDescendantsFinder
# When filtering subgroups, we want to find all matches withing the tree of # When filtering subgroups, we want to find all matches withing the tree of
# descendants to show to the user # descendants to show to the user
groups = if params[:filter] groups = if params[:filter]
ancestors_for_groups(subgroups_matching_filter) subgroups_matching_filter
else else
direct_child_groups direct_child_groups
end end
...@@ -121,8 +134,10 @@ class GroupDescendantsFinder ...@@ -121,8 +134,10 @@ class GroupDescendantsFinder
end end
def direct_child_projects def direct_child_projects
GroupProjectsFinder.new(group: parent_group, current_user: current_user, params: params) GroupProjectsFinder.new(group: parent_group,
.execute current_user: current_user,
options: { only_owned: true },
params: params).execute
end end
# Finds all projects nested under `parent_group` or any of its descendant # Finds all projects nested under `parent_group` or any of its descendant
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# label_name: string # label_name: string
# sort: string # sort: string
# my_reaction_emoji: string # my_reaction_emoji: string
# public_only: boolean
# #
class IssuesFinder < IssuableFinder class IssuesFinder < IssuableFinder
CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER
...@@ -40,7 +41,15 @@ class IssuesFinder < IssuableFinder ...@@ -40,7 +41,15 @@ class IssuesFinder < IssuableFinder
private private
def init_collection def init_collection
with_confidentiality_access_check if public_only?
Issue.public_only
else
with_confidentiality_access_check
end
end
def public_only?
params.fetch(:public_only, false)
end end
def user_can_see_all_confidential_issues? def user_can_see_all_confidential_issues?
......
...@@ -180,4 +180,8 @@ module SearchHelper ...@@ -180,4 +180,8 @@ module SearchHelper
# Truncato's filtered_tags and filtered_attributes are not quite the same # Truncato's filtered_tags and filtered_attributes are not quite the same
sanitize(html, tags: %w(a p ol ul li pre code)) sanitize(html, tags: %w(a p ol ul li pre code))
end end
def limited_count(count, limit = 1000)
count > limit ? "#{limit}+" : count
end
end end
...@@ -1042,6 +1042,8 @@ class Project < ActiveRecord::Base ...@@ -1042,6 +1042,8 @@ class Project < ActiveRecord::Base
end end
def fork_source def fork_source
return nil unless forked?
forked_from_project || fork_network&.root_project forked_from_project || fork_network&.root_project
end end
......
...@@ -266,15 +266,7 @@ class Repository ...@@ -266,15 +266,7 @@ class Repository
return if kept_around?(sha) return if kept_around?(sha)
# This will still fail if the file is corrupted (e.g. 0 bytes) # This will still fail if the file is corrupted (e.g. 0 bytes)
begin raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
raw_repository.write_ref(keep_around_ref_name(sha), sha, shell: false)
rescue Rugged::ReferenceError => ex
Rails.logger.error "Unable to create #{REF_KEEP_AROUND} reference for repository #{path}: #{ex}"
rescue Rugged::OSError => ex
raise unless ex.message =~ /Failed to create locked file/ && ex.message =~ /File exists/
Rails.logger.error "Unable to create #{REF_KEEP_AROUND} reference for repository #{path}: #{ex}"
end
end end
def kept_around?(sha) def kept_around?(sha)
......
...@@ -331,6 +331,8 @@ class User < ActiveRecord::Base ...@@ -331,6 +331,8 @@ class User < ActiveRecord::Base
# #
# Returns an ActiveRecord::Relation. # Returns an ActiveRecord::Relation.
def search(query) def search(query)
return none if query.blank?
query = query.downcase query = query.downcase
order = <<~SQL order = <<~SQL
...@@ -354,6 +356,8 @@ class User < ActiveRecord::Base ...@@ -354,6 +356,8 @@ class User < ActiveRecord::Base
# This method uses ILIKE on PostgreSQL and LIKE on MySQL. # This method uses ILIKE on PostgreSQL and LIKE on MySQL.
def search_with_secondary_emails(query) def search_with_secondary_emails(query)
return none if query.blank?
query = query.downcase query = query.downcase
email_table = Email.arel_table email_table = Email.arel_table
......
...@@ -56,6 +56,9 @@ module MergeRequests ...@@ -56,6 +56,9 @@ module MergeRequests
end end
true true
rescue PushRule::MatchError => e
handle_merge_error(log_message: e.message, save_message_on_model: true)
false
end end
private private
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe = (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe
%li %li
= _("Specify the following URL during the Runner setup:") = _("Specify the following URL during the Runner setup:")
%code= root_url(only_path: false) %code#coordinator_address= root_url(only_path: false)
%li %li
= _("Use the following registration token during setup:") = _("Use the following registration token during setup:")
%code#registration_token= registration_token %code#registration_token= registration_token
......
...@@ -7,10 +7,8 @@ ...@@ -7,10 +7,8 @@
- page_title "Activity" - page_title "Activity"
- header_title "Activity", activity_dashboard_path - header_title "Activity", activity_dashboard_path
.hidden-xs
= render "projects/last_push"
%div{ class: container_class } %div{ class: container_class }
= render "projects/last_push"
= render 'dashboard/activity_head' = render 'dashboard/activity_head'
%section.activities %section.activities
......
.js-groups-list-holder .js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'true', endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } } #js-groups-tree{ data: { hide_projects: 'true', endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
.loading-container.text-center
= icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
- header_title "Groups", dashboard_groups_path - header_title "Groups", dashboard_groups_path
= render 'dashboard/groups_head' = render 'dashboard/groups_head'
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'groups'
- if params[:filter].blank? && @groups.empty? - if params[:filter].blank? && @groups.empty?
= render 'shared/groups/empty_state' = render 'shared/groups/empty_state'
- else - else
......
...@@ -7,9 +7,8 @@ ...@@ -7,9 +7,8 @@
- page_title "Projects" - page_title "Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
= render "projects/last_push"
%div{ class: container_class } %div{ class: container_class }
= render "projects/last_push"
- if show_projects?(@projects, params) - if show_projects?(@projects, params)
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
= render 'nav' = render 'nav'
......
...@@ -4,9 +4,8 @@ ...@@ -4,9 +4,8 @@
- page_title "Starred Projects" - page_title "Starred Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
= render "projects/last_push"
%div{ class: container_class } %div{ class: container_class }
= render "projects/last_push"
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
- if params[:filter_projects] || any_projects?(@projects) - if params[:filter_projects] || any_projects?(@projects)
......
.js-groups-list-holder .js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'true', endpoint: explore_groups_path(format: :json), path: explore_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } } #js-groups-tree{ data: { hide_projects: 'true', endpoint: explore_groups_path(format: :json), path: explore_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
.loading-container.text-center
= icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
- page_title "Groups" - page_title "Groups"
- header_title "Groups", dashboard_groups_path - header_title "Groups", dashboard_groups_path
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'groups'
- if current_user - if current_user
= render 'dashboard/groups_head' = render 'dashboard/groups_head'
- else - else
......
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'groups'
.js-groups-list-holder .js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'false', group_id: group.id, endpoint: group_children_path(group, format: :json), path: group_path(group), form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } } #js-groups-tree{ data: { hide_projects: 'false', group_id: group.id, endpoint: group_children_path(group, format: :json), path: group_path(group), form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
.loading-container.text-center
= icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
- event = last_push_event - event = last_push_event
- if event && show_last_push_widget?(event) - if event && show_last_push_widget?(event)
%div{ class: container_class } .row-content-block.top-block.hidden-xs.white
.row-content-block.top-block.hidden-xs.white .event-last-push
.event-last-push .event-last-push-text
.event-last-push-text %span= s_("LastPushEvent|You pushed to")
%span= s_("LastPushEvent|You pushed to") %strong
%strong = link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name'
= link_to event.ref_name, project_commits_path(event.project, event.ref_name), class: 'ref-name'
- if event.project != @project - if event.project != @project
%span= s_("LastPushEvent|at") %span= s_("LastPushEvent|at")
%strong= link_to_project event.project %strong= link_to_project event.project
#{time_ago_with_tooltip(event.created_at)} #{time_ago_with_tooltip(event.created_at)}
.pull-right .pull-right
= link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do
#{ _('Create merge request') } #{ _('Create merge request') }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
- page_title _("Activity") - page_title _("Activity")
= render 'projects/last_push' %div{ class: container_class }
= render 'projects/last_push'
= render 'projects/activity' = render 'projects/activity'
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= webpack_bundle_tag 'blob' = webpack_bundle_tag 'blob'
= render 'projects/last_push'
%div{ class: container_class } %div{ class: container_class }
= render 'projects/last_push'
#tree-holder.tree-holder #tree-holder.tree-holder
= render 'blob', blob: @blob = render 'blob', blob: @blob
......
xml.entry do xml.entry do
xml.id project_commit_url(@project, id: commit.id) xml.id project_commit_url(@project, id: commit.id)
xml.link href: project_commit_url(@project, id: commit.id) xml.link href: project_commit_url(@project, id: commit.id)
xml.title truncate(commit.title, length: 80) xml.title truncate(commit.title, length: 80, escape: false)
xml.updated commit.committed_date.xmlschema xml.updated commit.committed_date.xmlschema
xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email)) xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email))
...@@ -10,5 +10,5 @@ xml.entry do ...@@ -10,5 +10,5 @@ xml.entry do
xml.email commit.author_email xml.email commit.author_email
end end
xml.summary markdown(commit.description, pipeline: :single_line) xml.summary markdown(commit.description, pipeline: :single_line), type: 'html'
end end
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'common_d3' = webpack_bundle_tag 'common_d3'
= webpack_bundle_tag 'monitoring'
.prometheus-container{ class: container_class } .prometheus-container{ class: container_class }
.top-area .top-area
......
- illustration = local_assigns.fetch(:illustration) - illustration = local_assigns.fetch(:illustration)
- illustration_size = local_assigns.fetch(:illustration_size) - illustration_size = local_assigns.fetch(:illustration_size)
- title = local_assigns.fetch(:title) - title = local_assigns.fetch(:title)
- content = local_assigns.fetch(:content, nil) - content = local_assigns.fetch(:content)
- action = local_assigns.fetch(:action, nil) - action = local_assigns.fetch(:action, nil)
.row.empty-state .row.empty-state
...@@ -11,8 +11,7 @@ ...@@ -11,8 +11,7 @@
.col-xs-12 .col-xs-12
.text-content .text-content
%h4.text-center= title %h4.text-center= title
- if content %p= content
%p= content
- if action - if action
.text-center .text-center
= action = action
...@@ -97,12 +97,18 @@ ...@@ -97,12 +97,18 @@
title: _('This job requires a manual action'), title: _('This job requires a manual action'),
content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'), content: _('This job depends on a user to trigger its process. Often they are used to deploy code to production environments'),
action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') ) action: ( link_to _('Trigger this manual action'), play_project_job_path(@project, @build), method: :post, class: 'btn btn-primary', title: _('Trigger this manual action') )
- else - elsif @build.created?
= render 'empty_state', = render 'empty_state',
illustration: 'illustrations/job_not_triggered.svg', illustration: 'illustrations/job_not_triggered.svg',
illustration_size: 'svg-306', illustration_size: 'svg-306',
title: _('This job has not been triggered yet') title: _('This job has not been triggered yet'),
content: _('This job depends on upstream jobs that need to succeed in order for this job to be triggered')
- else
= render 'empty_state',
illustration: 'illustrations/pending_job_empty.svg',
illustration_size: 'svg-430',
title: _('This job has not started yet'),
content: _('This job is in pending state and is waiting to be picked by a runner')
= render "sidebar" = render "sidebar"
.js-build-options{ data: javascript_build_options } .js-build-options{ data: javascript_build_options }
......
...@@ -10,7 +10,8 @@ ...@@ -10,7 +10,8 @@
= webpack_bundle_tag 'common_vue' = webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'filtered_search' = webpack_bundle_tag 'filtered_search'
= render 'projects/last_push' %div{ class: container_class }
= render 'projects/last_push'
- if @project.merge_requests.exists? - if @project.merge_requests.exists?
%div{ class: container_class } %div{ class: container_class }
......
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
= render partial: 'flash_messages', locals: { project: @project } = render partial: 'flash_messages', locals: { project: @project }
= render "projects/last_push" %div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render "projects/last_push"
= render "home_panel" = render "home_panel"
- if can?(current_user, :download_code, @project) - if can?(current_user, :download_code, @project)
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
.add-to-tree-dropdown .add-to-tree-dropdown
%ul.dropdown-menu %ul.dropdown-menu
- if can_edit_tree? - if can_edit_tree?
%li.dropdown-header
#{ _('This directory') }
%li %li
= link_to project_new_blob_path(@project, @id) do = link_to project_new_blob_path(@project, @id) do
#{ _('New file') } #{ _('New file') }
...@@ -60,6 +62,8 @@ ...@@ -60,6 +62,8 @@
#{ _('New directory') } #{ _('New directory') }
%li.divider %li.divider
%li.dropdown-header
#{ _('This repository') }
%li %li
= link_to new_project_branch_path(@project) do = link_to new_project_branch_path(@project) do
#{ _('New branch') } #{ _('New branch') }
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits") = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, rss_url_options), title: "#{@project.name}:#{@ref} commits")
= render 'projects/last_push'
%div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] } %div{ class: [(container_class), ("limit-container-width" unless fluid_layout)] }
= render 'projects/last_push'
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id) = render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)
...@@ -63,35 +63,35 @@ ...@@ -63,35 +63,35 @@
= link_to search_filter_path(scope: 'projects') do = link_to search_filter_path(scope: 'projects') do
Projects Projects
%span.badge %span.badge
= @search_results.projects_count = limited_count(@search_results.limited_projects_count)
%li{ class: active_when(@scope == 'issues') } %li{ class: active_when(@scope == 'issues') }
= link_to search_filter_path(scope: 'issues') do = link_to search_filter_path(scope: 'issues') do
Issues Issues
%span.badge %span.badge
= @search_results.issues_count = limited_count(@search_results.limited_issues_count)
%li{ class: active_when(@scope == 'merge_requests') } %li{ class: active_when(@scope == 'merge_requests') }
= link_to search_filter_path(scope: 'merge_requests') do = link_to search_filter_path(scope: 'merge_requests') do
Merge requests Merge requests
%span.badge %span.badge
= @search_results.merge_requests_count = limited_count(@search_results.limited_merge_requests_count)
%li{ class: active_when(@scope == 'milestones') } %li{ class: active_when(@scope == 'milestones') }
= link_to search_filter_path(scope: 'milestones') do = link_to search_filter_path(scope: 'milestones') do
Milestones Milestones
%span.badge %span.badge
= @search_results.milestones_count = limited_count(@search_results.limited_milestones_count)
- if current_application_settings.elasticsearch_search? - if current_application_settings.elasticsearch_search?
%li{ class: active_when(@scope == 'blobs') } %li{ class: active_when(@scope == 'blobs') }
= link_to search_filter_path(scope: 'blobs') do = link_to search_filter_path(scope: 'blobs') do
Code Code
%span.badge %span.badge
= @search_results.blobs_count = limited_count(@search_results.blobs_count)
%li{ class: active_when(@scope == 'commits') } %li{ class: active_when(@scope == 'commits') }
= link_to search_filter_path(scope: 'commits') do = link_to search_filter_path(scope: 'commits') do
Commits Commits
%span.badge %span.badge
= @search_results.commits_count = limited_count(@search_results.commits_count)
%li{ class: active_when(@scope == 'wiki_blobs') } %li{ class: active_when(@scope == 'wiki_blobs') }
= link_to search_filter_path(scope: 'wiki_blobs') do = link_to search_filter_path(scope: 'wiki_blobs') do
Wiki Wiki
%span.badge %span.badge
= @search_results.wiki_blobs_count = limited_count(@search_results.wiki_blobs_count)
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
= render 'shared/promotions/promote_advanced_search' = render 'shared/promotions/promote_advanced_search'
- else - else
.row-content-block .row-content-block
= search_entries_info(@search_objects, @scope, @search_term) - unless @search_objects.is_a?(Kaminari::PaginatableWithoutCount)
= search_entries_info(@search_objects, @scope, @search_term)
- unless @show_snippets - unless @show_snippets
- if @project - if @project
in project #{link_to @project.name_with_namespace, [@project.namespace.becomes(Namespace), @project]} in project #{link_to @project.name_with_namespace, [@project.namespace.becomes(Namespace), @project]}
...@@ -23,4 +24,4 @@ ...@@ -23,4 +24,4 @@
= render partial: "search/results/#{@scope.singularize}", collection: @search_objects = render partial: "search/results/#{@scope.singularize}", collection: @search_objects
- if @scope != 'projects' - if @scope != 'projects'
= paginate(@search_objects, theme: 'gitlab') = paginate_collection(@search_objects)
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('group')
- parent = @group.parent - parent = @group.parent
- group_path = root_url - group_path = root_url
- group_path << parent.full_path + '/' if parent - group_path << parent.full_path + '/' if parent
......
#!/usr/bin/env ruby
require 'optparse'
options = {}
opt_parser = OptionParser.new do |opt|
opt.banner = <<DOCSTRING
Profile a URL on this GitLab instance.
Usage:
#{__FILE__} url --output=<profile-html> --sql=<sql-log> [--user=<user>] [--post=<post-data>]
Example:
#{__FILE__} /dashboard/issues --output=dashboard-profile.html --sql=dashboard.log --user=root
DOCSTRING
opt.separator ''
opt.separator 'Options:'
opt.on('-o', '--output=/tmp/profile.html', 'profile output filename') do |output|
options[:profile_output] = output
end
opt.on('-s', '--sql=/tmp/profile_sql.txt', 'SQL output filename') do |sql|
options[:sql_output] = sql
end
opt.on('-u', '--user=root', 'User to authenticate as') do |username|
options[:username] = username
end
opt.on('-p', "--post='user=john&pass=test'", 'Send HTTP POST data') do |post_data|
options[:post_data] = post_data
end
end
opt_parser.parse!
options[:url] = ARGV[0]
if options[:url].nil? ||
options[:profile_output].nil? ||
options[:sql_output].nil?
puts opt_parser
exit
end
require File.expand_path('../config/environment', File.dirname(__FILE__))
result = Gitlab::Profiler.profile(options[:url],
logger: Logger.new(options[:sql_output]),
post_data: options[:post_data],
user: User.find_by_username(options[:username]),
private_token: ENV['PRIVATE_TOKEN'])
printer = RubyProf::CallStackPrinter.new(result)
file = File.open(options[:profile_output], 'w')
printer.print(file)
file.close
This source diff could not be displayed because it is too large. You can view the blob instead.
---
title: 'Geo: sync .gitattributes to info/attributes in secondary nodes'
merge_request: 4159
author:
type: changed
---
title: Update the Geo documentation to replicate all secrets to the secondary
merge_request: 4188
author:
type: fixed
---
title: Update Geo documentation to reuse the primary node SSH host key on secondary
node
merge_request: 4198
author:
type: fixed
---
title: Add details on how to disable GitLab to the DR documentation
merge_request: 4239
author:
type: changed
---
title: Fix Epic issue item reordering to handle different scenarios
merge_request: 4142
author:
type: fixed
---
title: Geo - Remove duplicated message on on geo:update_primary_node_url rake task
merge_request:
author:
type: fixed
---
title: Geo - Fix OPENSSH_EXPECTED_COMMAND in the geo:check rake task
merge_request:
author:
type: fixed
---
title: Execute group hooks after-commit when moving an issue
merge_request:
author:
type: fixed
---
title: Capture push rule regex errors and present them to user
merge_request: 4102
author:
type: fixed
---
title: Fix copy/paste on iOS devices due to a bug in webkit
merge_request: 15804
author:
type: fixed
---
title: Stop loading spinner on error of issuable templates
merge_request: 16600
author: Takuya Noguchi
type: fixed
--- ---
title: rework indexes on redirect_routes title: Optimize search queries on the search page by setting a limit for matching records.
merge_request: merge_request:
author: author:
type: performance type: performance
---
title: Allows html text in commits atom feed
merge_request: 16603
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: Fix error on empty query for Members API
merge_request: 16235
author:
type: fixed
---
title: Fix missing "allow users to request access" option in public project permissions
merge_request: 16485
author:
type: fixed
---
title: Correctly escape UTF-8 path elements for uploads
merge_request: 16560
author:
type: fixed
---
title: Adds empty state illustration for pending job
merge_request:
author:
type: other
---
title: Set timezone for karma to UTC
merge_request: 16602
author: Takuya Noguchi
type: other
---
title: Add section headers to plus button dropdown
merge_request: 16394
author: George Tsiolis
type: added
---
title: Adjust layout width for fixed layout
merge_request: 16337
author: George Tsiolis
type: fixed
---
title: Use has_table_privilege for TRIGGER on PostgreSQL
merge_request:
author:
type: fixed
---
title: Refactors mr widget components into vue files and adds i18n
merge_request:
author:
type: other
---
title: Default to Gitaly for 'git push' HTTP/SSH, and make Gitaly mandatory for SSH
pull
merge_request: 16586
author:
type: other
---
title: Remove unecessary query from labels filter
merge_request:
author:
type: performance
---
title: Ensure that users can reclaim a namespace or project path that is blocked by
an orphaned route
merge_request: 16242
author:
type: fixed
...@@ -121,6 +121,7 @@ module Gitlab ...@@ -121,6 +121,7 @@ module Gitlab
config.assets.paths << "ee/app/assets/stylesheets" config.assets.paths << "ee/app/assets/stylesheets"
config.assets.precompile << "*.png" config.assets.precompile << "*.png"
config.assets.precompile << "*.ico"
config.assets.precompile << "print.css" config.assets.precompile << "print.css"
config.assets.precompile << "notify.css" config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css" config.assets.precompile << "mailers/*.css"
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment