summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md22
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--Gemfile.rails5.lock11
-rw-r--r--app/assets/javascripts/notes/components/discussion_counter.vue2
-rw-r--r--app/assets/javascripts/sidebar/lib/sidebar_move_issue.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/identicon.vue2
-rw-r--r--app/assets/stylesheets/pages/notes.scss4
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb4
-rw-r--r--app/controllers/projects/notes_controller.rb4
-rw-r--r--app/finders/groups_finder.rb8
-rw-r--r--app/helpers/application_helper.rb74
-rw-r--r--app/helpers/auth_helper.rb8
-rw-r--r--app/helpers/avatars_helper.rb74
-rw-r--r--app/helpers/dropdowns_helper.rb2
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/helpers/system_note_helper.rb6
-rw-r--r--app/mailers/notify.rb1
-rw-r--r--app/models/ci/stage.rb21
-rw-r--r--app/models/commit.rb4
-rw-r--r--app/models/diff_note.rb15
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/ci/ensure_stage_service.rb1
-rw-r--r--app/services/notes/resolve_service.rb9
-rw-r--r--app/services/repository_archive_clean_up_service.rb5
-rw-r--r--app/views/groups/_group_admin_settings.html.haml52
-rw-r--r--app/views/projects/branches/_branch.html.haml11
-rw-r--r--app/views/projects/protected_branches/_branches_list.html.haml2
-rw-r--r--app/views/projects/protected_branches/shared/_protected_branch.html.haml2
-rw-r--r--changelogs/unreleased/bvl-fix-openid-redirect.yml5
-rw-r--r--changelogs/unreleased/dm-commit-trailer-without-gravatar.yml5
-rw-r--r--changelogs/unreleased/feature-show-only-groups-user-is-member-of-in-dashboard.yml5
-rw-r--r--changelogs/unreleased/fix-inconsistent-protected-branch-pill-baseline.yml5
-rw-r--r--changelogs/unreleased/helm-add-alpine-mirrors.yml5
-rw-r--r--changelogs/unreleased/jprovazn-commit-notes-api.yml5
-rw-r--r--changelogs/unreleased/jr-33320-lfs-settings-interface.yml5
-rw-r--r--changelogs/unreleased/osw-use-cached-highlighted-content-for-discussions.yml5
-rw-r--r--changelogs/unreleased/revert-discussion-counter-height.yml5
-rw-r--r--changelogs/unreleased/security-45689-fix-archive-cache-bug.yml5
-rw-r--r--changelogs/unreleased/security_issue_42029.yml5
-rw-r--r--changelogs/unreleased/update-timeline-icon-for-description-edit.yml5
-rw-r--r--config/initializers/9_fast_gettext.rb (renamed from config/initializers/fast_gettext.rb)0
-rw-r--r--config/initializers/doorkeeper.rb2
-rw-r--r--config/karma.config.js77
-rw-r--r--config/webpack.config.js3
-rw-r--r--db/migrate/20180417101040_add_tmp_stage_priority_index_to_ci_builds.rb16
-rw-r--r--db/migrate/20180417101940_add_index_to_ci_stage.rb9
-rw-r--r--db/post_migrate/20180420080616_schedule_stages_index_migration.rb29
-rw-r--r--db/schema.rb2
-rw-r--r--doc/api/discussions.md585
-rw-r--r--doc/api/groups.md4
-rw-r--r--doc/api/notes.md9
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/diffs.md115
-rw-r--r--doc/development/testing_guide/frontend_testing.md74
-rw-r--r--doc/workflow/lfs/manage_large_binaries_with_git_lfs.md11
-rw-r--r--lib/api/discussions.rb88
-rw-r--r--lib/api/entities.rb19
-rw-r--r--lib/api/groups.rb16
-rw-r--r--lib/api/helpers.rb4
-rw-r--r--lib/api/helpers/custom_attributes.rb3
-rw-r--r--lib/api/helpers/notes_helpers.rb56
-rw-r--r--lib/api/notes.rb30
-rw-r--r--lib/banzai/filter/commit_trailers_filter.rb1
-rw-r--r--lib/gitlab/background_migration/migrate_stage_index.rb47
-rw-r--r--lib/gitlab/base_doorkeeper_controller.rb8
-rw-r--r--lib/gitlab/ci/pipeline/seed/stage.rb1
-rw-r--r--lib/gitlab/diff/file_collection/base.rb2
-rw-r--r--lib/gitlab/diff/position.rb4
-rw-r--r--lib/gitlab/git/raw_diff_change.rb4
-rw-r--r--lib/gitlab/git/repository.rb51
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb4
-rw-r--r--lib/gitlab/kubernetes/helm/base_command.rb3
-rw-r--r--package.json9
-rw-r--r--qa/Gemfile.lock2
-rw-r--r--spec/factories/ci/stages.rb1
-rw-r--r--spec/factories/commit_statuses.rb1
-rw-r--r--spec/features/dashboard/groups_list_spec.rb22
-rw-r--r--spec/features/projects/issues/user_toggles_subscription_spec.rb8
-rw-r--r--spec/features/projects/settings/lfs_settings_spec.rb18
-rw-r--r--spec/finders/groups_finder_spec.rb84
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/notes.json5
-rw-r--r--spec/helpers/application_helper_spec.rb139
-rw-r--r--spec/helpers/auth_helper_spec.rb24
-rw-r--r--spec/helpers/avatars_helper_spec.rb139
-rw-r--r--spec/javascripts/sidebar/mock_data.js2
-rw-r--r--spec/javascripts/sidebar/sidebar_move_issue_spec.js9
-rw-r--r--spec/javascripts/test_bundle.js12
-rw-r--r--spec/lib/banzai/filter/commit_trailers_filter_spec.rb40
-rw-r--r--spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb35
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb3
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb87
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/kubernetes/helm/base_command_spec.rb18
-rw-r--r--spec/lib/gitlab/kubernetes/helm/init_command_spec.rb20
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb66
-rw-r--r--spec/migrations/schedule_stages_index_migration_spec.rb35
-rw-r--r--spec/models/ci/stage_spec.rb34
-rw-r--r--spec/models/diff_note_spec.rb33
-rw-r--r--spec/requests/api/discussions_spec.rb33
-rw-r--r--spec/requests/openid_connect_spec.rb9
-rw-r--r--spec/services/ci/retry_build_service_spec.rb4
-rw-r--r--spec/services/notes/resolve_service_spec.rb23
-rw-r--r--spec/services/repository_archive_clean_up_service_spec.rb68
-rw-r--r--spec/support/commit_trailers_spec_helper.rb2
-rw-r--r--spec/support/shared_examples/helm_generated_script.rb19
-rw-r--r--spec/support/shared_examples/requests/api/diff_discussions.rb57
-rw-r--r--spec/support/shared_examples/requests/api/resolvable_discussions.rb87
-rw-r--r--yarn.lock557
112 files changed, 2370 insertions, 1037 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8278119cf10..e7db98858e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 10.7.2 (2018-04-25)
+
+### Security (2 changes)
+
+- Serve archive requests with the correct file in all cases.
+- Sanitizes user name to avoid XSS attacks.
+
+
## 10.7.1 (2018-04-23)
### Fixed (11 changes)
@@ -237,6 +245,13 @@ entry.
- Upgrade Gitaly to upgrade its charlock_holmes.
+## 10.6.5 (2018-04-24)
+
+### Security (1 change)
+
+- Sanitizes user name to avoid XSS attacks.
+
+
## 10.6.4 (2018-04-09)
### Fixed (8 changes, 1 of them is from the community)
@@ -478,6 +493,13 @@ entry.
- Use host URL to build JIRA remote link icon.
+## 10.5.8 (2018-04-24)
+
+### Security (1 change)
+
+- Sanitizes user name to avoid XSS attacks.
+
+
## 10.5.7 (2018-04-03)
### Security (2 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 483b7719418..cf22efd819d 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.96.1
+0.96.2
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index ee74734aa22..6aba2b245a8 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-4.1.0
+4.2.0
diff --git a/Gemfile b/Gemfile
index 6ca242cbc97..caeaae96164 100644
--- a/Gemfile
+++ b/Gemfile
@@ -414,7 +414,7 @@ end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 0.97.0', require: 'gitaly'
-gem 'grpc', '~> 1.10.0'
+gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed
gem 'google-protobuf', '= 3.5.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index 29c37bc7f1f..9b2c47587ee 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -374,7 +374,7 @@ GEM
rake
grape_logging (1.7.0)
grape
- grpc (1.10.0)
+ grpc (1.11.0)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
googleauth (>= 0.5.1, < 0.7)
@@ -1073,7 +1073,7 @@ DEPENDENCIES
grape-entity (~> 0.6.0)
grape-route-helpers (~> 2.1.0)
grape_logging (~> 1.7)
- grpc (~> 1.10.0)
+ grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
hamlit (~> 2.6.1)
hashie-forbidden_attributes
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index a0330cbdd02..10d5cb6a23f 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -304,12 +304,12 @@ GEM
flowdock (~> 0.7)
gitlab-grit (>= 2.4.1)
multi_json
- gitlab-gollum-lib (4.2.7.1)
+ gitlab-gollum-lib (4.2.7.2)
gemojione (~> 3.2)
github-markup (~> 1.6)
gollum-grit_adapter (~> 1.0)
nokogiri (>= 1.6.1, < 2.0)
- rouge (~> 2.1)
+ rouge (~> 3.1)
sanitize (~> 2.1)
stringex (~> 2.6)
gitlab-gollum-rugged_adapter (0.4.4)
@@ -602,8 +602,6 @@ GEM
atomic (>= 1.0.0)
mysql2
peek
- peek-performance_bar (1.3.1)
- peek (>= 0.1.0)
peek-pg (1.3.0)
concurrent-ruby
concurrent-ruby-ext
@@ -752,7 +750,7 @@ GEM
retriable (3.1.1)
rinku (2.0.4)
rotp (2.1.2)
- rouge (2.2.1)
+ rouge (3.1.1)
rqrcode (0.10.1)
chunky_png (~> 1.0)
rqrcode-rails3 (0.1.7)
@@ -1134,7 +1132,6 @@ DEPENDENCIES
peek (~> 1.0.1)
peek-gc (~> 0.0.2)
peek-mysql2 (~> 1.1.0)
- peek-performance_bar (~> 1.3.0)
peek-pg (~> 1.3.0)
peek-rblineprof (~> 0.2.0)
peek-redis (~> 1.2.0)
@@ -1166,7 +1163,7 @@ DEPENDENCIES
redis-rails (~> 5.0.2)
request_store (~> 1.3)
responders (~> 2.0)
- rouge (~> 2.0)
+ rouge (~> 3.1)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
rspec-rails (~> 3.6.0)
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index d492d1cd001..cbe4774a360 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -86,7 +86,7 @@ export default {
v-html="resolveSvg"
></span>
</span>
- <span class=".line-resolve-text">
+ <span class="line-resolve-text">
{{ resolvedDiscussionCount }}/{{ discussionCount }} {{ countText }} resolved
</span>
</div>
diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
index 1eadebc7004..b267422cd97 100644
--- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
+++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
@@ -1,4 +1,5 @@
import $ from 'jquery';
+import _ from 'underscore';
function isValidProjectId(id) {
return id > 0;
@@ -43,7 +44,7 @@ class SidebarMoveIssue {
renderRow: project => `
<li>
<a href="#" class="js-move-issue-dropdown-item">
- ${project.name_with_namespace}
+ ${_.escape(project.name_with_namespace)}
</a>
</li>
`,
diff --git a/app/assets/javascripts/vue_shared/components/identicon.vue b/app/assets/javascripts/vue_shared/components/identicon.vue
index 0a30f467b08..23010f40f26 100644
--- a/app/assets/javascripts/vue_shared/components/identicon.vue
+++ b/app/assets/javascripts/vue_shared/components/identicon.vue
@@ -17,7 +17,7 @@ export default {
},
computed: {
/**
- * This method is based on app/helpers/application_helper.rb#project_identicon
+ * This method is based on app/helpers/avatars_helper.rb#project_identicon
*/
identiconStyles() {
const allowedColors = [
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 81e98f358a8..6d5c6cb136f 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -772,7 +772,3 @@ ul.notes {
height: auto;
}
}
-
-.line-resolve-text {
- vertical-align: middle;
-}
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 9137bc92810..40d9fa18a10 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -8,8 +8,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
omniauth_flow(Gitlab::Auth::OAuth)
end
- Gitlab.config.omniauth.providers.each do |provider|
- alias_method provider['name'], :handle_omniauth
+ AuthHelper.providers_for_base_controller.each do |provider|
+ alias_method provider, :handle_omniauth
end
# Extend the standard implementation to also increment
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 86c50d88a2a..bc13b8ad7ba 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -33,9 +33,7 @@ class Projects::NotesController < Projects::ApplicationController
def resolve
return render_404 unless note.resolvable?
- note.resolve!(current_user)
-
- MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(note.noteable)
+ Notes::ResolveService.new(project, current_user).execute(note)
discussion = note.discussion
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index 0282b378d88..0754123a3cf 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -39,7 +39,7 @@ class GroupsFinder < UnionFinder
def all_groups
return [owned_groups] if params[:owned]
- return [Group.all] if current_user&.full_private_access?
+ return [Group.all] if current_user&.full_private_access? && all_available?
groups = []
groups << Gitlab::GroupHierarchy.new(groups_for_ancestors, groups_for_descendants).all_groups if current_user
@@ -67,6 +67,10 @@ class GroupsFinder < UnionFinder
end
def include_public_groups?
- current_user.nil? || params.fetch(:all_available, true)
+ current_user.nil? || all_available?
+ end
+
+ def all_available?
+ params.fetch(:all_available, true)
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 228c8d2e8f9..6aa307b4db4 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -32,80 +32,6 @@ module ApplicationHelper
args.any? { |v| v.to_s.downcase == action_name }
end
- def project_icon(project_id, options = {})
- project =
- if project_id.respond_to?(:avatar_url)
- project_id
- else
- Project.find_by_full_path(project_id)
- end
-
- if project.avatar_url
- image_tag project.avatar_url, options
- else # generated icon
- project_identicon(project, options)
- end
- end
-
- def project_identicon(project, options = {})
- allowed_colors = {
- red: 'FFEBEE',
- purple: 'F3E5F5',
- indigo: 'E8EAF6',
- blue: 'E3F2FD',
- teal: 'E0F2F1',
- orange: 'FBE9E7',
- gray: 'EEEEEE'
- }
-
- options[:class] ||= ''
- options[:class] << ' identicon'
- bg_key = project.id % 7
- style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555"
-
- content_tag(:div, class: options[:class], style: style) do
- project.name[0, 1].upcase
- end
- end
-
- # Takes both user and email and returns the avatar_icon by
- # user (preferred) or email.
- def avatar_icon_for(user = nil, email = nil, size = nil, scale = 2, only_path: true)
- if user
- avatar_icon_for_user(user, size, scale, only_path: only_path)
- elsif email
- avatar_icon_for_email(email, size, scale, only_path: only_path)
- else
- default_avatar
- end
- end
-
- def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true)
- user = User.find_by_any_email(email.try(:downcase))
- if user
- avatar_icon_for_user(user, size, scale, only_path: only_path)
- else
- gravatar_icon(email, size, scale)
- end
- end
-
- def avatar_icon_for_user(user = nil, size = nil, scale = 2, only_path: true)
- if user
- user.avatar_url(size: size, only_path: only_path) || default_avatar
- else
- gravatar_icon(nil, size, scale)
- end
- end
-
- def gravatar_icon(user_email = '', size = nil, scale = 2)
- GravatarService.new.execute(user_email, size, scale) ||
- default_avatar
- end
-
- def default_avatar
- asset_path('no_avatar.png')
- end
-
def last_commit(project)
if project.repo_exists?
time_ago_with_tooltip(project.repository.commit.committed_date)
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index c109954f3a3..d2daee22aba 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -1,6 +1,6 @@
module AuthHelper
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq).freeze
- FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze
+ LDAP_PROVIDER = /\Aldap/
def ldap_enabled?
Gitlab::Auth::LDAP::Config.enabled?
@@ -23,7 +23,7 @@ module AuthHelper
end
def form_based_provider?(name)
- FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s }
+ [LDAP_PROVIDER, 'crowd'].any? { |pattern| pattern === name.to_s }
end
def form_based_providers
@@ -38,6 +38,10 @@ module AuthHelper
auth_providers.reject { |provider| form_based_provider?(provider) }
end
+ def providers_for_base_controller
+ auth_providers.reject { |provider| LDAP_PROVIDER === provider }
+ end
+
def enabled_button_based_providers
disabled_providers = Gitlab::CurrentSettings.disabled_oauth_sign_in_sources || []
diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb
index 21b6c0a8ad5..d339c01d492 100644
--- a/app/helpers/avatars_helper.rb
+++ b/app/helpers/avatars_helper.rb
@@ -1,4 +1,78 @@
module AvatarsHelper
+ def project_icon(project_id, options = {})
+ project =
+ if project_id.respond_to?(:avatar_url)
+ project_id
+ else
+ Project.find_by_full_path(project_id)
+ end
+
+ if project.avatar_url
+ image_tag project.avatar_url, options
+ else # generated icon
+ project_identicon(project, options)
+ end
+ end
+
+ def project_identicon(project, options = {})
+ allowed_colors = {
+ red: 'FFEBEE',
+ purple: 'F3E5F5',
+ indigo: 'E8EAF6',
+ blue: 'E3F2FD',
+ teal: 'E0F2F1',
+ orange: 'FBE9E7',
+ gray: 'EEEEEE'
+ }
+
+ options[:class] ||= ''
+ options[:class] << ' identicon'
+ bg_key = project.id % 7
+ style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555"
+
+ content_tag(:div, class: options[:class], style: style) do
+ project.name[0, 1].upcase
+ end
+ end
+
+ # Takes both user and email and returns the avatar_icon by
+ # user (preferred) or email.
+ def avatar_icon_for(user = nil, email = nil, size = nil, scale = 2, only_path: true)
+ if user
+ avatar_icon_for_user(user, size, scale, only_path: only_path)
+ elsif email
+ avatar_icon_for_email(email, size, scale, only_path: only_path)
+ else
+ default_avatar
+ end
+ end
+
+ def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true)
+ user = User.find_by_any_email(email.try(:downcase))
+ if user
+ avatar_icon_for_user(user, size, scale, only_path: only_path)
+ else
+ gravatar_icon(email, size, scale)
+ end
+ end
+
+ def avatar_icon_for_user(user = nil, size = nil, scale = 2, only_path: true)
+ if user
+ user.avatar_url(size: size, only_path: only_path) || default_avatar
+ else
+ gravatar_icon(nil, size, scale)
+ end
+ end
+
+ def gravatar_icon(user_email = '', size = nil, scale = 2)
+ GravatarService.new.execute(user_email, size, scale) ||
+ default_avatar
+ end
+
+ def default_avatar
+ ActionController::Base.helpers.image_path('no_avatar.png')
+ end
+
def author_avatar(commit_or_event, options = {})
user_avatar(options.merge({
user: commit_or_event.author,
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index 5089da519df..5a2360b4661 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -41,7 +41,7 @@ module DropdownsHelper
def dropdown_toggle(toggle_text, data_attr, options = {})
default_label = data_attr[:default_label]
- content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.key?(:toggle_class)}", id: (options[:id] if options.key?(:id)), type: "button", data: data_attr) do
+ content_tag(:button, disabled: options[:disabled], class: "dropdown-menu-toggle #{options[:toggle_class] if options.key?(:toggle_class)}", id: (options[:id] if options.key?(:id)), type: "button", data: data_attr) do
output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}")
output << icon('chevron-down')
output.html_safe
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 801e624e1de..eb81dc2de43 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -442,7 +442,7 @@ module ProjectsHelper
visibilityHelpPath: help_page_path('public_access/public_access'),
registryAvailable: Gitlab.config.registry.enabled,
registryHelpPath: help_page_path('user/project/container_registry'),
- lfsAvailable: Gitlab.config.lfs.enabled && current_user.admin?,
+ lfsAvailable: Gitlab.config.lfs.enabled,
lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
}
diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb
index 00fe67d6ffb..5b4a141dbcf 100644
--- a/app/helpers/system_note_helper.rb
+++ b/app/helpers/system_note_helper.rb
@@ -1,14 +1,14 @@
module SystemNoteHelper
ICON_NAMES_BY_ACTION = {
'commit' => 'commit',
- 'description' => 'pencil',
+ 'description' => 'pencil-square',
'merge' => 'git-merge',
'merged' => 'git-merge',
'opened' => 'issue-open',
'closed' => 'issue-close',
'time_tracking' => 'timer',
'assignee' => 'user',
- 'title' => 'pencil',
+ 'title' => 'pencil-square',
'task' => 'task-done',
'label' => 'label',
'cross_reference' => 'comment-dots',
@@ -18,7 +18,7 @@ module SystemNoteHelper
'milestone' => 'clock',
'discussion' => 'comment',
'moved' => 'arrow-right',
- 'outdated' => 'pencil',
+ 'outdated' => 'pencil-square',
'duplicate' => 'issue-duplicate',
'locked' => 'lock',
'unlocked' => 'lock-open'
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index e4212775956..3646e08a15f 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -16,6 +16,7 @@ class Notify < BaseMailer
helper BlobHelper
helper EmailsHelper
helper MembersHelper
+ helper AvatarsHelper
helper GitlabRoutingHelper
def test_email(recipient_email, subject, body)
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 75b8ea2a371..5a1eeb966aa 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -13,14 +13,27 @@ module Ci
has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id
has_many :builds, foreign_key: :stage_id
- validates :project, presence: true, unless: :importing?
- validates :pipeline, presence: true, unless: :importing?
- validates :name, presence: true, unless: :importing?
+ with_options unless: :importing? do
+ validates :project, presence: true
+ validates :pipeline, presence: true
+ validates :name, presence: true
+ validates :position, presence: true
+ end
- after_initialize do |stage|
+ after_initialize do
self.status = DEFAULT_STATUS if self.status.nil?
end
+ before_validation unless: :importing? do
+ next if position.present?
+
+ self.position = statuses.select(:stage_idx)
+ .where('stage_idx IS NOT NULL')
+ .group(:stage_idx)
+ .order('COUNT(*) DESC')
+ .first&.stage_idx.to_i
+ end
+
state_machine :status, initial: :created do
event :enqueue do
transition created: :pending
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 9750e9298ec..32f3d90595a 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -105,6 +105,10 @@ class Commit
end
end
end
+
+ def parent_class
+ ::Project
+ end
end
attr_accessor :raw
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index 15122cbc693..616a626419b 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -54,7 +54,20 @@ class DiffNote < Note
end
def diff_file
- @diff_file ||= self.original_position.diff_file(self.project.repository)
+ @diff_file ||=
+ begin
+ if created_at_diff?(noteable.diff_refs)
+ # We're able to use the already persisted diffs (Postgres) if we're
+ # presenting a "current version" of the MR discussion diff.
+ # So no need to make an extra Gitaly diff request for it.
+ # As an extra benefit, the returned `diff_file` already
+ # has `highlighted_diff_lines` data set from Redis on
+ # `Diff::FileCollection::MergeRequestDiff`.
+ noteable.diffs(paths: original_position.paths, expanded: true).diff_files.first
+ else
+ original_position.diff_file(self.project.repository)
+ end
+ end
end
def diff_line
diff --git a/app/models/user.rb b/app/models/user.rb
index b0668148972..4a602ffbb05 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -910,7 +910,7 @@ class User < ActiveRecord::Base
def delete_async(deleted_by:, params: {})
block if params[:hard_delete]
- DeleteUserWorker.perform_async(deleted_by.id, id, params)
+ DeleteUserWorker.perform_async(deleted_by.id, id, params.to_h)
end
def notification_service
diff --git a/app/services/ci/ensure_stage_service.rb b/app/services/ci/ensure_stage_service.rb
index 87f19b333de..b8c7be2d350 100644
--- a/app/services/ci/ensure_stage_service.rb
+++ b/app/services/ci/ensure_stage_service.rb
@@ -42,6 +42,7 @@ module Ci
def create_stage
Ci::Stage.create!(name: @build.stage,
+ position: @build.stage_idx,
pipeline: @build.pipeline,
project: @build.project)
end
diff --git a/app/services/notes/resolve_service.rb b/app/services/notes/resolve_service.rb
new file mode 100644
index 00000000000..0db8ee809a9
--- /dev/null
+++ b/app/services/notes/resolve_service.rb
@@ -0,0 +1,9 @@
+module Notes
+ class ResolveService < ::BaseService
+ def execute(note)
+ note.resolve!(current_user)
+
+ ::MergeRequests::ResolvedDiscussionNotificationService.new(project, current_user).execute(note.noteable)
+ end
+ end
+end
diff --git a/app/services/repository_archive_clean_up_service.rb b/app/services/repository_archive_clean_up_service.rb
index 9a88459b511..ba7be4b3f89 100644
--- a/app/services/repository_archive_clean_up_service.rb
+++ b/app/services/repository_archive_clean_up_service.rb
@@ -20,11 +20,12 @@ class RepositoryArchiveCleanUpService
private
def clean_up_old_archives
- run(%W(find #{path} -not -path #{path} -type f \( -name \*.tar -o -name \*.bz2 -o -name \*.tar.gz -o -name \*.zip \) -maxdepth 2 -mmin +#{mmin} -delete))
+ run(%W(find #{path} -mindepth 1 -maxdepth 3 -type f \( -name \*.tar -o -name \*.bz2 -o -name \*.tar.gz -o -name \*.zip \) -mmin +#{mmin} -delete))
end
def clean_up_empty_directories
- run(%W(find #{path} -not -path #{path} -type d -empty -name \*.git -maxdepth 1 -delete))
+ run(%W(find #{path} -mindepth 2 -maxdepth 2 -type d -empty -delete))
+ run(%W(find #{path} -mindepth 1 -maxdepth 1 -type d -empty -delete))
end
def run(cmd)
diff --git a/app/views/groups/_group_admin_settings.html.haml b/app/views/groups/_group_admin_settings.html.haml
index 2ace1e2dd1e..65e95f3aeef 100644
--- a/app/views/groups/_group_admin_settings.html.haml
+++ b/app/views/groups/_group_admin_settings.html.haml
@@ -1,28 +1,26 @@
-- if current_user.admin?
- .form-group
- = f.label :lfs_enabled, 'Large File Storage', class: 'control-label'
- .col-sm-10
- .checkbox
- = f.label :lfs_enabled do
- = f.check_box :lfs_enabled, checked: @group.lfs_enabled?
- %strong
- Allow projects within this group to use Git LFS
- = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
- %br/
- %span.descr This setting can be overridden in each project.
+.form-group
+ = f.label :lfs_enabled, 'Large File Storage', class: 'control-label'
+ .col-sm-10
+ .checkbox
+ = f.label :lfs_enabled do
+ = f.check_box :lfs_enabled, checked: @group.lfs_enabled?
+ %strong
+ Allow projects within this group to use Git LFS
+ = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ %br/
+ %span.descr This setting can be overridden in each project.
-- if can? current_user, :admin_group, @group
- .form-group
- = f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2'
- .col-sm-10
- .checkbox
- = f.label :require_two_factor_authentication do
- = f.check_box :require_two_factor_authentication
- %strong
- Require all users in this group to setup Two-factor authentication
- = link_to icon('question-circle'), help_page_path('security/two_factor_authentication', anchor: 'enforcing-2fa-for-all-users-in-a-group')
- .form-group
- .col-sm-offset-2.col-sm-10
- .checkbox
- = f.text_field :two_factor_grace_period, class: 'form-control'
- .help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
+.form-group
+ = f.label :require_two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2'
+ .col-sm-10
+ .checkbox
+ = f.label :require_two_factor_authentication do
+ = f.check_box :require_two_factor_authentication
+ %strong
+ Require all users in this group to setup Two-factor authentication
+ = link_to icon('question-circle'), help_page_path('security/two_factor_authentication', anchor: 'enforcing-2fa-for-all-users-in-a-group')
+.form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.text_field :two_factor_grace_period, class: 'form-control'
+ .help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index d0c01f95cb7..0e012b5a216 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -8,18 +8,17 @@
%li{ class: "branch-item js-branch-#{branch.name}" }
.branch-info
.branch-title
- = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name' do
- = sprite_icon('fork', size: 12)
+ = sprite_icon('fork', size: 12)
+ = link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name prepend-left-8' do
= branch.name
- &nbsp;
- if branch.name == @repository.root_ref
- %span.label.label-primary default
+ %span.label.label-primary.prepend-left-5 default
- elsif merged
- %span.label.label-info.has-tooltip{ title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref } }
+ %span.label.label-info.has-tooltip.prepend-left-5{ title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref } }
= s_('Branches|merged')
- if protected_branch?(@project, branch)
- %span.label.label-success
+ %span.label.label-success.prepend-left-5
= s_('Branches|protected')
.block-truncated
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
index 5377d745371..24d2b971472 100644
--- a/app/views/projects/protected_branches/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/_branches_list.html.haml
@@ -1,4 +1,4 @@
- can_admin_project = can?(current_user, :admin_project, @project)
= render layout: 'projects/protected_branches/shared/branches_list', locals: { can_admin_project: can_admin_project } do
- = render partial: 'projects/protected_branches/protected_branch', collection: @protected_branches, locals: { can_admin_project: can_admin_project}
+ = render partial: 'projects/protected_branches/protected_branch', collection: @protected_branches
diff --git a/app/views/projects/protected_branches/shared/_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
index f5b21f0e887..2d3b2af00c2 100644
--- a/app/views/projects/protected_branches/shared/_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
@@ -21,4 +21,4 @@
- if can_admin_project
%td
- = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning'
+ = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], disabled: local_assigns[:disabled], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning"
diff --git a/changelogs/unreleased/bvl-fix-openid-redirect.yml b/changelogs/unreleased/bvl-fix-openid-redirect.yml
new file mode 100644
index 00000000000..83ee6d953e4
--- /dev/null
+++ b/changelogs/unreleased/bvl-fix-openid-redirect.yml
@@ -0,0 +1,5 @@
+---
+title: Fix redirection error for applications using OpenID
+merge_request: 18599
+author:
+type: fixed
diff --git a/changelogs/unreleased/dm-commit-trailer-without-gravatar.yml b/changelogs/unreleased/dm-commit-trailer-without-gravatar.yml
new file mode 100644
index 00000000000..9f057c67122
--- /dev/null
+++ b/changelogs/unreleased/dm-commit-trailer-without-gravatar.yml
@@ -0,0 +1,5 @@
+---
+title: Fix commit trailer rendering when Gravatar is disabled
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/feature-show-only-groups-user-is-member-of-in-dashboard.yml b/changelogs/unreleased/feature-show-only-groups-user-is-member-of-in-dashboard.yml
new file mode 100644
index 00000000000..6e2273ed9af
--- /dev/null
+++ b/changelogs/unreleased/feature-show-only-groups-user-is-member-of-in-dashboard.yml
@@ -0,0 +1,5 @@
+---
+title: For group dashboard, we no longer show groups which the visitor is not a member of (this applies to admins and auditors)
+merge_request: 17884
+author: Roger Rüttimann
+type: changed
diff --git a/changelogs/unreleased/fix-inconsistent-protected-branch-pill-baseline.yml b/changelogs/unreleased/fix-inconsistent-protected-branch-pill-baseline.yml
new file mode 100644
index 00000000000..fb6dffaf226
--- /dev/null
+++ b/changelogs/unreleased/fix-inconsistent-protected-branch-pill-baseline.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed inconsistent protected branch pill baseline
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/helm-add-alpine-mirrors.yml b/changelogs/unreleased/helm-add-alpine-mirrors.yml
new file mode 100644
index 00000000000..656c4f911d0
--- /dev/null
+++ b/changelogs/unreleased/helm-add-alpine-mirrors.yml
@@ -0,0 +1,5 @@
+---
+title: Increase cluster applications installer availability using alpine linux mirrors
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/jprovazn-commit-notes-api.yml b/changelogs/unreleased/jprovazn-commit-notes-api.yml
new file mode 100644
index 00000000000..4665d800ccf
--- /dev/null
+++ b/changelogs/unreleased/jprovazn-commit-notes-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add discussion API for merge requests and commits
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/jr-33320-lfs-settings-interface.yml b/changelogs/unreleased/jr-33320-lfs-settings-interface.yml
new file mode 100644
index 00000000000..b39308f5474
--- /dev/null
+++ b/changelogs/unreleased/jr-33320-lfs-settings-interface.yml
@@ -0,0 +1,5 @@
+---
+title: Show group and project LFS settings in the interface to Owners and Masters
+merge_request: 18562
+author:
+type: changed
diff --git a/changelogs/unreleased/osw-use-cached-highlighted-content-for-discussions.yml b/changelogs/unreleased/osw-use-cached-highlighted-content-for-discussions.yml
new file mode 100644
index 00000000000..03a11a3038a
--- /dev/null
+++ b/changelogs/unreleased/osw-use-cached-highlighted-content-for-discussions.yml
@@ -0,0 +1,5 @@
+---
+title: Use persisted diff data instead fetching Git on discussions
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/revert-discussion-counter-height.yml b/changelogs/unreleased/revert-discussion-counter-height.yml
new file mode 100644
index 00000000000..331ff997009
--- /dev/null
+++ b/changelogs/unreleased/revert-discussion-counter-height.yml
@@ -0,0 +1,5 @@
+---
+title: Revert discussion counter height
+merge_request: 18656
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/security-45689-fix-archive-cache-bug.yml b/changelogs/unreleased/security-45689-fix-archive-cache-bug.yml
new file mode 100644
index 00000000000..0103a7fc430
--- /dev/null
+++ b/changelogs/unreleased/security-45689-fix-archive-cache-bug.yml
@@ -0,0 +1,5 @@
+---
+title: Serve archive requests with the correct file in all cases
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/security_issue_42029.yml b/changelogs/unreleased/security_issue_42029.yml
new file mode 100644
index 00000000000..0772e33f930
--- /dev/null
+++ b/changelogs/unreleased/security_issue_42029.yml
@@ -0,0 +1,5 @@
+---
+title: Sanitizes user name to avoid XSS attacks
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/update-timeline-icon-for-description-edit.yml b/changelogs/unreleased/update-timeline-icon-for-description-edit.yml
new file mode 100644
index 00000000000..560db00e503
--- /dev/null
+++ b/changelogs/unreleased/update-timeline-icon-for-description-edit.yml
@@ -0,0 +1,5 @@
+---
+title: Update timeline icon for description edit
+merge_request: 18633
+author: George Tsiolis
+type: changed
diff --git a/config/initializers/fast_gettext.rb b/config/initializers/9_fast_gettext.rb
index fd0167aa476..fd0167aa476 100644
--- a/config/initializers/fast_gettext.rb
+++ b/config/initializers/9_fast_gettext.rb
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
index 2079d3acb72..e3a342590d4 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -104,5 +104,5 @@ Doorkeeper.configure do
# set to true if you want this to be allowed
# wildcard_redirect_uri false
- base_controller 'ApplicationController'
+ base_controller '::Gitlab::BaseDoorkeeperController'
end
diff --git a/config/karma.config.js b/config/karma.config.js
index 691cda98861..3eb220eed99 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -1,8 +1,16 @@
-var path = require('path');
-var webpack = require('webpack');
-var argumentsParser = require('commander');
-var webpackConfig = require('./webpack.config.js');
-var ROOT_PATH = path.resolve(__dirname, '..');
+const path = require('path');
+const glob = require('glob');
+const chalk = require('chalk');
+const webpack = require('webpack');
+const argumentsParser = require('commander');
+const webpackConfig = require('./webpack.config.js');
+
+const ROOT_PATH = path.resolve(__dirname, '..');
+
+function fatalError(message) {
+ console.error(chalk.red(`\nError: ${message}\n`));
+ process.exit(1);
+}
// remove problematic plugins
if (webpackConfig.plugins) {
@@ -15,33 +23,70 @@ if (webpackConfig.plugins) {
});
}
-var testFiles = argumentsParser
+const specFilters = argumentsParser
.option(
'-f, --filter-spec [filter]',
'Filter run spec files by path. Multiple filters are like a logical OR.',
- (val, memo) => {
- memo.push(val);
+ (filter, memo) => {
+ memo.push(filter, filter.replace(/\/?$/, '/**/*.js'));
return memo;
},
[]
)
.parse(process.argv).filterSpec;
-webpackConfig.plugins.push(
- new webpack.DefinePlugin({
- 'process.env.TEST_FILES': JSON.stringify(testFiles),
- })
-);
+if (specFilters.length) {
+ const specsPath = /^(?:\.[\\\/])?spec[\\\/]javascripts[\\\/]/;
+
+ // resolve filters
+ let filteredSpecFiles = specFilters.map(filter =>
+ glob
+ .sync(filter, {
+ root: ROOT_PATH,
+ matchBase: true,
+ })
+ .filter(path => path.endsWith('spec.js'))
+ );
+
+ // flatten
+ filteredSpecFiles = Array.prototype.concat.apply([], filteredSpecFiles);
+
+ // remove duplicates
+ filteredSpecFiles = [...new Set(filteredSpecFiles)];
+
+ if (filteredSpecFiles.length < 1) {
+ fatalError('Your filter did not match any test files.');
+ }
+
+ if (!filteredSpecFiles.every(file => specsPath.test(file))) {
+ fatalError('Test files must be located within /spec/javascripts.');
+ }
+
+ const newContext = filteredSpecFiles.reduce((context, file) => {
+ const relativePath = file.replace(specsPath, '');
+ context[file] = `./${relativePath}`;
+ return context;
+ }, {});
+
+ webpackConfig.plugins.push(
+ new webpack.ContextReplacementPlugin(
+ /spec[\\\/]javascripts$/,
+ path.join(ROOT_PATH, 'spec/javascripts'),
+ newContext
+ )
+ );
+}
-webpackConfig.devtool = process.env.BABEL_ENV !== 'coverage' && 'cheap-inline-source-map';
+webpackConfig.entry = undefined;
+webpackConfig.devtool = 'cheap-inline-source-map';
// Karma configuration
module.exports = function(config) {
process.env.TZ = 'Etc/UTC';
- var progressReporter = process.env.CI ? 'mocha' : 'progress';
+ const progressReporter = process.env.CI ? 'mocha' : 'progress';
- var karmaConfig = {
+ const karmaConfig = {
basePath: ROOT_PATH,
browsers: ['ChromeHeadlessCustom'],
customLaunchers: {
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 39e9fbbd530..b9d098ff9b9 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -69,6 +69,9 @@ const config = {
test: /\.js$/,
exclude: /(node_modules|vendor\/assets)/,
loader: 'babel-loader',
+ options: {
+ cacheDirectory: path.join(ROOT_PATH, 'tmp/cache/babel-loader'),
+ },
},
{
test: /\.vue$/,
diff --git a/db/migrate/20180417101040_add_tmp_stage_priority_index_to_ci_builds.rb b/db/migrate/20180417101040_add_tmp_stage_priority_index_to_ci_builds.rb
new file mode 100644
index 00000000000..ee82c70ecf8
--- /dev/null
+++ b/db/migrate/20180417101040_add_tmp_stage_priority_index_to_ci_builds.rb
@@ -0,0 +1,16 @@
+class AddTmpStagePriorityIndexToCiBuilds < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:ci_builds, [:stage_id, :stage_idx],
+ where: 'stage_idx IS NOT NULL', name: 'tmp_build_stage_position_index')
+ end
+
+ def down
+ remove_concurrent_index_by_name(:ci_builds, 'tmp_build_stage_position_index')
+ end
+end
diff --git a/db/migrate/20180417101940_add_index_to_ci_stage.rb b/db/migrate/20180417101940_add_index_to_ci_stage.rb
new file mode 100644
index 00000000000..9dac78db774
--- /dev/null
+++ b/db/migrate/20180417101940_add_index_to_ci_stage.rb
@@ -0,0 +1,9 @@
+class AddIndexToCiStage < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :ci_stages, :position, :integer
+ end
+end
diff --git a/db/post_migrate/20180420080616_schedule_stages_index_migration.rb b/db/post_migrate/20180420080616_schedule_stages_index_migration.rb
new file mode 100644
index 00000000000..1d0daad002f
--- /dev/null
+++ b/db/post_migrate/20180420080616_schedule_stages_index_migration.rb
@@ -0,0 +1,29 @@
+class ScheduleStagesIndexMigration < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ MIGRATION = 'MigrateStageIndex'.freeze
+ BATCH_SIZE = 10000
+
+ disable_ddl_transaction!
+
+ class Stage < ActiveRecord::Base
+ include EachBatch
+ self.table_name = 'ci_stages'
+ end
+
+ def up
+ disable_statement_timeout
+
+ Stage.all.tap do |relation|
+ queue_background_migration_jobs_by_range_at_intervals(relation,
+ MIGRATION,
+ 5.minutes,
+ batch_size: BATCH_SIZE)
+ end
+ end
+
+ def down
+ # noop
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 5853b428430..10cd1bff125 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -322,6 +322,7 @@ ActiveRecord::Schema.define(version: 20180425131009) do
add_index "ci_builds", ["project_id", "id"], name: "index_ci_builds_on_project_id_and_id", using: :btree
add_index "ci_builds", ["protected"], name: "index_ci_builds_on_protected", using: :btree
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
+ add_index "ci_builds", ["stage_id", "stage_idx"], name: "tmp_build_stage_position_index", where: "(stage_idx IS NOT NULL)", using: :btree
add_index "ci_builds", ["stage_id"], name: "index_ci_builds_on_stage_id", using: :btree
add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree
add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree
@@ -486,6 +487,7 @@ ActiveRecord::Schema.define(version: 20180425131009) do
t.string "name"
t.integer "status"
t.integer "lock_version"
+ t.integer "position"
end
add_index "ci_stages", ["pipeline_id", "name"], name: "index_ci_stages_on_pipeline_id_and_name", unique: true, using: :btree
diff --git a/doc/api/discussions.md b/doc/api/discussions.md
index c341b7f2009..65e2f9d6cd9 100644
--- a/doc/api/discussions.md
+++ b/doc/api/discussions.md
@@ -1,6 +1,6 @@
# Discussions API
-Discussions are set of related notes on snippets or issues.
+Discussions are set of related notes on snippets, issues, merge requests or commits.
## Issues
@@ -61,7 +61,8 @@ GET /projects/:id/issues/:issue_iid/discussions
"system": false,
"noteable_id": 3,
"noteable_type": "Issue",
- "noteable_iid": null
+ "noteable_iid": null,
+ "resolvable": false
}
]
},
@@ -87,7 +88,8 @@ GET /projects/:id/issues/:issue_iid/discussions
"system": false,
"noteable_id": 3,
"noteable_type": "Issue",
- "noteable_iid": null
+ "noteable_iid": null,
+ "resolvable": false
}
]
}
@@ -265,7 +267,8 @@ GET /projects/:id/snippets/:snippet_id/discussions
"system": false,
"noteable_id": 3,
"noteable_type": "Snippet",
- "noteable_id": null
+ "noteable_id": null,
+ "resolvable": false
}
]
},
@@ -291,7 +294,8 @@ GET /projects/:id/snippets/:snippet_id/discussions
"system": false,
"noteable_id": 3,
"noteable_type": "Snippet",
- "noteable_id": null
+ "noteable_id": null,
+ "resolvable": false
}
]
}
@@ -409,3 +413,574 @@ Parameters:
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/snippets/11/discussions/636
```
+
+## Merge requests
+
+### List project merge request discussions
+
+Gets a list of all discussions for a single merge request.
+
+```
+GET /projects/:id/merge_requests/:merge_request_iid/discussions
+```
+
+| Attribute | Type | Required | Description |
+| ------------------- | ---------------- | ---------- | ------------ |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
+
+```json
+[
+ {
+ "id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
+ "individual_note": false,
+ "notes": [
+ {
+ "id": 1126,
+ "type": "DiscussionNote",
+ "body": "discussion text",
+ "attachment": null,
+ "author": {
+ "id": 1,
+ "name": "root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
+ "web_url": "http://localhost:3000/root"
+ },
+ "created_at": "2018-03-03T21:54:39.668Z",
+ "updated_at": "2018-03-03T21:54:39.668Z",
+ "system": false,
+ "noteable_id": 3,
+ "noteable_type": "Merge request",
+ "noteable_iid": null,
+ "resolved": false,
+ "resolvable": true,
+ "resolved_by": null
+ },
+ {
+ "id": 1129,
+ "type": "DiscussionNote",
+ "body": "reply to the discussion",
+ "attachment": null,
+ "author": {
+ "id": 1,
+ "name": "root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
+ "web_url": "http://localhost:3000/root"
+ },
+ "created_at": "2018-03-04T13:38:02.127Z",
+ "updated_at": "2018-03-04T13:38:02.127Z",
+ "system": false,
+ "noteable_id": 3,
+ "noteable_type": "Merge request",
+ "noteable_iid": null,
+ "resolved": false,
+ "resolvable": true,
+ "resolved_by": null
+ }
+ ]
+ },
+ {
+ "id": "87805b7c09016a7058e91bdbe7b29d1f284a39e6",
+ "individual_note": true,
+ "notes": [
+ {
+ "id": 1128,
+ "type": null,
+ "body": "a single comment",
+ "attachment": null,
+ "author": {
+ "id": 1,
+ "name": "root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
+ "web_url": "http://localhost:3000/root"
+ },
+ "created_at": "2018-03-04T09:17:22.520Z",
+ "updated_at": "2018-03-04T09:17:22.520Z",
+ "system": false,
+ "noteable_id": 3,
+ "noteable_type": "Merge request",
+ "noteable_iid": null,
+ "resolved": false,
+ "resolvable": true,
+ "resolved_by": null
+ }
+ ]
+ }
+]
+```
+
+Diff comments contain also position:
+
+```json
+[
+ {
+ "id": "87805b7c09016a7058e91bdbe7b29d1f284a39e6",
+ "individual_note": false,
+ "notes": [
+ {
+ "id": 1128,
+ "type": DiffNote,
+ "body": "diff comment",
+ "attachment": null,
+ "author": {
+ "id": 1,
+ "name": "root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
+ "web_url": "http://localhost:3000/root"
+ },
+ "created_at": "2018-03-04T09:17:22.520Z",
+ "updated_at": "2018-03-04T09:17:22.520Z",
+ "system": false,
+ "noteable_id": 3,
+ "noteable_type": "Merge request",
+ "noteable_iid": null,
+ "position": {
+ "base_sha": "b5d6e7b1613fca24d250fa8e5bc7bcc3dd6002ef",
+ "start_sha": "7c9c2ead8a320fb7ba0b4e234bd9529a2614e306",
+ "head_sha": "4803c71e6b1833ca72b8b26ef2ecd5adc8a38031",
+ "old_path": "package.json",
+ "new_path": "package.json",
+ "position_type": "text",
+ "old_line": 27,
+ "new_line": 27
+ },
+ "resolved": false,
+ "resolvable": true,
+ "resolved_by": null
+ }
+ ]
+ }
+]
+```
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions
+```
+
+### Get single merge request discussion
+
+Returns a single discussion for a specific project merge request
+
+```
+GET /projects/:id/merge_requests/:merge_request_iid/discussions/:discussion_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
+| `discussion_id` | integer | yes | The ID of a discussion |
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7
+```
+
+### Create new merge request discussion
+
+Creates a new discussion to a single project merge request. This is similar to creating
+a note but but another comments (replies) can be added to it later.
+
+```
+POST /projects/:id/merge_requests/:merge_request_iid/discussions
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
+| `body` | string | yes | The content of a discussion |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `position` | hash | no | Position when creating a diff note |
+| `position[base_sha]` | string | yes | Base commit SHA in the source branch |
+| `position[start_sha]` | string | yes | SHA referencing commit in target branch |
+| `position[head_sha]` | string | yes | SHA referencing HEAD of this merge request |
+| `position[position_type]` | string | yes | Type of the position reference', allowed values: 'text' or 'image' |
+| `position[new_path]` | string | no | File path after change |
+| `position[new_line]` | integer | no | Line number after change (for 'text' diff notes) |
+| `position[old_path]` | string | no | File path before change |
+| `position[old_line]` | integer | no | Line number before change (for 'text' diff notes) |
+| `position[width]` | integer | no | Width of the image (for 'image' diff notes) |
+| `position[height]` | integer | no | Height of the image (for 'image' diff notes) |
+| `position[x]` | integer | no | X coordinate (for 'image' diff notes) |
+| `position[y]` | integer | no | Y coordinate (for 'image' diff notes) |
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions?body=comment
+```
+
+### Resolve a merge request discussion
+
+Resolve/unresolve whole discussion of a merge request.
+
+```
+PUT /projects/:id/merge_requests/:merge_request_iid/discussions/:discussion_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
+| `discussion_id` | integer | yes | The ID of a discussion |
+| `resolved` | boolean | yes | Resolve/unresolve the discussion |
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7?resolved=true
+```
+
+
+### Add note to existing merge request discussion
+
+Adds a new note to the discussion.
+
+```
+POST /projects/:id/merge_requests/:merge_request_iid/discussions/:discussion_id/notes
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
+| `discussion_id` | integer | yes | The ID of a discussion |
+| `note_id` | integer | yes | The ID of a discussion note |
+| `body` | string | yes | The content of a discussion |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment
+```
+
+### Modify an existing merge request discussion note
+
+Modify or resolve an existing discussion note of a merge request.
+
+```
+PUT /projects/:id/merge_requests/:merge_request_iid/discussions/:discussion_id/notes/:note_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
+| `discussion_id` | integer | yes | The ID of a discussion |
+| `note_id` | integer | yes | The ID of a discussion note |
+| `body` | string | no | The content of a discussion (exactly one of `body` or `resolved` must be set |
+| `resolved` | boolean | no | Resolve/unresolve the note (exactly one of `body` or `resolved` must be set |
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/1108?body=comment
+```
+
+Resolving a note:
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/1108?resolved=true
+```
+
+### Delete a merge request discussion note
+
+Deletes an existing discussion note of a merge request.
+
+```
+DELETE /projects/:id/merge_requests/:merge_request_iid/discussions/:discussion_id/notes/:note_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `merge_request_iid` | integer | yes | The IID of a merge request |
+| `discussion_id` | integer | yes | The ID of a discussion |
+| `note_id` | integer | yes | The ID of a discussion note |
+
+```bash
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/merge_requests/11/discussions/636
+```
+
+## Commits
+
+### List project commit discussions
+
+Gets a list of all discussions for a single commit.
+
+```
+GET /projects/:id/commits/:commit_id/discussions
+```
+
+| Attribute | Type | Required | Description |
+| ------------------- | ---------------- | ---------- | ------------ |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `commit_id` | integer | yes | The ID of a commit |
+
+```json
+[
+ {
+ "id": "6a9c1750b37d513a43987b574953fceb50b03ce7",
+ "individual_note": false,
+ "notes": [
+ {
+ "id": 1126,
+ "type": "DiscussionNote",
+ "body": "discussion text",
+ "attachment": null,
+ "author": {
+ "id": 1,
+ "name": "root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
+ "web_url": "http://localhost:3000/root"
+ },
+ "created_at": "2018-03-03T21:54:39.668Z",
+ "updated_at": "2018-03-03T21:54:39.668Z",
+ "system": false,
+ "noteable_id": 3,
+ "noteable_type": "Commit",
+ "noteable_iid": null,
+ "resolvable": false
+ },
+ {
+ "id": 1129,
+ "type": "DiscussionNote",
+ "body": "reply to the discussion",
+ "attachment": null,
+ "author": {
+ "id": 1,
+ "name": "root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
+ "web_url": "http://localhost:3000/root"
+ },
+ "created_at": "2018-03-04T13:38:02.127Z",
+ "updated_at": "2018-03-04T13:38:02.127Z",
+ "system": false,
+ "noteable_id": 3,
+ "noteable_type": "Commit",
+ "noteable_iid": null,
+ "resolvable": false
+ }
+ ]
+ },
+ {
+ "id": "87805b7c09016a7058e91bdbe7b29d1f284a39e6",
+ "individual_note": true,
+ "notes": [
+ {
+ "id": 1128,
+ "type": null,
+ "body": "a single comment",
+ "attachment": null,
+ "author": {
+ "id": 1,
+ "name": "root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
+ "web_url": "http://localhost:3000/root"
+ },
+ "created_at": "2018-03-04T09:17:22.520Z",
+ "updated_at": "2018-03-04T09:17:22.520Z",
+ "system": false,
+ "noteable_id": 3,
+ "noteable_type": "Commit",
+ "noteable_iid": null,
+ "resolvable": false
+ }
+ ]
+ }
+]
+```
+
+Diff comments contain also position:
+
+```json
+[
+ {
+ "id": "87805b7c09016a7058e91bdbe7b29d1f284a39e6",
+ "individual_note": false,
+ "notes": [
+ {
+ "id": 1128,
+ "type": DiffNote,
+ "body": "diff comment",
+ "attachment": null,
+ "author": {
+ "id": 1,
+ "name": "root",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/00afb8fb6ab07c3ee3e9c1f38777e2f4?s=80&d=identicon",
+ "web_url": "http://localhost:3000/root"
+ },
+ "created_at": "2018-03-04T09:17:22.520Z",
+ "updated_at": "2018-03-04T09:17:22.520Z",
+ "system": false,
+ "noteable_id": 3,
+ "noteable_type": "Commit",
+ "noteable_iid": null,
+ "position": {
+ "base_sha": "b5d6e7b1613fca24d250fa8e5bc7bcc3dd6002ef",
+ "start_sha": "7c9c2ead8a320fb7ba0b4e234bd9529a2614e306",
+ "head_sha": "4803c71e6b1833ca72b8b26ef2ecd5adc8a38031",
+ "old_path": "package.json",
+ "new_path": "package.json",
+ "position_type": "text",
+ "old_line": 27,
+ "new_line": 27
+ },
+ "resolvable": false
+ }
+ ]
+ }
+]
+```
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/commits/11/discussions
+```
+
+### Get single commit discussion
+
+Returns a single discussion for a specific project commit
+
+```
+GET /projects/:id/commits/:commit_id/discussions/:discussion_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `commit_id` | integer | yes | The ID of a commit |
+| `discussion_id` | integer | yes | The ID of a discussion |
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/commits/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7
+```
+
+### Create new commit discussion
+
+Creates a new discussion to a single project commit. This is similar to creating
+a note but but another comments (replies) can be added to it later.
+
+```
+POST /projects/:id/commits/:commit_id/discussions
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `commit_id` | integer | yes | The ID of a commit |
+| `body` | string | yes | The content of a discussion |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+| `position` | hash | no | Position when creating a diff note |
+| `position[base_sha]` | string | yes | Base commit SHA in the source branch |
+| `position[start_sha]` | string | yes | SHA referencing commit in target branch |
+| `position[head_sha]` | string | yes | SHA referencing HEAD of this commit |
+| `position[position_type]` | string | yes | Type of the position reference', allowed values: 'text' or 'image' |
+| `position[new_path]` | string | no | File path after change |
+| `position[new_line]` | integer | no | Line number after change |
+| `position[old_path]` | string | no | File path before change |
+| `position[old_line]` | integer | no | Line number before change |
+| `position[width]` | integer | no | Width of the image (for 'image' diff notes) |
+| `position[height]` | integer | no | Height of the image (for 'image' diff notes) |
+| `position[x]` | integer | no | X coordinate (for 'image' diff notes) |
+| `position[y]` | integer | no | Y coordinate (for 'image' diff notes) |
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/commits/11/discussions?body=comment
+```
+
+### Add note to existing commit discussion
+
+Adds a new note to the discussion.
+
+```
+POST /projects/:id/commits/:commit_id/discussions/:discussion_id/notes
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `commit_id` | integer | yes | The ID of a commit |
+| `discussion_id` | integer | yes | The ID of a discussion |
+| `note_id` | integer | yes | The ID of a discussion note |
+| `body` | string | yes | The content of a discussion |
+| `created_at` | string | no | Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z |
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/commits/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes?body=comment
+```
+
+### Modify an existing commit discussion note
+
+Modify or resolve an existing discussion note of a commit.
+
+```
+PUT /projects/:id/commits/:commit_id/discussions/:discussion_id/notes/:note_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `commit_id` | integer | yes | The ID of a commit |
+| `discussion_id` | integer | yes | The ID of a discussion |
+| `note_id` | integer | yes | The ID of a discussion note |
+| `body` | string | no | The content of a note |
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/commits/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/1108?body=comment
+```
+
+Resolving a note:
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/commits/11/discussions/6a9c1750b37d513a43987b574953fceb50b03ce7/notes/1108?resolved=true
+```
+
+### Delete a commit discussion note
+
+Deletes an existing discussion note of a commit.
+
+```
+DELETE /projects/:id/commits/:commit_id/discussions/:discussion_id/notes/:note_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+| ------------------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `commit_id` | integer | yes | The ID of a commit |
+| `discussion_id` | integer | yes | The ID of a discussion |
+| `note_id` | integer | yes | The ID of a discussion note |
+
+```bash
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/commits/11/discussions/636
+```
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 1aed8aac64e..923fd662a5b 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -10,7 +10,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
@@ -94,7 +94,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) of the parent group |
| `skip_groups` | array of integers | no | Skip the group IDs passed |
-| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users) |
+| `all_available` | boolean | no | Show all the groups you have access to (defaults to `false` for authenticated users, `true` for admin) |
| `search` | string | no | Return the list of authorized groups matching the search criteria |
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
diff --git a/doc/api/notes.md b/doc/api/notes.md
index aa38d22845c..d29c5b94915 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -39,7 +39,8 @@ GET /projects/:id/issues/:issue_iid/notes?sort=asc&order_by=updated_at
"system": true,
"noteable_id": 377,
"noteable_type": "Issue",
- "noteable_iid": 377
+ "noteable_iid": 377,
+ "resolvable": false
},
{
"id": 305,
@@ -58,7 +59,8 @@ GET /projects/:id/issues/:issue_iid/notes?sort=asc&order_by=updated_at
"system": true,
"noteable_id": 121,
"noteable_type": "Issue",
- "noteable_iid": 121
+ "noteable_iid": 121,
+ "resolvable": false
}
]
```
@@ -314,7 +316,8 @@ Parameters:
"system": false,
"noteable_id": 2,
"noteable_type": "MergeRequest",
- "noteable_iid": 2
+ "noteable_iid": 2,
+ "resolvable": false
}
```
diff --git a/doc/development/README.md b/doc/development/README.md
index 99c6641e637..3c77e99b8cf 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -41,6 +41,7 @@ comments: false
- [Avoid modules with instance variables](module_with_instance_variables.md) if possible
- [How to dump production data to staging](db_dump.md)
- [Working with the GitHub importer](github_importer.md)
+- [Working with Merge Request diffs](diffs.md)
## Performance guides
diff --git a/doc/development/diffs.md b/doc/development/diffs.md
new file mode 100644
index 00000000000..55fc16e0b33
--- /dev/null
+++ b/doc/development/diffs.md
@@ -0,0 +1,115 @@
+# Working with Merge Request diffs
+
+Currently we rely on different sources to present merge request diffs, these include:
+
+- Rugged gem
+- Gitaly service
+- Database (through `merge_request_diff_files`)
+- Redis (cached highlighted diffs)
+
+We're constantly moving Rugged calls to Gitaly and the progress can be followed through [Gitaly repo](https://gitlab.com/gitlab-org/gitaly).
+
+## Architecture overview
+
+When refreshing a Merge Request (pushing to a source branch, force-pushing to target branch, or if the target branch now contains any commits from the MR)
+we fetch the comparison information using `Gitlab::Git::Compare`, which fetches `base` and `head` data using Gitaly and diff between them through
+`Gitlab::Git::Diff.between` (which uses _Gitaly_ if it's enabled, otherwise _Rugged_).
+The diffs fetching process _limits_ single file diff sizes and the overall size of the whole diff through a series of constant values. Raw diff files are
+then persisted on `merge_request_diff_files` table.
+
+Even though diffs higher than 10kb are collapsed (`Gitlab::Git::Diff::COLLAPSE_LIMIT`), we still keep them on Postgres. However, diff files over _safety limits_
+(see the [Diff limits section](#diff-limits)) are _not_ persisted.
+
+In order to present diffs information on the Merge Request diffs page, we:
+
+1. Fetch all diff files from database `merge_request_diff_files`
+2. Fetch the _old_ and _new_ file blobs in batch to:
+ 1. Highlight old and new file content
+ 2. Know which viewer it should use for each file (text, image, deleted, etc)
+ 3. Know if the file content changed
+ 4. Know if it was stored externally
+ 5. Know if it had storage errors
+3. If the diff file is cacheable (text-based), it's cached on Redis
+using `Gitlab::Diff::FileCollection::MergeRequestDiff`
+
+## Diff limits
+
+As explained above, we limit single diff files and the size of the whole diff. There are scenarios where we collapse the diff file,
+and cases where the diff file is not presented at all, and the user is guided to the Blob view. Here we'll go into details about
+these limits.
+
+### Diff collection limits
+
+Limits that act onto all diff files collection. Files number, lines number and files size are considered.
+
+```ruby
+Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_files] = 100
+```
+
+File diffs will be collapsed (but be expandable) if 100 files have already been rendered.
+
+
+```ruby
+Gitlab::Git::DiffCollection.collection_limits[:safe_max_lines] = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
+```
+
+File diffs will be collapsed (but be expandable) if 5000 lines have already been rendered.
+
+
+```ruby
+Gitlab::Git::DiffCollection.collection_limits[:safe_max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:safe_max_files] * 5.kilobytes = 500.kilobytes
+```
+
+File diffs will be collapsed (but be expandable) if 500 kilobytes have already been rendered.
+
+
+```ruby
+Gitlab::Git::DiffCollection.collection_limits[:max_files] = Commit::DIFF_HARD_LIMIT_FILES = 1000
+```
+
+No more files will be rendered at all if 1000 files have already been rendered.
+
+
+```ruby
+Gitlab::Git::DiffCollection.collection_limits[:max_lines] = Commit::DIFF_HARD_LIMIT_LINES = 50000
+```
+
+No more files will be rendered at all if 50,000 lines have already been rendered.
+
+```ruby
+Gitlab::Git::DiffCollection.collection_limits[:max_bytes] = Gitlab::Git::DiffCollection.collection_limits[:max_files] * 5.kilobytes = 5000.kilobytes
+```
+
+No more files will be rendered at all if 5 megabytes have already been rendered.
+
+
+### Individual diff file limits
+
+Limits that act onto each diff file of a collection. Files number, lines number and files size are considered.
+
+```ruby
+Gitlab::Git::Diff::COLLAPSE_LIMIT = 10.kilobytes
+```
+
+File diff will be collapsed (but be expandable) if it is larger than 10 kilobytes.
+
+```ruby
+Gitlab::Git::Diff::SIZE_LIMIT = 100.kilobytes
+```
+
+File diff will not be rendered if it's larger than 100 kilobytes.
+
+
+```ruby
+Commit::DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines] = 5000
+```
+
+File diff will be suppressed (technically different from collapsed, but behaves the same, and is expandable) if it has more than 5000 lines.
+
+## Viewers
+
+Diff Viewers, which can be found on `models/diff_viewer/*` are classes used to map metadata about each type of Diff File. It has information
+whether it's a binary, which partial should be used to render it or which File extensions this class accounts for.
+
+`DiffViewer::Base` validates _blobs_ (old and new versions) content, extension and file type in order to check if it can be rendered.
+
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index af477f5ab99..0d0d511582b 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -62,6 +62,7 @@ describe('.methodName', () => {
});
});
```
+
#### Testing promises
When testing Promises you should always make sure that the test is asynchronous and rejections are handled.
@@ -69,9 +70,9 @@ Your Promise chain should therefore end with a call of the `done` callback and `
```javascript
// Good
-it('tests a promise', (done) => {
+it('tests a promise', done => {
promise
- .then((data) => {
+ .then(data => {
expect(data).toBe(asExpected);
})
.then(done)
@@ -79,10 +80,10 @@ it('tests a promise', (done) => {
});
// Good
-it('tests a promise rejection', (done) => {
+it('tests a promise rejection', done => {
promise
.then(done.fail)
- .catch((error) => {
+ .catch(error => {
expect(error).toBe(expectedError);
})
.then(done)
@@ -91,38 +92,37 @@ it('tests a promise rejection', (done) => {
// Bad (missing done callback)
it('tests a promise', () => {
- promise
- .then((data) => {
- expect(data).toBe(asExpected);
- })
+ promise.then(data => {
+ expect(data).toBe(asExpected);
+ });
});
// Bad (missing catch)
-it('tests a promise', (done) => {
+it('tests a promise', done => {
promise
- .then((data) => {
+ .then(data => {
expect(data).toBe(asExpected);
})
- .then(done)
+ .then(done);
});
// Bad (use done.fail in asynchronous tests)
-it('tests a promise', (done) => {
+it('tests a promise', done => {
promise
- .then((data) => {
+ .then(data => {
expect(data).toBe(asExpected);
})
.then(done)
- .catch(fail)
+ .catch(fail);
});
// Bad (missing catch)
-it('tests a promise rejection', (done) => {
+it('tests a promise rejection', done => {
promise
- .catch((error) => {
+ .catch(error => {
expect(error).toBe(expectedError);
})
- .then(done)
+ .then(done);
});
```
@@ -139,7 +139,7 @@ documentation for these methods can be found in the [jasmine introduction page](
Sometimes you may need to spy on a method that is directly imported by another
module. GitLab has a custom `spyOnDependency` method which utilizes
[babel-plugin-rewire](https://github.com/speedskater/babel-plugin-rewire) to
-achieve this. It can be used like so:
+achieve this. It can be used like so:
```javascript
// my_module.js
@@ -181,8 +181,8 @@ See this [section][vue-test].
`rake karma` runs the frontend-only (JavaScript) tests.
It consists of two subtasks:
-- `rake karma:fixtures` (re-)generates fixtures
-- `rake karma:tests` actually executes the tests
+* `rake karma:fixtures` (re-)generates fixtures
+* `rake karma:tests` actually executes the tests
As long as the fixtures don't change, `rake karma:tests` (or `yarn karma`)
is sufficient (and saves you some time).
@@ -217,6 +217,14 @@ yarn karma-start --filter-spec profile/account/components/
yarn karma-start -f vue_shared -f vue_mr_widget
```
+You can also use glob syntax to match files. Remember to put quotes around the
+glob otherwise your shell may split it into multiple arguments:
+
+```bash
+# Run all specs named `file_spec` within the IDE subdirectory
+yarn karma -f 'spec/javascripts/ide/**/file_spec.js'
+```
+
## RSpec feature integration tests
Information on setting up and running RSpec integration tests with
@@ -231,14 +239,14 @@ supported by the PhantomJS test runner which is used for both Karma and RSpec
tests. We polyfill some JavaScript objects for older browsers, but some
features are still unavailable:
-- Array.from
-- Array.first
-- Async functions
-- Generators
-- Array destructuring
-- For..Of
-- Symbol/Symbol.iterator
-- Spread
+* Array.from
+* Array.first
+* Async functions
+* Generators
+* Array destructuring
+* For..Of
+* Symbol/Symbol.iterator
+* Spread
Until these are polyfilled appropriately, they should not be used. Please
update this list with additional unsupported features.
@@ -295,11 +303,11 @@ Scenario: Developer can approve merge request
[jasmine-focus]: https://jasmine.github.io/2.5/focused_specs.html
[jasmine-jquery]: https://github.com/velesin/jasmine-jquery
[karma]: http://karma-runner.github.io/
-[vue-test]:https://docs.gitlab.com/ce/development/fe_guide/vue.html#testing-vue-components
-[RSpec]: https://github.com/rspec/rspec-rails#feature-specs
-[Capybara]: https://github.com/teamcapybara/capybara
-[Karma]: http://karma-runner.github.io/
-[Jasmine]: https://jasmine.github.io/
+[vue-test]: https://docs.gitlab.com/ce/development/fe_guide/vue.html#testing-vue-components
+[rspec]: https://github.com/rspec/rspec-rails#feature-specs
+[capybara]: https://github.com/teamcapybara/capybara
+[karma]: http://karma-runner.github.io/
+[jasmine]: https://jasmine.github.io/
---
diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
index 0e29740b15f..0d592a6d43e 100644
--- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
+++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
@@ -251,13 +251,4 @@ It is possible to host LFS objects externally by setting a custom LFS url with `
Because GitLab verifies the existence of objects referenced by LFS pointers, push will fail when LFS is enabled for the project.
-LFS can be disabled for a project by Owners and Masters using the [Project API](../../api/projects.md#edit-project).
-
-```bash
-curl --request PUT \
- --url https://example.com/api/v4/projects/<PROJECT_ID> \
- --header 'Private-Token: <YOUR_PRIVATE_TOKEN>' \
- --data 'lfs_enabled=false'
-```
-
-Note, `<PROJECT_ID>` can also be substituted with a [namespaced path](../../api/README.md#namespaced-path-encoding).
+LFS can be disabled from the [Project settings](../../user/project/settings/index.md).
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index 7975f35ab1e..13c34e3473a 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -5,11 +5,12 @@ module API
before { authenticate! }
- NOTEABLE_TYPES = [Issue, Snippet].freeze
+ NOTEABLE_TYPES = [Issue, Snippet, MergeRequest, Commit].freeze
NOTEABLE_TYPES.each do |noteable_type|
parent_type = noteable_type.parent_class.to_s.underscore
noteables_str = noteable_type.to_s.underscore.pluralize
+ noteables_path = noteable_type == Commit ? "repository/#{noteables_str}" : noteables_str
params do
requires :id, type: String, desc: "The ID of a #{parent_type}"
@@ -19,14 +20,12 @@ module API
success Entities::Discussion
end
params do
- requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
+ requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
use :pagination
end
- get ":id/#{noteables_str}/:noteable_id/discussions" do
+ get ":id/#{noteables_path}/:noteable_id/discussions" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
- break not_found!("Discussions") unless can?(current_user, noteable_read_ability_name(noteable), noteable)
-
notes = noteable.notes
.inc_relations_for_view
.includes(:noteable)
@@ -43,13 +42,13 @@ module API
end
params do
requires :discussion_id, type: String, desc: 'The ID of a discussion'
- requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
+ requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
end
- get ":id/#{noteables_str}/:noteable_id/discussions/:discussion_id" do
+ get ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
notes = readable_discussion_notes(noteable, params[:discussion_id])
- if notes.empty? || !can?(current_user, noteable_read_ability_name(noteable), noteable)
+ if notes.empty?
break not_found!("Discussion")
end
@@ -62,19 +61,36 @@ module API
success Entities::Discussion
end
params do
- requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
+ requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
requires :body, type: String, desc: 'The content of a note'
optional :created_at, type: String, desc: 'The creation date of the note'
+ optional :position, type: Hash do
+ requires :base_sha, type: String, desc: 'Base commit SHA in the source branch'
+ requires :start_sha, type: String, desc: 'SHA referencing commit in target branch'
+ requires :head_sha, type: String, desc: 'SHA referencing HEAD of this merge request'
+ requires :position_type, type: String, desc: 'Type of the position reference', values: %w(text image)
+ optional :new_path, type: String, desc: 'File path after change'
+ optional :new_line, type: Integer, desc: 'Line number after change'
+ optional :old_path, type: String, desc: 'File path before change'
+ optional :old_line, type: Integer, desc: 'Line number before change'
+ optional :width, type: Integer, desc: 'Width of the image'
+ optional :height, type: Integer, desc: 'Height of the image'
+ optional :x, type: Integer, desc: 'X coordinate in the image'
+ optional :y, type: Integer, desc: 'Y coordinate in the image'
+ end
end
- post ":id/#{noteables_str}/:noteable_id/discussions" do
+ post ":id/#{noteables_path}/:noteable_id/discussions" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+ type = params[:position] ? 'DiffNote' : 'DiscussionNote'
+ id_key = noteable.is_a?(Commit) ? :commit_id : :noteable_id
opts = {
note: params[:body],
created_at: params[:created_at],
- type: 'DiscussionNote',
+ type: type,
noteable_type: noteables_str.classify,
- noteable_id: noteable.id
+ position: params[:position],
+ id_key => noteable.id
}
note = create_note(noteable, opts)
@@ -91,13 +107,13 @@ module API
end
params do
requires :discussion_id, type: String, desc: 'The ID of a discussion'
- requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
+ requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
end
- get ":id/#{noteables_str}/:noteable_id/discussions/:discussion_id/notes" do
+ get ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
notes = readable_discussion_notes(noteable, params[:discussion_id])
- if notes.empty? || !can?(current_user, noteable_read_ability_name(noteable), noteable)
+ if notes.empty?
break not_found!("Notes")
end
@@ -108,12 +124,12 @@ module API
success Entities::Note
end
params do
- requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
+ requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
requires :discussion_id, type: String, desc: 'The ID of a discussion'
requires :body, type: String, desc: 'The content of a note'
optional :created_at, type: String, desc: 'The creation date of the note'
end
- post ":id/#{noteables_str}/:noteable_id/discussions/:discussion_id/notes" do
+ post ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
notes = readable_discussion_notes(noteable, params[:discussion_id])
@@ -139,11 +155,11 @@ module API
success Entities::Note
end
params do
- requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
+ requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
requires :discussion_id, type: String, desc: 'The ID of a discussion'
requires :note_id, type: Integer, desc: 'The ID of a note'
end
- get ":id/#{noteables_str}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
+ get ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
get_note(noteable, params[:note_id])
@@ -153,30 +169,52 @@ module API
success Entities::Note
end
params do
- requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
+ requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
requires :discussion_id, type: String, desc: 'The ID of a discussion'
requires :note_id, type: Integer, desc: 'The ID of a note'
- requires :body, type: String, desc: 'The content of a note'
+ optional :body, type: String, desc: 'The content of a note'
+ optional :resolved, type: Boolean, desc: 'Mark note resolved/unresolved'
+ exactly_one_of :body, :resolved
end
- put ":id/#{noteables_str}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
+ put ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
- update_note(noteable, params[:note_id])
+ if params[:resolved].nil?
+ update_note(noteable, params[:note_id])
+ else
+ resolve_note(noteable, params[:note_id], params[:resolved])
+ end
end
desc "Delete a comment in a #{noteable_type.to_s.downcase} discussion" do
success Entities::Note
end
params do
- requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
+ requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
requires :discussion_id, type: String, desc: 'The ID of a discussion'
requires :note_id, type: Integer, desc: 'The ID of a note'
end
- delete ":id/#{noteables_str}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
+ delete ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
delete_note(noteable, params[:note_id])
end
+
+ if Noteable::RESOLVABLE_TYPES.include?(noteable_type.to_s)
+ desc "Resolve/unresolve an existing #{noteable_type.to_s.downcase} discussion" do
+ success Entities::Discussion
+ end
+ params do
+ requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
+ requires :discussion_id, type: String, desc: 'The ID of a discussion'
+ requires :resolved, type: Boolean, desc: 'Mark discussion resolved/unresolved'
+ end
+ put ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id" do
+ noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
+
+ resolve_discussion(noteable, params[:discussion_id], params[:resolved])
+ end
+ end
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 8aad320e376..75d56b82424 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -286,6 +286,10 @@ module API
end
end
+ class DiffRefs < Grape::Entity
+ expose :base_sha, :head_sha, :start_sha
+ end
+
class Commit < Grape::Entity
expose :id, :short_id, :title, :created_at
expose :parent_ids
@@ -601,6 +605,8 @@ module API
merge_request.metrics&.pipeline
end
+ expose :diff_refs, using: Entities::DiffRefs
+
def build_available?(options)
options[:project]&.feature_available?(:builds, options[:current_user])
end
@@ -642,6 +648,11 @@ module API
expose :id, :key, :created_at
end
+ class DiffPosition < Grape::Entity
+ expose :base_sha, :start_sha, :head_sha, :old_path, :new_path,
+ :position_type
+ end
+
class Note < Grape::Entity
# Only Issue and MergeRequest have iid
NOTEABLE_TYPES_WITH_IID = %w(Issue MergeRequest).freeze
@@ -655,6 +666,14 @@ module API
expose :system?, as: :system
expose :noteable_id, :noteable_type
+ expose :position, if: ->(note, options) { note.diff_note? } do |note|
+ note.position.to_h
+ end
+
+ expose :resolvable?, as: :resolvable
+ expose :resolved?, as: :resolved, if: ->(note, options) { note.resolvable? }
+ expose :resolved_by, using: Entities::UserBasic, if: ->(note, options) { note.resolvable? }
+
# Avoid N+1 queries as much as possible
expose(:noteable_iid) { |note| note.noteable.iid if NOTEABLE_TYPES_WITH_IID.include?(note.noteable_type) }
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 4a4df1b8b9e..92e3d5cc10a 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -37,13 +37,11 @@ module API
use :pagination
end
- def find_groups(params)
- find_params = {
- all_available: params[:all_available],
- custom_attributes: params[:custom_attributes],
- owned: params[:owned]
- }
- find_params[:parent] = find_group!(params[:id]) if params[:id]
+ def find_groups(params, parent_id = nil)
+ find_params = params.slice(:all_available, :custom_attributes, :owned)
+ find_params[:parent] = find_group!(parent_id) if parent_id
+ find_params[:all_available] =
+ find_params.fetch(:all_available, current_user&.full_private_access?)
groups = GroupsFinder.new(current_user, find_params).execute
groups = groups.search(params[:search]) if params[:search].present?
@@ -85,7 +83,7 @@ module API
use :with_custom_attributes
end
get do
- groups = find_groups(params)
+ groups = find_groups(declared_params(include_missing: false), params[:id])
present_groups params, groups
end
@@ -213,7 +211,7 @@ module API
use :with_custom_attributes
end
get ":id/subgroups" do
- groups = find_groups(params)
+ groups = find_groups(declared_params(include_missing: false), params[:id])
present_groups params, groups
end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index b8657cd7ee4..2ed331d4fd2 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -171,6 +171,10 @@ module API
MergeRequestsFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
end
+ def find_project_commit(id)
+ user_project.commit_by(oid: id)
+ end
+
def find_project_snippet(id)
finder_params = { project: user_project }
SnippetsFinder.new(current_user, finder_params).find(id)
diff --git a/lib/api/helpers/custom_attributes.rb b/lib/api/helpers/custom_attributes.rb
index 70e4eda95f8..10d652e33f5 100644
--- a/lib/api/helpers/custom_attributes.rb
+++ b/lib/api/helpers/custom_attributes.rb
@@ -7,6 +7,9 @@ module API
helpers do
params :with_custom_attributes do
optional :with_custom_attributes, type: Boolean, default: false, desc: 'Include custom attributes in the response'
+
+ optional :custom_attributes, type: Hash,
+ desc: 'Filter with custom attributes'
end
def with_custom_attributes(collection_or_resource, options = {})
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index b74b8149834..b4bfb677d72 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -21,6 +21,23 @@ module API
end
end
+ def resolve_note(noteable, note_id, resolved)
+ note = noteable.notes.find(note_id)
+
+ authorize! :resolve_note, note
+
+ bad_request!("Note is not resolvable") unless note.resolvable?
+
+ if resolved
+ parent = noteable_parent(noteable)
+ ::Notes::ResolveService.new(parent, current_user).execute(note)
+ else
+ note.unresolve!
+ end
+
+ present note, with: Entities::Note
+ end
+
def delete_note(noteable, note_id)
note = noteable.notes.find(note_id)
@@ -35,7 +52,7 @@ module API
def get_note(noteable, note_id)
note = noteable.notes.with_metadata.find(params[:note_id])
- can_read_note = can?(current_user, noteable_read_ability_name(noteable), noteable) && !note.cross_reference_not_visible_for?(current_user)
+ can_read_note = !note.cross_reference_not_visible_for?(current_user)
if can_read_note
present note, with: Entities::Note
@@ -49,7 +66,20 @@ module API
end
def find_noteable(parent, noteables_str, noteable_id)
- public_send("find_#{parent}_#{noteables_str.singularize}", noteable_id) # rubocop:disable GitlabSecurity/PublicSend
+ noteable = public_send("find_#{parent}_#{noteables_str.singularize}", noteable_id) # rubocop:disable GitlabSecurity/PublicSend
+
+ readable =
+ if noteable.is_a?(Commit)
+ # for commits there is not :read_commit policy, check if user
+ # has :read_note permission on the commit's project
+ can?(current_user, :read_note, user_project)
+ else
+ can?(current_user, noteable_read_ability_name(noteable), noteable)
+ end
+
+ return not_found!(noteables_str) unless readable
+
+ noteable
end
def noteable_parent(noteable)
@@ -57,11 +87,8 @@ module API
end
def create_note(noteable, opts)
- noteables_str = noteable.model_name.to_s.underscore.pluralize
-
- return not_found!(noteables_str) unless can?(current_user, noteable_read_ability_name(noteable), noteable)
-
- authorize! :create_note, noteable
+ policy_object = noteable.is_a?(Commit) ? user_project : noteable
+ authorize!(:create_note, policy_object)
parent = noteable_parent(noteable)
@@ -73,6 +100,21 @@ module API
project = parent if parent.is_a?(Project)
::Notes::CreateService.new(project, current_user, opts).execute
end
+
+ def resolve_discussion(noteable, discussion_id, resolved)
+ discussion = noteable.find_discussion(discussion_id)
+
+ forbidden! unless discussion.can_resolve?(current_user)
+
+ if resolved
+ parent = noteable_parent(noteable)
+ ::Discussions::ResolveService.new(parent, current_user, merge_request: noteable).execute(discussion)
+ else
+ discussion.unresolve!
+ end
+
+ present discussion, with: Entities::Discussion
+ end
end
end
end
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 69f1df6b341..39923e6d5b5 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -31,23 +31,19 @@ module API
get ":id/#{noteables_str}/:noteable_id/notes" do
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
- if can?(current_user, noteable_read_ability_name(noteable), noteable)
- # We exclude notes that are cross-references and that cannot be viewed
- # by the current user. By doing this exclusion at this level and not
- # at the DB query level (which we cannot in that case), the current
- # page can have less elements than :per_page even if
- # there's more than one page.
- raw_notes = noteable.notes.with_metadata.reorder(params[:order_by] => params[:sort])
- notes =
- # paginate() only works with a relation. This could lead to a
- # mismatch between the pagination headers info and the actual notes
- # array returned, but this is really a edge-case.
- paginate(raw_notes)
- .reject { |n| n.cross_reference_not_visible_for?(current_user) }
- present notes, with: Entities::Note
- else
- not_found!("Notes")
- end
+ # We exclude notes that are cross-references and that cannot be viewed
+ # by the current user. By doing this exclusion at this level and not
+ # at the DB query level (which we cannot in that case), the current
+ # page can have less elements than :per_page even if
+ # there's more than one page.
+ raw_notes = noteable.notes.with_metadata.reorder(params[:order_by] => params[:sort])
+ notes =
+ # paginate() only works with a relation. This could lead to a
+ # mismatch between the pagination headers info and the actual notes
+ # array returned, but this is really a edge-case.
+ paginate(raw_notes)
+ .reject { |n| n.cross_reference_not_visible_for?(current_user) }
+ present notes, with: Entities::Note
end
desc "Get a single #{noteable_type.to_s.downcase} note" do
diff --git a/lib/banzai/filter/commit_trailers_filter.rb b/lib/banzai/filter/commit_trailers_filter.rb
index ef16df1f3ae..7b55e8b36f6 100644
--- a/lib/banzai/filter/commit_trailers_filter.rb
+++ b/lib/banzai/filter/commit_trailers_filter.rb
@@ -13,7 +13,6 @@ module Banzai
# * https://git.wiki.kernel.org/index.php/CommitMessageConventions
class CommitTrailersFilter < HTML::Pipeline::Filter
include ActionView::Helpers::TagHelper
- include ApplicationHelper
include AvatarsHelper
TRAILER_REGEXP = /(?<label>[[:alpha:]-]+-by:)/i.freeze
diff --git a/lib/gitlab/background_migration/migrate_stage_index.rb b/lib/gitlab/background_migration/migrate_stage_index.rb
new file mode 100644
index 00000000000..f90f35a913d
--- /dev/null
+++ b/lib/gitlab/background_migration/migrate_stage_index.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+# rubocop:disable Style/Documentation
+
+module Gitlab
+ module BackgroundMigration
+ class MigrateStageIndex
+ def perform(start_id, stop_id)
+ migrate_stage_index_sql(start_id.to_i, stop_id.to_i).tap do |sql|
+ ActiveRecord::Base.connection.execute(sql)
+ end
+ end
+
+ private
+
+ def migrate_stage_index_sql(start_id, stop_id)
+ if Gitlab::Database.postgresql?
+ <<~SQL
+ WITH freqs AS (
+ SELECT stage_id, stage_idx, COUNT(*) AS freq FROM ci_builds
+ WHERE stage_id BETWEEN #{start_id} AND #{stop_id}
+ AND stage_idx IS NOT NULL
+ GROUP BY stage_id, stage_idx
+ ), indexes AS (
+ SELECT DISTINCT stage_id, last_value(stage_idx)
+ OVER (PARTITION BY stage_id ORDER BY freq ASC) AS index
+ FROM freqs
+ )
+
+ UPDATE ci_stages SET position = indexes.index
+ FROM indexes WHERE indexes.stage_id = ci_stages.id
+ AND ci_stages.position IS NULL;
+ SQL
+ else
+ <<~SQL
+ UPDATE ci_stages
+ SET position =
+ (SELECT stage_idx FROM ci_builds
+ WHERE ci_builds.stage_id = ci_stages.id
+ GROUP BY ci_builds.stage_idx ORDER BY COUNT(*) DESC LIMIT 1)
+ WHERE ci_stages.id BETWEEN #{start_id} AND #{stop_id}
+ AND ci_stages.position IS NULL
+ SQL
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/base_doorkeeper_controller.rb b/lib/gitlab/base_doorkeeper_controller.rb
new file mode 100644
index 00000000000..e4227af25d2
--- /dev/null
+++ b/lib/gitlab/base_doorkeeper_controller.rb
@@ -0,0 +1,8 @@
+# This is a base controller for doorkeeper.
+# It adds the `can?` helper used in the views.
+module Gitlab
+ class BaseDoorkeeperController < ActionController::Base
+ include Gitlab::Allowable
+ helper_method :can?
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/seed/stage.rb b/lib/gitlab/ci/pipeline/seed/stage.rb
index c101f30d6e8..2b58d9863a0 100644
--- a/lib/gitlab/ci/pipeline/seed/stage.rb
+++ b/lib/gitlab/ci/pipeline/seed/stage.rb
@@ -19,6 +19,7 @@ module Gitlab
def attributes
{ name: @attributes.fetch(:name),
+ position: @attributes.fetch(:index),
pipeline: @pipeline,
project: @pipeline.project }
end
diff --git a/lib/gitlab/diff/file_collection/base.rb b/lib/gitlab/diff/file_collection/base.rb
index a6007ebf531..c79d8d3cb21 100644
--- a/lib/gitlab/diff/file_collection/base.rb
+++ b/lib/gitlab/diff/file_collection/base.rb
@@ -36,6 +36,8 @@ module Gitlab
private
def decorate_diff!(diff)
+ return diff if diff.is_a?(File)
+
Gitlab::Diff::File.new(diff, repository: project.repository, diff_refs: diff_refs, fallback_diff_refs: fallback_diff_refs)
end
end
diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb
index 690b27cde81..978962ab2eb 100644
--- a/lib/gitlab/diff/position.rb
+++ b/lib/gitlab/diff/position.rb
@@ -12,6 +12,10 @@ module Gitlab
:head_sha,
:old_line,
:new_line,
+ :width,
+ :height,
+ :x,
+ :y,
:position_type, to: :formatter
# A position can belong to a text line or to an image coordinate
diff --git a/lib/gitlab/git/raw_diff_change.rb b/lib/gitlab/git/raw_diff_change.rb
index eb3d8819239..92f6c45ce25 100644
--- a/lib/gitlab/git/raw_diff_change.rb
+++ b/lib/gitlab/git/raw_diff_change.rb
@@ -38,7 +38,9 @@ module Gitlab
end
def extract_operation
- case @raw_operation&.first(1)
+ return :unknown unless @raw_operation
+
+ case @raw_operation[0]
when 'A'
:added
when 'C'
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 0ad2d385051..de0044fc149 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -391,18 +391,6 @@ module Gitlab
nil
end
- def archive_prefix(ref, sha, append_sha:)
- append_sha = (ref != sha) if append_sha.nil?
-
- project_name = self.name.chomp('.git')
- formatted_ref = ref.tr('/', '-')
-
- prefix_segments = [project_name, formatted_ref]
- prefix_segments << sha if append_sha
-
- prefix_segments.join('-')
- end
-
def archive_metadata(ref, storage_path, format = "tar.gz", append_sha:)
ref ||= root_ref
commit = Gitlab::Git::Commit.find(self, ref)
@@ -413,12 +401,44 @@ module Gitlab
{
'RepoPath' => path,
'ArchivePrefix' => prefix,
- 'ArchivePath' => archive_file_path(prefix, storage_path, format),
+ 'ArchivePath' => archive_file_path(storage_path, commit.id, prefix, format),
'CommitId' => commit.id
}
end
- def archive_file_path(name, storage_path, format = "tar.gz")
+ # This is both the filename of the archive (missing the extension) and the
+ # name of the top-level member of the archive under which all files go
+ #
+ # FIXME: The generated prefix is incorrect for projects with hashed
+ # storage enabled
+ def archive_prefix(ref, sha, append_sha:)
+ append_sha = (ref != sha) if append_sha.nil?
+
+ project_name = self.name.chomp('.git')
+ formatted_ref = ref.tr('/', '-')
+
+ prefix_segments = [project_name, formatted_ref]
+ prefix_segments << sha if append_sha
+
+ prefix_segments.join('-')
+ end
+ private :archive_prefix
+
+ # The full path on disk where the archive should be stored. This is used
+ # to cache the archive between requests.
+ #
+ # The path is a global namespace, so needs to be globally unique. This is
+ # achieved by including `gl_repository` in the path.
+ #
+ # Archives relating to a particular ref when the SHA is not present in the
+ # filename must be invalidated when the ref is updated to point to a new
+ # SHA. This is achieved by including the SHA in the path.
+ #
+ # As this is a full path on disk, it is not "cloud native". This should
+ # be resolved by either removing the cache, or moving the implementation
+ # into Gitaly and removing the ArchivePath parameter from the git-archive
+ # senddata response.
+ def archive_file_path(storage_path, sha, name, format = "tar.gz")
# Build file path
return nil unless name
@@ -436,8 +456,9 @@ module Gitlab
end
file_name = "#{name}.#{extension}"
- File.join(storage_path, self.name, file_name)
+ File.join(storage_path, self.gl_repository, sha, file_name)
end
+ private :archive_file_path
# Return repo size in megabytes
def size
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index bf5a491e28d..498187997e1 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -142,7 +142,7 @@ module Gitlab
:repository_service,
:is_rebase_in_progress,
request,
- timeout: GitalyClient.default_timeout
+ timeout: GitalyClient.fast_timeout
)
response.in_progress
@@ -159,7 +159,7 @@ module Gitlab
:repository_service,
:is_squash_in_progress,
request,
- timeout: GitalyClient.default_timeout
+ timeout: GitalyClient.fast_timeout
)
response.in_progress
diff --git a/lib/gitlab/kubernetes/helm/base_command.rb b/lib/gitlab/kubernetes/helm/base_command.rb
index 6e4df05aa7e..3d778da90c7 100644
--- a/lib/gitlab/kubernetes/helm/base_command.rb
+++ b/lib/gitlab/kubernetes/helm/base_command.rb
@@ -15,6 +15,9 @@ module Gitlab
def generate_script
<<~HEREDOC
set -eo pipefail
+ ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
+ echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
+ echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
apk add -U ca-certificates openssl >/dev/null
wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{Gitlab::Kubernetes::Helm::HELM_VERSION}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
mv /tmp/linux-amd64/helm /usr/bin/
diff --git a/package.json b/package.json
index 8d0473069c3..e95add1d7e9 100644
--- a/package.json
+++ b/package.json
@@ -98,10 +98,11 @@
"devDependencies": {
"axios-mock-adapter": "^1.10.0",
"babel-eslint": "^8.0.2",
- "babel-plugin-istanbul": "^4.1.5",
+ "babel-plugin-istanbul": "^4.1.6",
"babel-plugin-rewire": "^1.1.0",
"babel-template": "^6.26.0",
"babel-types": "^6.26.0",
+ "chalk": "^2.4.1",
"commander": "^2.15.1",
"eslint": "^3.18.0",
"eslint-config-airbnb-base": "^10.0.1",
@@ -116,13 +117,13 @@
"istanbul": "^0.4.5",
"jasmine-core": "^2.9.0",
"jasmine-jquery": "^2.1.1",
- "karma": "^2.0.0",
+ "karma": "^2.0.2",
"karma-chrome-launcher": "^2.2.0",
- "karma-coverage-istanbul-reporter": "^1.4.1",
+ "karma-coverage-istanbul-reporter": "^1.4.2",
"karma-jasmine": "^1.1.1",
"karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.7",
- "karma-webpack": "2.0.7",
+ "karma-webpack": "3.0.0",
"nodemon": "^1.15.1",
"prettier": "1.11.1",
"webpack-dev-server": "^2.11.2"
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 565adac7499..1bc424335f8 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -47,7 +47,7 @@ GEM
mini_portile2 (2.3.0)
minitest (5.11.1)
netrc (0.11.0)
- nokogiri (1.8.1)
+ nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
pry (0.11.3)
coderay (~> 1.1.0)
diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb
index 25309033571..ce61e6bf759 100644
--- a/spec/factories/ci/stages.rb
+++ b/spec/factories/ci/stages.rb
@@ -21,6 +21,7 @@ FactoryBot.define do
pipeline factory: :ci_empty_pipeline
name 'test'
+ position 1
status 'pending'
end
end
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index ce5fbc343ee..53368c64e10 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -2,6 +2,7 @@ FactoryBot.define do
factory :commit_status, class: CommitStatus do
name 'default'
stage 'test'
+ stage_idx 0
status 'success'
description 'commit status'
pipeline factory: :ci_pipeline_with_one_job
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index a71020002dc..ed47f7ed390 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -40,7 +40,7 @@ feature 'Dashboard Groups page', :js do
expect(page).to have_content(nested_group.name)
end
- describe 'when filtering groups', :nested_groups do
+ context 'when filtering groups', :nested_groups do
before do
group.add_owner(user)
nested_group.add_owner(user)
@@ -75,7 +75,7 @@ feature 'Dashboard Groups page', :js do
end
end
- describe 'group with subgroups', :nested_groups do
+ context 'with subgroups', :nested_groups do
let!(:subgroup) { create(:group, :public, parent: group) }
before do
@@ -106,7 +106,7 @@ feature 'Dashboard Groups page', :js do
end
end
- describe 'when using pagination' do
+ context 'when using pagination' do
let(:group) { create(:group, created_at: 5.days.ago) }
let(:group2) { create(:group, created_at: 2.days.ago) }
@@ -141,4 +141,20 @@ feature 'Dashboard Groups page', :js do
expect(page).not_to have_selector("#group-#{group2.id}")
end
end
+
+ context 'when signed in as admin' do
+ let(:admin) { create(:admin) }
+
+ it 'shows only groups admin is member of' do
+ group.add_owner(admin)
+ expect(another_group).to be_persisted
+
+ sign_in(admin)
+ visit dashboard_groups_path
+ wait_for_requests
+
+ expect(page).to have_content(group.name)
+ expect(page).not_to have_content(another_group.name)
+ end
+ end
end
diff --git a/spec/features/projects/issues/user_toggles_subscription_spec.rb b/spec/features/projects/issues/user_toggles_subscription_spec.rb
index 117a614b980..c2b2a193682 100644
--- a/spec/features/projects/issues/user_toggles_subscription_spec.rb
+++ b/spec/features/projects/issues/user_toggles_subscription_spec.rb
@@ -1,9 +1,9 @@
require "spec_helper"
describe "User toggles subscription", :js do
- set(:project) { create(:project_empty_repo, :public) }
- set(:user) { create(:user) }
- set(:issue) { create(:issue, project: project, author: user) }
+ let(:project) { create(:project_empty_repo, :public) }
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, project: project, author: user) }
before do
project.add_developer(user)
@@ -12,7 +12,7 @@ describe "User toggles subscription", :js do
visit(project_issue_path(project, issue))
end
- it "unsibscribes from issue" do
+ it "unsubscribes from issue" do
subscription_button = find(".js-issuable-subscribe-button")
# Check we're subscribed.
diff --git a/spec/features/projects/settings/lfs_settings_spec.rb b/spec/features/projects/settings/lfs_settings_spec.rb
index 0fd28a5681c..342be1d2a9d 100644
--- a/spec/features/projects/settings/lfs_settings_spec.rb
+++ b/spec/features/projects/settings/lfs_settings_spec.rb
@@ -1,21 +1,27 @@
require 'rails_helper'
describe 'Projects > Settings > LFS settings' do
- let(:admin) { create(:admin) }
let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:role) { :master }
context 'LFS enabled setting' do
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
- sign_in(admin)
+ sign_in(user)
+ project.add_role(user, role)
end
- it 'displays the correct elements', :js do
- visit edit_project_path(project)
+ context 'for master' do
+ let(:role) { :master }
- expect(page).to have_content('Git Large File Storage')
- expect(page).to have_selector('input[name="project[lfs_enabled]"] + button', visible: true)
+ it 'displays the correct elements', :js do
+ visit edit_project_path(project)
+
+ expect(page).to have_content('Git Large File Storage')
+ expect(page).to have_selector('input[name="project[lfs_enabled]"] + button', visible: true)
+ end
end
end
end
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index abc470788e1..16c0d418d98 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -2,43 +2,71 @@ require 'spec_helper'
describe GroupsFinder do
describe '#execute' do
- let(:user) { create(:user) }
-
- context 'root level groups' do
- let!(:private_group) { create(:group, :private) }
- let!(:internal_group) { create(:group, :internal) }
- let!(:public_group) { create(:group, :public) }
-
- context 'without a user' do
- subject { described_class.new.execute }
-
- it { is_expected.to eq([public_group]) }
+ let(:user) { create(:user) }
+
+ describe 'root level groups' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:user_type, :params, :results) do
+ nil | { all_available: true } | %i(public_group user_public_group)
+ nil | { all_available: false } | %i(public_group user_public_group)
+ nil | {} | %i(public_group user_public_group)
+
+ :regular | { all_available: true } | %i(public_group internal_group user_public_group user_internal_group
+ user_private_group)
+ :regular | { all_available: false } | %i(user_public_group user_internal_group user_private_group)
+ :regular | {} | %i(public_group internal_group user_public_group user_internal_group user_private_group)
+
+ :external | { all_available: true } | %i(public_group user_public_group user_internal_group user_private_group)
+ :external | { all_available: false } | %i(user_public_group user_internal_group user_private_group)
+ :external | {} | %i(public_group user_public_group user_internal_group user_private_group)
+
+ :admin | { all_available: true } | %i(public_group internal_group private_group user_public_group
+ user_internal_group user_private_group)
+ :admin | { all_available: false } | %i(user_public_group user_internal_group user_private_group)
+ :admin | {} | %i(public_group internal_group private_group user_public_group user_internal_group
+ user_private_group)
end
- context 'with a user' do
- subject { described_class.new(user).execute }
-
- context 'normal user' do
- it { is_expected.to contain_exactly(public_group, internal_group) }
- end
-
- context 'external user' do
- let(:user) { create(:user, external: true) }
-
- it { is_expected.to contain_exactly(public_group) }
+ with_them do
+ before do
+ # Fixme: Because of an issue: https://github.com/tomykaira/rspec-parameterized/issues/8#issuecomment-381888428
+ # The groups need to be created here, not with let syntax, and also compared by name and not ids
+
+ @groups = {
+ private_group: create(:group, :private, name: 'private_group'),
+ internal_group: create(:group, :internal, name: 'internal_group'),
+ public_group: create(:group, :public, name: 'public_group'),
+
+ user_private_group: create(:group, :private, name: 'user_private_group'),
+ user_internal_group: create(:group, :internal, name: 'user_internal_group'),
+ user_public_group: create(:group, :public, name: 'user_public_group')
+ }
+
+ if user_type
+ user =
+ case user_type
+ when :regular
+ create(:user)
+ when :external
+ create(:user, external: true)
+ when :admin
+ create(:user, :admin)
+ end
+ @groups.values_at(:user_private_group, :user_internal_group, :user_public_group).each do |group|
+ group.add_developer(user)
+ end
+ end
end
- context 'user is member of the private group' do
- before do
- private_group.add_guest(user)
- end
+ subject { described_class.new(User.last, params).execute.to_a }
- it { is_expected.to contain_exactly(public_group, internal_group, private_group) }
- end
+ it { is_expected.to match_array(@groups.values_at(*results)) }
end
end
context 'subgroups', :nested_groups do
+ let(:user) { create(:user) }
let!(:parent_group) { create(:group, :public) }
let!(:public_subgroup) { create(:group, :public, parent: parent_group) }
let!(:internal_subgroup) { create(:group, :internal, parent: parent_group) }
diff --git a/spec/fixtures/api/schemas/public_api/v4/notes.json b/spec/fixtures/api/schemas/public_api/v4/notes.json
index 4c4ca3b582f..0f9c221fd6d 100644
--- a/spec/fixtures/api/schemas/public_api/v4/notes.json
+++ b/spec/fixtures/api/schemas/public_api/v4/notes.json
@@ -24,7 +24,10 @@
"system": { "type": "boolean" },
"noteable_id": { "type": "integer" },
"noteable_iid": { "type": "integer" },
- "noteable_type": { "type": "string" }
+ "noteable_type": { "type": "string" },
+ "resolved": { "type": "boolean" },
+ "resolvable": { "type": "boolean" },
+ "resolved_by": { "type": ["string", "null"] }
},
"required": [
"id", "body", "attachment", "author", "created_at", "updated_at",
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 43cb0dfe163..5e454f8b310 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -2,8 +2,6 @@
require 'spec_helper'
describe ApplicationHelper do
- include UploadHelpers
-
describe 'current_controller?' do
it 'returns true when controller matches argument' do
stub_controller_name('foo')
@@ -54,143 +52,6 @@ describe ApplicationHelper do
end
end
- describe 'project_icon' do
- it 'returns an url for the avatar' do
- project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
-
- expect(helper.project_icon(project.full_path).to_s)
- .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
- end
- end
-
- describe 'avatar_icon_for' do
- let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') }
- let(:email) { 'foo@example.com' }
- let!(:another_user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: email) }
-
- it 'prefers the user to retrieve the avatar_url' do
- expect(helper.avatar_icon_for(user, email).to_s)
- .to eq(user.avatar.url)
- end
-
- it 'falls back to email lookup if no user given' do
- expect(helper.avatar_icon_for(nil, email).to_s)
- .to eq(another_user.avatar.url)
- end
- end
-
- describe 'avatar_icon_for_email' do
- let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
-
- context 'using an email' do
- context 'when there is a matching user' do
- it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon_for_email(user.email).to_s)
- .to eq(user.avatar.url)
- end
- end
-
- context 'when no user exists for the email' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
-
- helper.avatar_icon_for_email('foo@example.com', 20, 2)
- end
- end
-
- context 'without an email passed' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
-
- helper.avatar_icon_for_email(nil, 20, 2)
- end
- end
- end
- end
-
- describe 'avatar_icon_for_user' do
- let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
-
- context 'with a user object passed' do
- it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon_for_user(user).to_s)
- .to eq(user.avatar.url)
- end
- end
-
- context 'without a user object passed' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
-
- helper.avatar_icon_for_user(nil, 20, 2)
- end
- end
- end
-
- describe 'gravatar_icon' do
- let(:user_email) { 'user@email.com' }
-
- context 'with Gravatar disabled' do
- before do
- stub_application_setting(gravatar_enabled?: false)
- end
-
- it 'returns a generic avatar' do
- expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png')
- end
- end
-
- context 'with Gravatar enabled' do
- before do
- stub_application_setting(gravatar_enabled?: true)
- end
-
- it 'returns a generic avatar when email is blank' do
- expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png')
- end
-
- it 'returns a valid Gravatar URL' do
- stub_config_setting(https: false)
-
- expect(helper.gravatar_icon(user_email))
- .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
- end
-
- it 'uses HTTPs when configured' do
- stub_config_setting(https: true)
-
- expect(helper.gravatar_icon(user_email))
- .to match('https://secure.gravatar.com')
- end
-
- it 'returns custom gravatar path when gravatar_url is set' do
- stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}')
-
- expect(gravatar_icon(user_email, 20))
- .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
- end
-
- it 'accepts a custom size argument' do
- expect(helper.gravatar_icon(user_email, 64)).to include '?s=128'
- end
-
- it 'defaults size to 40@2x when given an invalid size' do
- expect(helper.gravatar_icon(user_email, nil)).to include '?s=80'
- end
-
- it 'accepts a scaling factor' do
- expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120'
- end
-
- it 'ignores case and surrounding whitespace' do
- normal = helper.gravatar_icon('foo@example.com')
- upcase = helper.gravatar_icon(' FOO@EXAMPLE.COM ')
-
- expect(normal).to eq upcase
- end
- end
- end
-
describe 'simple_sanitize' do
let(:a_tag) { '<a href="#">Foo</a>' }
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index c94fedd615b..120b23e66ac 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -18,6 +18,30 @@ describe AuthHelper do
end
end
+ describe "providers_for_base_controller" do
+ it 'returns all enabled providers from devise' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :github] }
+ expect(helper.providers_for_base_controller).to include(*[:twitter, :github])
+ end
+
+ it 'excludes ldap providers' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :ldapmain] }
+ expect(helper.providers_for_base_controller).not_to include(:ldapmain)
+ end
+ end
+
+ describe "form_based_providers" do
+ it 'includes LDAP providers' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :ldapmain] }
+ expect(helper.form_based_providers).to eq %i(ldapmain)
+ end
+
+ it 'includes crowd provider' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :crowd] }
+ expect(helper.form_based_providers).to eq %i(crowd)
+ end
+ end
+
describe 'enabled_button_based_providers' do
before do
allow(helper).to receive(:auth_providers) { [:twitter, :github] }
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index 04c6d259135..5856bccb5b8 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -1,10 +1,147 @@
require 'rails_helper'
describe AvatarsHelper do
- include ApplicationHelper
+ include UploadHelpers
let(:user) { create(:user) }
+ describe '#project_icon' do
+ it 'returns an url for the avatar' do
+ project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
+
+ expect(helper.project_icon(project.full_path).to_s)
+ .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
+ end
+ end
+
+ describe '#avatar_icon_for' do
+ let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') }
+ let(:email) { 'foo@example.com' }
+ let!(:another_user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: email) }
+
+ it 'prefers the user to retrieve the avatar_url' do
+ expect(helper.avatar_icon_for(user, email).to_s)
+ .to eq(user.avatar.url)
+ end
+
+ it 'falls back to email lookup if no user given' do
+ expect(helper.avatar_icon_for(nil, email).to_s)
+ .to eq(another_user.avatar.url)
+ end
+ end
+
+ describe '#avatar_icon_for_email' do
+ let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
+
+ context 'using an email' do
+ context 'when there is a matching user' do
+ it 'returns a relative URL for the avatar' do
+ expect(helper.avatar_icon_for_email(user.email).to_s)
+ .to eq(user.avatar.url)
+ end
+ end
+
+ context 'when no user exists for the email' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
+
+ helper.avatar_icon_for_email('foo@example.com', 20, 2)
+ end
+ end
+
+ context 'without an email passed' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
+
+ helper.avatar_icon_for_email(nil, 20, 2)
+ end
+ end
+ end
+ end
+
+ describe '#avatar_icon_for_user' do
+ let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
+
+ context 'with a user object passed' do
+ it 'returns a relative URL for the avatar' do
+ expect(helper.avatar_icon_for_user(user).to_s)
+ .to eq(user.avatar.url)
+ end
+ end
+
+ context 'without a user object passed' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
+
+ helper.avatar_icon_for_user(nil, 20, 2)
+ end
+ end
+ end
+
+ describe '#gravatar_icon' do
+ let(:user_email) { 'user@email.com' }
+
+ context 'with Gravatar disabled' do
+ before do
+ stub_application_setting(gravatar_enabled?: false)
+ end
+
+ it 'returns a generic avatar' do
+ expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png')
+ end
+ end
+
+ context 'with Gravatar enabled' do
+ before do
+ stub_application_setting(gravatar_enabled?: true)
+ end
+
+ it 'returns a generic avatar when email is blank' do
+ expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png')
+ end
+
+ it 'returns a valid Gravatar URL' do
+ stub_config_setting(https: false)
+
+ expect(helper.gravatar_icon(user_email))
+ .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
+ end
+
+ it 'uses HTTPs when configured' do
+ stub_config_setting(https: true)
+
+ expect(helper.gravatar_icon(user_email))
+ .to match('https://secure.gravatar.com')
+ end
+
+ it 'returns custom gravatar path when gravatar_url is set' do
+ stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}')
+
+ expect(gravatar_icon(user_email, 20))
+ .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
+ end
+
+ it 'accepts a custom size argument' do
+ expect(helper.gravatar_icon(user_email, 64)).to include '?s=128'
+ end
+
+ it 'defaults size to 40@2x when given an invalid size' do
+ expect(helper.gravatar_icon(user_email, nil)).to include '?s=80'
+ end
+
+ it 'accepts a scaling factor' do
+ expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120'
+ end
+
+ it 'ignores case and surrounding whitespace' do
+ normal = helper.gravatar_icon('foo@example.com')
+ upcase = helper.gravatar_icon(' FOO@EXAMPLE.COM ')
+
+ expect(normal).to eq upcase
+ end
+ end
+ end
+
describe '#user_avatar' do
subject { helper.user_avatar(user: user) }
diff --git a/spec/javascripts/sidebar/mock_data.js b/spec/javascripts/sidebar/mock_data.js
index 8b6e8b24f00..fcd7bea3f6d 100644
--- a/spec/javascripts/sidebar/mock_data.js
+++ b/spec/javascripts/sidebar/mock_data.js
@@ -138,7 +138,7 @@ const RESPONSE_MAP = {
},
{
id: 20,
- name_with_namespace: 'foo / bar',
+ name_with_namespace: '<img src=x onerror=alert(document.domain)> foo / bar',
},
],
},
diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
index a3fb965fbab..00847df4b60 100644
--- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js
+++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
@@ -69,6 +69,15 @@ describe('SidebarMoveIssue', function () {
expect($.fn.glDropdown).toHaveBeenCalled();
});
+
+ it('escapes html from project name', (done) => {
+ this.$toggleButton.dropdown('toggle');
+
+ setTimeout(() => {
+ expect(this.$content.find('.js-move-issue-dropdown-item')[1].innerHTML.trim()).toEqual('&lt;img src=x onerror=alert(document.domain)&gt; foo / bar');
+ done();
+ });
+ });
});
describe('onConfirmClicked', () => {
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index bcd15f5eae2..2411d33a496 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -84,21 +84,11 @@ beforeEach(() => {
const axiosDefaultAdapter = getDefaultAdapter();
-let testFiles = process.env.TEST_FILES || [];
-if (testFiles.length > 0) {
- testFiles = testFiles.map(path => path.replace(/^spec\/javascripts\//, '').replace(/\.js$/, ''));
- console.log(`Running only tests matching: ${testFiles}`);
-} else {
- console.log('Running all tests');
-}
-
// render all of our tests
const testsContext = require.context('.', true, /_spec$/);
testsContext.keys().forEach(function(path) {
try {
- if (testFiles.length === 0 || testFiles.some(p => path.includes(p))) {
- testsContext(path);
- }
+ testsContext(path);
} catch (err) {
console.error('[ERROR] Unable to load spec: ', path);
describe('Test bundle', function() {
diff --git a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
index 1fd145116df..068cdc85a07 100644
--- a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
@@ -47,16 +47,36 @@ describe Banzai::Filter::CommitTrailersFilter do
)
end
- it 'non GitLab users and replaces them with mailto links' do
- _, message_html = build_commit_message(
- trailer: trailer,
- name: FFaker::Name.name,
- email: email
- )
+ context 'non GitLab users' do
+ shared_examples 'mailto links' do
+ it 'replaces them with mailto links' do
+ _, message_html = build_commit_message(
+ trailer: trailer,
+ name: FFaker::Name.name,
+ email: email
+ )
- doc = filter(message_html)
+ doc = filter(message_html)
- expect_to_have_mailto_link(doc, email: email, trailer: trailer)
+ expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: trailer)
+ end
+ end
+
+ context 'when Gravatar is disabled' do
+ before do
+ stub_application_setting(gravatar_enabled: false)
+ end
+
+ it_behaves_like 'mailto links'
+ end
+
+ context 'when Gravatar is enabled' do
+ before do
+ stub_application_setting(gravatar_enabled: true)
+ end
+
+ it_behaves_like 'mailto links'
+ end
end
it 'multiple trailers in the same message' do
@@ -69,7 +89,7 @@ describe Banzai::Filter::CommitTrailersFilter do
doc = filter(message)
expect_to_have_user_link_with_avatar(doc, user: user, trailer: trailer)
- expect_to_have_mailto_link(doc, email: email, trailer: different_trailer)
+ expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: different_trailer)
end
context 'special names' do
@@ -90,7 +110,7 @@ describe Banzai::Filter::CommitTrailersFilter do
doc = filter(message_html)
- expect_to_have_mailto_link(doc, email: email, trailer: trailer)
+ expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: trailer)
expect(doc.text).to match Regexp.escape(message)
end
end
diff --git a/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb
new file mode 100644
index 00000000000..f8107dd40b9
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::MigrateStageIndex, :migration, schema: 20180420080616 do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:stages) { table(:ci_stages) }
+ let(:jobs) { table(:ci_builds) }
+
+ before do
+ namespaces.create(id: 10, name: 'gitlab-org', path: 'gitlab-org')
+ projects.create!(id: 11, namespace_id: 10, name: 'gitlab', path: 'gitlab')
+ pipelines.create!(id: 12, project_id: 11, ref: 'master', sha: 'adf43c3a')
+
+ stages.create(id: 100, project_id: 11, pipeline_id: 12, name: 'build')
+ stages.create(id: 101, project_id: 11, pipeline_id: 12, name: 'test')
+
+ jobs.create!(id: 121, commit_id: 12, project_id: 11,
+ stage_idx: 2, stage_id: 100)
+ jobs.create!(id: 122, commit_id: 12, project_id: 11,
+ stage_idx: 2, stage_id: 100)
+ jobs.create!(id: 123, commit_id: 12, project_id: 11,
+ stage_idx: 10, stage_id: 100)
+ jobs.create!(id: 124, commit_id: 12, project_id: 11,
+ stage_idx: 3, stage_id: 101)
+ end
+
+ it 'correctly migrates stages indices' do
+ expect(stages.all.pluck(:position)).to all(be_nil)
+
+ described_class.new.perform(100, 101)
+
+ expect(stages.all.pluck(:position)).to eq [2, 3]
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
index dc12ba076bc..0edc3f315bb 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
@@ -17,7 +17,7 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
context 'when pipeline is ready to be saved' do
before do
- pipeline.stages.build(name: 'test', project: project)
+ pipeline.stages.build(name: 'test', position: 0, project: project)
step.perform!
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
index eb1b285c7bd..05ce3412fd8 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
@@ -24,7 +24,8 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do
describe '#attributes' do
it 'returns hash attributes of a stage' do
expect(subject.attributes).to be_a Hash
- expect(subject.attributes).to include(:name, :project)
+ expect(subject.attributes)
+ .to include(:name, :position, :pipeline, :project)
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index da1a6229ccf..9924641f829 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -234,59 +234,72 @@ describe Gitlab::Git::Repository, seed_helper: true do
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tag_names
end
- shared_examples 'archive check' do |extenstion|
- it { expect(metadata['ArchivePath']).to match(%r{tmp/gitlab-git-test.git/gitlab-git-test-master-#{SeedRepo::LastCommit::ID}}) }
- it { expect(metadata['ArchivePath']).to end_with extenstion }
- end
+ describe '#archive_metadata' do
+ let(:storage_path) { '/tmp' }
+ let(:cache_key) { File.join(repository.gl_repository, SeedRepo::LastCommit::ID) }
- describe '#archive_prefix' do
- let(:project_name) { 'project-name'}
+ let(:append_sha) { true }
+ let(:ref) { 'master' }
+ let(:format) { nil }
- before do
- expect(repository).to receive(:name).once.and_return(project_name)
- end
+ let(:expected_extension) { 'tar.gz' }
+ let(:expected_filename) { "#{expected_prefix}.#{expected_extension}" }
+ let(:expected_path) { File.join(storage_path, cache_key, expected_filename) }
+ let(:expected_prefix) { "gitlab-git-test-#{ref}-#{SeedRepo::LastCommit::ID}" }
- it 'returns parameterised string for a ref containing slashes' do
- prefix = repository.archive_prefix('test/branch', 'SHA', append_sha: nil)
+ subject(:metadata) { repository.archive_metadata(ref, storage_path, format, append_sha: append_sha) }
- expect(prefix).to eq("#{project_name}-test-branch-SHA")
+ it 'sets RepoPath to the repository path' do
+ expect(metadata['RepoPath']).to eq(repository.path)
end
- it 'returns correct string for a ref containing dots' do
- prefix = repository.archive_prefix('test.branch', 'SHA', append_sha: nil)
-
- expect(prefix).to eq("#{project_name}-test.branch-SHA")
+ it 'sets CommitId to the commit SHA' do
+ expect(metadata['CommitId']).to eq(SeedRepo::LastCommit::ID)
end
- it 'returns string with sha when append_sha is false' do
- prefix = repository.archive_prefix('test.branch', 'SHA', append_sha: false)
-
- expect(prefix).to eq("#{project_name}-test.branch")
+ it 'sets ArchivePrefix to the expected prefix' do
+ expect(metadata['ArchivePrefix']).to eq(expected_prefix)
end
- end
- describe '#archive' do
- let(:metadata) { repository.archive_metadata('master', '/tmp', append_sha: true) }
+ it 'sets ArchivePath to the expected globally-unique path' do
+ # This is really important from a security perspective. Think carefully
+ # before changing it: https://gitlab.com/gitlab-org/gitlab-ce/issues/45689
+ expect(expected_path).to include(File.join(repository.gl_repository, SeedRepo::LastCommit::ID))
- it_should_behave_like 'archive check', '.tar.gz'
- end
-
- describe '#archive_zip' do
- let(:metadata) { repository.archive_metadata('master', '/tmp', 'zip', append_sha: true) }
+ expect(metadata['ArchivePath']).to eq(expected_path)
+ end
- it_should_behave_like 'archive check', '.zip'
- end
+ context 'append_sha varies archive path and filename' do
+ where(:append_sha, :ref, :expected_prefix) do
+ sha = SeedRepo::LastCommit::ID
- describe '#archive_bz2' do
- let(:metadata) { repository.archive_metadata('master', '/tmp', 'tbz2', append_sha: true) }
+ true | 'master' | "gitlab-git-test-master-#{sha}"
+ true | sha | "gitlab-git-test-#{sha}-#{sha}"
+ false | 'master' | "gitlab-git-test-master"
+ false | sha | "gitlab-git-test-#{sha}"
+ nil | 'master' | "gitlab-git-test-master-#{sha}"
+ nil | sha | "gitlab-git-test-#{sha}"
+ end
- it_should_behave_like 'archive check', '.tar.bz2'
- end
+ with_them do
+ it { expect(metadata['ArchivePrefix']).to eq(expected_prefix) }
+ it { expect(metadata['ArchivePath']).to eq(expected_path) }
+ end
+ end
- describe '#archive_fallback' do
- let(:metadata) { repository.archive_metadata('master', '/tmp', 'madeup', append_sha: true) }
+ context 'format varies archive path and filename' do
+ where(:format, :expected_extension) do
+ nil | 'tar.gz'
+ 'madeup' | 'tar.gz'
+ 'tbz2' | 'tar.bz2'
+ 'zip' | 'zip'
+ end
- it_should_behave_like 'archive check', '.tar.gz'
+ with_them do
+ it { expect(metadata['ArchivePrefix']).to eq(expected_prefix) }
+ it { expect(metadata['ArchivePath']).to eq(expected_path) }
+ end
+ end
end
describe '#size' do
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 31141807cb2..62da967cf96 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -232,6 +232,7 @@ Ci::Stage:
- id
- name
- status
+- position
- lock_version
- project_id
- pipeline_id
diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
index 3cfdae794f6..7be8be54d5e 100644
--- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
@@ -4,22 +4,10 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
let(:application) { create(:clusters_applications_helm) }
let(:base_command) { described_class.new(application.name) }
- describe '#generate_script' do
- let(:helm_version) { Gitlab::Kubernetes::Helm::HELM_VERSION }
- let(:command) do
- <<~HEREDOC
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{helm_version}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- HEREDOC
- end
-
- subject { base_command.generate_script }
+ subject { base_command }
- it 'should return a command that prepares the environment for helm-cli' do
- expect(subject).to eq(command)
- end
+ it_behaves_like 'helm commands' do
+ let(:commands) { '' }
end
describe '#pod_resource' do
diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
index e6920b0a76f..89e36a298f8 100644
--- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
@@ -2,23 +2,9 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InitCommand do
let(:application) { create(:clusters_applications_helm) }
- let(:init_command) { described_class.new(application.name) }
+ let(:commands) { 'helm init >/dev/null' }
- describe '#generate_script' do
- let(:command) do
- <<~MSG.chomp
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- helm init >/dev/null
- MSG
- end
+ subject { described_class.new(application.name) }
- subject { init_command.generate_script }
-
- it 'should return the appropriate command' do
- is_expected.to eq(command)
- end
- end
+ it_behaves_like 'helm commands'
end
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index 137b8f718de..547f3f1752c 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -12,50 +12,36 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
)
end
- describe '#generate_script' do
- let(:command) do
- <<~MSG
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- helm init --client-only >/dev/null
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
- MSG
- end
-
- subject { install_command.generate_script }
+ subject { install_command }
- it 'should return appropriate command' do
- is_expected.to eq(command)
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
end
+ end
- context 'with an application with a repository' do
- let(:ci_runner) { create(:ci_runner) }
- let(:application) { create(:clusters_applications_runner, runner: ci_runner) }
- let(:install_command) do
- described_class.new(
- application.name,
- chart: application.chart,
- values: application.values,
- repository: application.repository
- )
- end
-
- let(:command) do
- <<~MSG
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- helm init --client-only >/dev/null
- helm repo add #{application.name} #{application.repository}
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
- MSG
- end
+ context 'with an application with a repository' do
+ let(:ci_runner) { create(:ci_runner) }
+ let(:application) { create(:clusters_applications_runner, runner: ci_runner) }
+ let(:install_command) do
+ described_class.new(
+ application.name,
+ chart: application.chart,
+ values: application.values,
+ repository: application.repository
+ )
+ end
- it 'should return appropriate command' do
- is_expected.to eq(command)
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm repo add #{application.name} #{application.repository}
+ helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
end
end
end
diff --git a/spec/migrations/schedule_stages_index_migration_spec.rb b/spec/migrations/schedule_stages_index_migration_spec.rb
new file mode 100644
index 00000000000..710264da375
--- /dev/null
+++ b/spec/migrations/schedule_stages_index_migration_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180420080616_schedule_stages_index_migration')
+
+describe ScheduleStagesIndexMigration, :sidekiq, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:stages) { table(:ci_stages) }
+
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 1)
+
+ namespaces.create(id: 12, name: 'gitlab-org', path: 'gitlab-org')
+ projects.create!(id: 123, namespace_id: 12, name: 'gitlab', path: 'gitlab')
+ pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
+ stages.create!(id: 121, project_id: 123, pipeline_id: 1, name: 'build')
+ stages.create!(id: 122, project_id: 123, pipeline_id: 1, name: 'test')
+ stages.create!(id: 123, project_id: 123, pipeline_id: 1, name: 'deploy')
+ end
+
+ it 'schedules delayed background migrations in batches' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ expect(stages.all).to all(have_attributes(position: be_nil))
+
+ migrate!
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 121, 121)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 122, 122)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, 123, 123)
+ expect(BackgroundMigrationWorker.jobs.size).to eq 3
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index 586d073eb5e..a00db1d2bfc 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -51,7 +51,7 @@ describe Ci::Stage, :models do
end
end
- describe 'update_status' do
+ describe '#update_status' do
context 'when stage objects needs to be updated' do
before do
create(:ci_build, :success, stage_id: stage.id)
@@ -87,4 +87,36 @@ describe Ci::Stage, :models do
end
end
end
+
+ describe '#index' do
+ context 'when stage has been imported and does not have position index set' do
+ before do
+ stage.update_column(:position, nil)
+ end
+
+ context 'when stage has statuses' do
+ before do
+ create(:ci_build, :running, stage_id: stage.id, stage_idx: 10)
+ end
+
+ it 'recalculates index before updating status' do
+ expect(stage.reload.position).to be_nil
+
+ stage.update_status
+
+ expect(stage.reload.position).to eq 10
+ end
+ end
+
+ context 'when stage does not have statuses' do
+ it 'fallbacks to zero' do
+ expect(stage.reload.position).to be_nil
+
+ stage.update_status
+
+ expect(stage.reload.position).to eq 0
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index 2705421e540..fb51c0172ab 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -85,12 +85,35 @@ describe DiffNote do
end
describe "#diff_file" do
- it "returns the correct diff file" do
- diff_file = subject.diff_file
+ context 'when the discussion was created in the diff' do
+ it 'returns correct diff file' do
+ diff_file = subject.diff_file
- expect(diff_file.old_path).to eq(position.old_path)
- expect(diff_file.new_path).to eq(position.new_path)
- expect(diff_file.diff_refs).to eq(position.diff_refs)
+ expect(diff_file.old_path).to eq(position.old_path)
+ expect(diff_file.new_path).to eq(position.new_path)
+ expect(diff_file.diff_refs).to eq(position.diff_refs)
+ end
+ end
+
+ context 'when discussion is outdated or not created in the diff' do
+ let(:diff_refs) { project.commit(sample_commit.id).diff_refs }
+ let(:position) do
+ Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ diff_refs: diff_refs
+ )
+ end
+
+ it 'returns the correct diff file' do
+ diff_file = subject.diff_file
+
+ expect(diff_file.old_path).to eq(position.old_path)
+ expect(diff_file.new_path).to eq(position.new_path)
+ expect(diff_file.diff_refs).to eq(position.diff_refs)
+ end
end
end
diff --git a/spec/requests/api/discussions_spec.rb b/spec/requests/api/discussions_spec.rb
index 4a44b219a67..ef34192f888 100644
--- a/spec/requests/api/discussions_spec.rb
+++ b/spec/requests/api/discussions_spec.rb
@@ -2,32 +2,53 @@ require 'spec_helper'
describe API::Discussions do
let(:user) { create(:user) }
- let!(:project) { create(:project, :public, namespace: user.namespace) }
+ let!(:project) { create(:project, :public, :repository, namespace: user.namespace) }
let(:private_user) { create(:user) }
before do
- project.add_reporter(user)
+ project.add_developer(user)
end
- context "when noteable is an Issue" do
+ context 'when noteable is an Issue' do
let!(:issue) { create(:issue, project: project, author: user) }
let!(:issue_note) { create(:discussion_note_on_issue, noteable: issue, project: project, author: user) }
- it_behaves_like "discussions API", 'projects', 'issues', 'iid' do
+ it_behaves_like 'discussions API', 'projects', 'issues', 'iid' do
let(:parent) { project }
let(:noteable) { issue }
let(:note) { issue_note }
end
end
- context "when noteable is a Snippet" do
+ context 'when noteable is a Snippet' do
let!(:snippet) { create(:project_snippet, project: project, author: user) }
let!(:snippet_note) { create(:discussion_note_on_snippet, noteable: snippet, project: project, author: user) }
- it_behaves_like "discussions API", 'projects', 'snippets', 'id' do
+ it_behaves_like 'discussions API', 'projects', 'snippets', 'id' do
let(:parent) { project }
let(:noteable) { snippet }
let(:note) { snippet_note }
end
end
+
+ context 'when noteable is a Merge Request' do
+ let!(:noteable) { create(:merge_request_with_diffs, source_project: project, target_project: project, author: user) }
+ let!(:note) { create(:discussion_note_on_merge_request, noteable: noteable, project: project, author: user) }
+ let!(:diff_note) { create(:diff_note_on_merge_request, noteable: noteable, project: project, author: user) }
+ let(:parent) { project }
+
+ it_behaves_like 'discussions API', 'projects', 'merge_requests', 'iid'
+ it_behaves_like 'diff discussions API', 'projects', 'merge_requests', 'iid'
+ it_behaves_like 'resolvable discussions API', 'projects', 'merge_requests', 'iid'
+ end
+
+ context 'when noteable is a Commit' do
+ let!(:noteable) { create(:commit, project: project, author: user) }
+ let!(:note) { create(:discussion_note_on_commit, commit_id: noteable.id, project: project, author: user) }
+ let!(:diff_note) { create(:diff_note_on_commit, commit_id: noteable.id, project: project, author: user) }
+ let(:parent) { project }
+
+ it_behaves_like 'discussions API', 'projects', 'repository/commits', 'id'
+ it_behaves_like 'diff discussions API', 'projects', 'repository/commits', 'id'
+ end
end
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index 6bed8e812c0..cd1a6cfc427 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -153,4 +153,13 @@ describe 'OpenID Connect requests' do
end
end
end
+
+ context 'OpenID configuration information' do
+ it 'correctly returns the configuration' do
+ get '/.well-known/openid-configuration'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to have_key('issuer')
+ end
+ end
end
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index 8de0bdf92e2..5bc6031388e 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -6,7 +6,9 @@ describe Ci::RetryBuildService do
set(:pipeline) { create(:ci_pipeline, project: project) }
let(:stage) do
- Ci::Stage.create!(project: project, pipeline: pipeline, name: 'test')
+ create(:ci_stage_entity, project: project,
+ pipeline: pipeline,
+ name: 'test')
end
let(:build) { create(:ci_build, pipeline: pipeline, stage_id: stage.id) }
diff --git a/spec/services/notes/resolve_service_spec.rb b/spec/services/notes/resolve_service_spec.rb
new file mode 100644
index 00000000000..b54d40a7a5c
--- /dev/null
+++ b/spec/services/notes/resolve_service_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Notes::ResolveService do
+ let(:merge_request) { create(:merge_request) }
+ let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.project) }
+ let(:user) { merge_request.author }
+
+ describe '#execute' do
+ it "resolves the note" do
+ described_class.new(merge_request.project, user).execute(note)
+ note.reload
+
+ expect(note.resolved?).to be true
+ expect(note.resolved_by).to eq(user)
+ end
+
+ it "sends notifications if all discussions are resolved" do
+ expect_any_instance_of(MergeRequests::ResolvedDiscussionNotificationService).to receive(:execute).with(merge_request)
+
+ described_class.new(merge_request.project, user).execute(note)
+ end
+ end
+end
diff --git a/spec/services/repository_archive_clean_up_service_spec.rb b/spec/services/repository_archive_clean_up_service_spec.rb
index 2d7fa3f80f7..ab1c638fc39 100644
--- a/spec/services/repository_archive_clean_up_service_spec.rb
+++ b/spec/services/repository_archive_clean_up_service_spec.rb
@@ -1,15 +1,47 @@
require 'spec_helper'
describe RepositoryArchiveCleanUpService do
- describe '#execute' do
- subject(:service) { described_class.new }
+ subject(:service) { described_class.new }
+ describe '#execute (new archive locations)' do
+ let(:sha) { "0" * 40 }
+
+ it 'removes outdated archives and directories in a new-style path' do
+ in_directory_with_files("project-999/#{sha}", %w[tar tar.bz2 tar.gz zip], 3.hours) do |dirname, files|
+ service.execute
+
+ files.each { |filename| expect(File.exist?(filename)).to be_falsy }
+ expect(File.directory?(dirname)).to be_falsy
+ expect(File.directory?(File.dirname(dirname))).to be_falsy
+ end
+ end
+
+ it 'does not remove directories when they contain outdated non-archives' do
+ in_directory_with_files("project-999/#{sha}", %w[tar conf rb], 2.hours) do |dirname, files|
+ service.execute
+
+ expect(File.directory?(dirname)).to be_truthy
+ end
+ end
+
+ it 'does not remove in-date archives in a new-style path' do
+ in_directory_with_files("project-999/#{sha}", %w[tar tar.bz2 tar.gz zip], 1.hour) do |dirname, files|
+ service.execute
+
+ files.each { |filename| expect(File.exist?(filename)).to be_truthy }
+ end
+ end
+ end
+
+ describe '#execute (legacy archive locations)' do
context 'when the downloads directory does not exist' do
it 'does not remove any archives' do
path = '/invalid/path/'
stub_repository_downloads_path(path)
+ allow(File).to receive(:directory?).and_call_original
expect(File).to receive(:directory?).with(path).and_return(false)
+
expect(service).not_to receive(:clean_up_old_archives)
expect(service).not_to receive(:clean_up_empty_directories)
@@ -19,7 +51,7 @@ describe RepositoryArchiveCleanUpService do
context 'when the downloads directory exists' do
shared_examples 'invalid archive files' do |dirname, extensions, mtime|
- it 'does not remove files and directoy' do
+ it 'does not remove files and directory' do
in_directory_with_files(dirname, extensions, mtime) do |dir, files|
service.execute
@@ -43,7 +75,7 @@ describe RepositoryArchiveCleanUpService do
end
context 'with files older than 2 hours inside invalid directories' do
- it_behaves_like 'invalid archive files', 'john_doe/sample.git', %w[conf rb tar tar.gz], 2.hours
+ it_behaves_like 'invalid archive files', 'john/doe/sample.git', %w[conf rb tar tar.gz], 2.hours
end
context 'with files newer than 2 hours that matches valid archive extensions' do
@@ -58,24 +90,24 @@ describe RepositoryArchiveCleanUpService do
it_behaves_like 'invalid archive files', 'sample.git', %w[conf rb tar tar.gz], 1.hour
end
end
+ end
- def in_directory_with_files(dirname, extensions, mtime)
- Dir.mktmpdir do |tmpdir|
- stub_repository_downloads_path(tmpdir)
- dir = File.join(tmpdir, dirname)
- files = create_temporary_files(dir, extensions, mtime)
+ def in_directory_with_files(dirname, extensions, mtime)
+ Dir.mktmpdir do |tmpdir|
+ stub_repository_downloads_path(tmpdir)
+ dir = File.join(tmpdir, dirname)
+ files = create_temporary_files(dir, extensions, mtime)
- yield(dir, files)
- end
+ yield(dir, files)
end
+ end
- def stub_repository_downloads_path(path)
- allow(Gitlab.config.gitlab).to receive(:repository_downloads_path).and_return(path)
- end
+ def stub_repository_downloads_path(path)
+ allow(Gitlab.config.gitlab).to receive(:repository_downloads_path).and_return(path)
+ end
- def create_temporary_files(dir, extensions, mtime)
- FileUtils.mkdir_p(dir)
- FileUtils.touch(extensions.map { |ext| File.join(dir, "sample.#{ext}") }, mtime: Time.now - mtime)
- end
+ def create_temporary_files(dir, extensions, mtime)
+ FileUtils.mkdir_p(dir)
+ FileUtils.touch(extensions.map { |ext| File.join(dir, "sample.#{ext}") }, mtime: Time.now - mtime)
end
end
diff --git a/spec/support/commit_trailers_spec_helper.rb b/spec/support/commit_trailers_spec_helper.rb
index add359946db..efa317fd2f9 100644
--- a/spec/support/commit_trailers_spec_helper.rb
+++ b/spec/support/commit_trailers_spec_helper.rb
@@ -8,7 +8,7 @@ module CommitTrailersSpecHelper
expect(wrapper.attribute('data-user').value).to eq user.id.to_s
end
- def expect_to_have_mailto_link(doc, email:, trailer:)
+ def expect_to_have_mailto_link_with_avatar(doc, email:, trailer:)
wrapper = find_user_wrapper(doc, trailer)
expect_to_have_links_with_url_and_avatar(wrapper, "mailto:#{CGI.escape_html(email)}", email)
diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb
new file mode 100644
index 00000000000..56e86a87ab9
--- /dev/null
+++ b/spec/support/shared_examples/helm_generated_script.rb
@@ -0,0 +1,19 @@
+shared_examples 'helm commands' do
+ describe '#generate_script' do
+ let(:helm_setup) do
+ <<~EOS
+ set -eo pipefail
+ ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
+ echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
+ echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
+ apk add -U ca-certificates openssl >/dev/null
+ wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
+ mv /tmp/linux-amd64/helm /usr/bin/
+ EOS
+ end
+
+ it 'should return appropriate command' do
+ expect(subject.generate_script).to eq(helm_setup + commands)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/diff_discussions.rb b/spec/support/shared_examples/requests/api/diff_discussions.rb
new file mode 100644
index 00000000000..85a4bd8ca27
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/diff_discussions.rb
@@ -0,0 +1,57 @@
+shared_examples 'diff discussions API' do |parent_type, noteable_type, id_name|
+ describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do
+ it "includes diff discussions" do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user)
+
+ discussion = json_response.find { |record| record['id'] == diff_note.discussion_id }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(discussion).not_to be_nil
+ expect(discussion['individual_note']).to eq(false)
+ expect(discussion['notes'].first['body']).to eq(diff_note.note)
+ end
+ end
+
+ describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id" do
+ it "returns a discussion by id" do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions/#{diff_note.discussion_id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['id']).to eq(diff_note.discussion_id)
+ expect(json_response['notes'].first['body']).to eq(diff_note.note)
+ expect(json_response['notes'].first['position']).to eq(diff_note.position.to_h.stringify_keys)
+ end
+ end
+
+ describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do
+ it "creates a new diff note" do
+ position = diff_note.position.to_h
+
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user), body: 'hi!', position: position
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['notes'].first['body']).to eq('hi!')
+ expect(json_response['notes'].first['type']).to eq('DiffNote')
+ expect(json_response['notes'].first['position']).to eq(position.stringify_keys)
+ end
+
+ it "returns a 400 bad request error when position is invalid" do
+ position = diff_note.position.to_h.merge(new_line: '100000')
+
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user), body: 'hi!', position: position
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
+
+ describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id/notes" do
+ it 'adds a new note to the diff discussion' do
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{diff_note.discussion_id}/notes", user), body: 'hi!'
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['type']).to eq('DiffNote')
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/resolvable_discussions.rb b/spec/support/shared_examples/requests/api/resolvable_discussions.rb
new file mode 100644
index 00000000000..408ad08cc48
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/resolvable_discussions.rb
@@ -0,0 +1,87 @@
+shared_examples 'resolvable discussions API' do |parent_type, noteable_type, id_name|
+ describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id" do
+ it "resolves discussion if resolved is true" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", user), resolved: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['notes'].size).to eq(1)
+ expect(json_response['notes'][0]['resolved']).to eq(true)
+ end
+
+ it "unresolves discussion if resolved is false" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", user), resolved: false
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['notes'].size).to eq(1)
+ expect(json_response['notes'][0]['resolved']).to eq(false)
+ end
+
+ it "returns a 400 bad request error if resolved parameter is not passed" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it "returns a 401 unauthorized error if user is not authenticated" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}"), resolved: true
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+
+ it "returns a 403 error if user resolves discussion of someone else" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", private_user), resolved: true
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ context 'when user does not have access to read the discussion' do
+ before do
+ parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'responds with 404' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", private_user), resolved: true
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
+ describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
+ it 'returns resolved note when resolved parameter is true' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/#{note.id}", user), resolved: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['resolved']).to eq(true)
+ end
+
+ it 'returns a 404 error when note id not found' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/12345", user),
+ body: 'Hello!'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns a 400 bad request error if neither body nor resolved parameter is given' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/#{note.id}", user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it "returns a 403 error if user resolves note of someone else" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/#{note.id}", private_user), resolved: true
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+end
diff --git a/yarn.lock b/yarn.lock
index 395e2cfb0ed..1e6ffa5f524 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -66,13 +66,6 @@
version "2.0.48"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-2.0.48.tgz#3e90d8cde2d29015e5583017f7830cb3975b2eef"
-JSONStream@^1.0.3:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea"
- dependencies:
- jsonparse "^1.2.0"
- through ">=2.2.7 <3"
-
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -100,13 +93,6 @@ acorn-jsx@^3.0.0:
dependencies:
acorn "^3.0.4"
-acorn-node@^1.2.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.3.0.tgz#5f86d73346743810ef1269b901dbcbded020861b"
- dependencies:
- acorn "^5.4.1"
- xtend "^4.0.1"
-
acorn@^3.0.4:
version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
@@ -115,7 +101,7 @@ acorn@^4.0.3:
version "4.0.13"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
-acorn@^5.0.0, acorn@^5.2.1, acorn@^5.3.0, acorn@^5.4.1:
+acorn@^5.0.0, acorn@^5.2.1, acorn@^5.3.0:
version "5.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102"
@@ -226,12 +212,6 @@ ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
-ansi-styles@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88"
- dependencies:
- color-convert "^1.9.0"
-
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@@ -378,7 +358,7 @@ assert-plus@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
-assert@^1.1.1, assert@^1.4.0:
+assert@^1.1.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
dependencies:
@@ -392,12 +372,6 @@ ast-types@0.x.x:
version "0.11.1"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.1.tgz#5bb3a8d5ba292c3f4ae94d46df37afc30300b990"
-astw@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/astw/-/astw-2.2.0.tgz#7bd41784d32493987aeb239b6b4e1c57a873b917"
- dependencies:
- acorn "^4.0.3"
-
async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
@@ -410,16 +384,12 @@ async@1.x, async@^1.4.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
-async@^2.1.2, async@^2.1.4, async@^2.4.1:
+async@^2.0.0, async@^2.1.2, async@^2.1.4, async@^2.4.1:
version "2.6.0"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
dependencies:
lodash "^4.14.0"
-async@~0.9.0:
- version "0.9.2"
- resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
-
async@~2.1.2:
version "2.1.5"
resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc"
@@ -672,13 +642,14 @@ babel-plugin-check-es2015-constants@^6.22.0:
dependencies:
babel-runtime "^6.22.0"
-babel-plugin-istanbul@^4.1.5:
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.5.tgz#6760cdd977f411d3e175bb064f2bc327d99b2b6e"
+babel-plugin-istanbul@^4.1.6:
+ version "4.1.6"
+ resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45"
dependencies:
+ babel-plugin-syntax-object-rest-spread "^6.13.0"
find-up "^2.1.0"
- istanbul-lib-instrument "^1.7.5"
- test-exclude "^4.1.1"
+ istanbul-lib-instrument "^1.10.1"
+ test-exclude "^4.2.1"
babel-plugin-rewire@^1.1.0:
version "1.1.0"
@@ -708,7 +679,7 @@ babel-plugin-syntax-exponentiation-operator@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
-babel-plugin-syntax-object-rest-spread@^6.8.0:
+babel-plugin-syntax-object-rest-spread@^6.13.0, babel-plugin-syntax-object-rest-spread@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
@@ -1035,7 +1006,7 @@ babel-register@^6.26.0:
mkdirp "^0.5.1"
source-map-support "^0.4.15"
-babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+babel-runtime@^6.0.0, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
@@ -1294,23 +1265,6 @@ brorand@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
-browser-pack@^6.0.1:
- version "6.0.4"
- resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.4.tgz#9a73beb3b48f9e36868be007b64400102c04a99f"
- dependencies:
- JSONStream "^1.0.3"
- combine-source-map "~0.8.0"
- defined "^1.0.0"
- safe-buffer "^5.1.1"
- through2 "^2.0.0"
- umd "^3.0.0"
-
-browser-resolve@^1.11.0, browser-resolve@^1.7.0:
- version "1.11.2"
- resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce"
- dependencies:
- resolve "1.1.7"
-
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.1.1"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f"
@@ -1363,64 +1317,6 @@ browserify-zlib@^0.1.4:
dependencies:
pako "~0.2.0"
-browserify-zlib@~0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f"
- dependencies:
- pako "~1.0.5"
-
-browserify@^14.5.0:
- version "14.5.0"
- resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.5.0.tgz#0bbbce521acd6e4d1d54d8e9365008efb85a9cc5"
- dependencies:
- JSONStream "^1.0.3"
- assert "^1.4.0"
- browser-pack "^6.0.1"
- browser-resolve "^1.11.0"
- browserify-zlib "~0.2.0"
- buffer "^5.0.2"
- cached-path-relative "^1.0.0"
- concat-stream "~1.5.1"
- console-browserify "^1.1.0"
- constants-browserify "~1.0.0"
- crypto-browserify "^3.0.0"
- defined "^1.0.0"
- deps-sort "^2.0.0"
- domain-browser "~1.1.0"
- duplexer2 "~0.1.2"
- events "~1.1.0"
- glob "^7.1.0"
- has "^1.0.0"
- htmlescape "^1.1.0"
- https-browserify "^1.0.0"
- inherits "~2.0.1"
- insert-module-globals "^7.0.0"
- labeled-stream-splicer "^2.0.0"
- module-deps "^4.0.8"
- os-browserify "~0.3.0"
- parents "^1.0.1"
- path-browserify "~0.0.0"
- process "~0.11.0"
- punycode "^1.3.2"
- querystring-es3 "~0.2.0"
- read-only-stream "^2.0.0"
- readable-stream "^2.0.2"
- resolve "^1.1.4"
- shasum "^1.0.0"
- shell-quote "^1.6.1"
- stream-browserify "^2.0.0"
- stream-http "^2.0.0"
- string_decoder "~1.0.0"
- subarg "^1.0.0"
- syntax-error "^1.1.1"
- through2 "^2.0.0"
- timers-browserify "^1.0.1"
- tty-browserify "~0.0.0"
- url "~0.11.0"
- util "~0.10.1"
- vm-browserify "~0.0.1"
- xtend "^4.0.0"
-
browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
version "1.7.7"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
@@ -1448,13 +1344,6 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
-buffer@^5.0.2:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.1.0.tgz#c913e43678c7cb7c8bd16afbcddb6c5505e8f9fe"
- dependencies:
- base64-js "^1.0.2"
- ieee754 "^1.1.4"
-
buildmail@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/buildmail/-/buildmail-4.0.1.tgz#877f7738b78729871c9a105e3b837d2be11a7a72"
@@ -1527,10 +1416,6 @@ cacheable-request@^2.1.1:
normalize-url "2.0.1"
responselike "1.0.2"
-cached-path-relative@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7"
-
caller-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
@@ -1610,17 +1495,9 @@ chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
-chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796"
- dependencies:
- ansi-styles "^3.2.0"
- escape-string-regexp "^1.0.5"
- supports-color "^5.2.0"
-
-chalk@^2.3.2:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.0.tgz#a060a297a6b57e15b61ca63ce84995daa0fe6e52"
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
@@ -1830,24 +1707,6 @@ combine-lists@^1.0.0:
dependencies:
lodash "^4.5.0"
-combine-source-map@~0.7.1:
- version "0.7.2"
- resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e"
- dependencies:
- convert-source-map "~1.1.0"
- inline-source-map "~0.6.0"
- lodash.memoize "~3.0.3"
- source-map "~0.5.3"
-
-combine-source-map@~0.8.0:
- version "0.8.0"
- resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b"
- dependencies:
- convert-source-map "~1.1.0"
- inline-source-map "~0.6.0"
- lodash.memoize "~3.0.3"
- source-map "~0.5.3"
-
combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
@@ -1914,14 +1773,6 @@ concat-stream@^1.5.0, concat-stream@^1.5.2:
readable-stream "^2.2.2"
typedarray "^0.0.6"
-concat-stream@~1.5.0, concat-stream@~1.5.1:
- version "1.5.2"
- resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
- dependencies:
- inherits "~2.0.1"
- readable-stream "~2.0.0"
- typedarray "~0.0.5"
-
configstore@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90"
@@ -1962,7 +1813,7 @@ consolidate@^0.14.0:
dependencies:
bluebird "^3.1.1"
-constants-browserify@^1.0.0, constants-browserify@~1.0.0:
+constants-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
@@ -1982,10 +1833,6 @@ convert-source-map@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
-convert-source-map@~1.1.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860"
-
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
@@ -2105,7 +1952,7 @@ cryptiles@3.x.x:
dependencies:
boom "5.x.x"
-crypto-browserify@^3.0.0, crypto-browserify@^3.11.0:
+crypto-browserify@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
dependencies:
@@ -2511,15 +2358,6 @@ depd@1.1.1, depd@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
-deps-sort@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5"
- dependencies:
- JSONStream "^1.0.3"
- shasum "^1.0.0"
- subarg "^1.0.0"
- through2 "^2.0.0"
-
des.js@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
@@ -2552,13 +2390,6 @@ detect-port-alt@1.1.5:
address "^1.0.1"
debug "^2.6.0"
-detective@^4.0.0:
- version "4.7.1"
- resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e"
- dependencies:
- acorn "^5.2.1"
- defined "^1.0.0"
-
di@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
@@ -2633,7 +2464,7 @@ dom-serializer@0:
domelementtype "~1.1.1"
entities "~1.1.1"
-domain-browser@^1.1.1, domain-browser@~1.1.0:
+domain-browser@^1.1.1:
version "1.1.7"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
@@ -2672,12 +2503,6 @@ dropzone@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-4.2.0.tgz#fbe7acbb9918e0706489072ef663effeef8a79f3"
-duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
- dependencies:
- readable-stream "^2.0.2"
-
duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
@@ -2877,7 +2702,7 @@ es6-set@~0.1.5:
es6-symbol "3.1.1"
event-emitter "~0.3.5"
-es6-symbol@3, es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@~3.1, es6-symbol@~3.1.1:
+es6-symbol@3, es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
dependencies:
@@ -3141,7 +2966,7 @@ eventemitter3@1.x.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
-events@^1.0.0, events@~1.1.0:
+events@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
@@ -3690,7 +3515,7 @@ glob@^5.0.15:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2:
+glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
@@ -3909,6 +3734,10 @@ 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-symbols@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
+
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"
@@ -3946,7 +3775,7 @@ has-values@^1.0.0:
is-number "^3.0.0"
kind-of "^4.0.0"
-has@^1.0.0, has@^1.0.1:
+has@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
dependencies:
@@ -4055,10 +3884,6 @@ html-entities@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2"
-htmlescape@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351"
-
htmlparser2@^3.8.2, htmlparser2@^3.9.0:
version "3.9.2"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
@@ -4142,10 +3967,6 @@ https-browserify@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
-https-browserify@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
-
https-proxy-agent@1:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6"
@@ -4255,12 +4076,6 @@ ini@^1.3.4, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
-inline-source-map@~0.6.0:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5"
- dependencies:
- source-map "~0.5.3"
-
inquirer@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
@@ -4298,19 +4113,6 @@ inquirer@^0.12.0:
strip-ansi "^3.0.0"
through "^2.3.6"
-insert-module-globals@^7.0.0:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.1.tgz#c03bf4e01cb086d5b5e5ace8ad0afe7889d638c3"
- dependencies:
- JSONStream "^1.0.3"
- combine-source-map "~0.7.1"
- concat-stream "~1.5.1"
- is-buffer "^1.1.0"
- lexical-scope "^1.2.0"
- process "~0.11.0"
- through2 "^2.0.0"
- xtend "^4.0.0"
-
internal-ip@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c"
@@ -4383,7 +4185,7 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
-is-buffer@^1.1.0, is-buffer@^1.1.5:
+is-buffer@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
@@ -4666,7 +4468,7 @@ is-wsl@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
-isarray@0.0.1, isarray@~0.0.1:
+isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -4720,13 +4522,29 @@ istanbul-lib-coverage@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da"
+istanbul-lib-coverage@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341"
+
istanbul-lib-hook@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b"
dependencies:
append-transform "^0.4.0"
-istanbul-lib-instrument@^1.7.5, istanbul-lib-instrument@^1.9.1:
+istanbul-lib-instrument@^1.10.1:
+ version "1.10.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz#724b4b6caceba8692d3f1f9d0727e279c401af7b"
+ dependencies:
+ babel-generator "^6.18.0"
+ babel-template "^6.16.0"
+ babel-traverse "^6.18.0"
+ babel-types "^6.18.0"
+ babylon "^6.18.0"
+ istanbul-lib-coverage "^1.2.0"
+ semver "^5.3.0"
+
+istanbul-lib-instrument@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz#250b30b3531e5d3251299fdd64b0b2c9db6b558e"
dependencies:
@@ -4882,12 +4700,6 @@ json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1:
dependencies:
jsonify "~0.0.0"
-json-stable-stringify@~0.0.0:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45"
- dependencies:
- jsonify "~0.0.0"
-
json-stringify-safe@5.0.x, json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -4904,10 +4716,6 @@ jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
-jsonparse@^1.2.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
-
jsonpointer@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
@@ -4942,9 +4750,9 @@ karma-chrome-launcher@^2.2.0:
fs-access "^1.0.0"
which "^1.2.1"
-karma-coverage-istanbul-reporter@^1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.1.tgz#2b42d145ddbb4868d85d52888c495a21c61d873c"
+karma-coverage-istanbul-reporter@^1.4.2:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.2.tgz#a8d0c8815c7d6f6cea15a394a7c4b39ef151a939"
dependencies:
istanbul-api "^1.1.14"
minimatch "^3.0.4"
@@ -4967,23 +4775,23 @@ karma-sourcemap-loader@^0.3.7:
dependencies:
graceful-fs "^4.1.2"
-karma-webpack@2.0.7:
- version "2.0.7"
- resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-2.0.7.tgz#dc3a492b478f10e8e3ccb9f58171b623f7070a1f"
+karma-webpack@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-3.0.0.tgz#bf009c5b73c667c11c015717e9e520f581317c44"
dependencies:
- async "~0.9.0"
- loader-utils "^0.2.5"
- lodash "^3.8.0"
+ async "^2.0.0"
+ babel-runtime "^6.0.0"
+ loader-utils "^1.0.0"
+ lodash "^4.0.0"
source-map "^0.5.6"
- webpack-dev-middleware "^1.12.0"
+ webpack-dev-middleware "^2.0.6"
-karma@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.0.tgz#a02698dd7f0f05ff5eb66ab8f65582490b512e58"
+karma@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.2.tgz#4d2db9402850a66551fa784b0164fb0824ed8c4b"
dependencies:
bluebird "^3.3.0"
body-parser "^1.16.1"
- browserify "^14.5.0"
chokidar "^1.4.1"
colors "^1.1.0"
combine-lists "^1.0.0"
@@ -5008,7 +4816,7 @@ karma@^2.0.0:
socket.io "2.0.4"
source-map "^0.6.1"
tmp "0.0.33"
- useragent "^2.1.12"
+ useragent "2.2.1"
katex@^0.8.3:
version "0.8.3"
@@ -5046,14 +4854,6 @@ kind-of@^6.0.0, kind-of@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
-labeled-stream-splicer@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59"
- dependencies:
- inherits "^2.0.1"
- isarray "~0.0.1"
- stream-splicer "^2.0.0"
-
latest-version@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15"
@@ -5083,12 +4883,6 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
-lexical-scope@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4"
- dependencies:
- astw "^2.0.0"
-
libbase64@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-0.1.0.tgz#62351a839563ac5ff5bd26f12f60e9830bb751e6"
@@ -5134,7 +4928,7 @@ loader-runner@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
-loader-utils@^0.2.15, loader-utils@^0.2.5:
+loader-utils@^0.2.15:
version "0.2.16"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d"
dependencies:
@@ -5234,10 +5028,6 @@ lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
-lodash.memoize@~3.0.3:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
-
lodash.mergewith@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
@@ -5265,10 +5055,6 @@ lodash@4.17.4:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
-lodash@^3.8.0:
- version "3.10.1"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
-
lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
@@ -5310,6 +5096,13 @@ loglevel@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.4.1.tgz#95b383f91a3c2756fd4ab093667e4309161f2bcd"
+loglevelnext@^1.0.1:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.5.tgz#36fc4f5996d6640f539ff203ba819641680d75a2"
+ dependencies:
+ es6-symbol "^3.1.1"
+ object.assign "^4.1.0"
+
longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
@@ -5320,7 +5113,7 @@ loose-envify@^1.0.0:
dependencies:
js-tokens "^3.0.0"
-loud-rejection@^1.0.0:
+loud-rejection@^1.0.0, loud-rejection@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
dependencies:
@@ -5331,7 +5124,11 @@ lowercase-keys@1.0.0, lowercase-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
-lru-cache@4.1.x, lru-cache@^4.0.1, lru-cache@^4.1.1:
+lru-cache@2.2.x:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
+
+lru-cache@^4.0.1, lru-cache@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55"
dependencies:
@@ -5490,6 +5287,24 @@ micromatch@^3.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
+micromatch@^3.1.8:
+ version "3.1.10"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+ dependencies:
+ arr-diff "^4.0.0"
+ array-unique "^0.3.2"
+ braces "^2.3.1"
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ extglob "^2.0.4"
+ fragment-cache "^0.2.1"
+ kind-of "^6.0.2"
+ nanomatch "^1.2.9"
+ object.pick "^1.3.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.2"
+
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@@ -5515,6 +5330,10 @@ mime@^1.3.4, mime@^1.4.1, mime@^1.5.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+mime@^2.1.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369"
+
mimic-fn@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
@@ -5547,7 +5366,7 @@ minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0:
+minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -5583,26 +5402,6 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd
dependencies:
minimist "0.0.8"
-module-deps@^4.0.8:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd"
- dependencies:
- JSONStream "^1.0.3"
- browser-resolve "^1.7.0"
- cached-path-relative "^1.0.0"
- concat-stream "~1.5.0"
- defined "^1.0.0"
- detective "^4.0.0"
- duplexer2 "^0.1.2"
- inherits "^2.0.1"
- parents "^1.0.0"
- readable-stream "^2.0.2"
- resolve "^1.1.3"
- stream-combiner2 "^1.1.1"
- subarg "^1.0.0"
- through2 "^2.0.0"
- xtend "^4.0.0"
-
moment@2.x, moment@^2.18.1:
version "2.19.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe"
@@ -5935,7 +5734,7 @@ object-copy@^0.1.0:
define-property "^0.2.5"
kind-of "^3.0.3"
-object-keys@^1.0.8:
+object-keys@^1.0.11, object-keys@^1.0.8:
version "1.0.11"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
@@ -5945,6 +5744,15 @@ object-visit@^1.0.0:
dependencies:
isobject "^3.0.0"
+object.assign@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
+ dependencies:
+ define-properties "^1.1.2"
+ function-bind "^1.1.1"
+ has-symbols "^1.0.0"
+ object-keys "^1.0.11"
+
object.omit@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
@@ -6026,10 +5834,6 @@ os-browserify@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f"
-os-browserify@~0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
-
os-homedir@^1.0.0, os-homedir@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
@@ -6134,7 +5938,7 @@ pako@~0.2.0:
version "0.2.9"
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
-pako@~1.0.2, pako@~1.0.5:
+pako@~1.0.2:
version "1.0.6"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
@@ -6146,12 +5950,6 @@ parallel-transform@^1.1.0:
inherits "^2.0.3"
readable-stream "^2.1.5"
-parents@^1.0.0, parents@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751"
- dependencies:
- path-platform "~0.11.15"
-
parse-asn1@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712"
@@ -6201,7 +5999,7 @@ pascalcase@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
-path-browserify@0.0.0, path-browserify@~0.0.0:
+path-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a"
@@ -6235,10 +6033,6 @@ path-parse@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1"
-path-platform@~0.11.15:
- version "0.11.15"
- resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2"
-
path-proxy@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/path-proxy/-/path-proxy-1.0.0.tgz#18e8a36859fc9d2f1a53b48dee138543c020de5e"
@@ -6751,7 +6545,7 @@ punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
-punycode@1.4.1, punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1:
+punycode@1.4.1, punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
@@ -6794,7 +6588,7 @@ query-string@^5.0.1:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"
-querystring-es3@^0.2.0, querystring-es3@~0.2.0:
+querystring-es3@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -6893,12 +6687,6 @@ react-error-overlay@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
-read-only-stream@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0"
- dependencies:
- readable-stream "^2.0.2"
-
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
@@ -6950,7 +6738,7 @@ readable-stream@1.1.x, "readable-stream@1.x >=1.1.9":
isarray "0.0.1"
string_decoder "~0.10.x"
-readable-stream@~2.0.0, readable-stream@~2.0.5, readable-stream@~2.0.6:
+readable-stream@~2.0.5, readable-stream@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
dependencies:
@@ -7049,7 +6837,7 @@ regex-cache@^0.4.2:
dependencies:
is-equal-shallow "^0.1.3"
-regex-not@^1.0.0:
+regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
dependencies:
@@ -7258,11 +7046,11 @@ resolve-url@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
-resolve@1.1.7, resolve@1.1.x:
+resolve@1.1.x:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
-resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.2.0:
+resolve@^1.1.6, resolve@^1.2.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
dependencies:
@@ -7507,20 +7295,13 @@ setprototypeof@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
-sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4:
+sha.js@^2.4.0, sha.js@^2.4.8:
version "2.4.10"
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b"
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
-shasum@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f"
- dependencies:
- json-stable-stringify "~0.0.0"
- sha.js "~2.4.4"
-
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
@@ -7531,7 +7312,7 @@ shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
-shell-quote@1.6.1, shell-quote@^1.6.1:
+shell-quote@1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
dependencies:
@@ -7860,20 +7641,13 @@ statuses@~1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
-stream-browserify@^2.0.0, stream-browserify@^2.0.1:
+stream-browserify@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db"
dependencies:
inherits "~2.0.1"
readable-stream "^2.0.2"
-stream-combiner2@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe"
- dependencies:
- duplexer2 "~0.1.0"
- readable-stream "^2.0.2"
-
stream-combiner@~0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
@@ -7887,7 +7661,7 @@ stream-each@^1.1.0:
end-of-stream "^1.1.0"
stream-shift "^1.0.0"
-stream-http@^2.0.0, stream-http@^2.3.1:
+stream-http@^2.3.1:
version "2.8.0"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10"
dependencies:
@@ -7901,13 +7675,6 @@ stream-shift@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
-stream-splicer@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83"
- dependencies:
- inherits "^2.0.1"
- readable-stream "^2.0.2"
-
streamroller@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b"
@@ -7940,7 +7707,7 @@ string_decoder@^0.10.25, string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
-string_decoder@~1.0.0, string_decoder@~1.0.3:
+string_decoder@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
dependencies:
@@ -7993,12 +7760,6 @@ style-loader@^0.20.2:
loader-utils "^1.1.0"
schema-utils "^0.4.3"
-subarg@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
- dependencies:
- minimist "^1.1.0"
-
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -8043,12 +7804,6 @@ svgo@^0.7.0:
sax "~1.2.1"
whet.extend "~0.9.9"
-syntax-error@^1.1.1:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c"
- dependencies:
- acorn-node "^1.2.0"
-
table@^3.7.8:
version "3.8.3"
resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f"
@@ -8095,12 +7850,12 @@ term-size@^1.2.0:
dependencies:
execa "^0.7.0"
-test-exclude@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26"
+test-exclude@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.1.tgz#dfa222f03480bca69207ca728b37d74b45f724fa"
dependencies:
arrify "^1.0.1"
- micromatch "^2.3.11"
+ micromatch "^3.1.8"
object-assign "^4.1.0"
read-pkg-up "^1.0.1"
require-main-filename "^1.0.1"
@@ -8128,7 +7883,7 @@ through2@^2.0.0:
readable-stream "^2.1.5"
xtend "~4.0.1"
-through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1:
+through@2, through@^2.3.6, through@~2.3, through@~2.3.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@@ -8154,7 +7909,7 @@ timed-out@^4.0.0, timed-out@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
-timers-browserify@^1.0.1, timers-browserify@^1.4.2:
+timers-browserify@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d"
dependencies:
@@ -8217,6 +7972,15 @@ to-regex@^3.0.1:
extend-shallow "^2.0.1"
regex-not "^1.0.0"
+to-regex@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+ dependencies:
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ regex-not "^1.0.2"
+ safe-regex "^1.1.0"
+
touch@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
@@ -8257,10 +8021,6 @@ tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
-tty-browserify@~0.0.0:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
-
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
@@ -8288,7 +8048,7 @@ type-is@~1.6.15:
media-typer "0.3.0"
mime-types "~2.1.18"
-typedarray@^0.0.6, typedarray@~0.0.5:
+typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -8321,10 +8081,6 @@ ultron@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
-umd@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e"
-
unc-path-regex@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
@@ -8426,6 +8182,10 @@ urix@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+url-join@^2.0.2:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.5.tgz#5af22f18c052a000a48d7b82c5e9c2e2feeda728"
+
url-loader@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.6.2.tgz#a007a7109620e9d988d14bce677a1decb9a993f7"
@@ -8464,7 +8224,7 @@ 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, url@~0.11.0:
+url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
dependencies:
@@ -8485,18 +8245,18 @@ user-home@^2.0.0:
dependencies:
os-homedir "^1.0.0"
-useragent@^2.1.12:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.3.0.tgz#217f943ad540cb2128658ab23fc960f6a88c9972"
+useragent@2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.2.1.tgz#cf593ef4f2d175875e8bb658ea92e18a4fd06d8e"
dependencies:
- lru-cache "4.1.x"
+ lru-cache "2.2.x"
tmp "0.0.x"
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
-util@0.10.3, util@^0.10.3, util@~0.10.1:
+util@0.10.3, util@^0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9"
dependencies:
@@ -8545,7 +8305,7 @@ visibilityjs@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/visibilityjs/-/visibilityjs-1.2.4.tgz#bff8663da62c8c10ad4ee5ae6a1ae6fac4259d63"
-vm-browserify@0.0.4, vm-browserify@~0.0.1:
+vm-browserify@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
dependencies:
@@ -8659,7 +8419,7 @@ webpack-bundle-analyzer@^2.10.0:
opener "^1.4.3"
ws "^4.0.0"
-webpack-dev-middleware@1.12.2, webpack-dev-middleware@^1.12.0:
+webpack-dev-middleware@1.12.2:
version "1.12.2"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e"
dependencies:
@@ -8669,6 +8429,18 @@ webpack-dev-middleware@1.12.2, webpack-dev-middleware@^1.12.0:
range-parser "^1.0.3"
time-stamp "^2.0.0"
+webpack-dev-middleware@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-2.0.6.tgz#a51692801e8310844ef3e3790e1eacfe52326fd4"
+ dependencies:
+ loud-rejection "^1.6.0"
+ memory-fs "~0.4.1"
+ mime "^2.1.0"
+ path-is-absolute "^1.0.0"
+ range-parser "^1.0.3"
+ url-join "^2.0.2"
+ webpack-log "^1.0.1"
+
webpack-dev-server@^2.11.2:
version "2.11.2"
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.2.tgz#1f4f4c78bf1895378f376815910812daf79a216f"
@@ -8701,6 +8473,15 @@ webpack-dev-server@^2.11.2:
webpack-dev-middleware "1.12.2"
yargs "6.6.0"
+webpack-log@^1.0.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.2.0.tgz#a4b34cda6b22b518dbb0ab32e567962d5c72a43d"
+ dependencies:
+ chalk "^2.1.0"
+ log-symbols "^2.1.0"
+ loglevelnext "^1.0.1"
+ uuid "^3.1.0"
+
webpack-sources@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf"
@@ -8859,7 +8640,7 @@ xregexp@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
-xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
+xtend@^4.0.0, xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"