summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile7
-rw-r--r--Gemfile.lock18
-rw-r--r--app/assets/javascripts/activities.js8
-rw-r--r--app/assets/javascripts/admin.js114
-rw-r--r--app/assets/javascripts/aside.js24
-rw-r--r--app/assets/javascripts/commits.js3
-rw-r--r--app/assets/javascripts/compare.js3
-rw-r--r--app/assets/javascripts/contextual_sidebar.js2
-rw-r--r--app/assets/javascripts/deploy_keys/components/key.vue3
-rw-r--r--app/assets/javascripts/diff_notes/models/discussion.js3
-rw-r--r--app/assets/javascripts/dispatcher.js20
-rw-r--r--app/assets/javascripts/groups/components/item_actions.vue14
-rw-r--r--app/assets/javascripts/issuable_bulk_update_sidebar.js2
-rw-r--r--app/assets/javascripts/issue.js2
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue10
-rw-r--r--app/assets/javascripts/issue_show/components/description.vue6
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue2
-rw-r--r--app/assets/javascripts/job.js3
-rw-r--r--app/assets/javascripts/lib/utils/cache.js4
-rw-r--r--app/assets/javascripts/lib/utils/constants.js1
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js218
-rw-r--r--app/assets/javascripts/main.js18
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js3
-rw-r--r--app/assets/javascripts/merge_request_tabs.js8
-rw-r--r--app/assets/javascripts/milestone_select.js3
-rw-r--r--app/assets/javascripts/notes.js5
-rw-r--r--app/assets/javascripts/pipelines/components/empty_state.vue47
-rw-r--r--app/assets/javascripts/pipelines/pipelines_bundle.js3
-rw-r--r--app/assets/javascripts/profile/account/components/delete_account_modal.vue8
-rw-r--r--app/assets/javascripts/project_find_file.js298
-rw-r--r--app/assets/javascripts/render_gfm.js4
-rw-r--r--app/assets/javascripts/repo/components/new_dropdown/modal.vue8
-rw-r--r--app/assets/javascripts/repo/components/repo_commit_section.vue16
-rw-r--r--app/assets/javascripts/repo/components/repo_edit_button.vue6
-rw-r--r--app/assets/javascripts/repo/components/repo_preview.vue3
-rw-r--r--app/assets/javascripts/right_sidebar.js8
-rw-r--r--app/assets/javascripts/search.js207
-rw-r--r--app/assets/javascripts/search_autocomplete.js769
-rw-r--r--app/assets/javascripts/single_file_diff.js3
-rw-r--r--app/assets/javascripts/syntax_highlight.js14
-rw-r--r--app/assets/javascripts/users/activity_calendar.js5
-rw-r--r--app/assets/javascripts/users/user_tabs.js6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js19
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/memory_graph.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/modal.vue (renamed from app/assets/javascripts/vue_shared/components/popup_dialog.vue)4
-rw-r--r--app/assets/javascripts/vue_shared/components/recaptcha_modal.vue (renamed from app/assets/javascripts/vue_shared/components/recaptcha_dialog.vue)12
-rw-r--r--app/assets/javascripts/vue_shared/mixins/recaptcha_modal_implementor.js (renamed from app/assets/javascripts/vue_shared/mixins/recaptcha_dialog_implementor.js)4
-rw-r--r--app/assets/javascripts/vue_shared/mixins/timeago.js6
-rw-r--r--app/assets/stylesheets/framework/files.scss5
-rw-r--r--app/assets/stylesheets/framework/header.scss4
-rw-r--r--app/assets/stylesheets/framework/images.scss7
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss6
-rw-r--r--app/assets/stylesheets/framework/layout.scss5
-rw-r--r--app/assets/stylesheets/framework/mobile.scss17
-rw-r--r--app/assets/stylesheets/framework/modal.scss13
-rw-r--r--app/assets/stylesheets/framework/variables.scss3
-rw-r--r--app/assets/stylesheets/pages/boards.scss2
-rw-r--r--app/assets/stylesheets/pages/issuable.scss16
-rw-r--r--app/assets/stylesheets/pages/repo.scss13
-rw-r--r--app/controllers/concerns/with_performance_bar.rb1
-rw-r--r--app/controllers/projects/merge_requests/creations_controller.rb5
-rw-r--r--app/controllers/projects/tree_controller.rb1
-rw-r--r--app/controllers/projects_controller.rb1
-rw-r--r--app/helpers/issues_helper.rb2
-rw-r--r--app/mailers/emails/notes.rb10
-rw-r--r--app/mailers/notify.rb16
-rw-r--r--app/models/blob.rb12
-rw-r--r--app/models/ci/build.rb1
-rw-r--r--app/models/commit.rb14
-rw-r--r--app/models/merge_request.rb16
-rw-r--r--app/models/note.rb10
-rw-r--r--app/models/repository.rb15
-rw-r--r--app/services/ci/create_pipeline_service.rb2
-rw-r--r--app/services/issuable_base_service.rb6
-rw-r--r--app/views/dashboard/todos/index.html.haml6
-rw-r--r--app/views/layouts/_page.html.haml4
-rw-r--r--app/views/layouts/nav/projects_dropdown/_show.html.haml2
-rw-r--r--app/views/projects/blob/_header_content.html.haml3
-rw-r--r--app/views/projects/clusters/_empty_state.html.haml6
-rw-r--r--app/views/projects/commits/_commit.html.haml4
-rw-r--r--app/views/projects/environments/metrics.html.haml6
-rw-r--r--app/views/projects/issues/show.html.haml6
-rw-r--r--app/views/projects/merge_requests/_mr_title.html.haml2
-rw-r--r--app/views/projects/tree/_blob_item.html.haml3
-rw-r--r--app/views/shared/_show_aside.html.haml2
-rw-r--r--app/views/shared/empty_states/_issues.html.haml21
-rw-r--r--app/views/shared/empty_states/_labels.html.haml10
-rw-r--r--app/views/shared/empty_states/_merge_requests.html.haml17
-rw-r--r--app/workers/all_queues.yml98
-rw-r--r--app/workers/build_finished_worker.rb2
-rw-r--r--app/workers/build_hooks_worker.rb2
-rw-r--r--app/workers/build_queue_worker.rb2
-rw-r--r--app/workers/build_success_worker.rb2
-rw-r--r--app/workers/concerns/application_worker.rb24
-rw-r--r--app/workers/concerns/cluster_queue.rb2
-rw-r--r--app/workers/concerns/cronjob_queue.rb3
-rw-r--r--app/workers/concerns/gitlab/github_import/queue.rb4
-rw-r--r--app/workers/concerns/pipeline_queue.rb10
-rw-r--r--app/workers/concerns/repository_check_queue.rb4
-rw-r--r--app/workers/create_pipeline_worker.rb2
-rw-r--r--app/workers/expire_job_cache_worker.rb2
-rw-r--r--app/workers/expire_pipeline_cache_worker.rb2
-rw-r--r--app/workers/gitlab/github_import/advance_stage_worker.rb2
-rw-r--r--app/workers/pages_worker.rb2
-rw-r--r--app/workers/pipeline_hooks_worker.rb2
-rw-r--r--app/workers/pipeline_process_worker.rb2
-rw-r--r--app/workers/pipeline_success_worker.rb2
-rw-r--r--app/workers/pipeline_update_worker.rb2
-rw-r--r--app/workers/stage_update_worker.rb2
-rw-r--r--app/workers/stuck_merge_jobs_worker.rb7
-rw-r--r--app/workers/update_head_pipeline_for_merge_request_worker.rb16
-rw-r--r--changelogs/unreleased/13695-order-contributors-in-api.yml5
-rw-r--r--changelogs/unreleased/25317-prioritize-author-date-over-commit.yml5
-rw-r--r--changelogs/unreleased/33926-update-issuable-icons.yml5
-rw-r--r--changelogs/unreleased/38541-cancel-alignment.yml5
-rw-r--r--changelogs/unreleased/40285-prometheus-loading-screen-no-longer-seems-to-appear.yml5
-rw-r--r--changelogs/unreleased/40509_sorting_tags_api.yml5
-rw-r--r--changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml5
-rw-r--r--changelogs/unreleased/41016-import-gitlab-shell-projects.yml6
-rw-r--r--changelogs/unreleased/add-tcp-check-rake-task.yml5
-rw-r--r--changelogs/unreleased/fix-create-mr-from-issue-with-template.yml5
-rw-r--r--changelogs/unreleased/issue-description-field-typo.yml5
-rw-r--r--changelogs/unreleased/lfs-badge.yml5
-rw-r--r--changelogs/unreleased/tc-correct-email-in-reply-to.yml5
-rw-r--r--config/application.rb4
-rw-r--r--config/initializers/flipper.rb24
-rw-r--r--config/initializers/sidekiq.rb18
-rw-r--r--config/no_todos_messages.yml6
-rw-r--r--config/sidekiq_queues.yml5
-rw-r--r--db/post_migrate/20171213160445_migrate_github_importer_advance_stage_sidekiq_queue.rb16
-rw-r--r--db/schema.rb2
-rw-r--r--doc/administration/auth/README.md1
-rw-r--r--doc/administration/high_availability/README.md1
-rw-r--r--doc/administration/index.md35
-rw-r--r--doc/administration/monitoring/index.md9
-rw-r--r--doc/administration/monitoring/performance/index.md72
-rw-r--r--doc/administration/monitoring/performance/introduction.md71
-rw-r--r--doc/administration/raketasks/maintenance.md19
-rw-r--r--doc/administration/reply_by_email.md8
-rw-r--r--doc/api/issues.md6
-rw-r--r--doc/api/merge_requests.md8
-rw-r--r--doc/api/repositories.md2
-rw-r--r--doc/api/tags.md6
-rw-r--r--doc/ci/ssh_keys/README.md249
-rw-r--r--doc/ci/variables/README.md13
-rw-r--r--doc/customization/issue_closing.md4
-rw-r--r--doc/customization/welcome_message.md2
-rw-r--r--doc/development/README.md5
-rw-r--r--doc/development/performance.md2
-rw-r--r--doc/development/sidekiq_style_guide.md59
-rw-r--r--doc/development/ux_guide/components.md18
-rw-r--r--doc/development/ux_guide/copy.md12
-rw-r--r--doc/development/writing_documentation.md169
-rw-r--r--doc/install/installation.md4
-rw-r--r--doc/monitoring/performance/introduction.md2
-rw-r--r--doc/topics/git/index.md4
-rw-r--r--doc/topics/git/troubleshooting_git.md82
-rw-r--r--doc/update/10.2-to-10.3.md360
-rw-r--r--doc/user/discussions/img/commit_comment_mr_context.pngbin0 -> 25854 bytes
-rw-r--r--doc/user/discussions/img/commit_comment_mr_discussions_tab.pngbin0 -> 15139 bytes
-rw-r--r--doc/user/discussions/img/merge_request_commits_tab.pngbin0 -> 12792 bytes
-rw-r--r--doc/user/discussions/index.md47
-rw-r--r--doc/user/group/index.md10
-rw-r--r--doc/user/markdown.md11
-rw-r--r--doc/user/profile/index.md9
-rw-r--r--doc/user/project/merge_requests/img/create_from_email.pngbin0 -> 152975 bytes
-rw-r--r--doc/user/project/merge_requests/index.md9
-rw-r--r--doc/user/project/pipelines/settings.md2
-rw-r--r--doc/user/project/settings/index.md6
-rw-r--r--lib/api/repositories.rb4
-rw-r--r--lib/api/tags.rb7
-rw-r--r--lib/extracts_path.rb6
-rw-r--r--lib/feature.rb9
-rw-r--r--lib/gitlab/git/gitlab_projects.rb258
-rw-r--r--lib/gitlab/git/operation_service.rb2
-rw-r--r--lib/gitlab/git/repository.rb38
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb10
-rw-r--r--lib/gitlab/metrics/method_call.rb17
-rw-r--r--lib/gitlab/shell.rb113
-rw-r--r--lib/gitlab/sidekiq_config.rb37
-rw-r--r--lib/gitlab/sidekiq_versioning.rb25
-rw-r--r--lib/gitlab/sidekiq_versioning/manager.rb12
-rw-r--r--lib/gitlab/tcp_checker.rb45
-rw-r--r--lib/tasks/gitlab/tcp_check.rake20
-rw-r--r--qa/Dockerfile2
-rw-r--r--qa/Gemfile12
-rw-r--r--qa/Gemfile.lock84
-rw-r--r--rubocop/cop/include_sidekiq_worker.rb29
-rw-r--r--rubocop/cop/sidekiq_options_queue.rb27
-rw-r--r--rubocop/rubocop.rb2
-rwxr-xr-xscripts/trigger-build-omnibus15
-rw-r--r--spec/factories/commits.rb19
-rw-r--r--spec/features/commits_spec.rb2
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb2
-rw-r--r--spec/features/profile_spec.rb4
-rw-r--r--spec/features/projects/tree/create_directory_spec.rb2
-rw-r--r--spec/features/projects/tree/create_file_spec.rb2
-rw-r--r--spec/features/projects/tree/tree_show_spec.rb28
-rw-r--r--spec/fixtures/api/schemas/contributor.json18
-rw-r--r--spec/fixtures/api/schemas/contributors.json4
-rw-r--r--spec/fixtures/markdown.md.erb17
-rw-r--r--spec/helpers/tree_helper_spec.rb1
-rw-r--r--spec/javascripts/activities_spec.js4
-rw-r--r--spec/javascripts/datetime_utility_spec.js186
-rw-r--r--spec/javascripts/deploy_keys/components/key_spec.js3
-rw-r--r--spec/javascripts/groups/components/item_actions_spec.js24
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js6
-rw-r--r--spec/javascripts/issue_show/components/description_spec.js4
-rw-r--r--spec/javascripts/issue_spec.js2
-rw-r--r--spec/javascripts/notes_spec.js2
-rw-r--r--spec/javascripts/right_sidebar_spec.js2
-rw-r--r--spec/javascripts/search_autocomplete_spec.js4
-rw-r--r--spec/javascripts/syntax_highlight_spec.js74
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js3
-rw-r--r--spec/javascripts/vue_shared/components/modal_spec.js (renamed from spec/javascripts/vue_shared/components/popup_dialog_spec.js)8
-rw-r--r--spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js6
-rw-r--r--spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb27
-rw-r--r--spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb3
-rw-r--r--spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb19
-rw-r--r--spec/lib/gitlab/git/gitlab_projects_spec.rb309
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb45
-rw-r--r--spec/lib/gitlab/metrics/method_call_spec.rb32
-rw-r--r--spec/lib/gitlab/shell_spec.rb231
-rw-r--r--spec/lib/gitlab/sidekiq_config_spec.rb23
-rw-r--r--spec/lib/gitlab/sidekiq_versioning/manager_spec.rb22
-rw-r--r--spec/lib/gitlab/sidekiq_versioning_spec.rb44
-rw-r--r--spec/lib/gitlab/tcp_checker_spec.rb32
-rw-r--r--spec/mailers/notify_spec.rb40
-rw-r--r--spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb11
-rw-r--r--spec/models/ci/build_spec.rb7
-rw-r--r--spec/models/merge_request_spec.rb45
-rw-r--r--spec/models/note_spec.rb22
-rw-r--r--spec/models/repository_spec.rb121
-rw-r--r--spec/requests/api/repositories_spec.rb22
-rw-r--r--spec/requests/api/tags_spec.rb38
-rw-r--r--spec/rubocop/cop/include_sidekiq_worker_spec.rb31
-rw-r--r--spec/rubocop/cop/sidekiq_options_queue_spec.rb26
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb16
-rw-r--r--spec/services/ci/register_job_service_spec.rb9
-rw-r--r--spec/services/merge_requests/create_from_issue_service_spec.rb12
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb8
-rw-r--r--spec/support/batch_loader.rb5
-rw-r--r--spec/support/stub_env.rb2
-rw-r--r--spec/views/projects/tree/_blob_item.html.haml_spec.rb40
-rw-r--r--spec/views/projects/tree/show.html.haml_spec.rb1
-rw-r--r--spec/workers/concerns/application_worker_spec.rb8
-rw-r--r--spec/workers/concerns/cluster_queue_spec.rb2
-rw-r--r--spec/workers/concerns/cronjob_queue_spec.rb2
-rw-r--r--spec/workers/concerns/gitlab/github_import/queue_spec.rb2
-rw-r--r--spec/workers/concerns/pipeline_queue_spec.rb11
-rw-r--r--spec/workers/concerns/repository_check_queue_spec.rb2
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb31
-rw-r--r--spec/workers/stuck_merge_jobs_worker_spec.rb6
-rw-r--r--spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb2
-rw-r--r--vendor/Dockerfile/CONTRIBUTING.md25
-rw-r--r--vendor/Dockerfile/LICENSE16
-rw-r--r--vendor/gitignore/Global/Matlab.gitignore3
-rw-r--r--vendor/gitignore/Go.gitignore3
-rw-r--r--vendor/gitignore/Haskell.gitignore1
-rw-r--r--vendor/gitignore/Jekyll.gitignore1
-rw-r--r--vendor/gitignore/ROS.gitignore2
-rw-r--r--vendor/gitignore/Symfony.gitignore4
-rw-r--r--vendor/gitignore/TeX.gitignore6
-rw-r--r--vendor/gitignore/Unity.gitignore13
-rw-r--r--vendor/gitignore/UnrealEngine.gitignore2
-rw-r--r--vendor/gitignore/VisualStudio.gitignore14
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml31
-rw-r--r--vendor/gitlab-ci-yml/CONTRIBUTING.md19
-rw-r--r--vendor/gitlab-ci-yml/Chef.gitlab-ci.yml51
-rw-r--r--vendor/gitlab-ci-yml/Go.gitlab-ci.yml4
-rw-r--r--vendor/gitlab-ci-yml/LICENSE16
-rw-r--r--vendor/gitlab-ci-yml/Rust.gitlab-ci.yml2
-rw-r--r--vendor/licenses.csv1097
277 files changed, 4659 insertions, 3079 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index cb6b534abe1..7e750b4ebf3 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.59.0
+0.60.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 269fb5dfe2c..e030a0157c9 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-5.10.2
+5.10.3
diff --git a/Gemfile b/Gemfile
index e9701fab27a..06a9b2442af 100644
--- a/Gemfile
+++ b/Gemfile
@@ -400,13 +400,14 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.59.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 0.61.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false
# Feature toggles
-gem 'flipper', '~> 0.10.2'
-gem 'flipper-active_record', '~> 0.10.2'
+gem 'flipper', '~> 0.11.0'
+gem 'flipper-active_record', '~> 0.11.0'
+gem 'flipper-active_support_cache_store', '~> 0.11.0'
# Structured logging
gem 'lograge', '~> 0.5'
diff --git a/Gemfile.lock b/Gemfile.lock
index efae71efdb7..55600555e4d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -215,10 +215,13 @@ GEM
path_expander (~> 1.0)
ruby_parser (~> 3.0)
sexp_processor (~> 4.0)
- flipper (0.10.2)
- flipper-active_record (0.10.2)
+ flipper (0.11.0)
+ flipper-active_record (0.11.0)
activerecord (>= 3.2, < 6)
- flipper (~> 0.10.2)
+ flipper (~> 0.11.0)
+ flipper-active_support_cache_store (0.11.0)
+ activesupport (>= 3.2, < 6)
+ flipper (~> 0.11.0)
flowdock (0.7.1)
httparty (~> 0.7)
multi_json
@@ -281,7 +284,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
- gitaly-proto (0.59.0)
+ gitaly-proto (0.61.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
@@ -1021,8 +1024,9 @@ DEPENDENCIES
faraday (~> 0.12)
ffaker (~> 2.4)
flay (~> 2.8.0)
- flipper (~> 0.10.2)
- flipper-active_record (~> 0.10.2)
+ flipper (~> 0.11.0)
+ flipper-active_record (~> 0.11.0)
+ flipper-active_support_cache_store (~> 0.11.0)
fog-aliyun (~> 0.2.0)
fog-aws (~> 1.4)
fog-core (~> 1.44)
@@ -1038,7 +1042,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
- gitaly-proto (~> 0.59.0)
+ gitaly-proto (~> 0.61.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.6.2)
diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js
index 5d060165f4b..f5f6b67f26e 100644
--- a/app/assets/javascripts/activities.js
+++ b/app/assets/javascripts/activities.js
@@ -2,8 +2,9 @@
/* global Pager */
import Cookies from 'js-cookie';
+import { localTimeAgo } from './lib/utils/datetime_utility';
-class Activities {
+export default class Activities {
constructor() {
Pager.init(20, true, false, data => data, this.updateTooltips);
@@ -15,7 +16,7 @@ class Activities {
}
updateTooltips() {
- gl.utils.localTimeAgo($('.js-timeago', '.content_list'));
+ localTimeAgo($('.js-timeago', '.content_list'));
}
reloadActivities() {
@@ -33,6 +34,3 @@ class Activities {
$sender.closest('li').toggleClass('active');
}
}
-
-window.gl = window.gl || {};
-window.gl.Activities = Activities;
diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js
index b0b72c40f25..c1f7fa2aced 100644
--- a/app/assets/javascripts/admin.js
+++ b/app/assets/javascripts/admin.js
@@ -1,63 +1,59 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, no-unused-vars, no-else-return, prefer-arrow-callback, camelcase, quotes, comma-dangle, max-len */
import { refreshCurrentPage } from './lib/utils/url_utility';
-window.Admin = (function() {
- function Admin() {
- var modal, showBlacklistType;
- $('input#user_force_random_password').on('change', function(elem) {
- var elems;
- elems = $('#user_password, #user_password_confirmation');
- if ($(this).attr('checked')) {
- return elems.val('').attr('disabled', true);
- } else {
- return elems.removeAttr('disabled');
- }
- });
- $('body').on('click', '.js-toggle-colors-link', function(e) {
- e.preventDefault();
- return $('.js-toggle-colors-container').toggle();
- });
- $('.log-tabs a').click(function(e) {
- e.preventDefault();
- return $(this).tab('show');
- });
- $('.log-bottom').click(function(e) {
- var visible_log;
- e.preventDefault();
- visible_log = $(".file-content:visible");
- return visible_log.animate({
- scrollTop: visible_log.find('ol').height()
- }, "fast");
- });
- modal = $('.change-owner-holder');
- $('.change-owner-link').bind("click", function(e) {
- e.preventDefault();
- $(this).hide();
- return modal.show();
- });
- $('.change-owner-cancel-link').bind("click", function(e) {
- e.preventDefault();
- modal.hide();
- return $('.change-owner-link').show();
- });
- $('li.project_member').bind('ajax:success', function() {
- return refreshCurrentPage();
- });
- $('li.group_member').bind('ajax:success', function() {
- return refreshCurrentPage();
- });
- showBlacklistType = function() {
- if ($("input[name='blacklist_type']:checked").val() === 'file') {
- $('.blacklist-file').show();
- return $('.blacklist-raw').hide();
- } else {
- $('.blacklist-file').hide();
- return $('.blacklist-raw').show();
- }
- };
- $("input[name='blacklist_type']").click(showBlacklistType);
- showBlacklistType();
+function showBlacklistType() {
+ if ($('input[name="blacklist_type"]:checked').val() === 'file') {
+ $('.blacklist-file').show();
+ $('.blacklist-raw').hide();
+ } else {
+ $('.blacklist-file').hide();
+ $('.blacklist-raw').show();
}
+}
- return Admin;
-})();
+export default function adminInit() {
+ const modal = $('.change-owner-holder');
+
+ $('input#user_force_random_password').on('change', function randomPasswordClick() {
+ const $elems = $('#user_password, #user_password_confirmation');
+ if ($(this).attr('checked')) {
+ $elems.val('').attr('disabled', true);
+ } else {
+ $elems.removeAttr('disabled');
+ }
+ });
+
+ $('body').on('click', '.js-toggle-colors-link', (e) => {
+ e.preventDefault();
+ $('.js-toggle-colors-container').toggle();
+ });
+
+ $('.log-tabs a').on('click', function logTabsClick(e) {
+ e.preventDefault();
+ $(this).tab('show');
+ });
+
+ $('.log-bottom').on('click', (e) => {
+ e.preventDefault();
+ const $visibleLog = $('.file-content:visible');
+ $visibleLog.animate({
+ scrollTop: $visibleLog.find('ol').height(),
+ }, 'fast');
+ });
+
+ $('.change-owner-link').on('click', function changeOwnerLinkClick(e) {
+ e.preventDefault();
+ $(this).hide();
+ modal.show();
+ });
+
+ $('.change-owner-cancel-link').on('click', (e) => {
+ e.preventDefault();
+ modal.hide();
+ $('.change-owner-link').show();
+ });
+
+ $('li.project_member, li.group_member').on('ajax:success', refreshCurrentPage);
+
+ $("input[name='blacklist_type']").on('click', showBlacklistType);
+ showBlacklistType();
+}
diff --git a/app/assets/javascripts/aside.js b/app/assets/javascripts/aside.js
deleted file mode 100644
index 88756884d16..00000000000
--- a/app/assets/javascripts/aside.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, prefer-arrow-callback, no-var, one-var, one-var-declaration-per-line, no-else-return, max-len */
-
-window.Aside = (function() {
- function Aside() {
- $(document).off("click", "a.show-aside");
- $(document).on("click", 'a.show-aside', function(e) {
- var btn, icon;
- e.preventDefault();
- btn = $(e.currentTarget);
- icon = btn.find('i');
- if (icon.hasClass('fa-angle-left')) {
- btn.parent().find('section').hide();
- btn.parent().find('aside').fadeIn();
- return icon.removeClass('fa-angle-left').addClass('fa-angle-right');
- } else {
- btn.parent().find('aside').hide();
- btn.parent().find('section').fadeIn();
- return icon.removeClass('fa-angle-right').addClass('fa-angle-left');
- }
- });
- }
-
- return Aside;
-})();
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 9b952ea7b60..be58392135c 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -4,6 +4,7 @@
/* global Pager */
import { pluralize } from './lib/utils/text_utility';
+import { localTimeAgo } from './lib/utils/datetime_utility';
export default (function () {
const CommitsList = {};
@@ -91,7 +92,7 @@ export default (function () {
$commitsHeadersLast.find('span.commits-count').text(`${commitsCount} ${pluralize('commit', commitsCount)}`);
}
- gl.utils.localTimeAgo($processedData.find('.js-timeago'));
+ localTimeAgo($processedData.find('.js-timeago'));
return processedData;
};
diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js
index 0ce467a3bd4..144caf1d278 100644
--- a/app/assets/javascripts/compare.js
+++ b/app/assets/javascripts/compare.js
@@ -1,4 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */
+import { localTimeAgo } from './lib/utils/datetime_utility';
export default class Compare {
constructor(opts) {
@@ -81,7 +82,7 @@ export default class Compare {
loading.hide();
$target.html(html);
var className = '.' + $target[0].className.replace(' ', '.');
- gl.utils.localTimeAgo($('.js-timeago', className));
+ localTimeAgo($('.js-timeago', className));
}
});
}
diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js
index cd20dde2951..74520675a7c 100644
--- a/app/assets/javascripts/contextual_sidebar.js
+++ b/app/assets/javascripts/contextual_sidebar.js
@@ -9,7 +9,7 @@ export default class ContextualSidebar {
}
initDomElements() {
- this.$page = $('.page-with-sidebar');
+ this.$page = $('.layout-page');
this.$sidebar = $('.nav-sidebar');
this.$innerScroll = $('.nav-sidebar-inner-scroll', this.$sidebar);
this.$overlay = $('.mobile-overlay');
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index b41d464475f..2a05c6f001e 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -1,5 +1,6 @@
<script>
import actionBtn from './action_btn.vue';
+ import { getTimeago } from '../../lib/utils/datetime_utility';
export default {
props: {
@@ -21,7 +22,7 @@
},
computed: {
timeagoDate() {
- return gl.utils.getTimeago().format(this.deployKey.created_at);
+ return getTimeago().format(this.deployKey.created_at);
},
editDeployKeyPath() {
return `${this.endpoint}/${this.deployKey.id}/edit`;
diff --git a/app/assets/javascripts/diff_notes/models/discussion.js b/app/assets/javascripts/diff_notes/models/discussion.js
index dc43e4b2cc7..1b8a9af9390 100644
--- a/app/assets/javascripts/diff_notes/models/discussion.js
+++ b/app/assets/javascripts/diff_notes/models/discussion.js
@@ -2,6 +2,7 @@
/* global NoteModel */
import Vue from 'vue';
+import { localTimeAgo } from '../../lib/utils/datetime_utility';
class DiscussionModel {
constructor (discussionId) {
@@ -71,7 +72,7 @@ class DiscussionModel {
$(`${discussionSelector} .discussion-header`).append(data.discussion_headline_html);
}
- gl.utils.localTimeAgo($('.js-timeago', `${discussionSelector}`));
+ localTimeAgo($('.js-timeago', `${discussionSelector}`));
} else {
$discussionHeadline.remove();
}
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 299e43a4e90..522f5d12b30 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -15,8 +15,8 @@ import GroupLabelSubscription from './group_label_subscription';
import BuildArtifacts from './build_artifacts';
import CILintEditor from './ci_lint_editor';
import groupsSelect from './groups_select';
-/* global Search */
-/* global Admin */
+import Search from './search';
+import initAdmin from './admin';
import NamespaceSelect from './namespace_select';
import NewCommitForm from './new_commit_form';
import Project from './project';
@@ -24,7 +24,7 @@ import projectAvatar from './project_avatar';
/* global MergeRequest */
import Compare from './compare';
import initCompareAutocomplete from './compare_autocomplete';
-/* global ProjectFindFile */
+import ProjectFindFile from './project_find_file';
import ProjectNew from './project_new';
import projectImport from './project_import';
import Labels from './labels';
@@ -91,6 +91,8 @@ import DueDateSelectors from './due_date_select';
import Diff from './diff';
import ProjectLabelSubscription from './project_label_subscription';
import ProjectVariables from './project_variables';
+import SearchAutocomplete from './search_autocomplete';
+import Activities from './activities';
(function() {
var Dispatcher;
@@ -333,7 +335,7 @@ import ProjectVariables from './project_variables';
shortcut_handler = new ShortcutsIssuable(true);
break;
case 'dashboard:activity':
- new gl.Activities();
+ new Activities();
break;
case 'projects:commit:show':
new Diff();
@@ -354,7 +356,7 @@ import ProjectVariables from './project_variables';
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
break;
case 'projects:activity':
- new gl.Activities();
+ new Activities();
shortcut_handler = new ShortcutsNavigation();
break;
case 'projects:commits:show':
@@ -372,7 +374,7 @@ import ProjectVariables from './project_variables';
if ($('#tree-slider').length) new TreeView();
if ($('.blob-viewer').length) new BlobViewer();
- if ($('.project-show-activity').length) new gl.Activities();
+ if ($('.project-show-activity').length) new Activities();
$('#tree-slider').waitForImages(function() {
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath);
});
@@ -406,7 +408,7 @@ import ProjectVariables from './project_variables';
});
break;
case 'groups:activity':
- new gl.Activities();
+ new Activities();
break;
case 'groups:show':
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
@@ -583,7 +585,7 @@ import ProjectVariables from './project_variables';
// needed in rspec
gl.u2fAuthenticate = u2fAuthenticate;
case 'admin':
- new Admin();
+ initAdmin();
switch (path[1]) {
case 'broadcast_messages':
initBroadcastMessagesForm();
@@ -683,7 +685,7 @@ import ProjectVariables from './project_variables';
Dispatcher.prototype.initSearch = function() {
// Only when search form is present
if ($('.search').length) {
- return new gl.SearchAutocomplete();
+ return new SearchAutocomplete();
}
};
diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue
index 09cb79c1afd..58ba5aff7cf 100644
--- a/app/assets/javascripts/groups/components/item_actions.vue
+++ b/app/assets/javascripts/groups/components/item_actions.vue
@@ -1,7 +1,7 @@
<script>
import { s__ } from '../../locale';
import tooltip from '../../vue_shared/directives/tooltip';
-import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
+import modal from '../../vue_shared/components/modal.vue';
import eventHub from '../event_hub';
import { COMMON_STR } from '../constants';
import Icon from '../../vue_shared/components/icon.vue';
@@ -9,7 +9,7 @@ import Icon from '../../vue_shared/components/icon.vue';
export default {
components: {
Icon,
- PopupDialog,
+ modal,
},
directives: {
tooltip,
@@ -27,7 +27,7 @@ export default {
},
data() {
return {
- dialogStatus: false,
+ modalStatus: false,
};
},
computed: {
@@ -43,10 +43,10 @@ export default {
},
methods: {
onLeaveGroup() {
- this.dialogStatus = true;
+ this.modalStatus = true;
},
leaveGroup(leaveConfirmed) {
- this.dialogStatus = false;
+ this.modalStatus = false;
if (leaveConfirmed) {
eventHub.$emit('leaveGroup', this.group, this.parentGroup);
}
@@ -82,8 +82,8 @@ export default {
class="fa fa-sign-out"
aria-hidden="true"/>
</a>
- <popup-dialog
- v-show="dialogStatus"
+ <modal
+ v-show="modalStatus"
:primary-button-label="__('Leave')"
kind="warning"
:title="__('Are you sure?')"
diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js
index ba2b6737988..bf77b93b643 100644
--- a/app/assets/javascripts/issuable_bulk_update_sidebar.js
+++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js
@@ -21,7 +21,7 @@ export default class IssuableBulkUpdateSidebar {
}
initDomElements() {
- this.$page = $('.page-with-sidebar');
+ this.$page = $('.layout-page');
this.$sidebar = $('.right-sidebar');
this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar');
this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide');
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 91b5ef1c10a..411c820cc43 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -48,7 +48,7 @@ export default class Issue {
})
.fail(() => new Flash(issueFailMessage))
.done((data) => {
- const isClosedBadge = $('div.status-box-closed');
+ const isClosedBadge = $('div.status-box-issue-closed');
const isOpenBadge = $('div.status-box-open');
const projectIssuesCounter = $('.issue_counter');
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index fd1a50dd533..25ebe5314e0 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -9,7 +9,7 @@ import titleComponent from './title.vue';
import descriptionComponent from './description.vue';
import editedComponent from './edited.vue';
import formComponent from './form.vue';
-import RecaptchaDialogImplementor from '../../vue_shared/mixins/recaptcha_dialog_implementor';
+import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
export default {
props: {
@@ -152,7 +152,7 @@ export default {
},
mixins: [
- RecaptchaDialogImplementor,
+ recaptchaModalImplementor,
],
methods: {
@@ -197,7 +197,7 @@ export default {
});
},
- closeRecaptchaDialog() {
+ closeRecaptchaModal() {
this.store.setFormState({
updateLoading: false,
});
@@ -273,10 +273,10 @@ export default {
:enable-autocomplete="enableAutocomplete"
/>
- <recaptcha-dialog
+ <recaptcha-modal
v-show="showRecaptcha"
:html="recaptchaHTML"
- @close="closeRecaptchaDialog"
+ @close="closeRecaptchaModal"
/>
</div>
<div v-else>
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index feb73481422..c3f2bf130bb 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -1,12 +1,12 @@
<script>
import animateMixin from '../mixins/animate';
import TaskList from '../../task_list';
- import RecaptchaDialogImplementor from '../../vue_shared/mixins/recaptcha_dialog_implementor';
+ import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
export default {
mixins: [
animateMixin,
- RecaptchaDialogImplementor,
+ recaptchaModalImplementor,
],
props: {
@@ -126,7 +126,7 @@
>
</textarea>
- <recaptcha-dialog
+ <recaptcha-modal
v-show="showRecaptcha"
:html="recaptchaHTML"
@close="closeRecaptcha"
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index 52fe4ecd08b..4e577546551 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -53,7 +53,7 @@
<textarea
id="issue-description"
class="note-textarea js-gfm-input js-autosize markdown-area"
- data-supports-quick-actionss="false"
+ data-supports-quick-actions="false"
aria-label="Description"
v-model="formState.description"
ref="textarea"
diff --git a/app/assets/javascripts/job.js b/app/assets/javascripts/job.js
index 06b0e02a870..198a7823381 100644
--- a/app/assets/javascripts/job.js
+++ b/app/assets/javascripts/job.js
@@ -3,6 +3,7 @@ import { visitUrl } from './lib/utils/url_utility';
import bp from './breakpoints';
import { bytesToKiB } from './lib/utils/number_utils';
import { setCiStatusFavicon } from './lib/utils/common_utils';
+import { timeFor } from './lib/utils/datetime_utility';
export default class Job {
constructor(options) {
@@ -261,7 +262,7 @@ export default class Job {
if ($date.length) {
const date = $date.text();
return $date.text(
- gl.utils.timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' '),
+ timeFor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3'))),
);
}
}
diff --git a/app/assets/javascripts/lib/utils/cache.js b/app/assets/javascripts/lib/utils/cache.js
index 3141f1eeafc..596bd1e388a 100644
--- a/app/assets/javascripts/lib/utils/cache.js
+++ b/app/assets/javascripts/lib/utils/cache.js
@@ -1,4 +1,4 @@
-class Cache {
+export default class Cache {
constructor() {
this.internalStorage = { };
}
@@ -15,5 +15,3 @@ class Cache {
delete this.internalStorage[key];
}
}
-
-export default Cache;
diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js
index 7a72509d234..9a61003ef30 100644
--- a/app/assets/javascripts/lib/utils/constants.js
+++ b/app/assets/javascripts/lib/utils/constants.js
@@ -1,3 +1,2 @@
-/* eslint-disable import/prefer-default-export */
export const BYTES_IN_KIB = 1024;
export const HIDDEN_CLASS = 'hidden';
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index d0578b230b1..198b5164c92 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -1,9 +1,6 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len */
-
import timeago from 'timeago.js';
import dateFormat from 'vendor/date.format';
import { pluralize } from './text_utility';
-
import {
lang,
s__,
@@ -12,121 +9,125 @@ import {
window.timeago = timeago;
window.dateFormat = dateFormat;
-(function() {
- (function(w) {
- var base;
- var timeagoInstance;
+/**
+ * Given a date object returns the day of the week in English
+ * @param {date} date
+ * @returns {String}
+ */
+export const getDayName = date => ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][date.getDay()];
- if (w.gl == null) {
- w.gl = {};
- }
- if ((base = w.gl).utils == null) {
- base.utils = {};
- }
- w.gl.utils.days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+/**
+ * @example
+ * dateFormat('2017-12-05','mmm d, yyyy h:MMtt Z' ) -> "Dec 5, 2017 12:00am GMT+0000"
+ * @param {date} datetime
+ * @returns {String}
+ */
+export const formatDate = datetime => dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
- w.gl.utils.formatDate = function(datetime) {
- return dateFormat(datetime, 'mmm d, yyyy h:MMtt Z');
+let timeagoInstance;
+/**
+ * Sets a timeago Instance
+ */
+export function getTimeago() {
+ if (!timeagoInstance) {
+ const localeRemaining = function getLocaleRemaining(number, index) {
+ return [
+ [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
+ [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')],
+ [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')],
+ [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
+ [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')],
+ [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')],
+ [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')],
+ [s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')],
+ [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')],
+ [s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')],
+ [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')],
+ [s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')],
+ [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')],
+ [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')],
+ ][index];
};
-
- w.gl.utils.getDayName = function(date) {
- return this.days[date.getDay()];
+ const locale = function getLocale(number, index) {
+ return [
+ [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
+ [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')],
+ [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')],
+ [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
+ [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')],
+ [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')],
+ [s__('Timeago|a day ago'), s__('Timeago|in 1 day')],
+ [s__('Timeago|%s days ago'), s__('Timeago|in %s days')],
+ [s__('Timeago|a week ago'), s__('Timeago|in 1 week')],
+ [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')],
+ [s__('Timeago|a month ago'), s__('Timeago|in 1 month')],
+ [s__('Timeago|%s months ago'), s__('Timeago|in %s months')],
+ [s__('Timeago|a year ago'), s__('Timeago|in 1 year')],
+ [s__('Timeago|%s years ago'), s__('Timeago|in %s years')],
+ ][index];
};
- w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) {
- $timeagoEls.each((i, el) => {
- if (setTimeago) {
- // Recreate with custom template
- $(el).tooltip({
- template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
- });
- }
+ timeago.register(lang, locale);
+ timeago.register(`${lang}-remaining`, localeRemaining);
+ timeagoInstance = timeago();
+ }
- el.classList.add('js-timeago-render');
- });
+ return timeagoInstance;
+}
- gl.utils.renderTimeago($timeagoEls);
- };
+/**
+ * For the given element, renders a timeago instance.
+ * @param {jQuery} $els
+ */
+export const renderTimeago = ($els) => {
+ const timeagoEls = $els || document.querySelectorAll('.js-timeago-render');
- w.gl.utils.getTimeago = function() {
- var locale;
-
- if (!timeagoInstance) {
- const localeRemaining = function(number, index) {
- return [
- [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
- [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')],
- [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')],
- [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
- [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')],
- [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')],
- [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')],
- [s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')],
- [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')],
- [s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')],
- [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')],
- [s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')],
- [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')],
- [s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')]
- ][index];
- };
- locale = function(number, index) {
- return [
- [s__('Timeago|less than a minute ago'), s__('Timeago|in a while')],
- [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')],
- [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')],
- [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
- [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')],
- [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')],
- [s__('Timeago|a day ago'), s__('Timeago|in 1 day')],
- [s__('Timeago|%s days ago'), s__('Timeago|in %s days')],
- [s__('Timeago|a week ago'), s__('Timeago|in 1 week')],
- [s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')],
- [s__('Timeago|a month ago'), s__('Timeago|in 1 month')],
- [s__('Timeago|%s months ago'), s__('Timeago|in %s months')],
- [s__('Timeago|a year ago'), s__('Timeago|in 1 year')],
- [s__('Timeago|%s years ago'), s__('Timeago|in %s years')]
- ][index];
- };
-
- timeago.register(lang, locale);
- timeago.register(`${lang}-remaining`, localeRemaining);
- timeagoInstance = timeago();
- }
-
- return timeagoInstance;
- };
+ // timeago.js sets timeouts internally for each timeago value to be updated in real time
+ getTimeago().render(timeagoEls, lang);
+};
- w.gl.utils.timeFor = function(time, suffix, expiredLabel) {
- var timefor;
- if (!time) {
- return '';
- }
- if (new Date(time) < new Date()) {
- expiredLabel || (expiredLabel = s__('Timeago|Past due'));
- timefor = expiredLabel;
- } else {
- timefor = gl.utils.getTimeago().format(time, `${lang}-remaining`).trim();
- }
- return timefor;
- };
+/**
+ * For the given elements, sets a tooltip with a formatted date.
+ * @param {jQuery}
+ * @param {Boolean} setTimeago
+ */
+export const localTimeAgo = ($timeagoEls, setTimeago = true) => {
+ $timeagoEls.each((i, el) => {
+ if (setTimeago) {
+ // Recreate with custom template
+ $(el).tooltip({
+ template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
+ });
+ }
- w.gl.utils.renderTimeago = function($els) {
- const timeagoEls = $els || document.querySelectorAll('.js-timeago-render');
+ el.classList.add('js-timeago-render');
+ });
- // timeago.js sets timeouts internally for each timeago value to be updated in real time
- gl.utils.getTimeago().render(timeagoEls, lang);
- };
+ renderTimeago($timeagoEls);
+};
- w.gl.utils.getDayDifference = function(a, b) {
- var millisecondsPerDay = 1000 * 60 * 60 * 24;
- var date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
- var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
+/**
+ * Returns remaining or passed time over the given time.
+ * @param {*} time
+ * @param {*} expiredLabel
+ */
+export const timeFor = (time, expiredLabel) => {
+ if (!time) {
+ return '';
+ }
+ if (new Date(time) < new Date()) {
+ return expiredLabel || s__('Timeago|Past due');
+ }
+ return getTimeago().format(time, `${lang}-remaining`).trim();
+};
- return Math.floor((date2 - date1) / millisecondsPerDay);
- };
- })(window);
-}).call(window);
+export const getDayDifference = (a, b) => {
+ const millisecondsPerDay = 1000 * 60 * 60 * 24;
+ const date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
+ const date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
+
+ return Math.floor((date2 - date1) / millisecondsPerDay);
+};
/**
* Port of ruby helper time_interval_in_words.
@@ -162,3 +163,10 @@ export function dateInWords(date, abbreviated = false) {
return `${monthName} ${date.getDate()}, ${year}`;
}
+
+window.gl = window.gl || {};
+window.gl.utils = {
+ ...(window.gl.utils || {}),
+ getTimeago,
+ localTimeAgo,
+};
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 9e4047b6840..96284c4c168 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -1,6 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len, no-multi-spaces, import/newline-after-import, import/first */
/* global ConfirmDangerModal */
-/* global Aside */
import jQuery from 'jquery';
import _ from 'underscore';
@@ -28,7 +27,7 @@ import './commit/image_file';
// lib/utils
import { handleLocationHash } from './lib/utils/common_utils';
-import './lib/utils/datetime_utility';
+import { localTimeAgo, renderTimeago } from './lib/utils/datetime_utility';
import { getLocationHash, visitUrl } from './lib/utils/url_utility';
// behaviors
@@ -37,7 +36,6 @@ import './behaviors/';
// everything else
import './activities';
import './admin';
-import './aside';
import loadAwardsHandler from './awards_handler';
import bp from './breakpoints';
import './confirm_danger_modal';
@@ -60,15 +58,10 @@ import './notifications_dropdown';
import './notifications_form';
import './pager';
import './preview_markdown';
-import './project_find_file';
import './project_import';
import './projects_dropdown';
-import './projects_list';
-import './syntax_highlight';
import './render_gfm';
import './right_sidebar';
-import './search';
-import './search_autocomplete';
import initBreadcrumbs from './breadcrumb';
import './dispatcher';
@@ -125,7 +118,7 @@ $(function () {
});
if (bootstrapBreakpoint === 'xs') {
- const $rightSidebar = $('aside.right-sidebar, .page-with-sidebar');
+ const $rightSidebar = $('aside.right-sidebar, .layout-page');
$rightSidebar
.removeClass('right-sidebar-expanded')
@@ -185,13 +178,13 @@ $(function () {
trigger: 'focus',
// set the viewport to the main content, excluding the navigation bar, so
// the navigation can't overlap the popover
- viewport: '.page-with-sidebar'
+ viewport: '.layout-page'
});
$('.trigger-submit').on('change', function () {
return $(this).parents('form').submit();
// Form submitter
});
- gl.utils.localTimeAgo($('abbr.timeago, .js-timeago'), true);
+ localTimeAgo($('abbr.timeago, .js-timeago'), true);
// Disable form buttons while a form is submitting
$body.on('ajax:complete, ajax:beforeSend, submit', 'form', function (e) {
var buttons;
@@ -278,9 +271,8 @@ $(function () {
return fitSidebarForSize();
});
loadAwardsHandler();
- new Aside();
- gl.utils.renderTimeago();
+ renderTimeago();
$(document).trigger('init.scrolling-tabs');
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
index 17591829b76..94561d6b7c3 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
@@ -10,6 +10,7 @@ import './mixins/line_conflict_actions';
import './components/diff_file_editor';
import './components/inline_conflict_lines';
import './components/parallel_conflict_lines';
+import syntaxHighlight from '../syntax_highlight';
$(() => {
const INTERACTIVE_RESOLVE_MODE = 'interactive';
@@ -53,7 +54,7 @@ $(() => {
mergeConflictsStore.setLoadingState(false);
this.$nextTick(() => {
- $('.js-syntax-highlight').syntaxHighlight();
+ syntaxHighlight($('.js-syntax-highlight'));
});
});
},
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 589e65647ac..de84e28f915 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -14,6 +14,8 @@ import {
import { getLocationHash } from './lib/utils/url_utility';
import initDiscussionTab from './image_diff/init_discussion_tab';
import Diff from './diff';
+import { localTimeAgo } from './lib/utils/datetime_utility';
+import syntaxHighlight from './syntax_highlight';
/* eslint-disable max-len */
// MergeRequestTabs
@@ -247,7 +249,7 @@ import Diff from './diff';
url: `${source}.json`,
success: (data) => {
document.querySelector('div#commits').innerHTML = data.html;
- gl.utils.localTimeAgo($('.js-timeago', 'div#commits'));
+ localTimeAgo($('.js-timeago', 'div#commits'));
this.commitsLoaded = true;
this.scrollToElement('#commits');
},
@@ -294,8 +296,8 @@ import Diff from './diff';
gl.diffNotesCompileComponents();
}
- gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'));
- $('#diffs .js-syntax-highlight').syntaxHighlight();
+ localTimeAgo($('.js-timeago', 'div#diffs'));
+ syntaxHighlight($('#diffs .js-syntax-highlight'));
if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) {
this.expandViewContainer();
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 74e5a4f1cea..2e5e818d61d 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -2,6 +2,7 @@
/* global Issuable */
/* global ListMilestone */
import _ from 'underscore';
+import { timeFor } from './lib/utils/datetime_utility';
(function() {
this.MilestoneSelect = (function() {
@@ -216,7 +217,7 @@ import _ from 'underscore';
$value.css('display', '');
if (data.milestone != null) {
data.milestone.full_path = _this.currentProject.full_path;
- data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date);
+ data.milestone.remaining = timeFor(data.milestone.due_date);
data.milestone.name = data.milestone.title;
$value.html(milestoneLinkTemplate(data.milestone));
return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone));
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 2a570ac705e..042fe44e1c6 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -25,6 +25,7 @@ import Autosave from './autosave';
import TaskList from './task_list';
import { ajaxPost, isInViewport, getPagePath, scrollToElement, isMetaKey } from './lib/utils/common_utils';
import imageDiffHelper from './image_diff/helpers/index';
+import { localTimeAgo } from './lib/utils/datetime_utility';
window.autosize = Autosize;
@@ -311,7 +312,7 @@ export default class Notes {
setupNewNote($note) {
// Update datetime format on the recent note
- gl.utils.localTimeAgo($note.find('.js-timeago'), false);
+ localTimeAgo($note.find('.js-timeago'), false);
this.collapseLongCommitList();
this.taskList.init();
@@ -463,7 +464,7 @@ export default class Notes {
this.renderDiscussionAvatar(diffAvatarContainer, noteEntity);
}
- gl.utils.localTimeAgo($('.js-timeago'), false);
+ localTimeAgo($('.js-timeago'), false);
Notes.checkMergeRequestStatus();
return this.updateNotesCount(1);
}
diff --git a/app/assets/javascripts/pipelines/components/empty_state.vue b/app/assets/javascripts/pipelines/components/empty_state.vue
index 0eaac8dd64f..78322f30685 100644
--- a/app/assets/javascripts/pipelines/components/empty_state.vue
+++ b/app/assets/javascripts/pipelines/components/empty_state.vue
@@ -1,36 +1,41 @@
<script>
-export default {
- props: {
- helpPagePath: {
- type: String,
- required: true,
+ export default {
+ props: {
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ emptyStateSvgPath: {
+ type: String,
+ required: true,
+ },
},
- emptyStateSvgPath: {
- type: String,
- required: true,
- },
- },
-};
+ };
</script>
-
<template>
<div class="row empty-state js-empty-state">
<div class="col-xs-12">
- <div class="svg-content">
- <img :src="emptyStateSvgPath"/>
+ <div class="svg-content svg-250">
+ <img :src="emptyStateSvgPath" />
</div>
</div>
- <div class="col-xs-12 text-center">
+ <div class="col-xs-12">
<div class="text-content">
- <h4>Build with confidence</h4>
+ <h4 class="text-center">
+ {{ s__("Pipelines|Build with confidence") }}
+ </h4>
<p>
- Continous Integration can help catch bugs by running your tests automatically,
- while Continuous Deployment can help you deliver code to your product environment.
+ {{ s__("Pipelines|Continous Integration can help catch bugs by running your tests automatically, while Continuous Deployment can help you deliver code to your product environment.") }}
</p>
- <a :href="helpPagePath" class="btn btn-info">
- Get started with Pipelines
- </a>
+ <div class="text-center">
+ <a
+ :href="helpPagePath"
+ class="btn btn-info"
+ >
+ {{ s__("Pipelines|Get started with Pipelines") }}
+ </a>
+ </div>
</div>
</div>
</div>
diff --git a/app/assets/javascripts/pipelines/pipelines_bundle.js b/app/assets/javascripts/pipelines/pipelines_bundle.js
index 923d9bfb248..3e4b6eeb5bf 100644
--- a/app/assets/javascripts/pipelines/pipelines_bundle.js
+++ b/app/assets/javascripts/pipelines/pipelines_bundle.js
@@ -1,6 +1,9 @@
import Vue from 'vue';
import PipelinesStore from './stores/pipelines_store';
import pipelinesComponent from './components/pipelines.vue';
+import Translate from '../vue_shared/translate';
+
+Vue.use(Translate);
document.addEventListener('DOMContentLoaded', () => new Vue({
el: '#pipelines-list-vue',
diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
index 6348a2e331d..78be6b6e884 100644
--- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue
+++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
@@ -1,5 +1,5 @@
<script>
- import popupDialog from '../../../vue_shared/components/popup_dialog.vue';
+ import modal from '../../../vue_shared/components/modal.vue';
import { __, s__, sprintf } from '../../../locale';
import csrf from '../../../lib/utils/csrf';
@@ -26,7 +26,7 @@
};
},
components: {
- popupDialog,
+ modal,
},
computed: {
csrfToken() {
@@ -89,7 +89,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
<template>
<div>
- <popup-dialog
+ <modal
v-if="isOpen"
:title="s__('Profiles|Delete your account?')"
:text="text"
@@ -134,7 +134,7 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
</form>
</template>
- </popup-dialog>
+ </modal>
<button
type="button"
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 19682b20a4a..0da32b4a3cc 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -2,169 +2,163 @@
import fuzzaldrinPlus from 'fuzzaldrin-plus';
-(function() {
- this.ProjectFindFile = (function() {
- var highlighter;
-
- function ProjectFindFile(element1, options) {
- this.element = element1;
- this.options = options;
- this.goToBlob = this.goToBlob.bind(this);
- this.goToTree = this.goToTree.bind(this);
- this.selectRowDown = this.selectRowDown.bind(this);
- this.selectRowUp = this.selectRowUp.bind(this);
- this.filePaths = {};
- this.inputElement = this.element.find(".file-finder-input");
- // init event
- this.initEvent();
- // focus text input box
- this.inputElement.focus();
- // load file list
- this.load(this.options.url);
+// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
+const highlighter = function(element, text, matches) {
+ var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched;
+ lastIndex = 0;
+ highlightText = "";
+ matchedChars = [];
+ for (j = 0, len = matches.length; j < len; j += 1) {
+ matchIndex = matches[j];
+ unmatched = text.substring(lastIndex, matchIndex);
+ if (unmatched) {
+ if (matchedChars.length) {
+ element.append(matchedChars.join("").bold());
+ }
+ matchedChars = [];
+ element.append(document.createTextNode(unmatched));
}
-
- ProjectFindFile.prototype.initEvent = function() {
- this.inputElement.off("keyup");
- this.inputElement.on("keyup", (function(_this) {
- return function(event) {
- var oldValue, ref, target, value;
- target = $(event.target);
- value = target.val();
- oldValue = (ref = target.data("oldValue")) != null ? ref : "";
- if (value !== oldValue) {
- target.data("oldValue", value);
- _this.findFile();
- return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus();
- }
- };
- })(this));
- };
-
- ProjectFindFile.prototype.findFile = function() {
- var result, searchText;
- searchText = this.inputElement.val();
- result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
- return this.renderList(result, searchText);
- // find file
- };
+ matchedChars.push(text[matchIndex]);
+ lastIndex = matchIndex + 1;
+ }
+ if (matchedChars.length) {
+ element.append(matchedChars.join("").bold());
+ }
+ return element.append(document.createTextNode(text.substring(lastIndex)));
+};
+
+export default class ProjectFindFile {
+ constructor(element1, options) {
+ this.element = element1;
+ this.options = options;
+ this.goToBlob = this.goToBlob.bind(this);
+ this.goToTree = this.goToTree.bind(this);
+ this.selectRowDown = this.selectRowDown.bind(this);
+ this.selectRowUp = this.selectRowUp.bind(this);
+ this.filePaths = {};
+ this.inputElement = this.element.find(".file-finder-input");
+ // init event
+ this.initEvent();
+ // focus text input box
+ this.inputElement.focus();
+ // load file list
+ this.load(this.options.url);
+ }
+
+ initEvent() {
+ this.inputElement.off("keyup");
+ this.inputElement.on("keyup", (function(_this) {
+ return function(event) {
+ var oldValue, ref, target, value;
+ target = $(event.target);
+ value = target.val();
+ oldValue = (ref = target.data("oldValue")) != null ? ref : "";
+ if (value !== oldValue) {
+ target.data("oldValue", value);
+ _this.findFile();
+ return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus();
+ }
+ };
+ })(this));
+ }
+
+ findFile() {
+ var result, searchText;
+ searchText = this.inputElement.val();
+ result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
+ return this.renderList(result, searchText);
+ // find file
+ }
// files pathes load
- ProjectFindFile.prototype.load = function(url) {
- return $.ajax({
- url: url,
- method: "get",
- dataType: "json",
- success: (function(_this) {
- return function(data) {
- _this.element.find(".loading").hide();
- _this.filePaths = data;
- _this.findFile();
- return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus();
- };
- })(this)
- });
- };
+ load(url) {
+ return $.ajax({
+ url: url,
+ method: "get",
+ dataType: "json",
+ success: (function(_this) {
+ return function(data) {
+ _this.element.find(".loading").hide();
+ _this.filePaths = data;
+ _this.findFile();
+ return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus();
+ };
+ })(this)
+ });
+ }
// render result
- ProjectFindFile.prototype.renderList = function(filePaths, searchText) {
- var blobItemUrl, filePath, html, i, j, len, matches, results;
- this.element.find(".tree-table > tbody").empty();
- results = [];
- for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) {
- filePath = filePaths[i];
- if (i === 20) {
- break;
- }
- if (searchText) {
- matches = fuzzaldrinPlus.match(filePath, searchText);
- }
- blobItemUrl = this.options.blobUrlTemplate + "/" + filePath;
- html = this.makeHtml(filePath, matches, blobItemUrl);
- results.push(this.element.find(".tree-table > tbody").append(html));
- }
- return results;
- };
-
- // highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
- highlighter = function(element, text, matches) {
- var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched;
- lastIndex = 0;
- highlightText = "";
- matchedChars = [];
- for (j = 0, len = matches.length; j < len; j += 1) {
- matchIndex = matches[j];
- unmatched = text.substring(lastIndex, matchIndex);
- if (unmatched) {
- if (matchedChars.length) {
- element.append(matchedChars.join("").bold());
- }
- matchedChars = [];
- element.append(document.createTextNode(unmatched));
- }
- matchedChars.push(text[matchIndex]);
- lastIndex = matchIndex + 1;
- }
- if (matchedChars.length) {
- element.append(matchedChars.join("").bold());
+ renderList(filePaths, searchText) {
+ var blobItemUrl, filePath, html, i, j, len, matches, results;
+ this.element.find(".tree-table > tbody").empty();
+ results = [];
+ for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) {
+ filePath = filePaths[i];
+ if (i === 20) {
+ break;
}
- return element.append(document.createTextNode(text.substring(lastIndex)));
- };
-
- // make tbody row html
- ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) {
- var $tr;
- $tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>");
- if (matches) {
- $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl));
- } else {
- $tr.find("a").attr("href", blobItemUrl);
- $tr.find(".str-truncated").text(filePath);
+ if (searchText) {
+ matches = fuzzaldrinPlus.match(filePath, searchText);
}
- return $tr;
- };
-
- ProjectFindFile.prototype.selectRow = function(type) {
- var next, rows, selectedRow;
- rows = this.element.find(".files-slider tr.tree-item");
- selectedRow = this.element.find(".files-slider tr.tree-item.selected");
- if (rows && rows.length > 0) {
- if (selectedRow && selectedRow.length > 0) {
- if (type === "UP") {
- next = selectedRow.prev();
- } else if (type === "DOWN") {
- next = selectedRow.next();
- }
- if (next.length > 0) {
- selectedRow.removeClass("selected");
- selectedRow = next;
- }
- } else {
- selectedRow = rows.eq(0);
+ blobItemUrl = this.options.blobUrlTemplate + "/" + filePath;
+ html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl);
+ results.push(this.element.find(".tree-table > tbody").append(html));
+ }
+ return results;
+ }
+
+ // make tbody row html
+ static makeHtml(filePath, matches, blobItemUrl) {
+ var $tr;
+ $tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>");
+ if (matches) {
+ $tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl));
+ } else {
+ $tr.find("a").attr("href", blobItemUrl);
+ $tr.find(".str-truncated").text(filePath);
+ }
+ return $tr;
+ }
+
+ selectRow(type) {
+ var next, rows, selectedRow;
+ rows = this.element.find(".files-slider tr.tree-item");
+ selectedRow = this.element.find(".files-slider tr.tree-item.selected");
+ if (rows && rows.length > 0) {
+ if (selectedRow && selectedRow.length > 0) {
+ if (type === "UP") {
+ next = selectedRow.prev();
+ } else if (type === "DOWN") {
+ next = selectedRow.next();
+ }
+ if (next.length > 0) {
+ selectedRow.removeClass("selected");
+ selectedRow = next;
}
- return selectedRow.addClass("selected").focus();
+ } else {
+ selectedRow = rows.eq(0);
}
- };
-
- ProjectFindFile.prototype.selectRowUp = function() {
- return this.selectRow("UP");
- };
+ return selectedRow.addClass("selected").focus();
+ }
+ }
- ProjectFindFile.prototype.selectRowDown = function() {
- return this.selectRow("DOWN");
- };
+ selectRowUp() {
+ return this.selectRow("UP");
+ }
- ProjectFindFile.prototype.goToTree = function() {
- return location.href = this.options.treeUrl;
- };
+ selectRowDown() {
+ return this.selectRow("DOWN");
+ }
- ProjectFindFile.prototype.goToBlob = function() {
- var $link = this.element.find(".tree-item.selected .tree-item-file-name a");
+ goToTree() {
+ return location.href = this.options.treeUrl;
+ }
- if ($link.length) {
- $link.get(0).click();
- }
- };
+ goToBlob() {
+ var $link = this.element.find(".tree-item.selected .tree-item-file-name a");
- return ProjectFindFile;
- })();
-}).call(window);
+ if ($link.length) {
+ $link.get(0).click();
+ }
+ }
+}
diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js
index c91a0d9ba41..5482c55f8bb 100644
--- a/app/assets/javascripts/render_gfm.js
+++ b/app/assets/javascripts/render_gfm.js
@@ -1,12 +1,12 @@
import renderMath from './render_math';
import renderMermaid from './render_mermaid';
-
+import syntaxHighlight from './syntax_highlight';
// Render Gitlab flavoured Markdown
//
// Delegates to syntax highlight and render math & mermaid diagrams.
//
$.fn.renderGFM = function renderGFM() {
- this.find('.js-syntax-highlight').syntaxHighlight();
+ syntaxHighlight(this.find('.js-syntax-highlight'));
renderMath(this.find('.js-render-math'));
renderMermaid(this.find('.js-render-mermaid'));
return this;
diff --git a/app/assets/javascripts/repo/components/new_dropdown/modal.vue b/app/assets/javascripts/repo/components/new_dropdown/modal.vue
index ac1f613bb71..c191af7dec3 100644
--- a/app/assets/javascripts/repo/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/repo/components/new_dropdown/modal.vue
@@ -1,7 +1,7 @@
<script>
import { mapActions } from 'vuex';
import { __ } from '../../../locale';
- import popupDialog from '../../../vue_shared/components/popup_dialog.vue';
+ import modal from '../../../vue_shared/components/modal.vue';
export default {
props: {
@@ -20,7 +20,7 @@
};
},
components: {
- popupDialog,
+ modal,
},
methods: {
...mapActions([
@@ -68,7 +68,7 @@
</script>
<template>
- <popup-dialog
+ <modal
:title="modalTitle"
:primary-button-label="buttonLabel"
kind="success"
@@ -94,5 +94,5 @@
</div>
</fieldset>
</form>
- </popup-dialog>
+ </modal>
</template>
diff --git a/app/assets/javascripts/repo/components/repo_commit_section.vue b/app/assets/javascripts/repo/components/repo_commit_section.vue
index d3344d0c8dc..4e0178072cb 100644
--- a/app/assets/javascripts/repo/components/repo_commit_section.vue
+++ b/app/assets/javascripts/repo/components/repo_commit_section.vue
@@ -2,12 +2,12 @@
import { mapGetters, mapState, mapActions } from 'vuex';
import tooltip from '../../vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue';
-import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
+import modal from '../../vue_shared/components/modal.vue';
import commitFilesList from './commit_sidebar/list.vue';
export default {
components: {
- PopupDialog,
+ modal,
icon,
commitFilesList,
},
@@ -16,7 +16,7 @@ export default {
},
data() {
return {
- showNewBranchDialog: false,
+ showNewBranchModal: false,
submitCommitsLoading: false,
startNewMR: false,
commitMessage: '',
@@ -58,7 +58,7 @@ export default {
start_branch: createNewBranch ? this.currentBranch : undefined,
};
- this.showNewBranchDialog = false;
+ this.showNewBranchModal = false;
this.submitCommitsLoading = true;
this.commitChanges({ payload, newMr: this.startNewMR })
@@ -76,7 +76,7 @@ export default {
this.checkCommitStatus()
.then((branchChanged) => {
if (branchChanged) {
- this.showNewBranchDialog = true;
+ this.showNewBranchModal = true;
} else {
this.makeCommit();
}
@@ -99,13 +99,13 @@ export default {
'is-collapsed': collapsed,
}"
>
- <popup-dialog
- v-if="showNewBranchDialog"
+ <modal
+ v-if="showNewBranchModal"
:primary-button-label="__('Create new branch')"
kind="primary"
:title="__('Branch has changed')"
:text="__('This branch has changed since you started editing. Would you like to create a new branch?')"
- @toggle="showNewBranchDialog = false"
+ @toggle="showNewBranchModal = false"
@submit="makeCommit(true)"
/>
<button
diff --git a/app/assets/javascripts/repo/components/repo_edit_button.vue b/app/assets/javascripts/repo/components/repo_edit_button.vue
index 6c1bb4b8566..37bd9003e96 100644
--- a/app/assets/javascripts/repo/components/repo_edit_button.vue
+++ b/app/assets/javascripts/repo/components/repo_edit_button.vue
@@ -1,10 +1,10 @@
<script>
import { mapGetters, mapActions, mapState } from 'vuex';
-import popupDialog from '../../vue_shared/components/popup_dialog.vue';
+import modal from '../../vue_shared/components/modal.vue';
export default {
components: {
- popupDialog,
+ modal,
},
computed: {
...mapState([
@@ -43,7 +43,7 @@ export default {
{{buttonLabel}}
</span>
</button>
- <popup-dialog
+ <modal
v-if="discardPopupOpen"
class="text-left"
:primary-button-label="__('Discard changes')"
diff --git a/app/assets/javascripts/repo/components/repo_preview.vue b/app/assets/javascripts/repo/components/repo_preview.vue
index 6ce9267f598..425c55fafb5 100644
--- a/app/assets/javascripts/repo/components/repo_preview.vue
+++ b/app/assets/javascripts/repo/components/repo_preview.vue
@@ -1,6 +1,7 @@
<script>
/* global LineHighlighter */
import { mapGetters } from 'vuex';
+import syntaxHighlight from '../../syntax_highlight';
export default {
computed: {
@@ -13,7 +14,7 @@ export default {
},
methods: {
highlightFile() {
- $(this.$el).find('.file-content').syntaxHighlight();
+ syntaxHighlight($(this.$el).find('.file-content'));
},
},
mounted() {
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index fa7f6825d7e..ec85b8b6529 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -42,11 +42,11 @@ import Cookies from 'js-cookie';
if ($thisIcon.hasClass('fa-angle-double-right')) {
$allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left');
$('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
- $('.page-with-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
+ $('.layout-page').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed');
} else {
$allGutterToggleIcons.removeClass('fa-angle-double-left').addClass('fa-angle-double-right');
$('aside.right-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
- $('.page-with-sidebar').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
+ $('.layout-page').removeClass('right-sidebar-collapsed').addClass('right-sidebar-expanded');
if (gl.lazyLoader) gl.lazyLoader.loadCheck();
}
@@ -173,7 +173,7 @@ import Cookies from 'js-cookie';
Sidebar.prototype.setCollapseAfterUpdate = function($block) {
$block.addClass('collapse-after-update');
- return $('.page-with-sidebar').addClass('with-overlay');
+ return $('.layout-page').addClass('with-overlay');
};
Sidebar.prototype.onSidebarDropdownHidden = function(e) {
@@ -187,7 +187,7 @@ import Cookies from 'js-cookie';
Sidebar.prototype.sidebarDropdownHidden = function($block) {
if ($block.hasClass('collapse-after-update')) {
$block.removeClass('collapse-after-update');
- $('.page-with-sidebar').removeClass('with-overlay');
+ $('.layout-page').removeClass('with-overlay');
return this.toggleSidebar('hide');
}
};
diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js
index 07fee53d814..363322af47a 100644
--- a/app/assets/javascripts/search.js
+++ b/app/assets/javascripts/search.js
@@ -1,118 +1,113 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, max-len */
import Flash from './flash';
import Api from './api';
-(function() {
- this.Search = (function() {
- function Search() {
- var $groupDropdown, $projectDropdown;
- $groupDropdown = $('.js-search-group-dropdown');
- $projectDropdown = $('.js-search-project-dropdown');
- this.groupId = $groupDropdown.data('group-id');
- this.eventListeners();
- $groupDropdown.glDropdown({
- selectable: true,
- filterable: true,
- fieldName: 'group_id',
- search: {
- fields: ['full_name']
- },
- data: function(term, callback) {
- return Api.groups(term, {}, function(data) {
+export default class Search {
+ constructor() {
+ const $groupDropdown = $('.js-search-group-dropdown');
+ const $projectDropdown = $('.js-search-project-dropdown');
+
+ this.searchInput = '.js-search-input';
+ this.searchClear = '.js-search-clear';
+
+ this.groupId = $groupDropdown.data('group-id');
+ this.eventListeners();
+
+ $groupDropdown.glDropdown({
+ selectable: true,
+ filterable: true,
+ fieldName: 'group_id',
+ search: {
+ fields: ['full_name'],
+ },
+ data(term, callback) {
+ return Api.groups(term, {}, (data) => {
+ data.unshift({
+ full_name: 'Any',
+ });
+ data.splice(1, 0, 'divider');
+ return callback(data);
+ });
+ },
+ id(obj) {
+ return obj.id;
+ },
+ text(obj) {
+ return obj.full_name;
+ },
+ toggleLabel(obj) {
+ return `${($groupDropdown.data('default-label'))} ${obj.full_name}`;
+ },
+ clicked: () => Search.submitSearch(),
+ });
+
+ $projectDropdown.glDropdown({
+ selectable: true,
+ filterable: true,
+ fieldName: 'project_id',
+ search: {
+ fields: ['name'],
+ },
+ data: (term, callback) => {
+ this.getProjectsData(term)
+ .then((data) => {
data.unshift({
- full_name: 'Any'
+ name_with_namespace: 'Any',
});
data.splice(1, 0, 'divider');
- return callback(data);
- });
- },
- id: function(obj) {
- return obj.id;
- },
- text: function(obj) {
- return obj.full_name;
- },
- toggleLabel: function(obj) {
- return ($groupDropdown.data('default-label')) + " " + obj.full_name;
- },
- clicked: (function(_this) {
- return function() {
- return _this.submitSearch();
- };
- })(this)
- });
- $projectDropdown.glDropdown({
- selectable: true,
- filterable: true,
- fieldName: 'project_id',
- search: {
- fields: ['name']
- },
- data: (term, callback) => {
- this.getProjectsData(term)
- .then((data) => {
- data.unshift({
- name_with_namespace: 'Any'
- });
- data.splice(1, 0, 'divider');
- return data;
- })
- .then(data => callback(data))
- .catch(() => new Flash('Error fetching projects'));
- },
- id: function(obj) {
- return obj.id;
- },
- text: function(obj) {
- return obj.name_with_namespace;
- },
- toggleLabel: function(obj) {
- return ($projectDropdown.data('default-label')) + " " + obj.name_with_namespace;
- },
- clicked: (function(_this) {
- return function() {
- return _this.submitSearch();
- };
- })(this)
- });
- }
+ return data;
+ })
+ .then(data => callback(data))
+ .catch(() => new Flash('Error fetching projects'));
+ },
+ id(obj) {
+ return obj.id;
+ },
+ text(obj) {
+ return obj.name_with_namespace;
+ },
+ toggleLabel(obj) {
+ return `${($projectDropdown.data('default-label'))} ${obj.name_with_namespace}`;
+ },
+ clicked: () => Search.submitSearch(),
+ });
+ }
- Search.prototype.eventListeners = function() {
- $(document).off('keyup', '.js-search-input').on('keyup', '.js-search-input', this.searchKeyUp);
- return $(document).off('click', '.js-search-clear').on('click', '.js-search-clear', this.clearSearchField);
- };
+ eventListeners() {
+ $(document)
+ .off('keyup', this.searchInput)
+ .on('keyup', this.searchInput, this.searchKeyUp);
+ $(document)
+ .off('click', this.searchClear)
+ .on('click', this.searchClear, this.clearSearchField.bind(this));
+ }
- Search.prototype.submitSearch = function() {
- return $('.js-search-form').submit();
- };
+ static submitSearch() {
+ return $('.js-search-form').submit();
+ }
- Search.prototype.searchKeyUp = function() {
- var $input;
- $input = $(this);
- if ($input.val() === '') {
- return $('.js-search-clear').addClass('hidden');
- } else {
- return $('.js-search-clear').removeClass('hidden');
- }
- };
-
- Search.prototype.clearSearchField = function() {
- return $('.js-search-input').val('').trigger('keyup').focus();
- };
+ searchKeyUp() {
+ const $input = $(this);
+ if ($input.val() === '') {
+ $('.js-search-clear').addClass('hidden');
+ } else {
+ $('.js-search-clear').removeClass('hidden');
+ }
+ }
- Search.prototype.getProjectsData = function(term) {
- return new Promise((resolve) => {
- if (this.groupId) {
- Api.groupProjects(this.groupId, term, resolve);
- } else {
- Api.projects(term, {
- order_by: 'id',
- }, resolve);
- }
- });
- };
+ clearSearchField() {
+ return $(this.searchInput).val('').trigger('keyup').focus();
+ }
- return Search;
- })();
-}).call(window);
+ getProjectsData(term) {
+ return new Promise((resolve) => {
+ if (this.groupId) {
+ Api.groupProjects(this.groupId, term, resolve);
+ } else {
+ Api.projects(term, {
+ order_by: 'id',
+ }, resolve);
+ }
+ });
+ }
+}
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index e40a3596200..98b524f7e3f 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -8,448 +8,445 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '.
* When the user clicks `x` button it cleans the input and closes the dropdown.
*/
-((global) => {
- const KEYCODE = {
- ESCAPE: 27,
- BACKSPACE: 8,
- ENTER: 13,
- UP: 38,
- DOWN: 40,
- };
-
- class SearchAutocomplete {
- constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) {
- this.bindEventContext();
- this.wrap = wrap || $('.search');
- this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts');
- this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path');
- this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || '');
- this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || '');
- this.dropdown = this.wrap.find('.dropdown');
- this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle');
- this.dropdownContent = this.dropdown.find('.dropdown-content');
- this.locationBadgeEl = this.getElement('.location-badge');
- this.scopeInputEl = this.getElement('#scope');
- this.searchInput = this.getElement('.search-input');
- this.projectInputEl = this.getElement('#search_project_id');
- this.groupInputEl = this.getElement('#group_id');
- this.searchCodeInputEl = this.getElement('#search_code');
- this.repositoryInputEl = this.getElement('#repository_ref');
- this.clearInput = this.getElement('.js-clear-input');
- this.saveOriginalState();
-
- // Only when user is logged in
- if (gon.current_user_id) {
- this.createAutocomplete();
- }
+const KEYCODE = {
+ ESCAPE: 27,
+ BACKSPACE: 8,
+ ENTER: 13,
+ UP: 38,
+ DOWN: 40,
+};
+
+function setSearchOptions() {
+ var $projectOptionsDataEl = $('.js-search-project-options');
+ var $groupOptionsDataEl = $('.js-search-group-options');
+ var $dashboardOptionsDataEl = $('.js-search-dashboard-options');
+
+ if ($projectOptionsDataEl.length) {
+ gl.projectOptions = gl.projectOptions || {};
+
+ var projectPath = $projectOptionsDataEl.data('project-path');
+
+ gl.projectOptions[projectPath] = {
+ name: $projectOptionsDataEl.data('name'),
+ issuesPath: $projectOptionsDataEl.data('issues-path'),
+ issuesDisabled: $projectOptionsDataEl.data('issues-disabled'),
+ mrPath: $projectOptionsDataEl.data('mr-path'),
+ };
+ }
- this.searchInput.addClass('disabled');
- this.saveTextLength();
- this.bindEvents();
- this.dropdownToggle.dropdown();
- }
+ if ($groupOptionsDataEl.length) {
+ gl.groupOptions = gl.groupOptions || {};
- // Finds an element inside wrapper element
- bindEventContext() {
- this.onSearchInputBlur = this.onSearchInputBlur.bind(this);
- this.onClearInputClick = this.onClearInputClick.bind(this);
- this.onSearchInputFocus = this.onSearchInputFocus.bind(this);
- this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this);
- this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this);
- }
- getElement(selector) {
- return this.wrap.find(selector);
- }
+ var groupPath = $groupOptionsDataEl.data('group-path');
- saveOriginalState() {
- return this.originalState = this.serializeState();
- }
+ gl.groupOptions[groupPath] = {
+ name: $groupOptionsDataEl.data('name'),
+ issuesPath: $groupOptionsDataEl.data('issues-path'),
+ mrPath: $groupOptionsDataEl.data('mr-path'),
+ };
+ }
- saveTextLength() {
- return this.lastTextLength = this.searchInput.val().length;
+ if ($dashboardOptionsDataEl.length) {
+ gl.dashboardOptions = {
+ issuesPath: $dashboardOptionsDataEl.data('issues-path'),
+ mrPath: $dashboardOptionsDataEl.data('mr-path'),
+ };
+ }
+}
+
+export default class SearchAutocomplete {
+ constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) {
+ setSearchOptions();
+ this.bindEventContext();
+ this.wrap = wrap || $('.search');
+ this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts');
+ this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path');
+ this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || '');
+ this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || '');
+ this.dropdown = this.wrap.find('.dropdown');
+ this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle');
+ this.dropdownContent = this.dropdown.find('.dropdown-content');
+ this.locationBadgeEl = this.getElement('.location-badge');
+ this.scopeInputEl = this.getElement('#scope');
+ this.searchInput = this.getElement('.search-input');
+ this.projectInputEl = this.getElement('#search_project_id');
+ this.groupInputEl = this.getElement('#group_id');
+ this.searchCodeInputEl = this.getElement('#search_code');
+ this.repositoryInputEl = this.getElement('#repository_ref');
+ this.clearInput = this.getElement('.js-clear-input');
+ this.saveOriginalState();
+
+ // Only when user is logged in
+ if (gon.current_user_id) {
+ this.createAutocomplete();
}
- createAutocomplete() {
- return this.searchInput.glDropdown({
- filterInputBlur: false,
- filterable: true,
- filterRemote: true,
- highlight: true,
- enterCallback: false,
- filterInput: 'input#search',
- search: {
- fields: ['text'],
- },
- id: this.getSearchText,
- data: this.getData.bind(this),
- selectable: true,
- clicked: this.onClick.bind(this),
- });
+ this.searchInput.addClass('disabled');
+ this.saveTextLength();
+ this.bindEvents();
+ this.dropdownToggle.dropdown();
+ }
+
+ // Finds an element inside wrapper element
+ bindEventContext() {
+ this.onSearchInputBlur = this.onSearchInputBlur.bind(this);
+ this.onClearInputClick = this.onClearInputClick.bind(this);
+ this.onSearchInputFocus = this.onSearchInputFocus.bind(this);
+ this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this);
+ this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this);
+ }
+ getElement(selector) {
+ return this.wrap.find(selector);
+ }
+
+ saveOriginalState() {
+ return this.originalState = this.serializeState();
+ }
+
+ saveTextLength() {
+ return this.lastTextLength = this.searchInput.val().length;
+ }
+
+ createAutocomplete() {
+ return this.searchInput.glDropdown({
+ filterInputBlur: false,
+ filterable: true,
+ filterRemote: true,
+ highlight: true,
+ enterCallback: false,
+ filterInput: 'input#search',
+ search: {
+ fields: ['text'],
+ },
+ id: this.getSearchText,
+ data: this.getData.bind(this),
+ selectable: true,
+ clicked: this.onClick.bind(this),
+ });
+ }
+
+ getSearchText(selectedObject, el) {
+ return selectedObject.id ? selectedObject.text : '';
+ }
+
+ getData(term, callback) {
+ if (!term) {
+ const contents = this.getCategoryContents();
+ if (contents) {
+ this.searchInput.data('glDropdown').filter.options.callback(contents);
+ this.enableAutocomplete();
+ }
+ return;
}
- getSearchText(selectedObject, el) {
- return selectedObject.id ? selectedObject.text : '';
+ // Prevent multiple ajax calls
+ if (this.loadingSuggestions) {
+ return;
}
- getData(term, callback) {
- if (!term) {
- const contents = this.getCategoryContents();
- if (contents) {
- this.searchInput.data('glDropdown').filter.options.callback(contents);
- this.enableAutocomplete();
- }
- return;
- }
+ this.loadingSuggestions = true;
- // Prevent multiple ajax calls
- if (this.loadingSuggestions) {
+ return $.get(this.autocompletePath, {
+ project_id: this.projectId,
+ project_ref: this.projectRef,
+ term: term,
+ }, (response) => {
+ var firstCategory, i, lastCategory, len, suggestion;
+ // Hide dropdown menu if no suggestions returns
+ if (!response.length) {
+ this.disableAutocomplete();
return;
}
- this.loadingSuggestions = true;
-
- return $.get(this.autocompletePath, {
- project_id: this.projectId,
- project_ref: this.projectRef,
- term: term,
- }, (response) => {
- var firstCategory, i, lastCategory, len, suggestion;
- // Hide dropdown menu if no suggestions returns
- if (!response.length) {
- this.disableAutocomplete();
- return;
- }
-
- const data = [];
- // List results
- firstCategory = true;
- for (i = 0, len = response.length; i < len; i += 1) {
- suggestion = response[i];
- // Add group header before list each group
- if (lastCategory !== suggestion.category) {
- if (!firstCategory) {
- data.push('separator');
- }
- if (firstCategory) {
- firstCategory = false;
- }
- data.push({
- header: suggestion.category,
- });
- lastCategory = suggestion.category;
+ const data = [];
+ // List results
+ firstCategory = true;
+ for (i = 0, len = response.length; i < len; i += 1) {
+ suggestion = response[i];
+ // Add group header before list each group
+ if (lastCategory !== suggestion.category) {
+ if (!firstCategory) {
+ data.push('separator');
+ }
+ if (firstCategory) {
+ firstCategory = false;
}
data.push({
- id: (suggestion.category.toLowerCase()) + "-" + suggestion.id,
- category: suggestion.category,
- text: suggestion.label,
- url: suggestion.url,
- });
- }
- // Add option to proceed with the search
- if (data.length) {
- data.push('separator');
- data.push({
- text: "Result name contains \"" + term + "\"",
- url: "/search?search=" + term + "&project_id=" + (this.projectInputEl.val()) + "&group_id=" + (this.groupInputEl.val()),
+ header: suggestion.category,
});
+ lastCategory = suggestion.category;
}
- return callback(data);
- })
- .always(() => { this.loadingSuggestions = false; });
- }
-
- getCategoryContents() {
- const userId = gon.current_user_id;
- const userName = gon.current_username;
- const { projectOptions, groupOptions, dashboardOptions } = gl;
-
- // Get options
- let options;
- if (isInGroupsPage() && groupOptions) {
- options = groupOptions[getGroupSlug()];
- } else if (isInProjectPage() && projectOptions) {
- options = projectOptions[getProjectSlug()];
- } else if (dashboardOptions) {
- options = dashboardOptions;
+ data.push({
+ id: (suggestion.category.toLowerCase()) + "-" + suggestion.id,
+ category: suggestion.category,
+ text: suggestion.label,
+ url: suggestion.url,
+ });
}
-
- const { issuesPath, mrPath, name, issuesDisabled } = options;
- const baseItems = [];
-
- if (name) {
- baseItems.push({
- header: `${name}`,
+ // Add option to proceed with the search
+ if (data.length) {
+ data.push('separator');
+ data.push({
+ text: "Result name contains \"" + term + "\"",
+ url: "/search?search=" + term + "&project_id=" + (this.projectInputEl.val()) + "&group_id=" + (this.groupInputEl.val()),
});
}
+ return callback(data);
+ })
+ .always(() => { this.loadingSuggestions = false; });
+ }
- const issueItems = [
- {
- text: 'Issues assigned to me',
- url: `${issuesPath}/?assignee_username=${userName}`,
- },
- {
- text: "Issues I've created",
- url: `${issuesPath}/?author_username=${userName}`,
- },
- ];
- const mergeRequestItems = [
- {
- text: 'Merge requests assigned to me',
- url: `${mrPath}/?assignee_username=${userName}`,
- },
- {
- text: "Merge requests I've created",
- url: `${mrPath}/?author_username=${userName}`,
- },
- ];
-
- let items;
- if (issuesDisabled) {
- items = baseItems.concat(mergeRequestItems);
- } else {
- items = baseItems.concat(...issueItems, 'separator', ...mergeRequestItems);
- }
- return items;
+ getCategoryContents() {
+ const userId = gon.current_user_id;
+ const userName = gon.current_username;
+ const { projectOptions, groupOptions, dashboardOptions } = gl;
+
+ // Get options
+ let options;
+ if (isInGroupsPage() && groupOptions) {
+ options = groupOptions[getGroupSlug()];
+ } else if (isInProjectPage() && projectOptions) {
+ options = projectOptions[getProjectSlug()];
+ } else if (dashboardOptions) {
+ options = dashboardOptions;
}
- serializeState() {
- return {
- // Search Criteria
- search_project_id: this.projectInputEl.val(),
- group_id: this.groupInputEl.val(),
- search_code: this.searchCodeInputEl.val(),
- repository_ref: this.repositoryInputEl.val(),
- scope: this.scopeInputEl.val(),
- // Location badge
- _location: this.locationBadgeEl.text(),
- };
+ const { issuesPath, mrPath, name, issuesDisabled } = options;
+ const baseItems = [];
+
+ if (name) {
+ baseItems.push({
+ header: `${name}`,
+ });
}
- bindEvents() {
- this.searchInput.on('keydown', this.onSearchInputKeyDown);
- this.searchInput.on('keyup', this.onSearchInputKeyUp);
- this.searchInput.on('focus', this.onSearchInputFocus);
- this.searchInput.on('blur', this.onSearchInputBlur);
- this.clearInput.on('click', this.onClearInputClick);
- this.locationBadgeEl.on('click', () => this.searchInput.focus());
+ const issueItems = [
+ {
+ text: 'Issues assigned to me',
+ url: `${issuesPath}/?assignee_username=${userName}`,
+ },
+ {
+ text: "Issues I've created",
+ url: `${issuesPath}/?author_username=${userName}`,
+ },
+ ];
+ const mergeRequestItems = [
+ {
+ text: 'Merge requests assigned to me',
+ url: `${mrPath}/?assignee_username=${userName}`,
+ },
+ {
+ text: "Merge requests I've created",
+ url: `${mrPath}/?author_username=${userName}`,
+ },
+ ];
+
+ let items;
+ if (issuesDisabled) {
+ items = baseItems.concat(mergeRequestItems);
+ } else {
+ items = baseItems.concat(...issueItems, 'separator', ...mergeRequestItems);
}
+ return items;
+ }
- enableAutocomplete() {
- // No need to enable anything if user is not logged in
- if (!gon.current_user_id) {
- return;
- }
+ serializeState() {
+ return {
+ // Search Criteria
+ search_project_id: this.projectInputEl.val(),
+ group_id: this.groupInputEl.val(),
+ search_code: this.searchCodeInputEl.val(),
+ repository_ref: this.repositoryInputEl.val(),
+ scope: this.scopeInputEl.val(),
+ // Location badge
+ _location: this.locationBadgeEl.text(),
+ };
+ }
- // If the dropdown is closed, we'll open it
- if (!this.dropdown.hasClass('open')) {
- this.loadingSuggestions = false;
- this.dropdownToggle.dropdown('toggle');
- return this.searchInput.removeClass('disabled');
- }
+ bindEvents() {
+ this.searchInput.on('keydown', this.onSearchInputKeyDown);
+ this.searchInput.on('keyup', this.onSearchInputKeyUp);
+ this.searchInput.on('focus', this.onSearchInputFocus);
+ this.searchInput.on('blur', this.onSearchInputBlur);
+ this.clearInput.on('click', this.onClearInputClick);
+ this.locationBadgeEl.on('click', () => this.searchInput.focus());
+ }
+
+ enableAutocomplete() {
+ // No need to enable anything if user is not logged in
+ if (!gon.current_user_id) {
+ return;
}
- // Saves last length of the entered text
- onSearchInputKeyDown() {
- return this.saveTextLength();
+ // If the dropdown is closed, we'll open it
+ if (!this.dropdown.hasClass('open')) {
+ this.loadingSuggestions = false;
+ this.dropdownToggle.dropdown('toggle');
+ return this.searchInput.removeClass('disabled');
}
+ }
- onSearchInputKeyUp(e) {
- switch (e.keyCode) {
- case KEYCODE.BACKSPACE:
- // when trying to remove the location badge
- if (this.lastTextLength === 0 && this.badgePresent()) {
- this.removeLocationBadge();
- }
- // When removing the last character and no badge is present
- if (this.lastTextLength === 1) {
- this.disableAutocomplete();
- }
- // When removing any character from existin value
- if (this.lastTextLength > 1) {
- this.enableAutocomplete();
- }
- break;
- case KEYCODE.ESCAPE:
- this.restoreOriginalState();
- break;
- case KEYCODE.ENTER:
+ // Saves last length of the entered text
+ onSearchInputKeyDown() {
+ return this.saveTextLength();
+ }
+
+ onSearchInputKeyUp(e) {
+ switch (e.keyCode) {
+ case KEYCODE.BACKSPACE:
+ // when trying to remove the location badge
+ if (this.lastTextLength === 0 && this.badgePresent()) {
+ this.removeLocationBadge();
+ }
+ // When removing the last character and no badge is present
+ if (this.lastTextLength === 1) {
+ this.disableAutocomplete();
+ }
+ // When removing any character from existin value
+ if (this.lastTextLength > 1) {
+ this.enableAutocomplete();
+ }
+ break;
+ case KEYCODE.ESCAPE:
+ this.restoreOriginalState();
+ break;
+ case KEYCODE.ENTER:
+ this.disableAutocomplete();
+ break;
+ case KEYCODE.UP:
+ case KEYCODE.DOWN:
+ return;
+ default:
+ // Handle the case when deleting the input value other than backspace
+ // e.g. Pressing ctrl + backspace or ctrl + x
+ if (this.searchInput.val() === '') {
this.disableAutocomplete();
- break;
- case KEYCODE.UP:
- case KEYCODE.DOWN:
- return;
- default:
- // Handle the case when deleting the input value other than backspace
- // e.g. Pressing ctrl + backspace or ctrl + x
- if (this.searchInput.val() === '') {
- this.disableAutocomplete();
- } else {
- // We should display the menu only when input is not empty
- if (e.keyCode !== KEYCODE.ENTER) {
- this.enableAutocomplete();
- }
+ } else {
+ // We should display the menu only when input is not empty
+ if (e.keyCode !== KEYCODE.ENTER) {
+ this.enableAutocomplete();
}
- }
- this.wrap.toggleClass('has-value', !!e.target.value);
+ }
}
+ this.wrap.toggleClass('has-value', !!e.target.value);
+ }
- onSearchInputFocus() {
- this.isFocused = true;
- this.wrap.addClass('search-active');
- if (this.getValue() === '') {
- return this.getData();
- }
+ onSearchInputFocus() {
+ this.isFocused = true;
+ this.wrap.addClass('search-active');
+ if (this.getValue() === '') {
+ return this.getData();
}
+ }
- getValue() {
- return this.searchInput.val();
- }
+ getValue() {
+ return this.searchInput.val();
+ }
- onClearInputClick(e) {
- e.preventDefault();
- this.wrap.toggleClass('has-value', !!e.target.value);
- return this.searchInput.val('').focus();
- }
+ onClearInputClick(e) {
+ e.preventDefault();
+ this.wrap.toggleClass('has-value', !!e.target.value);
+ return this.searchInput.val('').focus();
+ }
- onSearchInputBlur(e) {
- this.isFocused = false;
- this.wrap.removeClass('search-active');
- // If input is blank then restore state
- if (this.searchInput.val() === '') {
- return this.restoreOriginalState();
- }
+ onSearchInputBlur(e) {
+ this.isFocused = false;
+ this.wrap.removeClass('search-active');
+ // If input is blank then restore state
+ if (this.searchInput.val() === '') {
+ return this.restoreOriginalState();
}
+ }
- addLocationBadge(item) {
- var badgeText, category, value;
- category = item.category != null ? item.category + ": " : '';
- value = item.value != null ? item.value : '';
- badgeText = "" + category + value;
- this.locationBadgeEl.text(badgeText).show();
- return this.wrap.addClass('has-location-badge');
- }
+ addLocationBadge(item) {
+ var badgeText, category, value;
+ category = item.category != null ? item.category + ": " : '';
+ value = item.value != null ? item.value : '';
+ badgeText = "" + category + value;
+ this.locationBadgeEl.text(badgeText).show();
+ return this.wrap.addClass('has-location-badge');
+ }
- hasLocationBadge() {
- return this.wrap.is('.has-location-badge');
- }
+ hasLocationBadge() {
+ return this.wrap.is('.has-location-badge');
+ }
- restoreOriginalState() {
- var i, input, inputs, len;
- inputs = Object.keys(this.originalState);
- for (i = 0, len = inputs.length; i < len; i += 1) {
- input = inputs[i];
- this.getElement("#" + input).val(this.originalState[input]);
- }
- if (this.originalState._location === '') {
- return this.locationBadgeEl.hide();
- } else {
- return this.addLocationBadge({
- value: this.originalState._location,
- });
- }
+ restoreOriginalState() {
+ var i, input, inputs, len;
+ inputs = Object.keys(this.originalState);
+ for (i = 0, len = inputs.length; i < len; i += 1) {
+ input = inputs[i];
+ this.getElement("#" + input).val(this.originalState[input]);
}
-
- badgePresent() {
- return this.locationBadgeEl.length;
+ if (this.originalState._location === '') {
+ return this.locationBadgeEl.hide();
+ } else {
+ return this.addLocationBadge({
+ value: this.originalState._location,
+ });
}
+ }
- resetSearchState() {
- var i, input, inputs, len, results;
- inputs = Object.keys(this.originalState);
- results = [];
- for (i = 0, len = inputs.length; i < len; i += 1) {
- input = inputs[i];
- // _location isnt a input
- if (input === '_location') {
- break;
- }
- results.push(this.getElement("#" + input).val(''));
+ badgePresent() {
+ return this.locationBadgeEl.length;
+ }
+
+ resetSearchState() {
+ var i, input, inputs, len, results;
+ inputs = Object.keys(this.originalState);
+ results = [];
+ for (i = 0, len = inputs.length; i < len; i += 1) {
+ input = inputs[i];
+ // _location isnt a input
+ if (input === '_location') {
+ break;
}
- return results;
+ results.push(this.getElement("#" + input).val(''));
}
+ return results;
+ }
- removeLocationBadge() {
- this.locationBadgeEl.hide();
- this.resetSearchState();
- this.wrap.removeClass('has-location-badge');
- return this.disableAutocomplete();
- }
+ removeLocationBadge() {
+ this.locationBadgeEl.hide();
+ this.resetSearchState();
+ this.wrap.removeClass('has-location-badge');
+ return this.disableAutocomplete();
+ }
- disableAutocomplete() {
- if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) {
- this.searchInput.addClass('disabled');
- this.dropdown.removeClass('open').trigger('hidden.bs.dropdown');
- this.restoreMenu();
- }
+ disableAutocomplete() {
+ if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) {
+ this.searchInput.addClass('disabled');
+ this.dropdown.removeClass('open').trigger('hidden.bs.dropdown');
+ this.restoreMenu();
}
+ }
- restoreMenu() {
- var html;
- html = '<ul><li class="dropdown-menu-empty-item"><a>Loading...</a></li></ul>';
- return this.dropdownContent.html(html);
- }
+ restoreMenu() {
+ var html;
+ html = '<ul><li class="dropdown-menu-empty-item"><a>Loading...</a></li></ul>';
+ return this.dropdownContent.html(html);
+ }
- onClick(item, $el, e) {
- if (location.pathname.indexOf(item.url) !== -1) {
- if (!e.metaKey) e.preventDefault();
- if (!this.badgePresent) {
- if (item.category === 'Projects') {
- this.projectInputEl.val(item.id);
- this.addLocationBadge({
- value: 'This project',
- });
- }
- if (item.category === 'Groups') {
- this.groupInputEl.val(item.id);
- this.addLocationBadge({
- value: 'This group',
- });
- }
+ onClick(item, $el, e) {
+ if (location.pathname.indexOf(item.url) !== -1) {
+ if (!e.metaKey) e.preventDefault();
+ if (!this.badgePresent) {
+ if (item.category === 'Projects') {
+ this.projectInputEl.val(item.id);
+ this.addLocationBadge({
+ value: 'This project',
+ });
+ }
+ if (item.category === 'Groups') {
+ this.groupInputEl.val(item.id);
+ this.addLocationBadge({
+ value: 'This group',
+ });
}
- $el.removeClass('is-active');
- this.disableAutocomplete();
- return this.searchInput.val('').focus();
}
+ $el.removeClass('is-active');
+ this.disableAutocomplete();
+ return this.searchInput.val('').focus();
}
}
-
- global.SearchAutocomplete = SearchAutocomplete;
-
- $(function() {
- var $projectOptionsDataEl = $('.js-search-project-options');
- var $groupOptionsDataEl = $('.js-search-group-options');
- var $dashboardOptionsDataEl = $('.js-search-dashboard-options');
-
- if ($projectOptionsDataEl.length) {
- gl.projectOptions = gl.projectOptions || {};
-
- var projectPath = $projectOptionsDataEl.data('project-path');
-
- gl.projectOptions[projectPath] = {
- name: $projectOptionsDataEl.data('name'),
- issuesPath: $projectOptionsDataEl.data('issues-path'),
- issuesDisabled: $projectOptionsDataEl.data('issues-disabled'),
- mrPath: $projectOptionsDataEl.data('mr-path'),
- };
- }
-
- if ($groupOptionsDataEl.length) {
- gl.groupOptions = gl.groupOptions || {};
-
- var groupPath = $groupOptionsDataEl.data('group-path');
-
- gl.groupOptions[groupPath] = {
- name: $groupOptionsDataEl.data('name'),
- issuesPath: $groupOptionsDataEl.data('issues-path'),
- mrPath: $groupOptionsDataEl.data('mr-path'),
- };
- }
-
- if ($dashboardOptionsDataEl.length) {
- gl.dashboardOptions = {
- issuesPath: $dashboardOptionsDataEl.data('issues-path'),
- mrPath: $dashboardOptionsDataEl.data('mr-path'),
- };
- }
- });
-})(window.gl || (window.gl = {}));
+}
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 3f811c59cb9..95e51bc4e7a 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -2,6 +2,7 @@
import FilesCommentButton from './files_comment_button';
import imageDiffHelper from './image_diff/helpers/index';
+import syntaxHighlight from './syntax_highlight';
const WRAPPER = '<div class="diff-content"></div>';
const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>';
@@ -64,7 +65,7 @@ export default class SingleFileDiff {
_this.loadingContent.hide();
if (data.html) {
_this.content = $(data.html);
- _this.content.syntaxHighlight();
+ syntaxHighlight(_this.content);
} else {
_this.hasError = true;
_this.content = $(ERROR_HTML);
diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js
index 662d6b36c16..62bdef76c55 100644
--- a/app/assets/javascripts/syntax_highlight.js
+++ b/app/assets/javascripts/syntax_highlight.js
@@ -10,17 +10,15 @@
// <div class="js-syntax-highlight"></div>
//
-$.fn.syntaxHighlight = function() {
- var $children;
-
- if ($(this).hasClass('js-syntax-highlight')) {
+export default function syntaxHighlight(el) {
+ if ($(el).hasClass('js-syntax-highlight')) {
// Given the element itself, apply highlighting
- return $(this).addClass(gon.user_color_scheme);
+ return $(el).addClass(gon.user_color_scheme);
} else {
// Given a parent element, recurse to any of its applicable children
- $children = $(this).find('.js-syntax-highlight');
+ const $children = $(el).find('.js-syntax-highlight');
if ($children.length) {
- return $children.syntaxHighlight();
+ return syntaxHighlight($children);
}
}
-};
+}
diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js
index 5e947769f8a..4fa8c680580 100644
--- a/app/assets/javascripts/users/activity_calendar.js
+++ b/app/assets/javascripts/users/activity_calendar.js
@@ -1,5 +1,6 @@
import _ from 'underscore';
import d3 from 'd3';
+import { getDayName, getDayDifference } from '../lib/utils/datetime_utility';
const LOADING_HTML = `
<div class="text-center">
@@ -17,7 +18,7 @@ function getSystemDate(systemUtcOffsetSeconds) {
function formatTooltipText({ date, count }) {
const dateObject = new Date(date);
- const dateDayName = gl.utils.getDayName(dateObject);
+ const dateDayName = getDayName(dateObject);
const dateText = dateObject.format('mmm d, yyyy');
let contribText = 'No contributions';
@@ -51,7 +52,7 @@ export default class ActivityCalendar {
const oneYearAgo = new Date(today);
oneYearAgo.setFullYear(today.getFullYear() - 1);
- const days = gl.utils.getDayDifference(oneYearAgo, today);
+ const days = getDayDifference(oneYearAgo, today);
for (let i = 0; i <= days; i += 1) {
const date = new Date(oneYearAgo);
diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js
index 1215b265e28..992baa9a1ef 100644
--- a/app/assets/javascripts/users/user_tabs.js
+++ b/app/assets/javascripts/users/user_tabs.js
@@ -1,4 +1,6 @@
+import Activities from '../activities';
import ActivityCalendar from './activity_calendar';
+import { localTimeAgo } from '../lib/utils/datetime_utility';
/**
* UserTabs
@@ -138,7 +140,7 @@ export default class UserTabs {
const tabSelector = `div#${action}`;
this.$parentEl.find(tabSelector).html(data.html);
this.loaded[action] = true;
- gl.utils.localTimeAgo($('.js-timeago', tabSelector));
+ localTimeAgo($('.js-timeago', tabSelector));
},
});
}
@@ -169,7 +171,7 @@ export default class UserTabs {
});
// eslint-disable-next-line no-new
- new gl.Activities();
+ new Activities();
this.loaded.activity = true;
}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
index 32028a4a609..ee1a45cc754 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
@@ -1,4 +1,4 @@
-import '~/lib/utils/datetime_utility';
+import { getTimeago } from '~/lib/utils/datetime_utility';
import { visitUrl } from '../../lib/utils/url_utility';
import Flash from '../../flash';
import MemoryUsage from './mr_widget_memory_usage';
@@ -17,7 +17,7 @@ export default {
},
methods: {
formatDate(date) {
- return gl.utils.getTimeago().format(date);
+ return getTimeago().format(date);
},
hasExternalUrls(deployment = {}) {
return deployment.external_url && deployment.external_url_formatted;
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
index 05c4a28be88..43b2d238f65 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.js
@@ -65,10 +65,12 @@ export default {
<div class="mr-widget-body media">
<status-icon status="success" />
<div class="media-body">
- <h4>
- Set by
- <mr-widget-author :author="mr.setToMWPSBy" />
- to be merged automatically when the pipeline succeeds
+ <h4 class="flex-container-block">
+ <span class="append-right-10">
+ Set by
+ <mr-widget-author :author="mr.setToMWPSBy" />
+ to be merged automatically when the pipeline succeeds
+ </span>
<a
v-if="mr.canCancelAutomaticMerge"
@click.prevent="cancelAutomaticMerge"
@@ -94,8 +96,13 @@ export default {
<p v-if="mr.shouldRemoveSourceBranch">
The source branch will be removed
</p>
- <p v-else>
- The source branch will not be removed
+ <p
+ v-else
+ class="flex-container-block"
+ >
+ <span class="append-right-10">
+ The source branch will not be removed
+ </span>
<a
v-if="canRemoveSourceBranch"
:disabled="isRemovingSourceBranch"
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index c1f7e64f580..707766e08e4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -1,5 +1,6 @@
import Timeago from 'timeago.js';
import { getStateKey } from '../dependencies';
+import { formatDate } from '../../lib/utils/datetime_utility';
export default class MergeRequestStore {
@@ -122,7 +123,7 @@ export default class MergeRequestStore {
static getEventObject(event) {
return {
author: MergeRequestStore.getAuthorObject(event),
- updatedAt: gl.utils.formatDate(MergeRequestStore.getEventUpdatedAtDate(event)),
+ updatedAt: formatDate(MergeRequestStore.getEventUpdatedAtDate(event)),
formattedUpdatedAt: MergeRequestStore.getEventDate(event),
};
}
diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.js b/app/assets/javascripts/vue_shared/components/memory_graph.js
index 643b77e04c7..f37ef1a5ca3 100644
--- a/app/assets/javascripts/vue_shared/components/memory_graph.js
+++ b/app/assets/javascripts/vue_shared/components/memory_graph.js
@@ -1,3 +1,5 @@
+import { getTimeago } from '../../lib/utils/datetime_utility';
+
export default {
name: 'MemoryGraph',
props: {
@@ -16,7 +18,7 @@ export default {
},
computed: {
getFormattedMedian() {
- const deployedSince = gl.utils.getTimeago().format(this.deploymentTime * 1000);
+ const deployedSince = getTimeago().format(this.deploymentTime * 1000);
return `Deployed ${deployedSince}`;
},
},
diff --git a/app/assets/javascripts/vue_shared/components/popup_dialog.vue b/app/assets/javascripts/vue_shared/components/modal.vue
index 6d15bbd84ba..55f466b7b41 100644
--- a/app/assets/javascripts/vue_shared/components/popup_dialog.vue
+++ b/app/assets/javascripts/vue_shared/components/modal.vue
@@ -1,6 +1,6 @@
<script>
export default {
- name: 'popup-dialog',
+ name: 'modal',
props: {
title: {
@@ -75,7 +75,7 @@ export default {
<template>
<div class="modal-open">
<div
- class="modal popup-dialog"
+ class="modal show"
role="dialog"
tabindex="-1"
>
diff --git a/app/assets/javascripts/vue_shared/components/recaptcha_dialog.vue b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
index 3ec50f14eb4..8053c65d498 100644
--- a/app/assets/javascripts/vue_shared/components/recaptcha_dialog.vue
+++ b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
@@ -1,8 +1,8 @@
<script>
-import PopupDialog from './popup_dialog.vue';
+import modal from './modal.vue';
export default {
- name: 'recaptcha-dialog',
+ name: 'recaptcha-modal',
props: {
html: {
@@ -20,7 +20,7 @@ export default {
},
components: {
- PopupDialog,
+ modal,
},
methods: {
@@ -65,9 +65,9 @@ export default {
</script>
<template>
-<popup-dialog
+<modal
kind="warning"
- class="recaptcha-dialog js-recaptcha-dialog"
+ class="recaptcha-modal js-recaptcha-modal"
:hide-footer="true"
:title="__('Please solve the reCAPTCHA')"
@toggle="close"
@@ -81,5 +81,5 @@ export default {
v-html="html"
></div>
</div>
-</popup-dialog>
+</modal>
</template>
diff --git a/app/assets/javascripts/vue_shared/mixins/recaptcha_dialog_implementor.js b/app/assets/javascripts/vue_shared/mixins/recaptcha_modal_implementor.js
index ef70f9432e3..ff1f565e79a 100644
--- a/app/assets/javascripts/vue_shared/mixins/recaptcha_dialog_implementor.js
+++ b/app/assets/javascripts/vue_shared/mixins/recaptcha_modal_implementor.js
@@ -1,4 +1,4 @@
-import RecaptchaDialog from '../components/recaptcha_dialog.vue';
+import recaptchaModal from '../components/recaptcha_modal.vue';
export default {
data() {
@@ -9,7 +9,7 @@ export default {
},
components: {
- RecaptchaDialog,
+ recaptchaModal,
},
methods: {
diff --git a/app/assets/javascripts/vue_shared/mixins/timeago.js b/app/assets/javascripts/vue_shared/mixins/timeago.js
index 20f63ab663c..4e3b9d7b767 100644
--- a/app/assets/javascripts/vue_shared/mixins/timeago.js
+++ b/app/assets/javascripts/vue_shared/mixins/timeago.js
@@ -1,4 +1,4 @@
-import '../../lib/utils/datetime_utility';
+import { formatDate, getTimeago } from '../../lib/utils/datetime_utility';
/**
* Mixin with time ago methods used in some vue components
@@ -6,13 +6,13 @@ import '../../lib/utils/datetime_utility';
export default {
methods: {
timeFormated(time) {
- const timeago = gl.utils.getTimeago();
+ const timeago = getTimeago();
return timeago.format(time);
},
tooltipTitle(time) {
- return gl.utils.formatDate(time);
+ return formatDate(time);
},
},
};
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 609f33582e1..1588036aeae 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -396,3 +396,8 @@ span.idiff {
.file-fork-suggestion-note {
margin-right: 1.5em;
}
+
+.label-lfs {
+ color: $common-gray-light;
+ border: 1px solid $common-gray-light;
+}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 2ce4de6a2d5..29714e348a0 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -1,7 +1,3 @@
-.content-wrapper.page-with-new-nav {
- margin-top: $header-height;
-}
-
.navbar-gitlab {
&.navbar-gitlab {
padding: 0 16px;
diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss
index 78a8e57ddbb..aa2d30a3cef 100644
--- a/app/assets/stylesheets/framework/images.scss
+++ b/app/assets/stylesheets/framework/images.scss
@@ -19,6 +19,13 @@
max-width: 425px;
width: 100%;
}
+
+ &.svg-250 {
+ img,
+ svg {
+ width: 250px;
+ }
+ }
}
@mixin svg-size($size) {
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index 1537b0744cc..1d8bd26cf1a 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -24,10 +24,14 @@
font-size: $gl-font-size;
line-height: 25px;
- &.status-box-closed {
+ &.status-box-mr-closed {
background-color: $gl-danger;
}
+ &.status-box-issue-closed {
+ background-color: $gl-primary;
+ }
+
&.status-box-merged {
background-color: $gl-primary;
}
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index cb324ccc440..3f0268541a4 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -24,6 +24,7 @@ body {
}
.content-wrapper {
+ margin-top: $header-height;
padding-bottom: 100px;
}
@@ -105,11 +106,11 @@ body {
}
}
-.page-with-sidebar > .content-wrapper {
+.layout-page > .content-wrapper {
min-height: calc(100vh - #{$header-height});
}
-.with-performance-bar .page-with-sidebar {
+.with-performance-bar .layout-page {
margin-top: $header-height + $performance-bar-height;
}
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index 600a1f53b58..a12f28efce6 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -111,21 +111,4 @@
aside:not(.right-sidebar) {
display: none;
}
-
- .show-aside {
- display: block !important;
- }
-}
-
-.show-aside {
- display: none;
- position: fixed;
- right: 0;
- top: 30%;
- padding: 5px 15px;
- background: $show-aside-bg;
- font-size: 20px;
- color: $show-aside-color;
- z-index: 100;
- box-shadow: 0 1px 2px $show-aside-shadow;
}
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index ce551e6b7ce..1be66d0ab21 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -44,11 +44,18 @@ body.modal-open {
}
}
-.modal.popup-dialog {
- display: block;
+.modal {
+ background-color: $black-transparent;
+ z-index: 2100;
+
+ @media (min-width: $screen-md-min) {
+ .modal-dialog {
+ margin: 30px auto;
+ }
+ }
}
-.recaptcha-dialog .recaptcha-form {
+.recaptcha-modal .recaptcha-form {
display: inline-block;
.recaptcha {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 5de5403916f..b84d6c140be 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -245,9 +245,6 @@ $btn-sm-side-margin: 7px;
$btn-xs-side-margin: 5px;
$issue-status-expired: $orange-500;
$issuable-sidebar-color: $gl-text-color-secondary;
-$show-aside-bg: #eee;
-$show-aside-color: #777;
-$show-aside-shadow: #ddd;
$group-path-color: #999;
$namespace-kind-color: #aaa;
$panel-heading-link-color: #777;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 862ea379cbc..2803144ef1d 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -415,7 +415,7 @@
margin: 5px;
}
-.page-with-contextual-sidebar.page-with-sidebar .issue-boards-sidebar {
+.page-with-contextual-sidebar.layout-page .issue-boards-sidebar {
.issuable-sidebar-header {
position: relative;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 8f8f11e3857..e19196e0c41 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -610,11 +610,19 @@
}
.issuable-status-box {
- float: none;
- display: inline-block;
+ align-self: stretch;
+ display: flex;
+ justify-content: center;
+ align-items: center;
margin-top: 0;
- height: auto;
- align-self: center;
+ padding-left: 9px;
+ padding-right: 9px;
+
+ @media (min-width: $screen-sm-min) {
+ display: inline-block;
+ height: auto;
+ align-self: center;
+ }
}
.issuable-gutter-toggle {
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 402412eae71..6eb92c7baee 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -1,16 +1,3 @@
-.modal.popup-dialog {
- display: block;
- background-color: $black-transparent;
- z-index: 2100;
-
- @media (min-width: $screen-md-min) {
- .modal-dialog {
- width: 600px;
- margin: 30px auto;
- }
- }
-}
-
.project-refs-form,
.project-refs-target-form {
display: inline-block;
diff --git a/app/controllers/concerns/with_performance_bar.rb b/app/controllers/concerns/with_performance_bar.rb
index ed253042701..230bbe4b1aa 100644
--- a/app/controllers/concerns/with_performance_bar.rb
+++ b/app/controllers/concerns/with_performance_bar.rb
@@ -6,6 +6,7 @@ module WithPerformanceBar
end
def peek_enabled?
+ return true if Rails.env.development?
return false unless Gitlab::PerformanceBar.enabled?(current_user)
if RequestStore.active?
diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb
index 1511fc08c89..dc524b790a0 100644
--- a/app/controllers/projects/merge_requests/creations_controller.rb
+++ b/app/controllers/projects/merge_requests/creations_controller.rb
@@ -9,7 +9,10 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
before_action :build_merge_request, except: [:create]
def new
- define_new_vars
+ # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/40934
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ define_new_vars
+ end
end
def create
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index f3719059f88..f752a46f828 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -26,6 +26,7 @@ class Projects::TreeController < Projects::ApplicationController
respond_to do |format|
format.html do
+ lfs_blob_ids
@last_commit = @repository.last_commit_for_path(@commit.id, @tree.path) || @commit
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 8e9d6766d80..6f609348402 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -9,6 +9,7 @@ class ProjectsController < Projects::ApplicationController
before_action :repository, except: [:index, :new, :create]
before_action :assign_ref_vars, only: [:show], if: :repo_exists?
before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?]
+ before_action :lfs_blob_ids, only: [:show], if: [:repo_exists?, :project_view_files?]
before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export]
# Authorize
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 212cdbb8157..0f110bd25c5 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -74,7 +74,7 @@ module IssuesHelper
elsif item.try(:merged?)
'status-box-merged'
elsif item.closed?
- 'status-box-closed'
+ 'status-box-mr-closed'
elsif item.try(:upcoming?)
'status-box-upcoming'
else
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index 77a82b895ce..50e17fe7717 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -5,7 +5,7 @@ module Emails
@commit = @note.noteable
@target_url = project_commit_url(*note_target_url_options)
- mail_answer_thread(@commit, note_thread_options(recipient_id))
+ mail_answer_note_thread(@commit, @note, note_thread_options(recipient_id))
end
def note_issue_email(recipient_id, note_id)
@@ -13,7 +13,7 @@ module Emails
@issue = @note.noteable
@target_url = project_issue_url(*note_target_url_options)
- mail_answer_thread(@issue, note_thread_options(recipient_id))
+ mail_answer_note_thread(@issue, @note, note_thread_options(recipient_id))
end
def note_merge_request_email(recipient_id, note_id)
@@ -21,7 +21,7 @@ module Emails
@merge_request = @note.noteable
@target_url = project_merge_request_url(*note_target_url_options)
- mail_answer_thread(@merge_request, note_thread_options(recipient_id))
+ mail_answer_note_thread(@merge_request, @note, note_thread_options(recipient_id))
end
def note_snippet_email(recipient_id, note_id)
@@ -29,7 +29,7 @@ module Emails
@snippet = @note.noteable
@target_url = project_snippet_url(*note_target_url_options)
- mail_answer_thread(@snippet, note_thread_options(recipient_id))
+ mail_answer_note_thread(@snippet, @note, note_thread_options(recipient_id))
end
def note_personal_snippet_email(recipient_id, note_id)
@@ -37,7 +37,7 @@ module Emails
@snippet = @note.noteable
@target_url = snippet_url(@note.noteable)
- mail_answer_thread(@snippet, note_thread_options(recipient_id))
+ mail_answer_note_thread(@snippet, @note, note_thread_options(recipient_id))
end
private
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 9efabe3f44e..ec886e993c3 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -119,8 +119,8 @@ class Notify < BaseMailer
headers['Reply-To'] = address
fallback_reply_message_id = "<reply-#{reply_key}@#{Gitlab.config.gitlab.host}>".freeze
- headers['References'] ||= ''
- headers['References'] << ' ' << fallback_reply_message_id
+ headers['References'] ||= []
+ headers['References'] << fallback_reply_message_id
@reply_by_email = true
end
@@ -156,6 +156,18 @@ class Notify < BaseMailer
mail_thread(model, headers)
end
+ def mail_answer_note_thread(model, note, headers = {})
+ headers['Message-ID'] = message_id(note)
+ headers['In-Reply-To'] = message_id(note.references.last)
+ headers['References'] = note.references.map { |ref| message_id(ref) }
+
+ headers['X-GitLab-Discussion-ID'] = note.discussion.id if note.part_of_discussion?
+
+ headers[:subject]&.prepend('Re: ')
+
+ mail_thread(model, headers)
+ end
+
def reply_key
@reply_key ||= SentNotification.reply_key
end
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 29e762724e3..19ad110db58 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -77,9 +77,15 @@ class Blob < SimpleDelegator
end
def self.lazy(project, commit_id, path)
- BatchLoader.for(commit_id: commit_id, path: path).batch do |items, loader|
- project.repository.blobs_at(items.map(&:values)).each do |blob|
- loader.call({ commit_id: blob.commit_id, path: blob.path }, blob) if blob
+ BatchLoader.for({ project: project, commit_id: commit_id, path: path }).batch do |items, loader|
+ items_by_project = items.group_by { |i| i[:project] }
+
+ items_by_project.each do |project, items|
+ items = items.map { |i| i.values_at(:commit_id, :path) }
+
+ project.repository.blobs_at(items).each do |blob|
+ loader.call({ project: blob.project, commit_id: blob.commit_id, path: blob.path }, blob) if blob
+ end
end
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 85960f1b6bb..83fe23606d1 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -491,7 +491,6 @@ module Ci
end
def valid_dependency?
- return false unless complete?
return false if artifacts_expired?
return false if erased?
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 307e4fcedfe..13c31111134 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -52,6 +52,20 @@ class Commit
diffs.reduce(0) { |sum, d| sum + Gitlab::Git::Util.count_lines(d.diff) }
end
+ def order_by(collection:, order_by:, sort:)
+ return collection unless %w[email name commits].include?(order_by)
+ return collection unless %w[asc desc].include?(sort)
+
+ collection.sort do |a, b|
+ operands = [a, b].tap { |o| o.reverse! if sort == 'desc' }
+
+ attr1, attr2 = operands.first.public_send(order_by), operands.second.public_send(order_by) # rubocop:disable PublicSend
+
+ # use case insensitive comparison for string values
+ order_by.in?(%w[email name]) ? attr1.casecmp(attr2) : attr1 <=> attr2
+ end
+ end
+
# Truncate sha to 8 characters
def truncate_sha(sha)
sha[0..MIN_SHA_LENGTH]
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 26a3388602a..c39789b047d 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -53,8 +53,8 @@ class MergeRequest < ActiveRecord::Base
serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize
after_create :ensure_merge_request_diff, unless: :importing?
- after_update :reload_diff_if_branch_changed
after_update :clear_memoized_shas
+ after_update :reload_diff_if_branch_changed
# When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests
@@ -85,6 +85,14 @@ class MergeRequest < ActiveRecord::Base
transition locked: :opened
end
+ before_transition any => :opened do |merge_request|
+ merge_request.merge_jid = nil
+
+ merge_request.run_after_commit do
+ UpdateHeadPipelineForMergeRequestWorker.perform_async(merge_request.id)
+ end
+ end
+
state :opened
state :closed
state :merged
@@ -879,11 +887,11 @@ class MergeRequest < ActiveRecord::Base
def state_icon_name
if merged?
- "check"
+ "git-merge"
elsif closed?
- "times"
+ "close"
else
- "circle-o"
+ "issue-open-m"
end
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 02f9fd61e49..184fbd5f5ae 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -360,6 +360,16 @@ class Note < ActiveRecord::Base
end
end
+ def references
+ refs = [noteable]
+
+ if part_of_discussion?
+ refs += discussion.notes.take_while { |n| n.id < id }
+ end
+
+ refs
+ end
+
def expire_etag_cache
return unless noteable&.discussions_rendered_on_frontend?
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c0e31eca8da..552a354d1ce 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -686,7 +686,9 @@ class Repository
def tags_sorted_by(value)
case value
- when 'name'
+ when 'name_asc'
+ VersionSorter.sort(tags) { |tag| tag.name }
+ when 'name_desc'
VersionSorter.rsort(tags) { |tag| tag.name }
when 'updated_desc'
tags_sorted_by_committed_date.reverse
@@ -697,10 +699,14 @@ class Repository
end
end
- def contributors
+ # Params:
+ #
+ # order_by: name|email|commits
+ # sort: asc|desc default: 'asc'
+ def contributors(order_by: nil, sort: 'asc')
commits = self.commits(nil, limit: 2000, offset: 0, skip_merges: true)
- commits.group_by(&:author_email).map do |email, commits|
+ commits = commits.group_by(&:author_email).map do |email, commits|
contributor = Gitlab::Contributor.new
contributor.email = email
@@ -714,6 +720,7 @@ class Repository
contributor
end
+ Commit.order_by(collection: commits, order_by: order_by, sort: sort)
end
def refs_contains_sha(ref_type, sha)
@@ -927,7 +934,7 @@ class Repository
def merge_base(first_commit_id, second_commit_id)
first_commit_id = commit(first_commit_id).try(:id) || first_commit_id
second_commit_id = commit(second_commit_id).try(:id) || second_commit_id
- rugged.merge_base(first_commit_id, second_commit_id)
+ raw_repository.merge_base(first_commit_id, second_commit_id)
rescue Rugged::ReferenceError
nil
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 85db2760e23..c8b112132b3 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -81,7 +81,7 @@ module Ci
end
def related_merge_requests
- MergeRequest.where(source_project: pipeline.project, source_branch: pipeline.ref)
+ MergeRequest.opened.where(source_project: pipeline.project, source_branch: pipeline.ref)
end
end
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 2c51ac13815..e7463e6e25c 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -106,12 +106,14 @@ class IssuableBaseService < BaseService
end
def merge_quick_actions_into_params!(issuable)
+ original_description = params.fetch(:description, issuable.description)
+
description, command_params =
QuickActions::InterpretService.new(project, current_user)
- .execute(params[:description], issuable)
+ .execute(original_description, issuable)
# Avoid a description already set on an issuable to be overwritten by a nil
- params[:description] = description if params.key?(:description)
+ params[:description] = description if description
params.merge!(command_params)
end
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index a5686002328..20ca6ec969a 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -83,12 +83,12 @@
You're all done!
- elsif current_user.todos.any?
.todos-all-done
- .svg-content
+ .svg-content.svg-250
= image_tag 'illustrations/todos_all_done.svg'
- if todos_filter_empty?
%h4.text-center
= Gitlab.config.gitlab.no_todos_messages.sample
- %p.text-center
+ %p
Are you looking for things to do? Take a look at
= succeed "," do
= link_to "the opened issues", issues_dashboard_path
@@ -104,7 +104,7 @@
= image_tag 'illustrations/todos_empty.svg'
.todos-empty-content
%h4
- Todos let you see what you should do next.
+ Todos let you see what you should do next
%p
When an issue or merge request is assigned to you, or when you
%strong
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 25ed610466a..eba9cd253bb 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -1,7 +1,7 @@
-.page-with-sidebar{ class: page_with_sidebar_class }
+.layout-page{ class: page_with_sidebar_class }
- if defined?(nav) && nav
= render "layouts/nav/sidebar/#{nav}"
- .content-wrapper.page-with-new-nav
+ .content-wrapper
= render 'shared/outdated_browser'
.mobile-overlay
.alert-wrapper
diff --git a/app/views/layouts/nav/projects_dropdown/_show.html.haml b/app/views/layouts/nav/projects_dropdown/_show.html.haml
index a7370180bf6..32a24c101fc 100644
--- a/app/views/layouts/nav/projects_dropdown/_show.html.haml
+++ b/app/views/layouts/nav/projects_dropdown/_show.html.haml
@@ -1,4 +1,4 @@
-- project_meta = { id: @project.id, name: @project.name, namespace: @project.name_with_namespace, web_url: @project.web_url, avatar_url: @project.avatar_url } if @project&.persisted?
+- project_meta = { id: @project.id, name: @project.name, namespace: @project.name_with_namespace, web_url: project_path(@project), avatar_url: @project.avatar_url } if @project&.persisted?
.projects-dropdown-container
.project-dropdown-sidebar
%ul
diff --git a/app/views/projects/blob/_header_content.html.haml b/app/views/projects/blob/_header_content.html.haml
index 98bedae650a..5d457a50c49 100644
--- a/app/views/projects/blob/_header_content.html.haml
+++ b/app/views/projects/blob/_header_content.html.haml
@@ -8,3 +8,6 @@
%small
= number_to_human_size(blob.raw_size)
+
+ - if blob.stored_externally? && blob.external_storage == :lfs
+ %span.label.label-lfs.append-right-5 LFS
diff --git a/app/views/projects/clusters/_empty_state.html.haml b/app/views/projects/clusters/_empty_state.html.haml
index e629cc58b06..b525f4efc83 100644
--- a/app/views/projects/clusters/_empty_state.html.haml
+++ b/app/views/projects/clusters/_empty_state.html.haml
@@ -1,12 +1,12 @@
.row.empty-state
.col-xs-12
.svg-content= image_tag 'illustrations/clusters_empty.svg'
- .col-xs-12.text-center
+ .col-xs-12
.text-content
- %h4= s_('ClusterIntegration|Integrate cluster automation')
+ %h4.text-center= s_('ClusterIntegration|Integrate cluster automation')
- link_to_help_page = link_to(s_('ClusterIntegration|Learn more about Clusters'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
%p= s_('ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page}
- %p
+ .text-center
= link_to s_('ClusterIntegration|Add cluster'), new_project_cluster_path(@project), class: 'btn btn-success'
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 618a6355d23..d66066a6d0b 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -38,8 +38,8 @@
.commiter
- commit_author_link = commit_author_link(commit, avatar: false, size: 24)
- - commit_timeago = time_ago_with_tooltip(commit.committed_date, placement: 'bottom')
- - commit_text = _('%{commit_author_link} committed %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
+ - commit_timeago = time_ago_with_tooltip(commit.authored_date, placement: 'bottom')
+ - commit_text = _('%{commit_author_link} authored %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
#{ commit_text.html_safe }
.commit-actions.flex-row.hidden-xs
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index 56cf16c3778..ad94113fffd 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -15,9 +15,9 @@
#prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'),
"documentation-path": help_page_path('administration/monitoring/prometheus/index.md'),
- "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started'),
- "empty-loading-svg-path": image_path('illustrations/monitoring/loading'),
- "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect'),
+ "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'),
+ "empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'),
+ "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'),
"additional-metrics": additional_metrics_project_environment_path(@project, @environment, format: :json),
"project-path": project_path(@project),
"tags-path": project_tags_path(@project),
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index d260aaee2d3..eab7879c7bf 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -14,12 +14,12 @@
.detail-page-header
.detail-page-header-body
- .issuable-status-box.status-box.status-box-closed{ class: issue_button_visibility(@issue, false) }
- = icon('check', class: "hidden-sm hidden-md hidden-lg")
+ .issuable-status-box.status-box.status-box-issue-closed{ class: issue_button_visibility(@issue, false) }
+ = sprite_icon('mobile-issue-close', size: 16, css_class: 'hidden-sm hidden-md hidden-lg')
%span.hidden-xs
Closed
.issuable-status-box.status-box.status-box-open{ class: issue_button_visibility(@issue, true) }
- = icon('circle-o', class: "hidden-sm hidden-md hidden-lg")
+ = sprite_icon('issue-open-m', size: 16, css_class: 'hidden-sm hidden-md hidden-lg')
%span.hidden-xs Open
.issuable-meta
diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml
index bc91758110e..22c8b6b513d 100644
--- a/app/views/projects/merge_requests/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/_mr_title.html.haml
@@ -7,7 +7,7 @@
.detail-page-header
.detail-page-header-body
.issuable-status-box.status-box{ class: status_box_class(@merge_request) }
- = icon(@merge_request.state_icon_name, class: "hidden-sm hidden-md hidden-lg")
+ = sprite_icon(@merge_request.state_icon_name, size: 16, css_class: 'hidden-sm hidden-md hidden-lg')
%span.hidden-xs
= @merge_request.state_human_name
diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml
index c51af901699..8c1c532cb3e 100644
--- a/app/views/projects/tree/_blob_item.html.haml
+++ b/app/views/projects/tree/_blob_item.html.haml
@@ -1,9 +1,12 @@
+- is_lfs_blob = @lfs_blob_ids.include?(blob_item.id)
%tr{ class: "tree-item #{tree_hex_class(blob_item)}" }
%td.tree-item-file-name
= tree_icon(type, blob_item.mode, blob_item.name)
- file_name = blob_item.name
= link_to project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name)), class: 'str-truncated', title: file_name do
%span= file_name
+ - if is_lfs_blob
+ %span.label.label-lfs.prepend-left-5 LFS
%td.hidden-xs.tree-commit
%td.tree-time-ago.cgray.text-right
= render 'projects/tree/spinner'
diff --git a/app/views/shared/_show_aside.html.haml b/app/views/shared/_show_aside.html.haml
deleted file mode 100644
index 3ac9b11b4fa..00000000000
--- a/app/views/shared/_show_aside.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-= link_to '#aside', class: 'show-aside' do
- %i.fa.fa-angle-left
diff --git a/app/views/shared/empty_states/_issues.html.haml b/app/views/shared/empty_states/_issues.html.haml
index de26fa8bbf3..e039a73cd3b 100644
--- a/app/views/shared/empty_states/_issues.html.haml
+++ b/app/views/shared/empty_states/_issues.html.haml
@@ -6,18 +6,21 @@
.col-xs-12
.svg-content
= image_tag 'illustrations/issues.svg'
- .col-xs-12.text-center
+ .col-xs-12
.text-content
- if has_button && current_user
%h4
- The Issue Tracker is the place to add things that need to be improved or solved in a project
+ = _("The Issue Tracker is the place to add things that need to be improved or solved in a project")
%p
- Issues can be bugs, tasks or ideas to be discussed.
- Also, issues are searchable and filterable.
- - if project_select_button
- = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues
- - else
- = link_to 'New issue', button_path, class: 'btn btn-new', title: 'New issue', id: 'new_issue_link'
+ = _("Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.")
+ .text-center
+ - if project_select_button
+ = render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue', type: :issues
+ - else
+ = link_to 'New issue', button_path, class: 'btn btn-success', title: 'New issue', id: 'new_issue_link'
- else
+ %h4.text-center= _("There are no issues to show")
+ %p
+ = _("The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project.")
.text-center
- %h4 There are no issues to show.
+ = link_to _('Register / Sign In'), new_user_session_path, class: 'btn btn-success'
diff --git a/app/views/shared/empty_states/_labels.html.haml b/app/views/shared/empty_states/_labels.html.haml
index a65634dce53..04db9de3606 100644
--- a/app/views/shared/empty_states/_labels.html.haml
+++ b/app/views/shared/empty_states/_labels.html.haml
@@ -2,10 +2,10 @@
.col-xs-12
.svg-content
= image_tag 'illustrations/labels.svg'
- .col-xs-12.text-center
+ .col-xs-12
.text-content
- %h4 Labels can be applied to issues and merge requests to categorize them.
- %p You can also star a label to make it a priority label.
+ %h4= _("Labels can be applied to issues and merge requests to categorize them.")
+ %p= _("You can also star a label to make it a priority label.")
- if can?(current_user, :admin_label, @project)
- = link_to 'New label', new_project_label_path(@project), class: 'btn btn-new', title: 'New label', id: 'new_label_link'
- = link_to 'Generate a default set of labels', generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: 'Generate a default set of labels', id: 'generate_labels_link'
+ = link_to _('New label'), new_project_label_path(@project), class: 'btn btn-new', title: _('New label'), id: 'new_label_link'
+ = link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link'
diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml
index 67f906903e9..2edf3557df4 100644
--- a/app/views/shared/empty_states/_merge_requests.html.haml
+++ b/app/views/shared/empty_states/_merge_requests.html.haml
@@ -6,17 +6,18 @@
.col-xs-12
.svg-content
= image_tag 'illustrations/merge_requests.svg'
- .col-xs-12.text-center
+ .col-xs-12
.text-content
- if has_button
%h4
- Merge requests are a place to propose changes you've made to a project and discuss those changes with others.
+ = _("Merge requests are a place to propose changes you've made to a project and discuss those changes with others")
%p
- Interested parties can even contribute by pushing commits if they want to.
- - if project_select_button
- = render 'shared/new_project_item_select', path: 'merge_requests/new', label: 'New merge request', type: :merge_requests
- - else
- = link_to 'New merge request', button_path, class: 'btn btn-new', title: 'New merge request', id: 'new_merge_request_link'
+ = _("Interested parties can even contribute by pushing commits if they want to.")
+ .text-center
+ - if project_select_button
+ = render 'shared/new_project_item_select', path: 'merge_requests/new', label: _('New merge request'), type: :merge_requests
+ - else
+ = link_to _('New merge request'), button_path, class: 'btn btn-new', title: _('New merge request'), id: 'new_merge_request_link'
- else
%h4.text-center
- There are no merge requests to show.
+ = _("There are no merge requests to show")
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
new file mode 100644
index 00000000000..ba31a5aa9c2
--- /dev/null
+++ b/app/workers/all_queues.yml
@@ -0,0 +1,98 @@
+---
+- cronjob:admin_email
+- cronjob:expire_build_artifacts
+- cronjob:gitlab_usage_ping
+- cronjob:import_export_project_cleanup
+- cronjob:pipeline_schedule
+- cronjob:prune_old_events
+- cronjob:remove_expired_group_links
+- cronjob:remove_expired_members
+- cronjob:remove_old_web_hook_logs
+- cronjob:remove_unreferenced_lfs_objects
+- cronjob:repository_archive_cache
+- cronjob:repository_check_batch
+- cronjob:requests_profiles
+- cronjob:schedule_update_user_activity
+- cronjob:stuck_ci_jobs
+- cronjob:stuck_import_jobs
+- cronjob:stuck_merge_jobs
+- cronjob:trending_projects
+
+- gcp_cluster:cluster_install_app
+- gcp_cluster:cluster_provision
+- gcp_cluster:cluster_wait_for_app_installation
+- gcp_cluster:wait_for_cluster_creation
+
+- github_import_advance_stage
+- github_importer:github_import_import_diff_note
+- github_importer:github_import_import_issue
+- github_importer:github_import_import_note
+- github_importer:github_import_import_pull_request
+- github_importer:github_import_refresh_import_jid
+- github_importer:github_import_stage_finish_import
+- github_importer:github_import_stage_import_base_data
+- github_importer:github_import_stage_import_issues_and_diff_notes
+- github_importer:github_import_stage_import_notes
+- github_importer:github_import_stage_import_pull_requests
+- github_importer:github_import_stage_import_repository
+
+- pipeline_cache:expire_job_cache
+- pipeline_cache:expire_pipeline_cache
+- pipeline_creation:create_pipeline
+- pipeline_default:build_coverage
+- pipeline_default:build_trace_sections
+- pipeline_default:pipeline_metrics
+- pipeline_default:pipeline_notification
+- pipeline_default:update_head_pipeline_for_merge_request
+- pipeline_hooks:build_hooks
+- pipeline_hooks:pipeline_hooks
+- pipeline_processing:build_finished
+- pipeline_processing:build_queue
+- pipeline_processing:build_success
+- pipeline_processing:pipeline_process
+- pipeline_processing:pipeline_success
+- pipeline_processing:pipeline_update
+- pipeline_processing:stage_update
+
+- repository_check:repository_check_clear
+- repository_check:repository_check_single_repository
+
+- default
+- mailers # ActionMailer::DeliveryJob.queue_name
+
+- authorized_projects
+- background_migration
+- create_gpg_signature
+- delete_merged_branches
+- delete_user
+- email_receiver
+- emails_on_push
+- expire_build_instance_artifacts
+- git_garbage_collect
+- gitlab_shell
+- group_destroy
+- invalid_gpg_signature_update
+- irker
+- merge
+- namespaceless_project_destroy
+- new_issue
+- new_merge_request
+- new_note
+- pages
+- post_receive
+- process_commit
+- project_cache
+- project_destroy
+- project_export
+- project_migrate_hashed_storage
+- project_service
+- propagate_service_template
+- reactive_caching
+- repository_fork
+- repository_import
+- storage_migrator
+- system_hook_push
+- update_merge_requests
+- update_user_activity
+- upload_checksum
+- web_hook
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index 5efa9180f5e..97d80305bec 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -2,7 +2,7 @@ class BuildFinishedWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :processing
+ queue_namespace :pipeline_processing
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb
index 6705a1c2709..cbfca8c342c 100644
--- a/app/workers/build_hooks_worker.rb
+++ b/app/workers/build_hooks_worker.rb
@@ -2,7 +2,7 @@ class BuildHooksWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :hooks
+ queue_namespace :pipeline_hooks
def perform(build_id)
Ci::Build.find_by(id: build_id)
diff --git a/app/workers/build_queue_worker.rb b/app/workers/build_queue_worker.rb
index fc775a84dc0..e4f4e6c1d9e 100644
--- a/app/workers/build_queue_worker.rb
+++ b/app/workers/build_queue_worker.rb
@@ -2,7 +2,7 @@ class BuildQueueWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :processing
+ queue_namespace :pipeline_processing
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb
index ec049821ad7..4b9097bc5e4 100644
--- a/app/workers/build_success_worker.rb
+++ b/app/workers/build_success_worker.rb
@@ -2,7 +2,7 @@ class BuildSuccessWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :processing
+ queue_namespace :pipeline_processing
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
diff --git a/app/workers/concerns/application_worker.rb b/app/workers/concerns/application_worker.rb
index 9c3bdabc49e..37586e161c9 100644
--- a/app/workers/concerns/application_worker.rb
+++ b/app/workers/concerns/application_worker.rb
@@ -3,13 +3,23 @@ Sidekiq::Worker.extend ActiveSupport::Concern
module ApplicationWorker
extend ActiveSupport::Concern
- include Sidekiq::Worker
+ include Sidekiq::Worker # rubocop:disable Cop/IncludeSidekiqWorker
included do
- sidekiq_options queue: base_queue_name
+ set_queue
end
module ClassMethods
+ def inherited(subclass)
+ subclass.set_queue
+ end
+
+ def set_queue
+ queue_name = [queue_namespace, base_queue_name].compact.join(':')
+
+ sidekiq_options queue: queue_name # rubocop:disable Cop/SidekiqOptionsQueue
+ end
+
def base_queue_name
name
.sub(/\AGitlab::/, '')
@@ -18,6 +28,16 @@ module ApplicationWorker
.tr('/', '_')
end
+ def queue_namespace(new_namespace = nil)
+ if new_namespace
+ sidekiq_options queue_namespace: new_namespace
+
+ set_queue
+ else
+ get_sidekiq_options['queue_namespace']&.to_s
+ end
+ end
+
def queue
get_sidekiq_options['queue'].to_s
end
diff --git a/app/workers/concerns/cluster_queue.rb b/app/workers/concerns/cluster_queue.rb
index a5074d13220..24b9f145220 100644
--- a/app/workers/concerns/cluster_queue.rb
+++ b/app/workers/concerns/cluster_queue.rb
@@ -5,6 +5,6 @@ module ClusterQueue
extend ActiveSupport::Concern
included do
- sidekiq_options queue: :gcp_cluster
+ queue_namespace :gcp_cluster
end
end
diff --git a/app/workers/concerns/cronjob_queue.rb b/app/workers/concerns/cronjob_queue.rb
index e918bb011e0..b6581779f6a 100644
--- a/app/workers/concerns/cronjob_queue.rb
+++ b/app/workers/concerns/cronjob_queue.rb
@@ -4,6 +4,7 @@ module CronjobQueue
extend ActiveSupport::Concern
included do
- sidekiq_options queue: :cronjob, retry: false
+ queue_namespace :cronjob
+ sidekiq_options retry: false
end
end
diff --git a/app/workers/concerns/gitlab/github_import/queue.rb b/app/workers/concerns/gitlab/github_import/queue.rb
index a2bee361b86..22c2ce458e8 100644
--- a/app/workers/concerns/gitlab/github_import/queue.rb
+++ b/app/workers/concerns/gitlab/github_import/queue.rb
@@ -4,12 +4,14 @@ module Gitlab
extend ActiveSupport::Concern
included do
+ queue_namespace :github_importer
+
# If a job produces an error it may block a stage from advancing
# forever. To prevent this from happening we prevent jobs from going to
# the dead queue. This does mean some resources may not be imported, but
# this is better than a project being stuck in the "import" state
# forever.
- sidekiq_options queue: 'github_importer', dead: false, retry: 5
+ sidekiq_options dead: false, retry: 5
end
end
end
diff --git a/app/workers/concerns/pipeline_queue.rb b/app/workers/concerns/pipeline_queue.rb
index ddf45b91345..e77093a6902 100644
--- a/app/workers/concerns/pipeline_queue.rb
+++ b/app/workers/concerns/pipeline_queue.rb
@@ -5,14 +5,6 @@ module PipelineQueue
extend ActiveSupport::Concern
included do
- sidekiq_options queue: 'pipeline_default'
- end
-
- class_methods do
- def enqueue_in(group:)
- raise ArgumentError, 'Unspecified queue group!' if group.empty?
-
- sidekiq_options queue: "pipeline_#{group}"
- end
+ queue_namespace :pipeline_default
end
end
diff --git a/app/workers/concerns/repository_check_queue.rb b/app/workers/concerns/repository_check_queue.rb
index a597321ccf4..43fb66c31b0 100644
--- a/app/workers/concerns/repository_check_queue.rb
+++ b/app/workers/concerns/repository_check_queue.rb
@@ -3,6 +3,8 @@ module RepositoryCheckQueue
extend ActiveSupport::Concern
included do
- sidekiq_options queue: :repository_check, retry: false
+ queue_namespace :repository_check
+
+ sidekiq_options retry: false
end
end
diff --git a/app/workers/create_pipeline_worker.rb b/app/workers/create_pipeline_worker.rb
index 00cd7b85b9f..c3ac35e54f5 100644
--- a/app/workers/create_pipeline_worker.rb
+++ b/app/workers/create_pipeline_worker.rb
@@ -2,7 +2,7 @@ class CreatePipelineWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :creation
+ queue_namespace :pipeline_creation
def perform(project_id, user_id, ref, source, params = {})
project = Project.find(project_id)
diff --git a/app/workers/expire_job_cache_worker.rb b/app/workers/expire_job_cache_worker.rb
index a591e2da519..7217364a9f2 100644
--- a/app/workers/expire_job_cache_worker.rb
+++ b/app/workers/expire_job_cache_worker.rb
@@ -2,7 +2,7 @@ class ExpireJobCacheWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :cache
+ queue_namespace :pipeline_cache
def perform(job_id)
job = CommitStatus.joins(:pipeline, :project).find_by(id: job_id)
diff --git a/app/workers/expire_pipeline_cache_worker.rb b/app/workers/expire_pipeline_cache_worker.rb
index a3ac32b437d..3e34de22c19 100644
--- a/app/workers/expire_pipeline_cache_worker.rb
+++ b/app/workers/expire_pipeline_cache_worker.rb
@@ -2,7 +2,7 @@ class ExpirePipelineCacheWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :cache
+ queue_namespace :pipeline_cache
def perform(pipeline_id)
pipeline = Ci::Pipeline.find_by(id: pipeline_id)
diff --git a/app/workers/gitlab/github_import/advance_stage_worker.rb b/app/workers/gitlab/github_import/advance_stage_worker.rb
index 400396d5755..f7f498af840 100644
--- a/app/workers/gitlab/github_import/advance_stage_worker.rb
+++ b/app/workers/gitlab/github_import/advance_stage_worker.rb
@@ -9,7 +9,7 @@ module Gitlab
class AdvanceStageWorker
include ApplicationWorker
- sidekiq_options queue: 'github_importer_advance_stage', dead: false
+ sidekiq_options dead: false
INTERVAL = 30.seconds.to_i
diff --git a/app/workers/pages_worker.rb b/app/workers/pages_worker.rb
index 62f733c02fc..3ec81d040b4 100644
--- a/app/workers/pages_worker.rb
+++ b/app/workers/pages_worker.rb
@@ -1,7 +1,7 @@
class PagesWorker
include ApplicationWorker
- sidekiq_options queue: :pages, retry: false
+ sidekiq_options retry: false
def perform(action, *arg)
send(action, *arg) # rubocop:disable GitlabSecurity/PublicSend
diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb
index 661c29efe88..c94918ff4ee 100644
--- a/app/workers/pipeline_hooks_worker.rb
+++ b/app/workers/pipeline_hooks_worker.rb
@@ -2,7 +2,7 @@ class PipelineHooksWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :hooks
+ queue_namespace :pipeline_hooks
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
diff --git a/app/workers/pipeline_process_worker.rb b/app/workers/pipeline_process_worker.rb
index 07dbf6a971e..24424b3f472 100644
--- a/app/workers/pipeline_process_worker.rb
+++ b/app/workers/pipeline_process_worker.rb
@@ -2,7 +2,7 @@ class PipelineProcessWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :processing
+ queue_namespace :pipeline_processing
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb
index 68c40a259e1..2ab0739a17f 100644
--- a/app/workers/pipeline_success_worker.rb
+++ b/app/workers/pipeline_success_worker.rb
@@ -2,7 +2,7 @@ class PipelineSuccessWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :processing
+ queue_namespace :pipeline_processing
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline|
diff --git a/app/workers/pipeline_update_worker.rb b/app/workers/pipeline_update_worker.rb
index 24a8a9fbed5..fc9da2d45b1 100644
--- a/app/workers/pipeline_update_worker.rb
+++ b/app/workers/pipeline_update_worker.rb
@@ -2,7 +2,7 @@ class PipelineUpdateWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :processing
+ queue_namespace :pipeline_processing
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
diff --git a/app/workers/stage_update_worker.rb b/app/workers/stage_update_worker.rb
index 69f2318d83b..e4b683fca33 100644
--- a/app/workers/stage_update_worker.rb
+++ b/app/workers/stage_update_worker.rb
@@ -2,7 +2,7 @@ class StageUpdateWorker
include ApplicationWorker
include PipelineQueue
- enqueue_in group: :processing
+ queue_namespace :pipeline_processing
def perform(stage_id)
Ci::Stage.find_by(id: stage_id).try do |stage|
diff --git a/app/workers/stuck_merge_jobs_worker.rb b/app/workers/stuck_merge_jobs_worker.rb
index 36d2a2e6466..16394293c79 100644
--- a/app/workers/stuck_merge_jobs_worker.rb
+++ b/app/workers/stuck_merge_jobs_worker.rb
@@ -23,7 +23,12 @@ class StuckMergeJobsWorker
merge_requests = MergeRequest.where(id: completed_ids)
merge_requests.where.not(merge_commit_sha: nil).update_all(state: :merged)
- merge_requests.where(merge_commit_sha: nil).update_all(state: :opened, merge_jid: nil)
+
+ merge_requests_to_reopen = merge_requests.where(merge_commit_sha: nil)
+
+ # Do not reopen merge requests using direct queries.
+ # We rely on state machine callbacks to update head_pipeline_id
+ merge_requests_to_reopen.each(&:unlock_mr)
Rails.logger.info("Updated state of locked merge jobs. JIDs: #{completed_jids.join(', ')}")
end
diff --git a/app/workers/update_head_pipeline_for_merge_request_worker.rb b/app/workers/update_head_pipeline_for_merge_request_worker.rb
index 0a2e9b63578..f09d89aa170 100644
--- a/app/workers/update_head_pipeline_for_merge_request_worker.rb
+++ b/app/workers/update_head_pipeline_for_merge_request_worker.rb
@@ -1,15 +1,25 @@
class UpdateHeadPipelineForMergeRequestWorker
include ApplicationWorker
-
- sidekiq_options queue: 'pipeline_default'
+ include PipelineQueue
def perform(merge_request_id)
merge_request = MergeRequest.find(merge_request_id)
pipeline = Ci::Pipeline.where(project: merge_request.source_project, ref: merge_request.source_branch).last
return unless pipeline && pipeline.latest?
- raise ArgumentError, 'merge request sha does not equal pipeline sha' if merge_request.diff_head_sha != pipeline.sha
+
+ if merge_request.diff_head_sha != pipeline.sha
+ log_error_message_for(merge_request)
+
+ return
+ end
merge_request.update_attribute(:head_pipeline_id, pipeline.id)
end
+
+ def log_error_message_for(merge_request)
+ Rails.logger.error(
+ "Outdated head pipeline for active merge request: id=#{merge_request.id}, source_branch=#{merge_request.source_branch}, diff_head_sha=#{merge_request.diff_head_sha}"
+ )
+ end
end
diff --git a/changelogs/unreleased/13695-order-contributors-in-api.yml b/changelogs/unreleased/13695-order-contributors-in-api.yml
new file mode 100644
index 00000000000..26bf8650a4a
--- /dev/null
+++ b/changelogs/unreleased/13695-order-contributors-in-api.yml
@@ -0,0 +1,5 @@
+---
+title: Adds ordering to projects contributors in API
+merge_request: 15469
+author: Jacopo Beschi @jacopo-beschi
+type: added
diff --git a/changelogs/unreleased/25317-prioritize-author-date-over-commit.yml b/changelogs/unreleased/25317-prioritize-author-date-over-commit.yml
new file mode 100644
index 00000000000..a5f6d316a7d
--- /dev/null
+++ b/changelogs/unreleased/25317-prioritize-author-date-over-commit.yml
@@ -0,0 +1,5 @@
+---
+title: Show authored date rather than committed date on the commit list
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/33926-update-issuable-icons.yml b/changelogs/unreleased/33926-update-issuable-icons.yml
new file mode 100644
index 00000000000..87076dde545
--- /dev/null
+++ b/changelogs/unreleased/33926-update-issuable-icons.yml
@@ -0,0 +1,5 @@
+---
+title: Update issuable status icons
+merge_request: 15898
+author:
+type: changed
diff --git a/changelogs/unreleased/38541-cancel-alignment.yml b/changelogs/unreleased/38541-cancel-alignment.yml
new file mode 100644
index 00000000000..c6d5136dd57
--- /dev/null
+++ b/changelogs/unreleased/38541-cancel-alignment.yml
@@ -0,0 +1,5 @@
+---
+title: fix button alignment on MWPS component
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/40285-prometheus-loading-screen-no-longer-seems-to-appear.yml b/changelogs/unreleased/40285-prometheus-loading-screen-no-longer-seems-to-appear.yml
new file mode 100644
index 00000000000..978930a5b8c
--- /dev/null
+++ b/changelogs/unreleased/40285-prometheus-loading-screen-no-longer-seems-to-appear.yml
@@ -0,0 +1,5 @@
+---
+title: Fix broken illustration images for monitoring page empty states
+merge_request: 15889
+author:
+type: fixed
diff --git a/changelogs/unreleased/40509_sorting_tags_api.yml b/changelogs/unreleased/40509_sorting_tags_api.yml
new file mode 100644
index 00000000000..38b198d0fe3
--- /dev/null
+++ b/changelogs/unreleased/40509_sorting_tags_api.yml
@@ -0,0 +1,5 @@
+---
+title: add support for sorting in tags api
+merge_request: 15772
+author: haseebeqx
+type: added
diff --git a/changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml b/changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml
new file mode 100644
index 00000000000..485133b46a7
--- /dev/null
+++ b/changelogs/unreleased/40895-fix-frequent-projects-stale-path.yml
@@ -0,0 +1,5 @@
+---
+title: Use relative URL for projects to avoid storing domains
+merge_request: 15876
+author:
+type: fixed
diff --git a/changelogs/unreleased/41016-import-gitlab-shell-projects.yml b/changelogs/unreleased/41016-import-gitlab-shell-projects.yml
new file mode 100644
index 00000000000..47a9e9c3eec
--- /dev/null
+++ b/changelogs/unreleased/41016-import-gitlab-shell-projects.yml
@@ -0,0 +1,6 @@
+---
+title: Import some code and functionality from gitlab-shell to improve subprocess
+ handling
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/add-tcp-check-rake-task.yml b/changelogs/unreleased/add-tcp-check-rake-task.yml
new file mode 100644
index 00000000000..a7c04bd0d55
--- /dev/null
+++ b/changelogs/unreleased/add-tcp-check-rake-task.yml
@@ -0,0 +1,5 @@
+---
+title: Add a gitlab:tcp_check rake task
+merge_request: 15759
+author:
+type: added
diff --git a/changelogs/unreleased/fix-create-mr-from-issue-with-template.yml b/changelogs/unreleased/fix-create-mr-from-issue-with-template.yml
new file mode 100644
index 00000000000..8668aa18669
--- /dev/null
+++ b/changelogs/unreleased/fix-create-mr-from-issue-with-template.yml
@@ -0,0 +1,5 @@
+---
+title: Execute quick actions (if present) when creating MR from issue
+merge_request: 15810
+author:
+type: fixed
diff --git a/changelogs/unreleased/issue-description-field-typo.yml b/changelogs/unreleased/issue-description-field-typo.yml
new file mode 100644
index 00000000000..9c4c179876d
--- /dev/null
+++ b/changelogs/unreleased/issue-description-field-typo.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed typo for issue description field declaration
+merge_request:
+author: Marcus Amargi
+type: fixed
diff --git a/changelogs/unreleased/lfs-badge.yml b/changelogs/unreleased/lfs-badge.yml
new file mode 100644
index 00000000000..e4ed4d6741f
--- /dev/null
+++ b/changelogs/unreleased/lfs-badge.yml
@@ -0,0 +1,5 @@
+---
+title: Added badge to tree & blob views to indicate LFS tracked files
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/tc-correct-email-in-reply-to.yml b/changelogs/unreleased/tc-correct-email-in-reply-to.yml
new file mode 100644
index 00000000000..1c8043f6a5c
--- /dev/null
+++ b/changelogs/unreleased/tc-correct-email-in-reply-to.yml
@@ -0,0 +1,5 @@
+---
+title: Make mail notifications of discussion notes In-Reply-To of each other
+merge_request: 14289
+author:
+type: changed
diff --git a/config/application.rb b/config/application.rb
index 6436f887d14..25c5a8f5a99 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -34,6 +34,10 @@ module Gitlab
config.generators.templates.push("#{config.root}/generator_templates")
+ # Rake tasks ignore the eager loading settings, so we need to set the
+ # autoload paths explicitly
+ config.autoload_paths = config.eager_load_paths.dup
+
# Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named.
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb
index bfab8c77a4b..cc9167d29b9 100644
--- a/config/initializers/flipper.rb
+++ b/config/initializers/flipper.rb
@@ -1,8 +1,22 @@
-require 'flipper/middleware/memoizer'
+require 'flipper/adapters/active_record'
+require 'flipper/adapters/active_support_cache_store'
-unless Rails.env.test?
- Rails.application.config.middleware.use Flipper::Middleware::Memoizer,
- lambda { Feature.flipper }
+Flipper.configure do |config|
+ config.default do
+ adapter = Flipper::Adapters::ActiveRecord.new(
+ feature_class: Feature::FlipperFeature, gate_class: Feature::FlipperGate)
+ cached_adapter = Flipper::Adapters::ActiveSupportCacheStore.new(
+ adapter,
+ Rails.cache,
+ expires_in: 10.seconds)
+
+ Flipper.new(cached_adapter)
+ end
+end
- Feature.register_feature_groups
+Feature.register_feature_groups
+
+unless Rails.env.test?
+ require 'flipper/middleware/memoizer'
+ Rails.application.config.middleware.use Flipper::Middleware::Memoizer
end
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index ba4481ae602..0f164e628f9 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -42,6 +42,8 @@ Sidekiq.configure_server do |config|
Gitlab::SidekiqThrottler.execute!
+ Gitlab::SidekiqVersioning.install!
+
config = Gitlab::Database.config ||
Rails.application.config.database_configuration[Rails.env]
config['pool'] = Sidekiq.options[:concurrency]
@@ -60,19 +62,3 @@ Sidekiq.configure_client do |config|
chain.add Gitlab::SidekiqStatus::ClientMiddleware
end
end
-
-# The Sidekiq client API always adds the queue to the Sidekiq queue
-# list, but mail_room and gitlab-shell do not. This is only necessary
-# for monitoring.
-begin
- queues = Gitlab::SidekiqConfig.worker_queues
-
- Sidekiq.redis do |conn|
- conn.pipelined do
- queues.each do |queue|
- conn.sadd('queues', queue)
- end
- end
- end
-rescue Redis::BaseError, SocketError, Errno::ENOENT, Errno::EADDRNOTAVAIL, Errno::EAFNOSUPPORT, Errno::ECONNRESET, Errno::ECONNREFUSED
-end
diff --git a/config/no_todos_messages.yml b/config/no_todos_messages.yml
index 264a975b614..da721a9b6e6 100644
--- a/config/no_todos_messages.yml
+++ b/config/no_todos_messages.yml
@@ -3,9 +3,9 @@
#
# If you come up with a fun one, please feel free to contribute it to GitLab!
# https://about.gitlab.com/contributing/
----
-- Good job! Looks like you don't have any todos left.
+---
+- Good job! Looks like you don't have any todos left
- Isn't an empty todo list beautiful?
- Give yourself a pat on the back!
- Nothing left to do, high five!
-- Henceforth you shall be known as "Todo Destroyer".
+- Henceforth you shall be known as "Todo Destroyer"
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index bc7c431731a..31a38f2b508 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -25,8 +25,6 @@
- [new_note, 2]
- [new_issue, 2]
- [new_merge_request, 2]
- - [build, 2]
- - [pipeline, 2]
- [pipeline_processing, 5]
- [pipeline_creation, 4]
- [pipeline_default, 3]
@@ -38,11 +36,12 @@
- [mailers, 2]
- [invalid_gpg_signature_update, 2]
- [create_gpg_signature, 2]
+ - [rebase, 2]
- [upload_checksum, 1]
- [repository_fork, 1]
- [repository_import, 1]
- [github_importer, 1]
- - [github_importer_advance_stage, 1]
+ - [github_import_advance_stage, 1]
- [project_service, 1]
- [delete_user, 1]
- [delete_merged_branches, 1]
diff --git a/db/post_migrate/20171213160445_migrate_github_importer_advance_stage_sidekiq_queue.rb b/db/post_migrate/20171213160445_migrate_github_importer_advance_stage_sidekiq_queue.rb
new file mode 100644
index 00000000000..149c28f1946
--- /dev/null
+++ b/db/post_migrate/20171213160445_migrate_github_importer_advance_stage_sidekiq_queue.rb
@@ -0,0 +1,16 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class MigrateGithubImporterAdvanceStageSidekiqQueue < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def up
+ sidekiq_queue_migrate 'github_importer_advance_stage', to: 'github_import_advance_stage'
+ end
+
+ def down
+ sidekiq_queue_migrate 'github_import_advance_stage', to: 'github_importer_advance_stage'
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f0b1da16d53..2048c50f892 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20171206221519) do
+ActiveRecord::Schema.define(version: 20171213160445) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
diff --git a/doc/administration/auth/README.md b/doc/administration/auth/README.md
index ee9b9a9466a..373d4239f71 100644
--- a/doc/administration/auth/README.md
+++ b/doc/administration/auth/README.md
@@ -14,3 +14,4 @@ providers.
- [CAS](../../integration/cas.md) Configure GitLab to sign in using CAS
- [SAML](../../integration/saml.md) Configure GitLab as a SAML 2.0 Service Provider
- [Okta](okta.md) Configure GitLab to sign in using Okta
+- [Authentiq](authentiq.md): Enable the Authentiq OmniAuth provider for passwordless authentication
diff --git a/doc/administration/high_availability/README.md b/doc/administration/high_availability/README.md
index a88e67bfeb5..ea8077f0623 100644
--- a/doc/administration/high_availability/README.md
+++ b/doc/administration/high_availability/README.md
@@ -37,6 +37,7 @@ Follow the steps below to configure an active/active setup:
1. [Configure the database](database.md)
1. [Configure Redis](redis.md)
+ 1. [Configure Redis for GitLab source installations](redis_source.md)
1. [Configure NFS](nfs.md)
1. [Configure the GitLab application servers](gitlab.md)
1. [Configure the load balancers](load_balancer.md)
diff --git a/doc/administration/index.md b/doc/administration/index.md
index e6986a2b07f..0b199eecefd 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -16,21 +16,27 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Install](../install/README.md): Requirements, directory structures, and installation methods.
- [High Availability](high_availability/README.md): Configure multiple servers for scaling or high availability.
+ - [High Availability on AWS](../university/high-availability/aws/README.md): Set up GitLab HA on Amazon AWS.
### Configuring GitLab
- [Adjust your instance's timezone](../workflow/timezone.md): Customize the default time zone of GitLab.
-- [Header logo](../customization/branded_page_and_email_header.md): Change the logo on all pages and email headers.
-- [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page.
- [System hooks](../system_hooks/system_hooks.md): Notifications when users, projects and keys are changed.
- [Security](../security/README.md): Learn what you can do to further secure your GitLab instance.
- [Usage statistics, version check, and usage ping](../user/admin_area/settings/usage_statistics.md): Enable or disable information about your instance to be sent to GitLab, Inc.
- [Polling](polling.md): Configure how often the GitLab UI polls for updates.
- [GitLab Pages configuration](pages/index.md): Enable and configure GitLab Pages.
-- [GitLab Pages configuration for installations from the source](pages/source.md): Enable and configure GitLab Pages on
+- [GitLab Pages configuration for GitLab source installations](pages/source.md): Enable and configure GitLab Pages on
[source installations](../install/installation.md#installation-from-source).
- [Environment variables](environment_variables.md): Supported environment variables that can be used to override their defaults values in order to configure GitLab.
+#### Customizing GitLab's appearance
+
+- [Header logo](../customization/branded_page_and_email_header.md): Change the logo on all pages and email headers.
+- [Branded login page](../customization/branded_login_page.md): Customize the login page with your own logo, title, and description.
+- [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page.
+- ["New Project" page](../customization/new_project_page.md): Customize the text to be displayed on the page that opens whenever your users create a new project.
+
### Maintaining GitLab
- [Raketasks](../raketasks/README.md): Perform various tasks for maintenance, backups, automatic webhooks setup, etc.
@@ -74,6 +80,7 @@ server with IMAP authentication on Ubuntu, to be used with Reply by email.
- [Issue closing pattern](issue_closing_pattern.md): Customize how to close an issue from commit messages.
- [Gitaly](gitaly/index.md): Configuring Gitaly, GitLab's Git repository storage service.
- [Default labels](../user/admin_area/labels.html): Create labels that will be automatically added to every new project.
+- [Restrict the use of public or internal projects](../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects): Restrict the use of visibility levels for users when they create a project or a snippet.
### Repository settings
@@ -99,20 +106,22 @@ server with IMAP authentication on Ubuntu, to be used with Reply by email.
## Monitoring GitLab
-- [Monitoring uptime](../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint.
- - [IP whitelist](monitoring/ip_whitelist.md): Monitor endpoints that provide health check information when probed.
-- [Monitoring GitHub imports](monitoring/github_imports.md): GitLab's GitHub Importer displays Prometheus metrics to monitor the health and progress of the importer.
+- [Monitoring GitLab](monitoring/index.md):
+ - [Monitoring uptime](../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint.
+ - [IP whitelist](monitoring/ip_whitelist.md): Monitor endpoints that provide health check information when probed.
+ - [Monitoring GitHub imports](monitoring/github_imports.md): GitLab's GitHub Importer displays Prometheus metrics to monitor the health and progress of the importer.
- [Conversational Development (ConvDev) Index](../user/admin_area/monitoring/convdev.md): Provides an overview of your entire instance's feature usage.
### Performance Monitoring
-- [GitLab Performance Monitoring](monitoring/performance/gitlab_configuration.md): Enable GitLab Performance Monitoring.
-- [GitLab performance monitoring with InfluxDB](monitoring/performance/introduction.md): Configure GitLab and InfluxDB for measuring performance metrics.
- - [InfluxDB Schema](monitoring/performance/influxdb_schema.md): Measurements stored in InfluxDB.
-- [GitLab performance monitoring with Prometheus](monitoring/prometheus/index.md): Configure GitLab and Prometheus for measuring performance metrics.
-- [GitLab performance monitoring with Grafana](monitoring/prometheus/index.md): Configure GitLab to visualize time series metrics through graphs and dashboards.
-- [Request Profiling](monitoring/performance/request_profiling.md): Get a detailed profile on slow requests.
-- [Performance Bar](monitoring/performance/performance_bar.md): Get performance information for the current page.
+- [GitLab Performance Monitoring](monitoring/performance/index.md):
+ - [Enable Performance Monitoring](monitoring/performance/gitlab_configuration.md): Enable GitLab Performance Monitoring.
+ - [GitLab performance monitoring with InfluxDB](monitoring/performance/influxdb_configuration.md): Configure GitLab and InfluxDB for measuring performance metrics.
+ - [InfluxDB Schema](monitoring/performance/influxdb_schema.md): Measurements stored in InfluxDB.
+ - [GitLab performance monitoring with Prometheus](monitoring/prometheus/index.md): Configure GitLab and Prometheus for measuring performance metrics.
+ - [GitLab performance monitoring with Grafana](monitoring/performance/grafana_configuration.md): Configure GitLab to visualize time series metrics through graphs and dashboards.
+ - [Request Profiling](monitoring/performance/request_profiling.md): Get a detailed profile on slow requests.
+ - [Performance Bar](monitoring/performance/performance_bar.md): Get performance information for the current page.
## Troubleshooting
diff --git a/doc/administration/monitoring/index.md b/doc/administration/monitoring/index.md
new file mode 100644
index 00000000000..d6333ee62b4
--- /dev/null
+++ b/doc/administration/monitoring/index.md
@@ -0,0 +1,9 @@
+# Monitoring GitLab
+
+Explore our features to monitor your GitLab instance:
+
+- [Performance monitoring](performance/index.md): GitLab Performance Monitoring makes it possible to measure a wide variety of statistics of your instance.
+- [Prometheus](prometheus/index.md): Prometheus is a powerful time-series monitoring service, providing a flexible platform for monitoring GitLab and other software products.
+- [GitHub imports](github_imports.md): Monitor the health and progress of GitLab's GitHub importer with various Prometheus metrics.
+- [Monitoring uptime](../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint.
+ - [IP whitelists](ip_whitelist.md): Configure GitLab for monitoring endpoints that provide health check information when probed.
diff --git a/doc/administration/monitoring/performance/index.md b/doc/administration/monitoring/performance/index.md
new file mode 100644
index 00000000000..f5f0363ed38
--- /dev/null
+++ b/doc/administration/monitoring/performance/index.md
@@ -0,0 +1,72 @@
+# GitLab Performance Monitoring
+
+GitLab comes with its own application performance measuring system as of GitLab
+8.4, simply called "GitLab Performance Monitoring". GitLab Performance Monitoring is available in both the
+Community and Enterprise editions.
+
+Apart from this introduction, you are advised to read through the following
+documents in order to understand and properly configure GitLab Performance Monitoring:
+
+- [GitLab Configuration](gitlab_configuration.md)
+- [InfluxDB Install/Configuration](influxdb_configuration.md)
+- [InfluxDB Schema](influxdb_schema.md)
+- [Grafana Install/Configuration](grafana_configuration.md)
+- [Performance bar](performance_bar.md)
+- [Request profiling](request_profiling.md)
+
+>**Note:**
+Omnibus GitLab 8.16 includes Prometheus as an additional tool to collect
+metrics. It will eventually replace InfluxDB when their metrics collection is
+on par. Read more in the [Prometheus documentation](../prometheus/index.md).
+
+## Introduction to GitLab Performance Monitoring
+
+GitLab Performance Monitoring makes it possible to measure a wide variety of statistics
+including (but not limited to):
+
+- The time it took to complete a transaction (a web request or Sidekiq job).
+- The time spent in running SQL queries and rendering HAML views.
+- The time spent executing (instrumented) Ruby methods.
+- Ruby object allocations, and retained objects in particular.
+- System statistics such as the process' memory usage and open file descriptors.
+- Ruby garbage collection statistics.
+
+Metrics data is written to [InfluxDB][influxdb] over [UDP][influxdb-udp]. Stored
+data can be visualized using [Grafana][grafana] or any other application that
+supports reading data from InfluxDB. Alternatively data can be queried using the
+InfluxDB CLI.
+
+## Metric Types
+
+Two types of metrics are collected:
+
+1. Transaction specific metrics.
+1. Sampled metrics, collected at a certain interval in a separate thread.
+
+### Transaction Metrics
+
+Transaction metrics are metrics that can be associated with a single
+transaction. This includes statistics such as the transaction duration, timings
+of any executed SQL queries, time spent rendering HAML views, etc. These metrics
+are collected for every Rack request and Sidekiq job processed.
+
+### Sampled Metrics
+
+Sampled metrics are metrics that can't be associated with a single transaction.
+Examples include garbage collection statistics and retained Ruby objects. These
+metrics are collected at a regular interval. This interval is made up out of two
+parts:
+
+1. A user defined interval.
+1. A randomly generated offset added on top of the interval, the same offset
+ can't be used twice in a row.
+
+The actual interval can be anywhere between a half of the defined interval and a
+half above the interval. For example, for a user defined interval of 15 seconds
+the actual interval can be anywhere between 7.5 and 22.5. The interval is
+re-generated for every sampling run instead of being generated once and re-used
+for the duration of the process' lifetime.
+
+[influxdb]: https://influxdata.com/time-series-platform/influxdb/
+[influxdb-udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/
+[grafana]: http://grafana.org/
diff --git a/doc/administration/monitoring/performance/introduction.md b/doc/administration/monitoring/performance/introduction.md
index 17c2b4b70d3..37a5388d2fc 100644
--- a/doc/administration/monitoring/performance/introduction.md
+++ b/doc/administration/monitoring/performance/introduction.md
@@ -1,70 +1 @@
-# GitLab Performance Monitoring
-
-GitLab comes with its own application performance measuring system as of GitLab
-8.4, simply called "GitLab Performance Monitoring". GitLab Performance Monitoring is available in both the
-Community and Enterprise editions.
-
-Apart from this introduction, you are advised to read through the following
-documents in order to understand and properly configure GitLab Performance Monitoring:
-
-- [GitLab Configuration](gitlab_configuration.md)
-- [InfluxDB Install/Configuration](influxdb_configuration.md)
-- [InfluxDB Schema](influxdb_schema.md)
-- [Grafana Install/Configuration](grafana_configuration.md)
-
->**Note:**
-Omnibus GitLab 8.16 includes Prometheus as an additional tool to collect
-metrics. It will eventually replace InfluxDB when their metrics collection is
-on par. Read more in the [Prometheus documentation](../prometheus/index.md).
-
-## Introduction to GitLab Performance Monitoring
-
-GitLab Performance Monitoring makes it possible to measure a wide variety of statistics
-including (but not limited to):
-
-- The time it took to complete a transaction (a web request or Sidekiq job).
-- The time spent in running SQL queries and rendering HAML views.
-- The time spent executing (instrumented) Ruby methods.
-- Ruby object allocations, and retained objects in particular.
-- System statistics such as the process' memory usage and open file descriptors.
-- Ruby garbage collection statistics.
-
-Metrics data is written to [InfluxDB][influxdb] over [UDP][influxdb-udp]. Stored
-data can be visualized using [Grafana][grafana] or any other application that
-supports reading data from InfluxDB. Alternatively data can be queried using the
-InfluxDB CLI.
-
-## Metric Types
-
-Two types of metrics are collected:
-
-1. Transaction specific metrics.
-1. Sampled metrics, collected at a certain interval in a separate thread.
-
-### Transaction Metrics
-
-Transaction metrics are metrics that can be associated with a single
-transaction. This includes statistics such as the transaction duration, timings
-of any executed SQL queries, time spent rendering HAML views, etc. These metrics
-are collected for every Rack request and Sidekiq job processed.
-
-### Sampled Metrics
-
-Sampled metrics are metrics that can't be associated with a single transaction.
-Examples include garbage collection statistics and retained Ruby objects. These
-metrics are collected at a regular interval. This interval is made up out of two
-parts:
-
-1. A user defined interval.
-1. A randomly generated offset added on top of the interval, the same offset
- can't be used twice in a row.
-
-The actual interval can be anywhere between a half of the defined interval and a
-half above the interval. For example, for a user defined interval of 15 seconds
-the actual interval can be anywhere between 7.5 and 22.5. The interval is
-re-generated for every sampling run instead of being generated once and re-used
-for the duration of the process' lifetime.
-
-[influxdb]: https://influxdata.com/time-series-platform/influxdb/
-[influxdb-udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/
-[grafana]: http://grafana.org/
+This document was moved to [another location](index.md).
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index 136192191f9..ecf92c379fd 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -221,3 +221,22 @@ sudo gitlab-rake gitlab:shell:create_hooks
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:shell:create_hooks RAILS_ENV=production
```
+
+## Check TCP connectivity to a remote site
+
+Sometimes you need to know if your GitLab installation can connect to a TCP
+service on another machine - perhaps a PostgreSQL or HTTPS server. A rake task
+is included to help you with this:
+
+**Omnibus Installation**
+
+```
+sudo gitlab-rake gitlab:tcp_check[example.com,80]
+```
+
+**Source Installation**
+
+```
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:tcp_check[example.com,80] RAILS_ENV=production
+```
diff --git a/doc/administration/reply_by_email.md b/doc/administration/reply_by_email.md
index 1304476e678..3a2cced37bf 100644
--- a/doc/administration/reply_by_email.md
+++ b/doc/administration/reply_by_email.md
@@ -89,9 +89,11 @@ email address in order to sign up.
If you also host a public-facing GitLab instance at `hooli.com` and set your
incoming email domain to `hooli.com`, an attacker could abuse the "Create new
-issue by email" feature by using a project's unique address as the email when
-signing up for Slack, which would send a confirmation email, which would create
-a new issue on the project owned by the attacker, allowing them to click the
+issue by email" or
+"[Create new merge request by email](../user/project/merge_requests/index.md#create-new-merge-requests-by-email)"
+features by using a project's unique address as the email when signing up for
+Slack, which would send a confirmation email, which would create a new issue or
+merge request on the project owned by the attacker, allowing them to click the
confirmation link and validate their account on your company's private Slack
instance.
diff --git a/doc/api/issues.md b/doc/api/issues.md
index ec8ff3cd3f3..d2fefbe68aa 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -514,9 +514,9 @@ PUT /projects/:id/issues/:issue_iid
| `title` | string | no | The title of an issue |
| `description` | string | no | The description of an issue |
| `confidential` | boolean | no | Updates an issue to be confidential |
-| `assignee_ids` | Array[integer] | no | The ID of the users to assign the issue to |
-| `milestone_id` | integer | no | The ID of a milestone to assign the issue to |
-| `labels` | string | no | Comma-separated label names for an issue |
+| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
+| `milestone_id` | integer | no | The ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
+| `labels` | string | no | Comma-separated label names for an issue. Set to an empty string to unassign all labels. |
| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it |
| `updated_at` | string | no | Date time string, ISO 8601 formatted, e.g. `2016-03-11T03:45:40Z` (requires admin or project owner rights) |
| `due_date` | string | no | Date time string in the format YEAR-MONTH-DAY, e.g. `2016-03-11` |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index b2e4b6d0955..880b0ed2c65 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -524,15 +524,15 @@ PUT /projects/:id/merge_requests/:merge_request_iid
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The ID of a merge request |
| `target_branch` | string | no | The target branch |
| `title` | string | no | Title of MR |
-| `assignee_id` | integer | no | Assignee user ID |
+| `assignee_id` | integer | no | The ID of the user to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. |
+| `milestone_id` | integer | no | The ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.|
+| `labels` | string | no | Comma-separated label names for an merge request. Set to an empty string to unassign all labels. |
| `description` | string | no | Description of MR |
| `state_event` | string | no | New state (close/reopen) |
-| `labels` | string | no | Labels for MR as a comma-separated list |
-| `milestone_id` | integer | no | The ID of a milestone |
| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging |
| `discussion_locked` | boolean | no | Flag indicating if the merge request's discussion is locked. If the discussion is locked only project members can add, edit or resolve comments. |
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 594babc74be..03b32577872 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -182,6 +182,8 @@ GET /projects/:id/repository/contributors
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
+- `order_by` (optional) - Return contributors ordered by `name`, `email`, or `commits` fields. If not given contributors are ordered by commit date.
+- `sort` (optional) - Return contributors sorted in `asc` or `desc` order. Default is `asc`
Response:
diff --git a/doc/api/tags.md b/doc/api/tags.md
index bebe6536b6e..fa25dc76452 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -12,7 +12,11 @@ GET /projects/:id/repository/tags
Parameters:
-- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string| yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user|
+| `order_by` | string | no | Return tags ordered by `name` or `updated` fields. Default is `updated` |
+| `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc` |
```json
[
diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md
index e5a2bbd1773..df0e1521150 100644
--- a/doc/ci/ssh_keys/README.md
+++ b/doc/ci/ssh_keys/README.md
@@ -1,84 +1,106 @@
-# Using SSH keys
+---
+last_updated: 2017-12-13
+---
+
+# Using SSH keys with GitLab CI/CD
GitLab currently doesn't have built-in support for managing SSH keys in a build
-environment.
+environment (where the GitLab Runner runs).
The SSH keys can be useful when:
1. You want to checkout internal submodules
-2. You want to download private packages using your package manager (eg. bundler)
-3. You want to deploy your application to eg. Heroku or your own server
-4. You want to execute SSH commands from the build server to the remote server
-5. You want to rsync files from your build server to the remote server
+1. You want to download private packages using your package manager (e.g., Bundler)
+1. You want to deploy your application to your own server, or, for example, Heroku
+1. You want to execute SSH commands from the build environment to a remote server
+1. You want to rsync files from the build environment to a remote server
If anything of the above rings a bell, then you most likely need an SSH key.
-## Inject keys in your build server
-
The most widely supported method is to inject an SSH key into your build
-environment by extending your `.gitlab-ci.yml`.
-
-This is the universal solution which works with any type of executor
-(docker, shell, etc.).
-
-### How it works
-
-1. Create a new SSH key pair with [ssh-keygen][]
-2. Add the private key as a **Secret Variable** to the project
-3. Run the [ssh-agent][] during job to load the private key.
+environment by extending your `.gitlab-ci.yml`, and it's a solution which works
+with any type of [executor](https://docs.gitlab.com/runner/executors/)
+(Docker, shell, etc.).
+
+## How it works
+
+1. Create a new SSH key pair locally with [ssh-keygen](http://linux.die.net/man/1/ssh-keygen)
+1. Add the private key as a [secret variable](../variables/README.md) to
+ your project
+1. Run the [ssh-agent](http://linux.die.net/man/1/ssh-agent) during job to load
+ the private key.
+1. Copy the public key to the servers you want to have access to (usually in
+ `~/.ssh/authorized_keys`) or add it as a [deploy key](../../ssh/README.md#deploy-keys)
+ if you are accessing a private GitLab repository.
+
+NOTE: **Note:**
+The private key will not be displayed in the job trace, unless you enable
+[debug tracing](../variables/README.md#debug-tracing). You might also want to
+check the [visibility of your pipelines](../../user/project/pipelines/settings.md#visibility-of-pipelines).
## SSH keys when using the Docker executor
-You will first need to create an SSH key pair. For more information, follow the
-instructions to [generate an SSH key](../../ssh/README.md). Do not add a
-passphrase to the SSH key, or the `before_script` will prompt for it.
-
-Then, create a new **Secret Variable** in your project settings on GitLab
-following **Settings > CI/CD** and look for the "Secret Variables" section.
-As **Key** add the name `SSH_PRIVATE_KEY` and in the **Value** field paste the
-content of your _private_ key that you created earlier.
-
-It is also good practice to check the server's own public key to make sure you
-are not being targeted by a man-in-the-middle attack. To do this, add another
-variable named `SSH_SERVER_HOSTKEYS`. To find out the hostkeys of your server, run
-the `ssh-keyscan YOUR_SERVER` command from a trusted network (ideally, from the
-server itself), and paste its output into the `SSH_SERVER_HOSTKEYS` variable. If
-you need to connect to multiple servers, concatenate all the server public keys
-that you collected into the **Value** of the variable. There must be one key per
-line.
-
-Next you need to modify your `.gitlab-ci.yml` with a `before_script` action.
-Add it to the top:
-
-```
-before_script:
- # Install ssh-agent if not already installed, it is required by Docker.
- # (change apt-get to yum if you use a CentOS-based image)
- - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
-
- # Run ssh-agent (inside the build environment)
- - eval $(ssh-agent -s)
-
- # Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
- - ssh-add <(echo "$SSH_PRIVATE_KEY")
-
- # For Docker builds disable host key checking. Be aware that by adding that
- # you are suspectible to man-in-the-middle attacks.
- # WARNING: Use this only with the Docker executor, if you use it with shell
- # you will overwrite your user's SSH config.
- - mkdir -p ~/.ssh
- - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- # In order to properly check the server's host key, assuming you created the
- # SSH_SERVER_HOSTKEYS variable previously, uncomment the following two lines
- # instead.
- # - mkdir -p ~/.ssh
- # - '[[ -f /.dockerenv ]] && echo "$SSH_SERVER_HOSTKEYS" > ~/.ssh/known_hosts'
-```
-
-As a final step, add the _public_ key from the one you created earlier to the
-services that you want to have an access to from within the build environment.
-If you are accessing a private GitLab repository you need to add it as a
-[deploy key](../../ssh/README.md#deploy-keys).
+When your CI/CD jobs run inside Docker containers (meaning the environment is
+contained) and you want to deploy your code in a private server, you need a way
+to access it. This is where an SSH key pair comes in handy.
+
+1. You will first need to create an SSH key pair. For more information, follow
+ the instructions to [generate an SSH key](../../ssh/README.md#generating-a-new-ssh-key-pair).
+ **Do not** add a passphrase to the SSH key, or the `before_script` will\
+ prompt for it.
+
+1. Create a new [secret variable](../variables/README.md#secret-variables).
+ As **Key** enter the name `SSH_PRIVATE_KEY` and in the **Value** field paste
+ the content of your _private_ key that you created earlier.
+
+1. Modify your `.gitlab-ci.yml` with a `before_script` action. In the following
+ example, a Debian based image is assumed. Edit to your needs:
+
+ ```yaml
+ before_script:
+ ##
+ ## Install ssh-agent if not already installed, it is required by Docker.
+ ## (change apt-get to yum if you use an RPM-based image)
+ ##
+ - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
+
+ ##
+ ## Run ssh-agent (inside the build environment)
+ ##
+ - eval $(ssh-agent -s)
+
+ ##
+ ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
+ ## We're using tr to fix line endings which makes ed25519 keys work
+ ## without extra base64 encoding.
+ ## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556
+ ##
+ - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
+
+ ##
+ ## Create the SSH directory and give it the right permissions
+ ##
+ - mkdir -p ~/.ssh
+ - chmod 700 ~/.ssh
+
+ ##
+ ## Optionally, if you will be using any Git commands, set the user name and
+ ## and email.
+ ##
+ #- git config --global user.email "user@example.com"
+ #- git config --global user.name "User name"
+ ```
+
+ NOTE: **Note:**
+ The [`before_script`](../yaml/README.md#before-script) can be set globally
+ or per-job.
+
+1. Make sure the private server's [SSH host keys are verified](#verifying-the-ssh-host-keys).
+
+1. As a final step, add the _public_ key from the one you created in the first
+ step to the services that you want to have an access to from within the build
+ environment. If you are accessing a private GitLab repository you need to add
+ it as a [deploy key](../../ssh/README.md#deploy-keys).
That's it! You can now have access to private servers or repositories in your
build environment.
@@ -91,24 +113,93 @@ SSH key.
You can generate the SSH key from the machine that GitLab Runner is installed
on, and use that key for all projects that are run on this machine.
-First, you need to login to the server that runs your jobs.
+1. First, you need to login to the server that runs your jobs.
+
+1. Then from the terminal login as the `gitlab-runner` user:
-Then from the terminal login as the `gitlab-runner` user and generate the SSH
-key pair as described in the [SSH keys documentation](../../ssh/README.md).
+ ```
+ sudo su - gitlab-runner
+ ```
-As a final step, add the _public_ key from the one you created earlier to the
-services that you want to have an access to from within the build environment.
-If you are accessing a private GitLab repository you need to add it as a
-[deploy key](../../ssh/README.md#deploy-keys).
+1. Generate the SSH key pair as described in the instructions to
+ [generate an SSH key](../../ssh/README.md#generating-a-new-ssh-key-pair).
+ **Do not** add a passphrase to the SSH key, or the `before_script` will
+ prompt for it.
+
+1. As a final step, add the _public_ key from the one you created earlier to the
+ services that you want to have an access to from within the build environment.
+ If you are accessing a private GitLab repository you need to add it as a
+ [deploy key](../../ssh/README.md#deploy-keys).
Once done, try to login to the remote server in order to accept the fingerprint:
```bash
-ssh <address-of-my-server>
+ssh example.com
+```
+
+For accessing repositories on GitLab.com, you would use `git@gitlab.com`.
+
+## Verifying the SSH host keys
+
+It is a good practice to check the private server's own public key to make sure
+you are not being targeted by a man-in-the-middle attack. In case anything
+suspicious happens, you will notice it since the job would fail (the SSH
+connection would fail if the public keys would not match).
+
+To find out the host keys of your server, run the `ssh-keyscan` command from a
+trusted network (ideally, from the private server itself):
+
+```sh
+## Use the domain name
+ssh-keyscan example.com
+
+## Or use an IP
+ssh-keyscan 1.2.3.4
```
-For accessing repositories on GitLab.com, the `<address-of-my-server>` would be
-`git@gitlab.com`.
+Create a new [secret variable](../variables/README.md#secret-variables) with
+`SSH_KNOWN_HOSTS` as "Key", and as a "Value" add the output of `ssh-keyscan`.
+
+NOTE: **Note:**
+If you need to connect to multiple servers, all the server host keys
+need to be collected in the **Value** of the variable, one key per line.
+
+TIP: **Tip:**
+By using a secret variable instead of `ssh-keyscan` directly inside
+`.gitlab-ci.yml`, it has the benefit that you don't have to change `.gitlab-ci.yml`
+if the host domain name changes for some reason. Also, the values are predefined
+by you, meaning that if the host keys suddenly change, the CI/CD job will fail,
+and you'll know there's something wrong with the server or the network.
+
+Now that the `SSH_KNOWN_HOSTS` variable is created, in addition to the
+[content of `.gitlab-ci.yml`](#ssh-keys-when-using-the-docker-executor)
+above, here's what more you need to add:
+
+ ```yaml
+before_script:
+ ##
+ ## Assuming you created the SSH_KNOWN_HOSTS variable, uncomment the
+ ## following two lines.
+ ##
+ - echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts'
+ - chmod 644 ~/.ssh/known_hosts
+
+ ##
+ ## Alternatively, use ssh-keyscan to scan the keys of your private server.
+ ## Replace example.com with your private server's domain name. Repeat that
+ ## command if you have more than one server to connect to.
+ ##
+ #- ssh-keyscan example.com >> ~/.ssh/known_hosts
+ #- chmod 644 ~/.ssh/known_hosts
+
+ ##
+ ## You can optionally disable host key checking. Be aware that by adding that
+ ## you are suspectible to man-in-the-middle attacks.
+ ## WARNING: Use this only with the Docker executor, if you use it with shell
+ ## you will overwrite your user's SSH config.
+ ##
+ #- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
+```
## Example project
@@ -119,6 +210,4 @@ that runs on [GitLab.com](https://gitlab.com) using our publicly available
Want to hack on it? Simply fork it, commit and push your changes. Within a few
moments the changes will be picked by a public runner and the job will begin.
-[ssh-keygen]: http://linux.die.net/man/1/ssh-keygen
-[ssh-agent]: http://linux.die.net/man/1/ssh-agent
[ssh-example-repo]: https://gitlab.com/gitlab-examples/ssh-private-key/
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index a9e6bda9916..b9d4a2098ed 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -213,14 +213,15 @@ An example project service that defines deployment variables is
## Debug tracing
> Introduced in GitLab Runner 1.7.
->
-> **WARNING:** Enabling debug tracing can have severe security implications. The
- output **will** contain the content of all your secret variables and any other
- secrets! The output **will** be uploaded to the GitLab server and made visible
- in job traces!
+
+CAUTION: **Warning:**
+Enabling debug tracing can have severe security implications. The
+output **will** contain the content of all your secret variables and any other
+secrets! The output **will** be uploaded to the GitLab server and made visible
+in job traces!
By default, GitLab Runner hides most of the details of what it is doing when
-processing a job. This behaviour keeps job traces short, and prevents secrets
+processing a job. This behavior keeps job traces short, and prevents secrets
from being leaked into the trace unless your script writes them to the screen.
If a job isn't working as expected, this can make the problem difficult to
diff --git a/doc/customization/issue_closing.md b/doc/customization/issue_closing.md
index 31164ccd465..d14ba6ad522 100644
--- a/doc/customization/issue_closing.md
+++ b/doc/customization/issue_closing.md
@@ -1,3 +1,7 @@
+---
+comments: false
+---
+
This document was split into:
- [administration/issue_closing_pattern.md](../administration/issue_closing_pattern.md).
diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md
index a0cb234bea0..0aef0bf5abb 100644
--- a/doc/customization/welcome_message.md
+++ b/doc/customization/welcome_message.md
@@ -8,5 +8,5 @@ It is possible to add a markdown-formatted welcome message to your GitLab
sign-in page. Users of GitLab Enterprise Edition should use the [branded login
page feature](branded_login_page.md) instead.
-The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI.
+The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI.
Admin area > Settings
diff --git a/doc/development/README.md b/doc/development/README.md
index 7e4c767692a..c9ffa912d51 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -81,10 +81,9 @@ comments: false
## Documentation guides
-- [Documentation styleguide](doc_styleguide.md): Use this styleguide if you are
- contributing to the documentation.
- [Writing documentation](writing_documentation.md)
- - [Distinction between general documentation and technical articles](writing_documentation.md#distinction-between-general-documentation-and-technical-articles)
+- [Documentation styleguide](doc_styleguide.md)
+- [Markdown](../user/markdown.md)
## Internationalization (i18n) guides
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 04419650b12..e7c5a6ca07a 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -37,7 +37,7 @@ graphs/dashboards.
GitLab provides built-in tools to aid the process of improving performance:
* [Sherlock](profiling.md#sherlock)
-* [GitLab Performance Monitoring](../administration/monitoring/performance/introduction.md)
+* [GitLab Performance Monitoring](../administration/monitoring/performance/index.md)
* [Request Profiling](../administration/monitoring/performance/request_profiling.md)
* [QueryRecoder](query_recorder.md) for preventing `N+1` regressions
diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md
index 085fb8f902c..59ebf41e09f 100644
--- a/doc/development/sidekiq_style_guide.md
+++ b/doc/development/sidekiq_style_guide.md
@@ -9,25 +9,54 @@ All workers should include `ApplicationWorker` instead of `Sidekiq::Worker`,
which adds some convenience methods and automatically sets the queue based on
the worker's name.
-## Default Queue
+## Dedicated Queues
-Use of the "default" queue is not allowed. Every worker should use a queue that
-matches the worker's purpose the closest. For example, workers that are to be
-executed periodically should use the "cronjob" queue.
+All workers should use their own queue, which is automatically set based on the
+worker class name. For a worker named `ProcessSomethingWorker`, the queue name
+would be `process_something`. If you're not sure what queue a worker uses,
+you can find it using `SomeWorker.queue`. There is almost never a reason to
+manually override the queue name using `sidekiq_options queue: :some_queue`.
-A list of all available queues can be found in `config/sidekiq_queues.yml`.
+## Queue Namespaces
-## Dedicated Queues
+While different workers cannot share a queue, they can share a queue namespace.
-Most workers should use their own queue, which is automatically set based on the
-worker class name. For a worker named `ProcessSomethingWorker`, the queue name
-would be `process_something`. If you're not sure what a worker's queue name is,
-you can find it using `SomeWorker.queue`.
+Defining a queue namespace for a worker makes it possible to start a Sidekiq
+process that automatically handles jobs for all workers in that namespace,
+without needing to explicitly list all their queue names. If, for example, all
+workers that are managed by sidekiq-cron use the `cronjob` queue namespace, we
+can spin up a Sidekiq process specifically for these kinds of scheduled jobs.
+If a new worker using the `cronjob` namespace is added later on, the Sidekiq
+process will automatically pick up jobs for that worker too (after having been
+restarted), without the need to change any configuration.
+
+A queue namespace can be set using the `queue_namespace` DSL class method:
+
+```ruby
+class SomeScheduledTaskWorker
+ include ApplicationWorker
+
+ queue_namespace :cronjob
+
+ # ...
+end
+```
+
+Behind the scenes, this will set `SomeScheduledTaskWorker.queue` to
+`cronjob:some_scheduled_task`. Commonly used namespaces will have their own
+concern module that can easily be included into the worker class, and that may
+set other Sidekiq options besides the queue namespace. `CronjobQueue`, for
+example, sets the namespace, but also disables retries.
+
+`bundle exec sidekiq` is namespace-aware, and will automatically listen on all
+queues in a namespace (technically: all queues prefixed with the namespace name)
+when a namespace is provided instead of a simple queue name in the `--queue`
+(`-q`) option, or in the `:queues:` section in `config/sidekiq_queues.yml`.
-In some cases multiple workers do use the same queue. For example, the various
-workers for updating CI pipelines all use the `pipeline` queue. Adding workers
-to existing queues should be done with care, as adding more workers can lead to
-slow jobs blocking work (even for different jobs) on the shared queue.
+Note that adding a worker to an existing namespace should be done with care, as
+the extra jobs will take resources away from jobs from workers that were already
+there, if the resources available to the Sidekiq process handling the namespace
+are not adjusted appropriately.
## Tests
@@ -36,7 +65,7 @@ tests should be placed in `spec/workers`.
## Removing or renaming queues
-Try to avoid renaming or removing queues in minor and patch releases.
+Try to avoid renaming or removing workers and their queues in minor and patch releases.
During online update instance can have pending jobs and removing the queue can
lead to those jobs being stuck forever. If you can't write migration for those
Sidekiq jobs, please consider doing rename or remove queue in major release only.
diff --git a/doc/development/ux_guide/components.md b/doc/development/ux_guide/components.md
index 16a811dbc74..d396964e7c1 100644
--- a/doc/development/ux_guide/components.md
+++ b/doc/development/ux_guide/components.md
@@ -10,7 +10,7 @@
* [Tables](#tables)
* [Blocks](#blocks)
* [Panels](#panels)
-* [Dialog modals](#dialog-modals)
+* [Modals](#modals)
* [Alerts](#alerts)
* [Forms](#forms)
* [Search box](#search-box)
@@ -255,18 +255,18 @@ Skeleton loading can replace any existing UI elements for the period in which th
---
-## Dialog modals
+## Modals
-Dialog modals are only used for having a conversation and confirmation with the user. The user is not able to access the features on the main page until closing the modal.
+Modals are only used for having a conversation and confirmation with the user. The user is not able to access the features on the main page until closing the modal.
### Usage
-* When the action is irreversible, dialog modals provide the details and confirm with the user before they take an advanced action.
-* When the action will affect privacy or authorization, dialog modals provide advanced information and confirm with the user.
+* When the action is irreversible, modals provide the details and confirm with the user before they take an advanced action.
+* When the action will affect privacy or authorization, modals provide advanced information and confirm with the user.
### Style
-* Dialog modals contain the header, body, and actions.
+* Modals contain the header, body, and actions.
* **Header(1):** The header title is a question instead of a descriptive phrase.
* **Body(2):** The content in body should never be ambiguous and unclear. It provides specific information.
* **Actions(3):** Contains a affirmative action, a dismissive action, and an extra action. The order of actions from left to right: Dismissive action → Extra action → Affirmative action
@@ -277,13 +277,13 @@ Dialog modals are only used for having a conversation and confirmation with the
### Placement
-* Dialog modals should always be the center of the screen horizontally and be positioned **72px** from the top.
+* Modals should always be the center of the screen horizontally and be positioned **72px** from the top.
-| Dialog with 2 actions | Dialog with 3 actions | Special confirmation |
+| Modal with 2 actions | Modal with 3 actions | Special confirmation |
| --------------------- | --------------------- | -------------------- |
| ![two-actions](img/modals-general-confimation-dialog.png) | ![three-actions](img/modals-three-buttons.png) | ![spcial-confirmation](img/modals-special-confimation-dialog.png) |
-> TODO: Special case for dialog modal.
+> TODO: Special case for modal.
---
diff --git a/doc/development/ux_guide/copy.md b/doc/development/ux_guide/copy.md
index 12e8d0a31bb..af842da7f62 100644
--- a/doc/development/ux_guide/copy.md
+++ b/doc/development/ux_guide/copy.md
@@ -46,11 +46,11 @@ Avoid using periods in solitary sentences in these elements:
* Labels
* Hover text
* Bulleted lists
-* Dialog body text
+* Modal body text
Periods should be used for:
-* Lists or dialogs with multiple sentences
+* Lists or modals with multiple sentences
* Any sentence followed by a link
| :white_check_mark: **Do** place periods after sentences followed by a link | :no_entry_sign: **Don’t** place periods after a link if it‘s not followed by a sentence |
@@ -80,7 +80,7 @@ Omit punctuation after phrases and labels to create a cleaner and more readable
| Punctuation mark | Copy and paste | HTML entity | Unicode | Mac shortcut | Windows shortcut | Description |
|---|---|---|---|---|---|---|
-| Period | **.** | | | | | Omit for single sentences in affordances like labels, hover text, bulleted lists, and dialog body text.<br><br>Use in lists or dialogs with multiple sentences, and any sentence followed by a link or inline code.<br><br>Place inside quotation marks unless you’re telling the reader what to enter and it’s ambiguous whether to include the period. |
+| Period | **.** | | | | | Omit for single sentences in affordances like labels, hover text, bulleted lists, and modal body text.<br><br>Use in lists or modals with multiple sentences, and any sentence followed by a link or inline code.<br><br>Place inside quotation marks unless you’re telling the reader what to enter and it’s ambiguous whether to include the period. |
| Comma | **,** | | | | | Place inside quotation marks.<br><br>Use a [serial comma][serial comma] in lists of three or more terms. |
| Exclamation point | **!** | | | | | Avoid exclamation points as they tend to come across as shouting. Some exceptions include greetings or congratulatory messages. |
| Colon | **:** | `&#58;` | `\u003A` | | | Omit from labels, for example, in the labels for fields in a form. |
@@ -88,7 +88,7 @@ Omit punctuation after phrases and labels to create a cleaner and more readable
| Quotation marks | **“**<br><br>**”**<br><br>**‘**<br><br>**’** | `&ldquo;`<br><br>`&rdquo;`<br><br>`&lsquo;`<br><br>`&rsquo;` | `\u201C`<br><br>`\u201D`<br><br>`\u2018`<br><br>`\u2019` | <kbd>⌥ Option</kbd>+<kbd>[</kbd><br><br><kbd>⌥ Option</kbd>+<kbd>⇧ Shift</kbd>+<kbd>[</kbd><br><br><kbd>⌥ Option</kbd>+<kbd>]</kbd><br><br><kbd>⌥ Option</kbd>+<kbd>⇧ Shift</kbd>+<kbd>]</kbd> | <kbd>Alt</kbd>+<kbd>0 1 4 7</kbd><br><br><kbd>Alt</kbd>+<kbd>0 1 4 8</kbd><br><br><kbd>Alt</kbd>+<kbd>0 1 4 5</kbd><br><br><kbd>Alt</kbd>+<kbd>0 1 4 6</kbd> | Use proper quotation marks (also known as smart quotes, curly quotes, or typographer’s quotes) for quotes. Single quotation marks are used for quotes inside of quotes.<br><br>The right single quotation mark symbol is also used for apostrophes.<br><br>Don’t use primes, straight quotes, or free-standing accents for quotation marks. |
| Primes | **′**<br><br>**″** | `&prime;`<br><br>`&Prime;` | `\u2032`<br><br>`\u2033` | | <kbd>Alt</kbd>+<kbd>8 2 4 2</kbd><br><br><kbd>Alt</kbd>+<kbd>8 2 4 3</kbd> | Use prime (′) only in abbreviations for feet, arcminutes, and minutes: 3° 15′<br><br>Use double-prime (″) only in abbreviations for inches, arcseconds, and seconds: 3° 15′ 35″<br><br>Don’t use quotation marks, straight quotes, or free-standing accents for primes. |
| Straight quotes and accents | **"**<br><br>**'**<br><br>**`**<br><br>**´** | `&quot;`<br><br>`&#39;`<br><br>`&#96;`<br><br>`&acute;` | `\u0022`<br><br>`\u0027`<br><br>`\u0060`<br><br>`\u00B4` | | | Don’t use straight quotes or free-standing accents for primes or quotation marks.<br><br>Proper typography never uses straight quotes. They are left over from the age of typewriters and their only modern use is for code. |
-| Ellipsis | **…** | `&hellip;` | | <kbd>⌥ Option</kbd>+<kbd>;</kbd> | <kbd>Alt</kbd>+<kbd>0 1 3 3</kbd> | Use to indicate an action in progress (“Downloading…”) or incomplete or truncated text. No space before the ellipsis.<br><br>Omit from menu items or buttons that open a dialog or start some other process. |
+| Ellipsis | **…** | `&hellip;` | | <kbd>⌥ Option</kbd>+<kbd>;</kbd> | <kbd>Alt</kbd>+<kbd>0 1 3 3</kbd> | Use to indicate an action in progress (“Downloading…”) or incomplete or truncated text. No space before the ellipsis.<br><br>Omit from menu items or buttons that open a modal or start some other process. |
| Chevrons | **«**<br><br>**»**<br><br>**‹**<br><br>**›**<br><br>**<**<br><br>**>** | `&#171;`<br><br>`&#187;`<br><br>`&#8249;`<br><br>`&#8250;`<br><br>`&lt;`<br><br>`&gt;` | `\u00AB`<br><br>`\u00BB`<br><br>`\u2039`<br><br>`\u203A`<br><br>`\u003C`<br><br>`\u003E`<br><br> | | | Omit from links or buttons that open another page or move to the next or previous step in a process. Also known as angle brackets, angular quote brackets, or guillemets. |
| Em dash | **—** | `&mdash;` | `\u2014` | <kbd>⌥ Option</kbd>+<kbd>⇧ Shift</kbd>+<kbd>-</kbd> | <kbd>Alt</kbd>+<kbd>0 1 5 1</kbd> | Avoid using dashes to separate text. If you must use dashes for this purpose — like this — use an em dash surrounded by spaces. |
| En dash | **–** | `&ndash;` | `\u2013` | <kbd>⌥ Option</kbd>+<kbd>-</kbd> | <kbd>Alt</kbd>+<kbd>0 1 5 0</kbd> | Use an en dash without spaces instead of a hyphen to indicate a range of values, such as numbers, times, and dates: “3–5 kg”, “8:00 AM–12:30 PM”, “10–17 Jan” |
@@ -175,7 +175,7 @@ A **comment** is a written piece of text that users of GitLab can create. Commen
#### Discussion
A **discussion** is a group of 1 or more comments. A discussion can include subdiscussions. Some discussions have the special capability of being able to be **resolved**. Both the comments in the discussion and the discussion itself can be resolved.
-## Confirmation dialogs
+## Modals
- Destruction buttons should be clear and always say what they are destroying.
E.g., `Delete page` instead of just `Delete`.
@@ -184,6 +184,8 @@ A **discussion** is a group of 1 or more comments. A discussion can include subd
- Avoid the word `cancel` or `canceled` in the descriptive copy. It can be
confusing when you then see the `Cancel` button.
+see also: guidelines for [modal components](components.md#modals)
+
---
Portions of this page are modifications based on work created and shared by the [Android Open Source Project][android project] and used according to terms described in the [Creative Commons 2.5 Attribution License][creative commons].
diff --git a/doc/development/writing_documentation.md b/doc/development/writing_documentation.md
index 48e04a40050..43a79ffcaa5 100644
--- a/doc/development/writing_documentation.md
+++ b/doc/development/writing_documentation.md
@@ -1,75 +1,14 @@
# Writing documentation
- **General Documentation**: written by the [developers responsible by creating features](#contributing-to-docs). Should be submitted in the same merge request containing code. Feature proposals (by GitLab contributors) should also be accompanied by its respective documentation. They can be later improved by PMs and Technical Writers.
- - **Technical Articles**: written by any [GitLab Team](https://about.gitlab.com/team/) member, GitLab contributors, or [Community Writers](https://about.gitlab.com/handbook/product/technical-writing/community-writers/).
+ - **[Technical Articles](#technical-articles)**: written by any [GitLab Team](https://about.gitlab.com/team/) member, GitLab contributors, or [Community Writers](https://about.gitlab.com/handbook/product/technical-writing/community-writers/).
- **Indexes per topic**: initially prepared by the Technical Writing Team, and kept up-to-date by developers and PMs in the same merge request containing code. They gather all resources for that topic in a single page (user and admin documentation, articles, and third-party docs).
-## Distinction between General Documentation and Technical Articles
-
-### General documentation
-
-General documentation is categorized by _User_, _Admin_, and _Contributor_, and describe what that feature is, what it does, and its available settings.
-
-### Technical Articles
-
-Technical articles replace technical content that once lived in the [GitLab Blog](https://about.gitlab.com/blog/), where they got out-of-date and weren't easily found.
-
-They are topic-related documentation, written with an user-friendly approach and language, aiming to provide the community with guidance on specific processes to achieve certain objectives.
-
-A technical article guides users and/or admins to achieve certain objectives (within guides and tutorials), or provide an overview of that particular topic or feature (within technical overviews). It can also describe the use, implementation, or integration of third-party tools with GitLab.
-
-They live under `doc/articles/article-title/index.md`, and their images should be placed under `doc/articles/article-title/img/`. Find a list of existing [technical articles](../articles/index.md) here.
-
-#### Types of Technical Articles
-
-- **User guides**: technical content to guide regular users from point A to point B
-- **Admin guides**: technical content to guide administrators of GitLab instances from point A to point B
-- **Technical Overviews**: technical content describing features, solutions, and third-party integrations
-- **Tutorials**: technical content provided step-by-step on how to do things, or how to reach very specific objectives
-
-#### Understanding guides, tutorials, and technical overviews
-
-Suppose there's a process to go from point A to point B in 5 steps: `(A) 1 > 2 > 3 > 4 > 5 (B)`.
-
-A **guide** can be understood as a description of certain processes to achieve a particular objective. A guide brings you from A to B describing the characteristics of that process, but not necessarily going over each step. It can mention, for example, steps 2 and 3, but does not necessarily explain how to accomplish them.
-
-- Live example: "GitLab Pages from A to Z - [Part 1](../user/project/pages/getting_started_part_one.md) to [Part 4](../user/project/pages/getting_started_part_four.md)"
-
-A **tutorial** requires a clear **step-by-step** guidance to achieve a singular objective. It brings you from A to B, describing precisely all the necessary steps involved in that process, showing each of the 5 steps to go from A to B.
-It does not only describes steps 2 and 3, but also shows you how to accomplish them.
-
-- Live example (on the blog): [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/)
-
-A **technical overview** is a description of what a certain feature is, and what it does, but does not walk
-through the process of how to use it systematically.
-
-- Live example (on the blog): [GitLab Workflow, an overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/)
-
-#### Special format
-
-Every **Technical Article** contains, in the very beginning, a blockquote with the following information:
-
-- A reference to the **type of article** (user guide, admin guide, tech overview, tutorial)
-- A reference to the **knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced)
-- A reference to the **author's name** and **GitLab.com handle**
-- A reference of the **publication date**
-
-```md
-> **Article [Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial ||
-> **Level:** intermediary ||
-> **Author:** [Name Surname](https://gitlab.com/username) ||
-> **Publication date:** AAAA/MM/DD
-```
-
-#### Technical Articles - Writing Method
-
-Use the [writing method](https://about.gitlab.com/handbook/product/technical-writing/#writing-method) defined by the Technical Writing team.
-
## Documentation style guidelines
All the docs follow the same [styleguide](doc_styleguide.md).
-### Contributing to docs
+## Contributing to docs
Whenever a feature is changed, updated, introduced, or deprecated, the merge
request introducing these changes must be accompanied by the documentation
@@ -118,13 +57,31 @@ and for every **major** feature present in Community Edition.
Currently GitLab docs use Redcarpet as [markdown](../user/markdown.md) engine, but there's an [open discussion](https://gitlab.com/gitlab-com/gitlab-docs/issues/50) for implementing Kramdown in the near future.
-## Testing
+### Previewing locally
-We try to treat documentation as code, thus have implemented some testing.
+To preview your changes to documentation locally, please follow
+this [development guide](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md#development).
+
+### Testing
+
+We treat documentation as code, thus have implemented some testing.
Currently, the following tests are in place:
1. `docs lint`: Check that all internal (relative) links work correctly and
- that all cURL examples in API docs use the full switches.
+ that all cURL examples in API docs use the full switches. It's recommended
+ to [check locally](#previewing-locally) before pushing to GitLab by executing the command
+ `bundle exec nanoc check internal_links` on your local
+ [`gitlab-docs`](https://gitlab.com/gitlab-com/gitlab-docs) directory.
+1. [`ee_compat_check`](https://docs.gitlab.com/ee/development/automatic_ce_ee_merge.html#avoiding-ce-gt-ee-merge-conflicts-beforehand) (runs on CE only):
+ When you submit a merge request to GitLab Community Edition (CE),
+ there is this additional job that runs against Enterprise Edition (EE)
+ and checks if your changes can apply cleanly to the EE codebase.
+ If that job fails, read the instructions in the job log for what to do next.
+ As CE is merged into EE once a day, it's important to avoid merge conflicts.
+ Submitting an EE-equivalent merge request cherry-picking all commits from CE to EE is
+ essential to avoid them.
+
+### Branch naming
If your contribution contains **only** documentation changes, you can speed up
the CI process by following some branch naming conventions. You have three
@@ -139,17 +96,7 @@ choices:
If your branch name matches any of the above, it will run only the docs
tests. If it doesn't, the whole test suite will run (including docs).
----
-
-When you submit a merge request to GitLab Community Edition (CE), there is an
-additional job called `ee_compat_check` that runs against Enterprise
-Edition (EE) and checks if your changes can apply cleanly to the EE codebase.
-If that job fails, read the instructions in the job log for what to do next.
-Contributors do not need to submit their changes to EE, GitLab Inc. employees
-on the other hand need to make sure that their changes apply cleanly to both
-CE and EE.
-
-## Previewing the changes live
+### Previewing the changes live
If you want to preview the doc changes of your merge request live, you can use
the manual `review-docs-deploy` job in your merge request. You will need at
@@ -164,7 +111,7 @@ You will need to push a branch to those repositories, it doesn't work for forks.
TIP: **Tip:**
If your branch contains only documentation changes, you can use
-[special branch names](#testing) to avoid long running pipelines.
+[special branch names](#branch-naming) to avoid long running pipelines.
In the mini pipeline graph, you should see an `>>` icon. Clicking on it will
reveal the `review-docs-deploy` job. Hit the play button for the job to start.
@@ -209,12 +156,12 @@ working on. If you don't, the remote docs branch won't be removed either,
and the server where the Review Apps are hosted will eventually be out of
disk space.
-### Behind the scenes
+#### Technical aspects
If you want to know the hot details, here's what's really happening:
1. You manually run the `review-docs-deploy` job in a CE/EE merge request.
-1. The job runs the [`scirpts/trigger-build-docs`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/trigger-build-docs)
+1. The job runs the [`scripts/trigger-build-docs`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/trigger-build-docs)
script with the `deploy` flag, which in turn:
1. Takes your branch name and applies the following:
- The slug of the branch name is used to avoid special characters since
@@ -243,3 +190,65 @@ The following GitLab features are used among others:
- [Review Apps](../ci/review_apps/index.md)
- [Artifacts](../ci/yaml/README.md#artifacts)
- [Specific Runner](../ci/runners/README.md#locking-a-specific-runner-from-being-enabled-for-other-projects)
+
+## General Documentation vs Technical Articles
+
+### General documentation
+
+General documentation is categorized by _User_, _Admin_, and _Contributor_, and describe what that feature is, what it does, and its available settings.
+
+### Technical Articles
+
+Technical articles replace technical content that once lived in the [GitLab Blog](https://about.gitlab.com/blog/), where they got out-of-date and weren't easily found.
+
+They are topic-related documentation, written with an user-friendly approach and language, aiming to provide the community with guidance on specific processes to achieve certain objectives.
+
+A technical article guides users and/or admins to achieve certain objectives (within guides and tutorials), or provide an overview of that particular topic or feature (within technical overviews). It can also describe the use, implementation, or integration of third-party tools with GitLab.
+
+They should be placed in a new directory named `/article-title/index.md` under a topic-related folder, and their images should be placed in `/article-title/img/`. For example, a new article on GitLab Pages should be placed in `doc/user/project/pages/article-title/` and a new article on GitLab CI/CD should be placed in `doc/ci/article-title/`.
+
+#### Types of Technical Articles
+
+- **User guides**: technical content to guide regular users from point A to point B
+- **Admin guides**: technical content to guide administrators of GitLab instances from point A to point B
+- **Technical Overviews**: technical content describing features, solutions, and third-party integrations
+- **Tutorials**: technical content provided step-by-step on how to do things, or how to reach very specific objectives
+
+#### Understanding guides, tutorials, and technical overviews
+
+Suppose there's a process to go from point A to point B in 5 steps: `(A) 1 > 2 > 3 > 4 > 5 (B)`.
+
+A **guide** can be understood as a description of certain processes to achieve a particular objective. A guide brings you from A to B describing the characteristics of that process, but not necessarily going over each step. It can mention, for example, steps 2 and 3, but does not necessarily explain how to accomplish them.
+
+- Live example: "GitLab Pages from A to Z - [Part 1](../user/project/pages/getting_started_part_one.md) to [Part 4](../user/project/pages/getting_started_part_four.md)"
+
+A **tutorial** requires a clear **step-by-step** guidance to achieve a singular objective. It brings you from A to B, describing precisely all the necessary steps involved in that process, showing each of the 5 steps to go from A to B.
+It does not only describes steps 2 and 3, but also shows you how to accomplish them.
+
+- Live example (on the blog): [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/)
+
+A **technical overview** is a description of what a certain feature is, and what it does, but does not walk
+through the process of how to use it systematically.
+
+- Live example (on the blog): [GitLab Workflow, an overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/)
+
+#### Special format
+
+Every **Technical Article** contains, in the very beginning, a blockquote with the following information:
+
+- A reference to the **type of article** (user guide, admin guide, tech overview, tutorial)
+- A reference to the **knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced)
+- A reference to the **author's name** and **GitLab.com handle**
+- A reference of the **publication date**
+
+```md
+> **[Article Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial ||
+> **Level:** intermediary ||
+> **Author:** [Name Surname](https://gitlab.com/username) ||
+> **Publication date:** AAAA-MM-DD
+```
+
+#### Technical Articles - Writing Method
+
+Use the [writing method](https://about.gitlab.com/handbook/product/technical-writing/#writing-method) defined by the Technical Writing team.
+
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 570b0d5b22f..6c6e5db4cd9 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -299,9 +299,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-2-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-3-stable gitlab
-**Note:** You can change `10-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `10-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
diff --git a/doc/monitoring/performance/introduction.md b/doc/monitoring/performance/introduction.md
index ae88baa0c14..4d6f02b6547 100644
--- a/doc/monitoring/performance/introduction.md
+++ b/doc/monitoring/performance/introduction.md
@@ -1 +1 @@
-This document was moved to [administration/monitoring/performance/introduction](../../administration/monitoring/performance/introduction.md).
+This document was moved to [administration/monitoring/performance/introduction](../../administration/monitoring/performance/index.md).
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index df56f031970..588f4fa369f 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -61,6 +61,10 @@ We've gathered some resources to help you to get the best from Git with GitLab.
- [Getting Started with Git LFS](https://about.gitlab.com/2017/01/30/getting-started-with-git-lfs-tutorial/)
- [Towards a production quality open source Git LFS server](https://about.gitlab.com/2015/08/13/towards-a-production-quality-open-source-git-lfs-server/)
+## Troubleshooting
+
+- Learn a few [Git troubleshooting](troubleshooting_git.md) techniques to help you out.
+
## General information
- **Articles:**
diff --git a/doc/topics/git/troubleshooting_git.md b/doc/topics/git/troubleshooting_git.md
new file mode 100644
index 00000000000..8555c5e91ea
--- /dev/null
+++ b/doc/topics/git/troubleshooting_git.md
@@ -0,0 +1,82 @@
+# Troubleshooting Git
+
+Sometimes things don't work the way they should or as you might expect when
+you're using Git. Here are some tips on troubleshooting and resolving issues
+with Git.
+
+## Broken pipe errors on git push
+
+'Broken pipe' errors can occur when attempting to push to a remote repository.
+When pushing you will usually see:
+
+```
+Write failed: Broken pipe
+fatal: The remote end hung up unexpectedly
+```
+
+To fix this issue, here are some possible solutions.
+
+### Increase the POST buffer size in Git
+
+**If pushing over HTTP**, you can try increasing the POST buffer size in Git's
+configuration. Open a terminal and enter:
+
+```sh
+git config http.postBuffer 52428800
+```
+
+The value is specified in bytes, so in the above case the buffer size has been
+set to 50MB. The default is 1MB.
+
+### Check your SSH configuration
+
+**If pushing over SSH**, first check your SSH configuration as 'Broken pipe'
+errors can sometimes be caused by underlying issues with SSH (such as
+authentication). Make sure that SSH is correctly configured by following the
+instructions in the [SSH troubleshooting] docs.
+
+There's another option where you can prevent session timeouts by configuring
+SSH 'keep alive' either on the client or on the server (if you are a GitLab
+admin and have access to the server).
+
+NOTE: **Note:** configuring *both* the client and the server is unnecessary.
+
+**To configure SSH on the client side**:
+
+- On UNIX, edit `~/.ssh/config` (create the file if it doesn’t exist) and
+ add or edit:
+
+ ```
+ Host your-gitlab-instance-url.com
+ ServerAliveInterval 60
+ ServerAliveCountMax 5
+ ```
+
+- On Windows, if you are using PuTTY, go to your session properties, then
+ navigate to "Connection" and under "Sending of null packets to keep
+ session active", set "Seconds between keepalives (0 to turn off)" to `60`.
+
+**To configure SSH on the server side**, edit `/etc/ssh/sshd_config` and add:
+
+```
+ClientAliveInterval 60
+ClientAliveCountMax 5
+```
+
+### Running a git repack
+
+**If 'pack-objects' type errors are also being displayed**, you can try to
+run a `git repack` before attempting to push to the remote repository again:
+
+```sh
+git repack
+git push
+```
+
+### Upgrade your Git client
+
+In case you're running an older version of Git (< 2.9), consider upgrading
+to >= 2.9 (see [Broken pipe when pushing to Git repository][Broken-Pipe]).
+
+[SSH troubleshooting]: ../../ssh/README.md#troubleshooting "SSH Troubleshooting"
+[Broken-Pipe]: https://stackoverflow.com/questions/19120120/broken-pipe-when-pushing-to-git-repository/36971469#36971469 "StackOverflow: 'Broken pipe when pushing to Git repository'"
diff --git a/doc/update/10.2-to-10.3.md b/doc/update/10.2-to-10.3.md
new file mode 100644
index 00000000000..07f9ee965f0
--- /dev/null
+++ b/doc/update/10.2-to-10.3.md
@@ -0,0 +1,360 @@
+---
+comments: false
+---
+
+# From 10.2 to 10.3
+
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+NOTE: GitLab 9.0 and higher only support Ruby 2.3.x and dropped support for Ruby 2.1.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download and compile Ruby:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.5.tar.gz
+echo '3247e217d6745c27ef23bdc77b6abdb4b57a118f ruby-2.3.5.tar.gz' | shasum -c - && tar xzf ruby-2.3.5.tar.gz
+cd ruby-2.3.5
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets and
+it has a minimum requirement of node v4.3.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v4.3.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+
+Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
+JavaScript dependencies.
+
+```bash
+curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt-get update
+sudo apt-get install yarn
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 9.2 and higher only supports Go 1.8.3 and dropped support for Go
+1.5.x through 1.7.x. Be sure to upgrade your installation if necessary.
+
+You can check which version you are running with `go version`.
+
+Download and install Go:
+
+```bash
+# Remove former Go installation folder
+sudo rm -rf /usr/local/go
+
+curl --remote-name --progress https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz
+echo '1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz
+sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+rm go1.8.3.linux-amd64.tar.gz
+```
+
+### 6. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+sudo -u git -H git checkout -- locale
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 10-3-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 10-3-stable-ee
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 8. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. GitLab-Workhorse uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
+sudo -u git -H make
+```
+
+### 9. Update Gitaly
+
+#### New Gitaly configuration options required
+
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
+
+```shell
+echo '
+[gitaly-ruby]
+dir = "/home/git/gitaly/ruby"
+
+[gitlab-shell]
+dir = "/home/git/gitlab-shell"
+' | sudo -u git tee -a /home/git/gitaly/config.toml
+```
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update MySQL permissions
+
+If you are using MySQL you need to grant the GitLab user the necessary
+permissions on the database:
+
+```bash
+mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
+```
+
+If you use MySQL with replication, or just have MySQL configured with binary logging,
+you will need to also run the following on all of your MySQL servers:
+
+```bash
+mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
+```
+
+You can make this setting permanent by adding it to your `my.cnf`:
+
+```
+log_bin_trust_function_creators=1
+```
+
+### 11. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/10-2-stable:config/gitlab.yml.example origin/10-3-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/10-2-stable:lib/support/nginx/gitlab-ssl origin/10-3-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/10-2-stable:lib/support/nginx/gitlab origin/10-3-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-3-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-3-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/10-2-stable:lib/support/init.d/gitlab.default.example origin/10-3-stable:lib/support/init.d/gitlab.default.example
+```
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 12. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Compile GetText PO files
+
+sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
+
+# Update node dependencies and recompile assets
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
+
+# Clean up cache
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 13. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 14. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (10.0)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 9.5 to 10.0](9.5-to-10.0.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-3-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-3-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/user/discussions/img/commit_comment_mr_context.png b/doc/user/discussions/img/commit_comment_mr_context.png
new file mode 100644
index 00000000000..b363e0035e8
--- /dev/null
+++ b/doc/user/discussions/img/commit_comment_mr_context.png
Binary files differ
diff --git a/doc/user/discussions/img/commit_comment_mr_discussions_tab.png b/doc/user/discussions/img/commit_comment_mr_discussions_tab.png
new file mode 100644
index 00000000000..2b06cdcc055
--- /dev/null
+++ b/doc/user/discussions/img/commit_comment_mr_discussions_tab.png
Binary files differ
diff --git a/doc/user/discussions/img/merge_request_commits_tab.png b/doc/user/discussions/img/merge_request_commits_tab.png
new file mode 100644
index 00000000000..41a3648f390
--- /dev/null
+++ b/doc/user/discussions/img/merge_request_commits_tab.png
Binary files differ
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 2206b2860f4..eacfe2baa27 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -32,6 +32,43 @@ hide discussions that are no longer relevant.
Comments and discussions can be resolved by anyone with at least Developer
access to the project or the author of the merge request.
+### Commit discussions in the context of a merge request
+
+> [Introduced][ce-31847] in GitLab 10.3.
+
+For reviewers with commit-based workflow, it may be useful to add discussions to
+specific commit diffs in the context of a merge request. These discussions will
+persist through a commit ID change when:
+
+- force-pushing after a rebase
+- amending a commit
+
+To create a commit diff discussion:
+
+1. Navigate to the merge request **Commits** tab. A list of commits that
+ constitute the merge request will be shown.
+
+ ![Merge request commits tab](img/merge_request_commits_tab.png)
+
+1. Navigate to a specific commit, click on the **Changes** tab (where you
+ will only be presented diffs from the selected commit), and leave a comment.
+
+ ![Commit diff discussion in merge request context](img/commit_comment_mr_context.png)
+
+1. Any discussions created this way will be shown in the merge request's
+ **Discussions** tab and are resolvable.
+
+ ![Merge request Discussions tab](img/commit_comment_mr_discussions_tab.png)
+
+Discussions created this way will only appear in the original merge request
+and not when navigating to that commit under your project's
+**Repository > Commits** page.
+
+TIP: **Tip:**
+When a link of a commit reference is found in a discussion inside a merge
+request, it will be automatically converted to a link in the context of the
+current merge request.
+
### Jumping between unresolved discussions
When a merge request has a large number of comments it can be difficult to track
@@ -133,6 +170,15 @@ From now on, any discussions on a diff will be resolved by default if a push
makes that diff section outdated. Discussions on lines that don't change and
top-level resolvable discussions are not automatically resolved.
+## Commit discussions
+
+You can add comments and discussion threads to a particular commit under your
+project's **Repository > Commits**.
+
+CAUTION: **Attention:**
+Discussions created this way will be lost if the commit ID changes after a
+force push.
+
## Threaded discussions
> [Introduced][ce-7527] in GitLab 9.1.
@@ -229,6 +275,7 @@ edit existing comments. Non-team members are restricted from adding or editing c
[ce-14053]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14053
[ce-14061]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14061
[ce-14531]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14531
+[ce-31847]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/31847
[resolve-discussion-button]: img/resolve_discussion_button.png
[resolve-comment-button]: img/resolve_comment_button.png
[discussion-view]: img/discussion_view.png
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index a1671f9dd91..1733017cbc0 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -197,11 +197,11 @@ username, you can create a new group and transfer projects to it.
Changing a group's path can have unintended side effects.
* Existing web URLs for the group and anything under it (i.e. projects) will
-redirect to the new URLs
-* Existing Git remote URLs for projects under the group will no longer work, but
-Git responses will show an error with the new remote URL
-* The original namespace can be claimed again by any group or user, which will
-destroy web redirects and Git remote warnings
+redirect to the new URLs.
+* Existing Git remote URLs for projects under the group will redirect to the new remote URL, and they
+will show a warning with the new remote URL.
+* The redirect to the new URL is permanent, that implies the original namespace
+can't be claimed again by any group or user.
* If you are vacating the path so it can be claimed by another group or user,
you may need to rename the group name as well since both names and paths must be
unique
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index a671c92640a..552abac747b 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -195,12 +195,23 @@ With inline diffs tags you can display {+ additions +} or [- deletions -].
The wrapping tags can be either curly braces or square brackets [+ additions +] or {- deletions -}.
+Examples:
+
+```
+- {+ additions +}
+- [+ additions +]
+- {- deletions -}
+- [- deletions -]
+```
+
However the wrapping tags cannot be mixed as such:
+```
- {+ additions +]
- [+ additions +}
- {- deletions -]
- [- deletions -}
+```
### Emoji
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 04e615330ce..dae4cbe170b 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -45,11 +45,10 @@ Alternatively, you can follow [this detailed procedure from the GitLab Team Hand
Changing your username can have unintended side effects.
* Existing web URLs for the user and anything under it (i.e. projects) will
-redirect to the new URLs
-* Existing Git remote URLs for projects under the user will no longer work, but
-Git responses will show an error with the new remote URL
-* The original namespace can be claimed again by any group or user, which will
-destroy any web redirects and Git remote warnings
+redirect to the new URLs.
+* Existing Git remote URLs for projects under the user will redirect to the new remote URL. Git responses
+will show a warning with the new remote URL.
+* The redirect to the new URL is permanent, that implies the original namespace can't be claimed again by any group or user.
> It is currently not possible to rename a namespace if it contains a
project with container registry tags, because the project cannot be moved.
diff --git a/doc/user/project/merge_requests/img/create_from_email.png b/doc/user/project/merge_requests/img/create_from_email.png
new file mode 100644
index 00000000000..71eb4bf267d
--- /dev/null
+++ b/doc/user/project/merge_requests/img/create_from_email.png
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index b5c3f74a113..7037d7f5989 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -27,7 +27,7 @@ With GitLab merge requests, you can:
- [Resolve merge conflicts from the UI](#resolve-conflicts)
- Enable [fast-forward merge requests](#fast-forward-merge-requests)
- Enable [semi-linear history merge requests](#semi-linear-history-merge-requests) as another security layer to guarantee the pipeline is passing in the target branch
-- [Create new merge requests by email](#create_by_email)
+- [Create new merge requests by email](#create-new-merge-requests-by-email)
With **[GitLab Enterprise Edition][ee]**, you can also:
@@ -139,7 +139,12 @@ address. The address can be obtained on the merge requests page by clicking on
a **Email a new merge request to this project** button. The subject will be
used as the source branch name for the new merge request and the target branch
will be the default branch for the project. The message body (if not empty)
-will be used as the merge request description.
+will be used as the merge request description. You need
+["Reply by email"](../../../administration/reply_by_email.md) enabled to use
+this feature. If it's not enabled to your instance, you may ask your GitLab
+administrator to do so.
+
+![Create new merge requests by email](img/create_from_email.png)
## Revert changes
diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md
index daa5463d680..43451844f2d 100644
--- a/doc/user/project/pipelines/settings.md
+++ b/doc/user/project/pipelines/settings.md
@@ -68,7 +68,7 @@ in the pipelines settings page.
Access to pipelines and job details (including output of logs and artifacts)
is checked against your current user access level and the **Public pipelines**
-project setting.
+project setting under your project's **Settings > CI/CD > General pipelines settings**.
If **Public pipelines** is enabled (default):
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index a234a647b77..2b6fde1e2a5 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -50,3 +50,9 @@ Here you can run housekeeping, archive, rename, transfer, or remove a project.
It's possible to mark a project as archived via the Project Settings. An archived project will be hidden by default in the project listings.
An archived project can be fully restored and will therefore retain it's repository and all associated resources whilst in an archived state.
+
+#### Renaming a project
+
+>**Note:** Only Project Owners and Admin users have the permission to rename a project
+
+It's possible to rename a project from "Rename repository" or "Transfer project" sections. When doing so, you will need to update your local repositories to point to the new location, otherwise Git operations will be rejected.
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 7887b886c03..4f36bbd760f 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -110,10 +110,12 @@ module API
end
params do
use :pagination
+ optional :order_by, type: String, values: %w[email name commits], default: nil, desc: 'Return contributors ordered by `name` or `email` or `commits`'
+ optional :sort, type: String, values: %w[asc desc], default: nil, desc: 'Sort by asc (ascending) or desc (descending)'
end
get ':id/repository/contributors' do
begin
- contributors = ::Kaminari.paginate_array(user_project.repository.contributors)
+ contributors = ::Kaminari.paginate_array(user_project.repository.contributors(order_by: params[:order_by], sort: params[:sort]))
present paginate(contributors), with: Entities::Contributor
rescue
not_found!
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index 0d394a7b441..5e0afc6a7e4 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -14,10 +14,15 @@ module API
success Entities::Tag
end
params do
+ optional :sort, type: String, values: %w[asc desc], default: 'desc',
+ desc: 'Return tags sorted in updated by `asc` or `desc` order.'
+ optional :order_by, type: String, values: %w[name updated], default: 'updated',
+ desc: 'Return tags ordered by `name` or `updated` fields.'
use :pagination
end
get ':id/repository/tags' do
- tags = ::Kaminari.paginate_array(user_project.repository.tags.sort_by(&:name).reverse)
+ tags = ::Kaminari.paginate_array(::TagsFinder.new(user_project.repository, sort: "#{params[:order_by]}_#{params[:sort]}").execute)
+
present paginate(tags), with: Entities::Tag, project: user_project
end
diff --git a/lib/extracts_path.rb b/lib/extracts_path.rb
index 721ed97bb6b..27c712a84d4 100644
--- a/lib/extracts_path.rb
+++ b/lib/extracts_path.rb
@@ -127,7 +127,6 @@ module ExtractsPath
@hex_path = Digest::SHA1.hexdigest(@path)
@logs_path = logs_file_project_ref_path(@project, @ref, @path)
-
rescue RuntimeError, NoMethodError, InvalidPathError
render_404
end
@@ -136,6 +135,11 @@ module ExtractsPath
@tree ||= @repo.tree(@commit.id, @path)
end
+ def lfs_blob_ids
+ blob_ids = tree.blobs.map(&:id)
+ @lfs_blob_ids = Gitlab::Git::Blob.batch_lfs_pointers(@project.repository, blob_ids).map(&:id)
+ end
+
private
# overriden in subclasses, do not remove
diff --git a/lib/feature.rb b/lib/feature.rb
index ac3bc65c0d5..8e9ba5c530a 100644
--- a/lib/feature.rb
+++ b/lib/feature.rb
@@ -1,5 +1,3 @@
-require 'flipper/adapters/active_record'
-
class Feature
# Classes to override flipper table names
class FlipperFeature < Flipper::Adapters::ActiveRecord::Feature
@@ -62,12 +60,7 @@ class Feature
end
def flipper
- @flipper ||= begin
- adapter = Flipper::Adapters::ActiveRecord.new(
- feature_class: FlipperFeature, gate_class: FlipperGate)
-
- Flipper.new(adapter)
- end
+ @flipper ||= Flipper.instance
end
# This method is called from config/initializers/flipper.rb and can be used
diff --git a/lib/gitlab/git/gitlab_projects.rb b/lib/gitlab/git/gitlab_projects.rb
new file mode 100644
index 00000000000..d948d7895ed
--- /dev/null
+++ b/lib/gitlab/git/gitlab_projects.rb
@@ -0,0 +1,258 @@
+module Gitlab
+ module Git
+ class GitlabProjects
+ include Gitlab::Git::Popen
+
+ # Absolute path to directory where repositories are stored.
+ # Example: /home/git/repositories
+ attr_reader :shard_path
+
+ # Relative path is a directory name for repository with .git at the end.
+ # Example: gitlab-org/gitlab-test.git
+ attr_reader :repository_relative_path
+
+ # Absolute path to the repository.
+ # Example: /home/git/repositorities/gitlab-org/gitlab-test.git
+ attr_reader :repository_absolute_path
+
+ # This is the path at which the gitlab-shell hooks directory can be found.
+ # It's essential for integration between git and GitLab proper. All new
+ # repositories should have their hooks directory symlinked here.
+ attr_reader :global_hooks_path
+
+ attr_reader :logger
+
+ def initialize(shard_path, repository_relative_path, global_hooks_path:, logger:)
+ @shard_path = shard_path
+ @repository_relative_path = repository_relative_path
+
+ @logger = logger
+ @global_hooks_path = global_hooks_path
+ @repository_absolute_path = File.join(shard_path, repository_relative_path)
+ @output = StringIO.new
+ end
+
+ def output
+ io = @output.dup
+ io.rewind
+ io.read
+ end
+
+ def rm_project
+ logger.info "Removing repository <#{repository_absolute_path}>."
+ FileUtils.rm_rf(repository_absolute_path)
+ end
+
+ # Move repository from one directory to another
+ #
+ # Example: gitlab/gitlab-ci.git -> randx/six.git
+ #
+ # Won't work if target namespace directory does not exist
+ #
+ def mv_project(new_path)
+ new_absolute_path = File.join(shard_path, new_path)
+
+ # verify that the source repo exists
+ unless File.exist?(repository_absolute_path)
+ logger.error "mv-project failed: source path <#{repository_absolute_path}> does not exist."
+ return false
+ end
+
+ # ...and that the target repo does not exist
+ if File.exist?(new_absolute_path)
+ logger.error "mv-project failed: destination path <#{new_absolute_path}> already exists."
+ return false
+ end
+
+ logger.info "Moving repository from <#{repository_absolute_path}> to <#{new_absolute_path}>."
+ FileUtils.mv(repository_absolute_path, new_absolute_path)
+ end
+
+ # Import project via git clone --bare
+ # URL must be publicly cloneable
+ def import_project(source, timeout)
+ # Skip import if repo already exists
+ return false if File.exist?(repository_absolute_path)
+
+ masked_source = mask_password_in_url(source)
+
+ logger.info "Importing project from <#{masked_source}> to <#{repository_absolute_path}>."
+ cmd = %W(git clone --bare -- #{source} #{repository_absolute_path})
+
+ success = run_with_timeout(cmd, timeout, nil)
+
+ unless success
+ logger.error("Importing project from <#{masked_source}> to <#{repository_absolute_path}> failed.")
+ FileUtils.rm_rf(repository_absolute_path)
+ return false
+ end
+
+ Gitlab::Git::Repository.create_hooks(repository_absolute_path, global_hooks_path)
+
+ # The project was imported successfully.
+ # Remove the origin URL since it may contain password.
+ remove_origin_in_repo
+
+ true
+ end
+
+ def fork_repository(new_shard_path, new_repository_relative_path)
+ from_path = repository_absolute_path
+ to_path = File.join(new_shard_path, new_repository_relative_path)
+
+ # The repository cannot already exist
+ if File.exist?(to_path)
+ logger.error "fork-repository failed: destination repository <#{to_path}> already exists."
+ return false
+ end
+
+ # Ensure the namepsace / hashed storage directory exists
+ FileUtils.mkdir_p(File.dirname(to_path), mode: 0770)
+
+ logger.info "Forking repository from <#{from_path}> to <#{to_path}>."
+ cmd = %W(git clone --bare --no-local -- #{from_path} #{to_path})
+
+ run(cmd, nil) && Gitlab::Git::Repository.create_hooks(to_path, global_hooks_path)
+ end
+
+ def fetch_remote(name, timeout, force:, tags:, ssh_key: nil, known_hosts: nil)
+ tags_option = tags ? '--tags' : '--no-tags'
+
+ logger.info "Fetching remote #{name} for repository #{repository_absolute_path}."
+ cmd = %W(git fetch #{name} --prune --quiet)
+ cmd << '--force' if force
+ cmd << tags_option
+
+ setup_ssh_auth(ssh_key, known_hosts) do |env|
+ success = run_with_timeout(cmd, timeout, repository_absolute_path, env)
+
+ unless success
+ logger.error "Fetching remote #{name} for repository #{repository_absolute_path} failed."
+ end
+
+ success
+ end
+ end
+
+ def push_branches(remote_name, timeout, force, branch_names)
+ logger.info "Pushing branches from #{repository_absolute_path} to remote #{remote_name}: #{branch_names}"
+ cmd = %w(git push)
+ cmd << '--force' if force
+ cmd += %W(-- #{remote_name}).concat(branch_names)
+
+ success = run_with_timeout(cmd, timeout, repository_absolute_path)
+
+ unless success
+ logger.error("Pushing branches to remote #{remote_name} failed.")
+ end
+
+ success
+ end
+
+ def delete_remote_branches(remote_name, branch_names)
+ branches = branch_names.map { |branch_name| ":#{branch_name}" }
+
+ logger.info "Pushing deleted branches from #{repository_absolute_path} to remote #{remote_name}: #{branch_names}"
+ cmd = %W(git push -- #{remote_name}).concat(branches)
+
+ success = run(cmd, repository_absolute_path)
+
+ unless success
+ logger.error("Pushing deleted branches to remote #{remote_name} failed.")
+ end
+
+ success
+ end
+
+ protected
+
+ def run(*args)
+ output, exitstatus = popen(*args)
+ @output << output
+
+ exitstatus&.zero?
+ end
+
+ def run_with_timeout(*args)
+ output, exitstatus = popen_with_timeout(*args)
+ @output << output
+
+ exitstatus&.zero?
+ rescue Timeout::Error
+ @output.puts('Timed out')
+
+ false
+ end
+
+ def mask_password_in_url(url)
+ result = URI(url)
+ result.password = "*****" unless result.password.nil?
+ result.user = "*****" unless result.user.nil? # it's needed for oauth access_token
+ result
+ rescue
+ url
+ end
+
+ def remove_origin_in_repo
+ cmd = %w(git remote rm origin)
+ run(cmd, repository_absolute_path)
+ end
+
+ # Builds a small shell script that can be used to execute SSH with a set of
+ # custom options.
+ #
+ # Options are expanded as `'-oKey="Value"'`, so SSH will correctly interpret
+ # paths with spaces in them. We trust the user not to embed single or double
+ # quotes in the key or value.
+ def custom_ssh_script(options = {})
+ args = options.map { |k, v| %Q{'-o#{k}="#{v}"'} }.join(' ')
+
+ [
+ "#!/bin/sh",
+ "exec ssh #{args} \"$@\""
+ ].join("\n")
+ end
+
+ # Known hosts data and private keys can be passed to gitlab-shell in the
+ # environment. If present, this method puts them into temporary files, writes
+ # a script that can substitute as `ssh`, setting the options to respect those
+ # files, and yields: { "GIT_SSH" => "/tmp/myScript" }
+ def setup_ssh_auth(key, known_hosts)
+ options = {}
+
+ if key
+ key_file = Tempfile.new('gitlab-shell-key-file')
+ key_file.chmod(0o400)
+ key_file.write(key)
+ key_file.close
+
+ options['IdentityFile'] = key_file.path
+ options['IdentitiesOnly'] = 'yes'
+ end
+
+ if known_hosts
+ known_hosts_file = Tempfile.new('gitlab-shell-known-hosts')
+ known_hosts_file.chmod(0o400)
+ known_hosts_file.write(known_hosts)
+ known_hosts_file.close
+
+ options['StrictHostKeyChecking'] = 'yes'
+ options['UserKnownHostsFile'] = known_hosts_file.path
+ end
+
+ return yield({}) if options.empty?
+
+ script = Tempfile.new('gitlab-shell-ssh-wrapper')
+ script.chmod(0o755)
+ script.write(custom_ssh_script(options))
+ script.close
+
+ yield('GIT_SSH' => script.path)
+ ensure
+ key_file&.close!
+ known_hosts_file&.close!
+ script&.close!
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/operation_service.rb b/lib/gitlab/git/operation_service.rb
index 7e8fe173056..ef5bdbaf819 100644
--- a/lib/gitlab/git/operation_service.rb
+++ b/lib/gitlab/git/operation_service.rb
@@ -126,7 +126,7 @@ module Gitlab
oldrev = branch.target
- if oldrev == repository.rugged.merge_base(newrev, branch.target)
+ if oldrev == repository.merge_base(newrev, branch.target)
oldrev
else
raise Gitlab::Git::CommitError.new('Branch diverged')
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 0e0a1987c7d..848a782446a 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -39,10 +39,31 @@ module Gitlab
repo = Rugged::Repository.init_at(repo_path, bare)
repo.close
- if symlink_hooks_to.present?
- hooks_path = File.join(repo_path, 'hooks')
- FileUtils.rm_rf(hooks_path)
- FileUtils.ln_s(symlink_hooks_to, hooks_path)
+ create_hooks(repo_path, symlink_hooks_to) if symlink_hooks_to.present?
+
+ true
+ end
+
+ def create_hooks(repo_path, global_hooks_path)
+ local_hooks_path = File.join(repo_path, 'hooks')
+ real_local_hooks_path = :not_found
+
+ begin
+ real_local_hooks_path = File.realpath(local_hooks_path)
+ rescue Errno::ENOENT
+ # real_local_hooks_path == :not_found
+ end
+
+ # Do nothing if hooks already exist
+ unless real_local_hooks_path == File.realpath(global_hooks_path)
+ # Move the existing hooks somewhere safe
+ FileUtils.mv(
+ local_hooks_path,
+ "#{local_hooks_path}.old.#{Time.now.to_i}"
+ ) if File.exist?(local_hooks_path)
+
+ # Create the hooks symlink
+ FileUtils.ln_sf(global_hooks_path, local_hooks_path)
end
true
@@ -517,8 +538,15 @@ module Gitlab
# Returns the SHA of the most recent common ancestor of +from+ and +to+
def merge_base_commit(from, to)
- rugged.merge_base(from, to)
+ gitaly_migrate(:merge_base) do |is_enabled|
+ if is_enabled
+ gitaly_repository_client.find_merge_base(from, to)
+ else
+ rugged.merge_base(from, to)
+ end
+ end
end
+ alias_method :merge_base, :merge_base_commit
# Gitaly note: JV: check gitlab-ee before removing this method.
def rugged_is_ancestor?(ancestor_id, descendant_id)
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index a477d618f63..c1f95396878 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -69,6 +69,16 @@ module Gitlab
response.value
end
+ def find_merge_base(*revisions)
+ request = Gitaly::FindMergeBaseRequest.new(
+ repository: @gitaly_repo,
+ revisions: revisions.map { |r| GitalyClient.encode(r) }
+ )
+
+ response = GitalyClient.call(@storage, :repository_service, :find_merge_base, request)
+ response.base.presence
+ end
+
def fetch_source_branch(source_repository, source_branch, local_ref)
request = Gitaly::FetchSourceBranchRequest.new(
repository: @gitaly_repo,
diff --git a/lib/gitlab/metrics/method_call.rb b/lib/gitlab/metrics/method_call.rb
index 65d55576ac2..9112164f22e 100644
--- a/lib/gitlab/metrics/method_call.rb
+++ b/lib/gitlab/metrics/method_call.rb
@@ -1,7 +1,11 @@
+# rubocop:disable Style/ClassVars
+
module Gitlab
module Metrics
# Class for tracking timing information about method calls
class MethodCall
+ @@measurement_enabled_cache = Concurrent::AtomicBoolean.new(false)
+ @@measurement_enabled_cache_expires_at = Concurrent::AtomicFixnum.new(Time.now.to_i)
MUTEX = Mutex.new
BASE_LABELS = { module: nil, method: nil }.freeze
attr_reader :real_time, :cpu_time, :call_count, :labels
@@ -18,6 +22,10 @@ module Gitlab
end
end
+ def self.measurement_enabled_cache_expires_at
+ @@measurement_enabled_cache_expires_at
+ end
+
# name - The full name of the method (including namespace) such as
# `User#sign_in`.
#
@@ -72,7 +80,14 @@ module Gitlab
end
def call_measurement_enabled?
- Feature.get(:prometheus_metrics_method_instrumentation).enabled?
+ expires_at = @@measurement_enabled_cache_expires_at.value
+ if expires_at < Time.now.to_i
+ if @@measurement_enabled_cache_expires_at.compare_and_set(expires_at, 1.minute.from_now.to_i)
+ @@measurement_enabled_cache.value = Feature.get(:prometheus_metrics_method_instrumentation).enabled?
+ end
+ end
+
+ @@measurement_enabled_cache.value
end
end
end
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index a22a63665be..9cdd3d22f18 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -66,7 +66,7 @@ module Gitlab
# Init new repository
#
# storage - project's storage name
- # name - project path with namespace
+ # name - project disk path
#
# Ex.
# add_repository("/path/to/storage", "gitlab/gitlab-ci")
@@ -94,23 +94,28 @@ module Gitlab
# Import repository
#
# storage - project's storage path
- # name - project path with namespace
+ # name - project disk path
+ # url - URL to import from
#
# Ex.
- # import_repository("/path/to/storage", "gitlab/gitlab-ci", "https://github.com/randx/six.git")
+ # import_repository("/path/to/storage", "gitlab/gitlab-ci", "https://gitlab.com/gitlab-org/gitlab-test.git")
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387
def import_repository(storage, name, url)
# The timeout ensures the subprocess won't hang forever
- cmd = [gitlab_shell_projects_path, 'import-project',
- storage, "#{name}.git", url, "#{Gitlab.config.gitlab_shell.git_timeout}"]
- gitlab_shell_fast_execute_raise_error(cmd)
+ cmd = gitlab_projects(storage, "#{name}.git")
+ success = cmd.import_project(url, git_timeout)
+
+ raise Error, cmd.output unless success
+
+ success
end
# Fetch remote for repository
#
# repository - an instance of Git::Repository
# remote - remote name
+ # ssh_auth - SSH known_hosts data and a private key to use for public-key authentication
# forced - should we use --force flag?
# no_tags - should we use --no-tags flag?
#
@@ -131,16 +136,15 @@ module Gitlab
# Move repository
# storage - project's storage path
- # path - project path with namespace
- # new_path - new project path with namespace
+ # path - project disk path
+ # new_path - new project disk path
#
# Ex.
# mv_repository("/path/to/storage", "gitlab/gitlab-ci", "randx/gitlab-ci-new")
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387
def mv_repository(storage, path, new_path)
- gitlab_shell_fast_execute([gitlab_shell_projects_path, 'mv-project',
- storage, "#{path}.git", "#{new_path}.git"])
+ gitlab_projects(storage, "#{path}.git").mv_project("#{new_path}.git")
end
# Fork repository to new path
@@ -154,30 +158,21 @@ module Gitlab
#
# Gitaly note: JV: not easy to migrate because this involves two Gitaly servers, not one.
def fork_repository(forked_from_storage, forked_from_disk_path, forked_to_storage, forked_to_disk_path)
- gitlab_shell_fast_execute(
- [
- gitlab_shell_projects_path,
- 'fork-repository',
- forked_from_storage,
- "#{forked_from_disk_path}.git",
- forked_to_storage,
- "#{forked_to_disk_path}.git"
- ]
- )
+ gitlab_projects(forked_from_storage, "#{forked_from_disk_path}.git")
+ .fork_repository(forked_to_storage, "#{forked_to_disk_path}.git")
end
# Remove repository from file system
#
# storage - project's storage path
- # name - project path with namespace
+ # name - project disk path
#
# Ex.
# remove_repository("/path/to/storage", "gitlab/gitlab-ci")
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/387
def remove_repository(storage, name)
- gitlab_shell_fast_execute([gitlab_shell_projects_path,
- 'rm-project', storage, "#{name}.git"])
+ gitlab_projects(storage, "#{name}.git").rm_project
end
# Add new key to gitlab-shell
@@ -311,6 +306,47 @@ module Gitlab
end
end
+ # Push branch to remote repository
+ #
+ # storage - project's storage path
+ # project_name - project's disk path
+ # remote_name - remote name
+ # branch_names - remote branch names to push
+ # forced - should we use --force flag
+ #
+ # Ex.
+ # push_remote_branches('/path/to/storage', 'gitlab-org/gitlab-test' 'upstream', ['feature'])
+ #
+ def push_remote_branches(storage, project_name, remote_name, branch_names, forced: true)
+ cmd = gitlab_projects(storage, "#{project_name}.git")
+
+ success = cmd.push_branches(remote_name, git_timeout, forced, branch_names)
+
+ raise Error, cmd.output unless success
+
+ success
+ end
+
+ # Delete branch from remote repository
+ #
+ # storage - project's storage path
+ # project_name - project's disk path
+ # remote_name - remote name
+ # branch_names - remote branch names
+ #
+ # Ex.
+ # delete_remote_branches('/path/to/storage', 'gitlab-org/gitlab-test', 'upstream', ['feature'])
+ #
+ def delete_remote_branches(storage, project_name, remote_name, branch_names)
+ cmd = gitlab_projects(storage, "#{project_name}.git")
+
+ success = cmd.delete_remote_branches(remote_name, branch_names)
+
+ raise Error, cmd.output unless success
+
+ success
+ end
+
protected
def gitlab_shell_path
@@ -341,24 +377,35 @@ module Gitlab
private
- def local_fetch_remote(storage, name, remote, ssh_auth: nil, forced: false, no_tags: false)
- args = [gitlab_shell_projects_path, 'fetch-remote', storage, name, remote, "#{Gitlab.config.gitlab_shell.git_timeout}"]
- args << '--force' if forced
- args << '--no-tags' if no_tags
+ def gitlab_projects(shard_path, disk_path)
+ Gitlab::Git::GitlabProjects.new(
+ shard_path,
+ disk_path,
+ global_hooks_path: Gitlab.config.gitlab_shell.hooks_path,
+ logger: Rails.logger
+ )
+ end
- vars = {}
+ def local_fetch_remote(storage_path, repository_relative_path, remote, ssh_auth: nil, forced: false, no_tags: false)
+ vars = { force: forced, tags: !no_tags }
if ssh_auth&.ssh_import?
if ssh_auth.ssh_key_auth? && ssh_auth.ssh_private_key.present?
- vars['GITLAB_SHELL_SSH_KEY'] = ssh_auth.ssh_private_key
+ vars[:ssh_key] = ssh_auth.ssh_private_key
end
if ssh_auth.ssh_known_hosts.present?
- vars['GITLAB_SHELL_KNOWN_HOSTS'] = ssh_auth.ssh_known_hosts
+ vars[:known_hosts] = ssh_auth.ssh_known_hosts
end
end
- gitlab_shell_fast_execute_raise_error(args, vars)
+ cmd = gitlab_projects(storage_path, repository_relative_path)
+
+ success = cmd.fetch_remote(remote, git_timeout, vars)
+
+ raise Error, cmd.output unless success
+
+ success
end
def gitlab_shell_fast_execute(cmd)
@@ -394,6 +441,10 @@ module Gitlab
Gitlab::GitalyClient::NamespaceService.new(storage)
end
+ def git_timeout
+ Gitlab.config.gitlab_shell.git_timeout
+ end
+
def gitaly_migrate(method, &block)
Gitlab::GitalyClient.migrate(method, &block)
rescue GRPC::NotFound, GRPC::BadStatus => e
diff --git a/lib/gitlab/sidekiq_config.rb b/lib/gitlab/sidekiq_config.rb
index dc9886732b5..c3d7814551c 100644
--- a/lib/gitlab/sidekiq_config.rb
+++ b/lib/gitlab/sidekiq_config.rb
@@ -1,16 +1,35 @@
require 'yaml'
+require 'set'
module Gitlab
module SidekiqConfig
- def self.redis_queues
- @redis_queues ||= Sidekiq::Queue.all.map(&:name)
+ # This method is called by `bin/sidekiq-cluster` in EE, which runs outside
+ # of bundler/Rails context, so we cannot use any gem or Rails methods.
+ def self.worker_queues(rails_path = Rails.root.to_s)
+ @worker_queues ||= {}
+ @worker_queues[rails_path] ||= YAML.load_file(File.join(rails_path, 'app/workers/all_queues.yml'))
end
# This method is called by `bin/sidekiq-cluster` in EE, which runs outside
# of bundler/Rails context, so we cannot use any gem or Rails methods.
- def self.config_queues(rails_path = Rails.root.to_s)
+ def self.expand_queues(queues, all_queues = self.worker_queues)
+ return [] if queues.empty?
+
+ queues_set = all_queues.to_set
+
+ queues.flat_map do |queue|
+ [queue, *queues_set.grep(/\A#{queue}:/)]
+ end
+ end
+
+ def self.redis_queues
+ # Not memoized, because this can change during the life of the application
+ Sidekiq::Queue.all.map(&:name)
+ end
+
+ def self.config_queues
@config_queues ||= begin
- config = YAML.load_file(File.join(rails_path, 'config', 'sidekiq_queues.yml'))
+ config = YAML.load_file(Rails.root.join('config/sidekiq_queues.yml'))
config[:queues].map(&:first)
end
end
@@ -23,14 +42,6 @@ module Gitlab
@workers ||= find_workers(Rails.root.join('app', 'workers'))
end
- def self.default_queues
- [ActionMailer::DeliveryJob.queue_name, 'default']
- end
-
- def self.worker_queues
- @worker_queues ||= (workers.map(&:queue) + default_queues).uniq
- end
-
def self.find_workers(root)
concerns = root.join('concerns').to_s
@@ -43,7 +54,7 @@ module Gitlab
ns.camelize.constantize
end
- # Skip concerns
+ # Skip things that aren't workers
workers.select { |w| w < Sidekiq::Worker }
end
end
diff --git a/lib/gitlab/sidekiq_versioning.rb b/lib/gitlab/sidekiq_versioning.rb
new file mode 100644
index 00000000000..9683214ec18
--- /dev/null
+++ b/lib/gitlab/sidekiq_versioning.rb
@@ -0,0 +1,25 @@
+module Gitlab
+ module SidekiqVersioning
+ def self.install!
+ Sidekiq::Manager.prepend SidekiqVersioning::Manager
+
+ # The Sidekiq client API always adds the queue to the Sidekiq queue
+ # list, but mail_room and gitlab-shell do not. This is only necessary
+ # for monitoring.
+ begin
+ queues = SidekiqConfig.worker_queues
+
+ if queues.any?
+ Sidekiq.redis do |conn|
+ conn.pipelined do
+ queues.each do |queue|
+ conn.sadd('queues', queue)
+ end
+ end
+ end
+ end
+ rescue ::Redis::BaseError, SocketError, Errno::ENOENT, Errno::EADDRNOTAVAIL, Errno::EAFNOSUPPORT, Errno::ECONNRESET, Errno::ECONNREFUSED
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_versioning/manager.rb b/lib/gitlab/sidekiq_versioning/manager.rb
new file mode 100644
index 00000000000..308be0fdf76
--- /dev/null
+++ b/lib/gitlab/sidekiq_versioning/manager.rb
@@ -0,0 +1,12 @@
+module Gitlab
+ module SidekiqVersioning
+ module Manager
+ def initialize(options = {})
+ options[:strict] = false
+ options[:queues] = SidekiqConfig.expand_queues(options[:queues])
+ Sidekiq.logger.info "Listening on queues #{options[:queues].uniq.sort}"
+ super
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/tcp_checker.rb b/lib/gitlab/tcp_checker.rb
new file mode 100644
index 00000000000..6e24e46d0ea
--- /dev/null
+++ b/lib/gitlab/tcp_checker.rb
@@ -0,0 +1,45 @@
+module Gitlab
+ class TcpChecker
+ attr_reader :remote_host, :remote_port, :local_host, :local_port, :error
+
+ def initialize(remote_host, remote_port, local_host = nil, local_port = nil)
+ @remote_host = remote_host
+ @remote_port = remote_port
+ @local_host = local_host
+ @local_port = local_port
+ end
+
+ def local
+ join_host_port(local_host, local_port)
+ end
+
+ def remote
+ join_host_port(remote_host, remote_port)
+ end
+
+ def check(timeout: 10)
+ Socket.tcp(
+ remote_host, remote_port,
+ local_host, local_port,
+ connect_timeout: timeout
+ ) do |sock|
+ @local_port, @local_host = Socket.unpack_sockaddr_in(sock.local_address)
+ @remote_port, @remote_host = Socket.unpack_sockaddr_in(sock.remote_address)
+ end
+
+ true
+ rescue => err
+ @error = err
+
+ false
+ end
+
+ private
+
+ def join_host_port(host, port)
+ host = "[#{host}]" if host.include?(':')
+
+ "#{host}:#{port}"
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/tcp_check.rake b/lib/tasks/gitlab/tcp_check.rake
new file mode 100644
index 00000000000..1400f57d6b9
--- /dev/null
+++ b/lib/tasks/gitlab/tcp_check.rake
@@ -0,0 +1,20 @@
+namespace :gitlab do
+ desc "GitLab | Check TCP connectivity to a specific host and port"
+ task :tcp_check, [:host, :port] => :environment do |_t, args|
+ unless args.host && args.port
+ puts "Please specify a host and port: `rake gitlab:tcp_check[example.com,80]`".color(:red)
+ exit 1
+ end
+
+ checker = Gitlab::TcpChecker.new(args.host, args.port)
+
+ if checker.check
+ puts "TCP connection from #{checker.local} to #{checker.remote} succeeded".color(:green)
+ else
+ puts "TCP connection to #{checker.remote} failed: #{checker.error}".color(:red)
+ puts
+ puts 'Check that host and port are correct, and that the traffic is permitted through any firewalls.'
+ exit 1
+ end
+ end
+end
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 9b6ffff7c4d..ed2ee73bea0 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -1,4 +1,4 @@
-FROM ruby:2.3
+FROM ruby:2.4
LABEL maintainer "Grzegorz Bizon <grzegorz@gitlab.com>"
ENV DEBIAN_FRONTEND noninteractive
diff --git a/qa/Gemfile b/qa/Gemfile
index ff29824529f..4c866a3f893 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -1,8 +1,8 @@
source 'https://rubygems.org'
-gem 'pry-byebug', '~> 3.4.1', platform: :mri
-gem 'capybara', '~> 2.12.1'
-gem 'capybara-screenshot', '~> 1.0.14'
-gem 'rake', '~> 12.0.0'
-gem 'rspec', '~> 3.5'
-gem 'selenium-webdriver', '~> 2.53'
+gem 'pry-byebug', '~> 3.5.1', platform: :mri
+gem 'capybara', '~> 2.16.1'
+gem 'capybara-screenshot', '~> 1.0.18'
+gem 'rake', '~> 12.3.0'
+gem 'rspec', '~> 3.7'
+gem 'selenium-webdriver', '~> 3.8.0'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 22d12b479cb..88d5fe834a0 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -1,78 +1,72 @@
GEM
remote: https://rubygems.org/
specs:
- addressable (2.5.0)
- public_suffix (~> 2.0, >= 2.0.2)
- byebug (9.0.6)
- capybara (2.12.1)
+ addressable (2.5.2)
+ public_suffix (>= 2.0.2, < 4.0)
+ byebug (9.1.0)
+ capybara (2.16.1)
addressable
- mime-types (>= 1.16)
+ mini_mime (>= 0.1.3)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
- capybara-screenshot (1.0.14)
+ capybara-screenshot (1.0.18)
capybara (>= 1.0, < 3)
launchy
- childprocess (0.7.0)
+ childprocess (0.8.0)
ffi (~> 1.0, >= 1.0.11)
- coderay (1.1.1)
+ coderay (1.1.2)
diff-lcs (1.3)
ffi (1.9.18)
launchy (2.4.3)
addressable (~> 2.3)
- method_source (0.8.2)
- mime-types (3.1)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0521)
+ method_source (0.9.0)
+ mini_mime (1.0.0)
mini_portile2 (2.3.0)
nokogiri (1.8.1)
mini_portile2 (~> 2.3.0)
- pry (0.10.4)
+ pry (0.11.3)
coderay (~> 1.1.0)
- method_source (~> 0.8.1)
- slop (~> 3.4)
- pry-byebug (3.4.2)
- byebug (~> 9.0)
+ method_source (~> 0.9.0)
+ pry-byebug (3.5.1)
+ byebug (~> 9.1)
pry (~> 0.10)
- public_suffix (2.0.5)
- rack (2.0.1)
- rack-test (0.6.3)
- rack (>= 1.0)
- rake (12.0.0)
- rspec (3.5.0)
- rspec-core (~> 3.5.0)
- rspec-expectations (~> 3.5.0)
- rspec-mocks (~> 3.5.0)
- rspec-core (3.5.4)
- rspec-support (~> 3.5.0)
- rspec-expectations (3.5.0)
+ public_suffix (3.0.1)
+ rack (2.0.3)
+ rack-test (0.8.2)
+ rack (>= 1.0, < 3)
+ rake (12.3.0)
+ rspec (3.7.0)
+ rspec-core (~> 3.7.0)
+ rspec-expectations (~> 3.7.0)
+ rspec-mocks (~> 3.7.0)
+ rspec-core (3.7.0)
+ rspec-support (~> 3.7.0)
+ rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.5.0)
- rspec-mocks (3.5.0)
+ rspec-support (~> 3.7.0)
+ rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.5.0)
- rspec-support (3.5.0)
+ rspec-support (~> 3.7.0)
+ rspec-support (3.7.0)
rubyzip (1.2.1)
- selenium-webdriver (2.53.4)
+ selenium-webdriver (3.8.0)
childprocess (~> 0.5)
rubyzip (~> 1.0)
- websocket (~> 1.0)
- slop (3.6.0)
- websocket (1.2.4)
- xpath (2.0.0)
+ xpath (2.1.0)
nokogiri (~> 1.3)
PLATFORMS
ruby
DEPENDENCIES
- capybara (~> 2.12.1)
- capybara-screenshot (~> 1.0.14)
- pry-byebug (~> 3.4.1)
- rake (~> 12.0.0)
- rspec (~> 3.5)
- selenium-webdriver (~> 2.53)
+ capybara (~> 2.16.1)
+ capybara-screenshot (~> 1.0.18)
+ pry-byebug (~> 3.5.1)
+ rake (~> 12.3.0)
+ rspec (~> 3.7)
+ selenium-webdriver (~> 3.8.0)
BUNDLED WITH
- 1.15.4
+ 1.16.0
diff --git a/rubocop/cop/include_sidekiq_worker.rb b/rubocop/cop/include_sidekiq_worker.rb
new file mode 100644
index 00000000000..4a6332286a2
--- /dev/null
+++ b/rubocop/cop/include_sidekiq_worker.rb
@@ -0,0 +1,29 @@
+require_relative '../spec_helpers'
+
+module RuboCop
+ module Cop
+ # Cop that makes sure workers include `ApplicationWorker`, not `Sidekiq::Worker`.
+ class IncludeSidekiqWorker < RuboCop::Cop::Cop
+ include SpecHelpers
+
+ MSG = 'Include `ApplicationWorker`, not `Sidekiq::Worker`.'.freeze
+
+ def_node_matcher :includes_sidekiq_worker?, <<~PATTERN
+ (send nil :include (const (const nil :Sidekiq) :Worker))
+ PATTERN
+
+ def on_send(node)
+ return if in_spec?(node)
+ return unless includes_sidekiq_worker?(node)
+
+ add_offense(node.arguments.first, :expression)
+ end
+
+ def autocorrect(node)
+ lambda do |corrector|
+ corrector.replace(node.source_range, 'ApplicationWorker')
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/cop/sidekiq_options_queue.rb b/rubocop/cop/sidekiq_options_queue.rb
new file mode 100644
index 00000000000..43b35ba0214
--- /dev/null
+++ b/rubocop/cop/sidekiq_options_queue.rb
@@ -0,0 +1,27 @@
+require_relative '../spec_helpers'
+
+module RuboCop
+ module Cop
+ # Cop that prevents manually setting a queue in Sidekiq workers.
+ class SidekiqOptionsQueue < RuboCop::Cop::Cop
+ include SpecHelpers
+
+ MSG = 'Do not manually set a queue; `ApplicationWorker` sets one automatically.'.freeze
+
+ def_node_matcher :sidekiq_options?, <<~PATTERN
+ (send nil :sidekiq_options $...)
+ PATTERN
+
+ def on_send(node)
+ return if in_spec?(node)
+ return unless sidekiq_options?(node)
+
+ node.arguments.first.each_node(:pair) do |pair|
+ key_name = pair.key.children[0]
+
+ add_offense(pair, :expression) if key_name == :queue
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index eb52be3d731..3e3b4c8349a 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -3,10 +3,12 @@ require_relative 'cop/active_record_serialize'
require_relative 'cop/custom_error_class'
require_relative 'cop/gem_fetcher'
require_relative 'cop/in_batches'
+require_relative 'cop/include_sidekiq_worker'
require_relative 'cop/line_break_after_guard_clauses'
require_relative 'cop/polymorphic_associations'
require_relative 'cop/project_path_helper'
require_relative 'cop/redirect_with_status'
+require_relative 'cop/sidekiq_options_queue'
require_relative 'cop/migration/add_column'
require_relative 'cop/migration/add_concurrent_foreign_key'
require_relative 'cop/migration/add_concurrent_index'
diff --git a/scripts/trigger-build-omnibus b/scripts/trigger-build-omnibus
index 4ff0e8e10b7..85ea4aa74ac 100755
--- a/scripts/trigger-build-omnibus
+++ b/scripts/trigger-build-omnibus
@@ -21,6 +21,7 @@ module Omnibus
if id
puts "Triggered https://gitlab.com/#{Omnibus::PROJECT_PATH}/pipelines/#{id}"
+ puts "Waiting for downstream pipeline status"
else
raise "Trigger failed! The response from the trigger is: #{res.body}"
end
@@ -39,7 +40,9 @@ module Omnibus
"ref" => ENV["OMNIBUS_BRANCH"] || "master",
"variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"],
"variables[ALTERNATIVE_SOURCES]" => true,
- "variables[ee]" => ee? ? 'true' : 'false'
+ "variables[ee]" => ee? ? 'true' : 'false',
+ "variables[TRIGGERED_USER]" => ENV["GITLAB_USER_NAME"],
+ "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}"
}
end
@@ -63,14 +66,14 @@ module Omnibus
def wait!
loop do
- raise 'Pipeline timeout!' if timeout?
+ raise "Pipeline timed out after waiting for #{duration} minutes!" if timeout?
case status
when :created, :pending, :running
- puts "Waiting another #{INTERVAL} seconds ..."
+ print "."
sleep INTERVAL
when :success
- puts "Omnibus pipeline succeeded!"
+ puts "Omnibus pipeline succeeded in #{duration} minutes!"
break
else
raise "Omnibus pipeline did not succeed!"
@@ -84,6 +87,10 @@ module Omnibus
Time.now.to_i > (@start + MAX_DURATION)
end
+ def duration
+ (Time.now.to_i - @start) / 60
+ end
+
def status
req = Net::HTTP::Get.new(@uri)
req['PRIVATE-TOKEN'] = ENV['GITLAB_QA_ACCESS_TOKEN']
diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb
index f4f12a095fc..4e2d8e8969e 100644
--- a/spec/factories/commits.rb
+++ b/spec/factories/commits.rb
@@ -2,15 +2,28 @@ require_relative '../support/repo_helpers'
FactoryGirl.define do
factory :commit do
- git_commit RepoHelpers.sample_commit
+ transient do
+ author nil
+ end
+
+ git_commit do
+ commit = RepoHelpers.sample_commit
+
+ if author
+ commit.author_email = author.email
+ commit.author_name = author.name
+ end
+
+ commit
+ end
project
initialize_with do
new(git_commit, project)
end
- after(:build) do |commit|
- allow(commit).to receive(:author).and_return build(:author)
+ after(:build) do |commit, evaluator|
+ allow(commit).to receive(:author).and_return(evaluator.author || build(:author))
end
trait :without_author do
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index c870910c8ea..77dcdf89f37 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -197,7 +197,7 @@ describe 'Commits' do
commits = project.repository.commits(branch_name)
commits.each do |commit|
- expect(page).to have_content("committed #{commit.committed_date.strftime("%b %d, %Y")}")
+ expect(page).to have_content("authored #{commit.authored_date.strftime("%b %d, %Y")}")
end
end
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index 6f916078b1a..94133c62b5c 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -13,7 +13,7 @@ feature 'Dashboard Todos' do
end
it 'shows "All done" message' do
- expect(page).to have_content 'Todos let you see what you should do next.'
+ expect(page).to have_content 'Todos let you see what you should do next'
end
end
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index c60883911f7..0848857ed1e 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -25,7 +25,7 @@ describe 'Profile account page', :js do
fill_in 'password', with: '12345678'
- page.within '.popup-dialog' do
+ page.within '.modal' do
click_button 'Delete account'
end
@@ -38,7 +38,7 @@ describe 'Profile account page', :js do
fill_in 'password', with: 'testing123'
- page.within '.popup-dialog' do
+ page.within '.modal' do
click_button 'Delete account'
end
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index 156293289dd..8f06328962e 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -20,7 +20,7 @@ feature 'Multi-file editor new directory', :js do
click_link('New directory')
- page.within('.popup-dialog') do
+ page.within('.modal') do
find('.form-control').set('foldername')
click_button('Create directory')
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index 8fb8476e631..bdebc12ef47 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -20,7 +20,7 @@ feature 'Multi-file editor new file', :js do
click_link('New file')
- page.within('.popup-dialog') do
+ page.within('.modal') do
find('.form-control').set('filename')
click_button('Create file')
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
new file mode 100644
index 00000000000..c8a17871508
--- /dev/null
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+feature 'Projects tree' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+
+ before do
+ project.add_master(user)
+ sign_in(user)
+
+ visit project_tree_path(project, 'master')
+ end
+
+ it 'renders tree table' do
+ expect(page).to have_selector('.tree-item')
+ expect(page).not_to have_selector('.label-lfs', text: 'LFS')
+ end
+
+ context 'LFS' do
+ before do
+ visit project_tree_path(project, File.join('master', 'files/lfs'))
+ end
+
+ it 'renders LFS badge on blob item' do
+ expect(page).to have_selector('.label-lfs', text: 'LFS')
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/contributor.json b/spec/fixtures/api/schemas/contributor.json
new file mode 100644
index 00000000000..e88470a2363
--- /dev/null
+++ b/spec/fixtures/api/schemas/contributor.json
@@ -0,0 +1,18 @@
+{
+ "type": "object",
+ "required" : [
+ "name",
+ "email",
+ "commits",
+ "additions",
+ "deletions"
+ ],
+ "properties" : {
+ "name": { "type": "string" },
+ "email": { "type": "string" },
+ "commits": { "type": "integer" },
+ "additions": { "type": "integer" },
+ "deletions": { "type": "integer" }
+ },
+ "additionalProperties": false
+}
diff --git a/spec/fixtures/api/schemas/contributors.json b/spec/fixtures/api/schemas/contributors.json
new file mode 100644
index 00000000000..a9f1d1ea64f
--- /dev/null
+++ b/spec/fixtures/api/schemas/contributors.json
@@ -0,0 +1,4 @@
+{
+ "type": "array",
+ "items": { "$ref": "contributor.json" }
+}
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 638cd8b07c8..71abb6da607 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -258,12 +258,23 @@ With inline diffs tags you can display {+ additions +} or [- deletions -].
The wrapping tags can be either curly braces or square brackets [+ additions +] or {- deletions -}.
-However the wrapping tags can not be mixed as such -
+Examples:
+```
+- {+ additions +}
+- [+ additions +]
+- {- deletions -}
+- [- deletions -]
+```
+
+However the wrapping tags cannot be mixed as such:
+
+```
- {+ additions +]
- [+ additions +}
-- {- delletions -]
-- [- delletions -}
+- {- deletions -]
+- [- deletions -}
+```
### Videos
diff --git a/spec/helpers/tree_helper_spec.rb b/spec/helpers/tree_helper_spec.rb
index c358ccae9c3..d3b1be599dd 100644
--- a/spec/helpers/tree_helper_spec.rb
+++ b/spec/helpers/tree_helper_spec.rb
@@ -9,6 +9,7 @@ describe TreeHelper do
before do
@id = sha
@project = project
+ @lfs_blob_ids = []
end
it 'displays all entries without a warning' do
diff --git a/spec/javascripts/activities_spec.js b/spec/javascripts/activities_spec.js
index e8c5f721423..fc9be14df8f 100644
--- a/spec/javascripts/activities_spec.js
+++ b/spec/javascripts/activities_spec.js
@@ -2,7 +2,7 @@
import 'vendor/jquery.endless-scroll';
import '~/pager';
-import '~/activities';
+import Activities from '~/activities';
(() => {
window.gon || (window.gon = {});
@@ -35,7 +35,7 @@ import '~/activities';
describe('Activities', () => {
beforeEach(() => {
loadFixtures(fixtureTemplate);
- new gl.Activities();
+ new Activities();
});
for (let i = 0; i < filters.length; i += 1) {
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
index 0f7bf9ec712..2e5b65f5610 100644
--- a/spec/javascripts/datetime_utility_spec.js
+++ b/spec/javascripts/datetime_utility_spec.js
@@ -1,110 +1,108 @@
import * as datetimeUtility from '~/lib/utils/datetime_utility';
-(() => {
- describe('Date time utils', () => {
- describe('timeFor', () => {
- it('returns `past due` when in past', () => {
- const date = new Date();
- date.setFullYear(date.getFullYear() - 1);
-
- expect(
- gl.utils.timeFor(date),
- ).toBe('Past due');
- });
-
- it('returns remaining time when in the future', () => {
- const date = new Date();
- date.setFullYear(date.getFullYear() + 1);
-
- // Add a day to prevent a transient error. If date is even 1 second
- // short of a full year, timeFor will return '11 months remaining'
- date.setDate(date.getDate() + 1);
-
- expect(
- gl.utils.timeFor(date),
- ).toBe('1 year remaining');
- });
+describe('Date time utils', () => {
+ describe('timeFor', () => {
+ it('returns `past due` when in past', () => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() - 1);
+
+ expect(
+ datetimeUtility.timeFor(date),
+ ).toBe('Past due');
});
- describe('get day name', () => {
- it('should return Sunday', () => {
- const day = gl.utils.getDayName(new Date('07/17/2016'));
- expect(day).toBe('Sunday');
- });
-
- it('should return Monday', () => {
- const day = gl.utils.getDayName(new Date('07/18/2016'));
- expect(day).toBe('Monday');
- });
-
- it('should return Tuesday', () => {
- const day = gl.utils.getDayName(new Date('07/19/2016'));
- expect(day).toBe('Tuesday');
- });
-
- it('should return Wednesday', () => {
- const day = gl.utils.getDayName(new Date('07/20/2016'));
- expect(day).toBe('Wednesday');
- });
-
- it('should return Thursday', () => {
- const day = gl.utils.getDayName(new Date('07/21/2016'));
- expect(day).toBe('Thursday');
- });
-
- it('should return Friday', () => {
- const day = gl.utils.getDayName(new Date('07/22/2016'));
- expect(day).toBe('Friday');
- });
-
- it('should return Saturday', () => {
- const day = gl.utils.getDayName(new Date('07/23/2016'));
- expect(day).toBe('Saturday');
- });
- });
+ it('returns remaining time when in the future', () => {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() + 1);
+
+ // Add a day to prevent a transient error. If date is even 1 second
+ // short of a full year, timeFor will return '11 months remaining'
+ date.setDate(date.getDate() + 1);
- describe('get day difference', () => {
- it('should return 7', () => {
- const firstDay = new Date('07/01/2016');
- const secondDay = new Date('07/08/2016');
- const difference = gl.utils.getDayDifference(firstDay, secondDay);
- expect(difference).toBe(7);
- });
-
- it('should return 31', () => {
- const firstDay = new Date('07/01/2016');
- const secondDay = new Date('08/01/2016');
- const difference = gl.utils.getDayDifference(firstDay, secondDay);
- expect(difference).toBe(31);
- });
-
- it('should return 365', () => {
- const firstDay = new Date('07/02/2015');
- const secondDay = new Date('07/01/2016');
- const difference = gl.utils.getDayDifference(firstDay, secondDay);
- expect(difference).toBe(365);
- });
+ expect(
+ datetimeUtility.timeFor(date),
+ ).toBe('1 year remaining');
});
});
- describe('timeIntervalInWords', () => {
- it('should return string with number of minutes and seconds', () => {
- expect(datetimeUtility.timeIntervalInWords(9.54)).toEqual('9 seconds');
- expect(datetimeUtility.timeIntervalInWords(1)).toEqual('1 second');
- expect(datetimeUtility.timeIntervalInWords(200)).toEqual('3 minutes 20 seconds');
- expect(datetimeUtility.timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds');
+ describe('get day name', () => {
+ it('should return Sunday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/17/2016'));
+ expect(day).toBe('Sunday');
+ });
+
+ it('should return Monday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/18/2016'));
+ expect(day).toBe('Monday');
+ });
+
+ it('should return Tuesday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/19/2016'));
+ expect(day).toBe('Tuesday');
+ });
+
+ it('should return Wednesday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/20/2016'));
+ expect(day).toBe('Wednesday');
+ });
+
+ it('should return Thursday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/21/2016'));
+ expect(day).toBe('Thursday');
+ });
+
+ it('should return Friday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/22/2016'));
+ expect(day).toBe('Friday');
+ });
+
+ it('should return Saturday', () => {
+ const day = datetimeUtility.getDayName(new Date('07/23/2016'));
+ expect(day).toBe('Saturday');
});
});
- describe('dateInWords', () => {
- const date = new Date('07/01/2016');
+ describe('get day difference', () => {
+ it('should return 7', () => {
+ const firstDay = new Date('07/01/2016');
+ const secondDay = new Date('07/08/2016');
+ const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
+ expect(difference).toBe(7);
+ });
- it('should return date in words', () => {
- expect(datetimeUtility.dateInWords(date)).toEqual('July 1, 2016');
+ it('should return 31', () => {
+ const firstDay = new Date('07/01/2016');
+ const secondDay = new Date('08/01/2016');
+ const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
+ expect(difference).toBe(31);
});
- it('should return abbreviated month name', () => {
- expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
+ it('should return 365', () => {
+ const firstDay = new Date('07/02/2015');
+ const secondDay = new Date('07/01/2016');
+ const difference = datetimeUtility.getDayDifference(firstDay, secondDay);
+ expect(difference).toBe(365);
});
});
-})();
+});
+
+describe('timeIntervalInWords', () => {
+ it('should return string with number of minutes and seconds', () => {
+ expect(datetimeUtility.timeIntervalInWords(9.54)).toEqual('9 seconds');
+ expect(datetimeUtility.timeIntervalInWords(1)).toEqual('1 second');
+ expect(datetimeUtility.timeIntervalInWords(200)).toEqual('3 minutes 20 seconds');
+ expect(datetimeUtility.timeIntervalInWords(6008)).toEqual('100 minutes 8 seconds');
+ });
+});
+
+describe('dateInWords', () => {
+ const date = new Date('07/01/2016');
+
+ it('should return date in words', () => {
+ expect(datetimeUtility.dateInWords(date)).toEqual('July 1, 2016');
+ });
+
+ it('should return abbreviated month name', () => {
+ expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
+ });
+});
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
index 5b64cbb2dfc..2f28c5bbf01 100644
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ b/spec/javascripts/deploy_keys/components/key_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import DeployKeysStore from '~/deploy_keys/store';
import key from '~/deploy_keys/components/key.vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
describe('Deploy keys key', () => {
let vm;
@@ -37,7 +38,7 @@ describe('Deploy keys key', () => {
it('renders human friendly formatted created date', () => {
expect(
vm.$el.querySelector('.key-created-at').textContent.trim(),
- ).toBe(`created ${gl.utils.getTimeago().format(deployKey.created_at)}`);
+ ).toBe(`created ${getTimeago().format(deployKey.created_at)}`);
});
it('shows edit button', () => {
diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js
index 2ce1a749a96..7a5c1da4d1d 100644
--- a/spec/javascripts/groups/components/item_actions_spec.js
+++ b/spec/javascripts/groups/components/item_actions_spec.js
@@ -36,27 +36,27 @@ describe('ItemActionsComponent', () => {
describe('methods', () => {
describe('onLeaveGroup', () => {
- it('should change `dialogStatus` prop to `true` which shows confirmation dialog', () => {
- expect(vm.dialogStatus).toBeFalsy();
+ it('should change `modalStatus` prop to `true` which shows confirmation dialog', () => {
+ expect(vm.modalStatus).toBeFalsy();
vm.onLeaveGroup();
- expect(vm.dialogStatus).toBeTruthy();
+ expect(vm.modalStatus).toBeTruthy();
});
});
describe('leaveGroup', () => {
- it('should change `dialogStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => {
+ it('should change `modalStatus` prop to `false` and emit `leaveGroup` event with required params when called with `leaveConfirmed` as `true`', () => {
spyOn(eventHub, '$emit');
- vm.dialogStatus = true;
+ vm.modalStatus = true;
vm.leaveGroup(true);
- expect(vm.dialogStatus).toBeFalsy();
+ expect(vm.modalStatus).toBeFalsy();
expect(eventHub.$emit).toHaveBeenCalledWith('leaveGroup', vm.group, vm.parentGroup);
});
- it('should change `dialogStatus` prop to `false` and should NOT emit `leaveGroup` event when called with `leaveConfirmed` as `false`', () => {
+ it('should change `modalStatus` prop to `false` and should NOT emit `leaveGroup` event when called with `leaveConfirmed` as `false`', () => {
spyOn(eventHub, '$emit');
- vm.dialogStatus = true;
+ vm.modalStatus = true;
vm.leaveGroup(false);
- expect(vm.dialogStatus).toBeFalsy();
+ expect(vm.modalStatus).toBeFalsy();
expect(eventHub.$emit).not.toHaveBeenCalled();
});
});
@@ -99,9 +99,9 @@ describe('ItemActionsComponent', () => {
newVm.$destroy();
});
- it('should show modal dialog when `dialogStatus` is set to `true`', () => {
- vm.dialogStatus = true;
- const modalDialogEl = vm.$el.querySelector('.modal.popup-dialog');
+ it('should show modal dialog when `modalStatus` is set to `true`', () => {
+ vm.modalStatus = true;
+ const modalDialogEl = vm.$el.querySelector('.modal');
expect(modalDialogEl).toBeDefined();
expect(modalDialogEl.querySelector('.modal-title').innerText.trim()).toBe('Are you sure?');
expect(modalDialogEl.querySelector('.btn.btn-warning').innerText.trim()).toBe('Leave');
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 729c3c29f22..7159148f8fa 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -272,10 +272,10 @@ describe('Issuable output', () => {
});
});
- it('opens recaptcha dialog if update rejected as spam', (done) => {
+ it('opens recaptcha modal if update rejected as spam', (done) => {
function mockScriptSrc() {
const recaptchaChild = vm.$children
- .find(child => child.$options._componentTag === 'recaptcha-dialog'); // eslint-disable-line no-underscore-dangle
+ .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle
recaptchaChild.scriptSrc = '//scriptsrc';
}
@@ -302,7 +302,7 @@ describe('Issuable output', () => {
.then(promise)
.then(() => setTimeoutPromise())
.then(() => {
- modal = vm.$el.querySelector('.js-recaptcha-dialog');
+ modal = vm.$el.querySelector('.js-recaptcha-modal');
expect(modal.style.display).not.toEqual('none');
expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js
index 2e000a1063f..0da25bdca9c 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/javascripts/issue_show/components/description_spec.js
@@ -54,7 +54,7 @@ describe('Description component', () => {
it('opens recaptcha dialog if update rejected as spam', (done) => {
let modal;
const recaptchaChild = vm.$children
- .find(child => child.$options._componentTag === 'recaptcha-dialog'); // eslint-disable-line no-underscore-dangle
+ .find(child => child.$options._componentTag === 'recaptcha-modal'); // eslint-disable-line no-underscore-dangle
recaptchaChild.scriptSrc = '//scriptsrc';
@@ -64,7 +64,7 @@ describe('Description component', () => {
vm.$nextTick()
.then(() => {
- modal = vm.$el.querySelector('.js-recaptcha-dialog');
+ modal = vm.$el.querySelector('.js-recaptcha-modal');
expect(modal.style.display).not.toEqual('none');
expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index 3636aac79a0..2cd2e63b15d 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -55,7 +55,7 @@ describe('Issue', function() {
}
function findElements(isIssueInitiallyOpen) {
- $boxClosed = $('div.status-box-closed');
+ $boxClosed = $('div.status-box-issue-closed');
expect($boxClosed).toExist();
expect($boxClosed).toHaveText('Closed');
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 2612c5fd7bc..e09b8dc7fc5 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -222,7 +222,6 @@ import '~/notes';
notes.note_ids = [];
notes.updatedNotesTrackingMap = {};
- spyOn(gl.utils, 'localTimeAgo');
spyOn(Notes, 'isNewNote').and.callThrough();
spyOn(Notes, 'isUpdatedNote').and.callThrough();
spyOn(Notes, 'animateAppendNote').and.callThrough();
@@ -349,7 +348,6 @@ import '~/notes';
]);
notes.note_ids = [];
- spyOn(gl.utils, 'localTimeAgo');
spyOn(Notes, 'isNewNote');
spyOn(Notes, 'animateAppendNote');
Notes.isNewNote.and.returnValue(true);
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 5505f983d71..72790eb215a 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -41,7 +41,7 @@ import '~/right_sidebar';
loadFixtures(fixtureName);
this.sidebar = new Sidebar;
$aside = $('.right-sidebar');
- $page = $('.page-with-sidebar');
+ $page = $('.layout-page');
$icon = $aside.find('i');
$toggle = $aside.find('.js-sidebar-toggle');
return $labelsIcon = $aside.find('.sidebar-collapsed-icon');
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index 1e4c2c9faad..206f95abc1a 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -1,7 +1,7 @@
/* eslint-disable space-before-function-paren, max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, comma-dangle, object-shorthand, prefer-template, quotes, new-parens, vars-on-top, new-cap, max-len */
import '~/gl_dropdown';
-import '~/search_autocomplete';
+import SearchAutocomplete from '~/search_autocomplete';
import '~/lib/utils/common_utils';
import * as urlUtils from '~/lib/utils/url_utility';
@@ -128,7 +128,7 @@ import * as urlUtils from '~/lib/utils/url_utility';
window.gon.current_user_id = userId;
window.gon.current_username = userName;
- return widget = new gl.SearchAutocomplete;
+ return widget = new SearchAutocomplete();
});
afterEach(function() {
diff --git a/spec/javascripts/syntax_highlight_spec.js b/spec/javascripts/syntax_highlight_spec.js
index 946f98379ce..763a15e710b 100644
--- a/spec/javascripts/syntax_highlight_spec.js
+++ b/spec/javascripts/syntax_highlight_spec.js
@@ -1,44 +1,42 @@
/* eslint-disable space-before-function-paren, no-var, no-return-assign, quotes */
-import '~/syntax_highlight';
+import syntaxHighlight from '~/syntax_highlight';
-(function() {
- describe('Syntax Highlighter', function() {
- var stubUserColorScheme;
- stubUserColorScheme = function(value) {
- if (window.gon == null) {
- window.gon = {};
- }
- return window.gon.user_color_scheme = value;
- };
- describe('on a js-syntax-highlight element', function() {
- beforeEach(function() {
- return setFixtures('<div class="js-syntax-highlight"></div>');
- });
- return it('applies syntax highlighting', function() {
- stubUserColorScheme('monokai');
- $('.js-syntax-highlight').syntaxHighlight();
- return expect($('.js-syntax-highlight')).toHaveClass('monokai');
- });
+describe('Syntax Highlighter', function() {
+ var stubUserColorScheme;
+ stubUserColorScheme = function(value) {
+ if (window.gon == null) {
+ window.gon = {};
+ }
+ return window.gon.user_color_scheme = value;
+ };
+ describe('on a js-syntax-highlight element', function() {
+ beforeEach(function() {
+ return setFixtures('<div class="js-syntax-highlight"></div>');
});
- return describe('on a parent element', function() {
- beforeEach(function() {
- return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>");
- });
- it('applies highlighting to all applicable children', function() {
- stubUserColorScheme('monokai');
- $('.parent').syntaxHighlight();
- expect($('.parent, .foo')).not.toHaveClass('monokai');
- return expect($('.monokai').length).toBe(2);
- });
- return it('prevents an infinite loop when no matches exist', function() {
- var highlight;
- setFixtures('<div></div>');
- highlight = function() {
- return $('div').syntaxHighlight();
- };
- return expect(highlight).not.toThrow();
- });
+ return it('applies syntax highlighting', function() {
+ stubUserColorScheme('monokai');
+ syntaxHighlight($('.js-syntax-highlight'));
+ return expect($('.js-syntax-highlight')).toHaveClass('monokai');
});
});
-}).call(window);
+ return describe('on a parent element', function() {
+ beforeEach(function() {
+ return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>");
+ });
+ it('applies highlighting to all applicable children', function() {
+ stubUserColorScheme('monokai');
+ syntaxHighlight($('.parent'));
+ expect($('.parent, .foo')).not.toHaveClass('monokai');
+ return expect($('.monokai').length).toBe(2);
+ });
+ return it('prevents an infinite loop when no matches exist', function() {
+ var highlight;
+ setFixtures('<div></div>');
+ highlight = function() {
+ return syntaxHighlight($('div'));
+ };
+ return expect(highlight).not.toThrow();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
index d5b8947c86f..db7d083065b 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import * as urlUtils from '~/lib/utils/url_utility';
import deploymentComponent from '~/vue_merge_request_widget/components/mr_widget_deployment';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
+import { getTimeago } from '~/lib/utils/datetime_utility';
const deploymentMockData = [
{
@@ -49,7 +50,7 @@ describe('MRWidgetDeployment', () => {
describe('formatDate', () => {
it('should work', () => {
- const readable = gl.utils.getTimeago().format(deployment.deployed_at);
+ const readable = getTimeago().format(deployment.deployed_at);
expect(vm.formatDate(deployment.deployed_at)).toEqual(readable);
});
});
diff --git a/spec/javascripts/vue_shared/components/popup_dialog_spec.js b/spec/javascripts/vue_shared/components/modal_spec.js
index 5c1d2a196f4..721f4044659 100644
--- a/spec/javascripts/vue_shared/components/popup_dialog_spec.js
+++ b/spec/javascripts/vue_shared/components/modal_spec.js
@@ -1,11 +1,11 @@
import Vue from 'vue';
-import PopupDialog from '~/vue_shared/components/popup_dialog.vue';
+import modal from '~/vue_shared/components/modal.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
-describe('PopupDialog', () => {
+describe('Modal', () => {
it('does not render a primary button if no primaryButtonLabel', () => {
- const popupDialog = Vue.extend(PopupDialog);
- const vm = mountComponent(popupDialog);
+ const modalComponent = Vue.extend(modal);
+ const vm = mountComponent(modalComponent);
expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
});
diff --git a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
index b4c1f70ed1e..b4fb568f1d4 100644
--- a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
+++ b/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import timeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
-import '~/lib/utils/datetime_utility';
+import { formatDate, getTimeago } from '~/lib/utils/datetime_utility';
describe('Time ago with tooltip component', () => {
let TimeagoTooltip;
@@ -24,10 +24,10 @@ describe('Time ago with tooltip component', () => {
expect(vm.$el.tagName).toEqual('TIME');
expect(
vm.$el.getAttribute('data-original-title'),
- ).toEqual(gl.utils.formatDate('2017-05-08T14:57:39.781Z'));
+ ).toEqual(formatDate('2017-05-08T14:57:39.781Z'));
expect(vm.$el.getAttribute('data-placement')).toEqual('top');
- const timeago = gl.utils.getTimeago();
+ const timeago = getTimeago();
expect(vm.$el.textContent.trim()).toEqual(timeago.format('2017-05-08T14:57:39.781Z'));
});
diff --git a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
index 79d2c071446..e1c4f9cfea7 100644
--- a/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
+++ b/spec/lib/gitlab/background_migration/create_fork_network_memberships_range_spec.rb
@@ -2,19 +2,20 @@ require 'spec_helper'
describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migration, schema: 20170929131201 do
let(:migration) { described_class.new }
+ let(:projects) { table(:projects) }
- let(:base1) { create(:project) }
- let(:base1_fork1) { create(:project) }
- let(:base1_fork2) { create(:project) }
+ let(:base1) { projects.create }
+ let(:base1_fork1) { projects.create }
+ let(:base1_fork2) { projects.create }
- let(:base2) { create(:project) }
- let(:base2_fork1) { create(:project) }
- let(:base2_fork2) { create(:project) }
+ let(:base2) { projects.create }
+ let(:base2_fork1) { projects.create }
+ let(:base2_fork2) { projects.create }
- let(:fork_of_fork) { create(:project) }
- let(:fork_of_fork2) { create(:project) }
- let(:second_level_fork) { create(:project) }
- let(:third_level_fork) { create(:project) }
+ let(:fork_of_fork) { projects.create }
+ let(:fork_of_fork2) { projects.create }
+ let(:second_level_fork) { projects.create }
+ let(:third_level_fork) { projects.create }
let(:fork_network1) { fork_networks.find_by(root_project_id: base1.id) }
let(:fork_network2) { fork_networks.find_by(root_project_id: base2.id) }
@@ -97,7 +98,7 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat
end
it 'does not miss members for forks of forks for which the root was deleted' do
- forked_project_links.create(id: 9, forked_from_project_id: base1_fork1.id, forked_to_project_id: create(:project).id)
+ forked_project_links.create(id: 9, forked_from_project_id: base1_fork1.id, forked_to_project_id: projects.create.id)
base1.destroy
expect(migration.missing_members?(7, 10)).to be_falsy
@@ -105,8 +106,8 @@ describe Gitlab::BackgroundMigration::CreateForkNetworkMembershipsRange, :migrat
context 'with more forks' do
before do
- forked_project_links.create(id: 9, forked_from_project_id: fork_of_fork.id, forked_to_project_id: create(:project).id)
- forked_project_links.create(id: 10, forked_from_project_id: fork_of_fork.id, forked_to_project_id: create(:project).id)
+ forked_project_links.create(id: 9, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id)
+ forked_project_links.create(id: 10, forked_from_project_id: fork_of_fork.id, forked_to_project_id: projects.create.id)
end
it 'only processes a single batch of links at a time' do
diff --git a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
index cb52d971047..3998ca940a4 100644
--- a/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_events_to_push_event_payloads_spec.rb
@@ -225,7 +225,8 @@ describe Gitlab::BackgroundMigration::MigrateEventsToPushEventPayloads, :migrati
let(:user_class) { table(:users) }
let(:author) { build(:user).becomes(user_class).tap(&:save!).becomes(User) }
let(:namespace) { create(:namespace, owner: author) }
- let(:project) { create(:project_empty_repo, namespace: namespace, creator: author) }
+ let(:projects) { table(:projects) }
+ let(:project) { projects.create(namespace_id: namespace.id, creator_id: author.id) }
# We can not rely on FactoryGirl as the state of Event may change in ways that
# the background migration does not expect, hence we use the Event class of
diff --git a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb b/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb
index e52baf8dde7..8582af96199 100644
--- a/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_fork_networks_range_spec.rb
@@ -2,10 +2,11 @@ require 'spec_helper'
describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, schema: 20170929131201 do
let(:migration) { described_class.new }
- let(:base1) { create(:project) }
+ let(:projects) { table(:projects) }
+ let(:base1) { projects.create }
- let(:base2) { create(:project) }
- let(:base2_fork1) { create(:project) }
+ let(:base2) { projects.create }
+ let(:base2_fork1) { projects.create }
let!(:forked_project_links) { table(:forked_project_links) }
let!(:fork_networks) { table(:fork_networks) }
@@ -18,10 +19,10 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch
# A normal fork link
forked_project_links.create(id: 1,
forked_from_project_id: base1.id,
- forked_to_project_id: create(:project).id)
+ forked_to_project_id: projects.create.id)
forked_project_links.create(id: 2,
forked_from_project_id: base1.id,
- forked_to_project_id: create(:project).id)
+ forked_to_project_id: projects.create.id)
forked_project_links.create(id: 3,
forked_from_project_id: base2.id,
forked_to_project_id: base2_fork1.id)
@@ -29,10 +30,10 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch
# create a fork of a fork
forked_project_links.create(id: 4,
forked_from_project_id: base2_fork1.id,
- forked_to_project_id: create(:project).id)
+ forked_to_project_id: projects.create.id)
forked_project_links.create(id: 5,
- forked_from_project_id: create(:project).id,
- forked_to_project_id: create(:project).id)
+ forked_from_project_id: projects.create.id,
+ forked_to_project_id: projects.create.id)
# Stub out the calls to the other migrations
allow(BackgroundMigrationWorker).to receive(:perform_in)
@@ -63,7 +64,7 @@ describe Gitlab::BackgroundMigration::PopulateForkNetworksRange, :migration, sch
end
it 'creates a fork network for the fork of which the source was deleted' do
- fork = create(:project)
+ fork = projects.create
forked_project_links.create(id: 6, forked_from_project_id: 99999, forked_to_project_id: fork.id)
migration.perform(5, 8)
diff --git a/spec/lib/gitlab/git/gitlab_projects_spec.rb b/spec/lib/gitlab/git/gitlab_projects_spec.rb
new file mode 100644
index 00000000000..18906955df6
--- /dev/null
+++ b/spec/lib/gitlab/git/gitlab_projects_spec.rb
@@ -0,0 +1,309 @@
+require 'spec_helper'
+
+describe Gitlab::Git::GitlabProjects do
+ after do
+ TestEnv.clean_test_path
+ end
+
+ let(:project) { create(:project, :repository) }
+
+ if $VERBOSE
+ let(:logger) { Logger.new(STDOUT) }
+ else
+ let(:logger) { double('logger').as_null_object }
+ end
+
+ let(:tmp_repos_path) { TestEnv.repos_path }
+ let(:repo_name) { project.disk_path + '.git' }
+ let(:tmp_repo_path) { File.join(tmp_repos_path, repo_name) }
+ let(:gl_projects) { build_gitlab_projects(tmp_repos_path, repo_name) }
+
+ describe '#initialize' do
+ it { expect(gl_projects.shard_path).to eq(tmp_repos_path) }
+ it { expect(gl_projects.repository_relative_path).to eq(repo_name) }
+ it { expect(gl_projects.repository_absolute_path).to eq(File.join(tmp_repos_path, repo_name)) }
+ it { expect(gl_projects.logger).to eq(logger) }
+ end
+
+ describe '#mv_project' do
+ let(:new_repo_path) { File.join(tmp_repos_path, 'repo.git') }
+
+ it 'moves a repo directory' do
+ expect(File.exist?(tmp_repo_path)).to be_truthy
+
+ message = "Moving repository from <#{tmp_repo_path}> to <#{new_repo_path}>."
+ expect(logger).to receive(:info).with(message)
+
+ expect(gl_projects.mv_project('repo.git')).to be_truthy
+
+ expect(File.exist?(tmp_repo_path)).to be_falsy
+ expect(File.exist?(new_repo_path)).to be_truthy
+ end
+
+ it "fails if the source path doesn't exist" do
+ expect(logger).to receive(:error).with("mv-project failed: source path <#{tmp_repos_path}/bad-src.git> does not exist.")
+
+ result = build_gitlab_projects(tmp_repos_path, 'bad-src.git').mv_project('repo.git')
+ expect(result).to be_falsy
+ end
+
+ it 'fails if the destination path already exists' do
+ FileUtils.mkdir_p(File.join(tmp_repos_path, 'already-exists.git'))
+
+ message = "mv-project failed: destination path <#{tmp_repos_path}/already-exists.git> already exists."
+ expect(logger).to receive(:error).with(message)
+
+ expect(gl_projects.mv_project('already-exists.git')).to be_falsy
+ end
+ end
+
+ describe '#rm_project' do
+ it 'removes a repo directory' do
+ expect(File.exist?(tmp_repo_path)).to be_truthy
+ expect(logger).to receive(:info).with("Removing repository <#{tmp_repo_path}>.")
+
+ expect(gl_projects.rm_project).to be_truthy
+
+ expect(File.exist?(tmp_repo_path)).to be_falsy
+ end
+ end
+
+ describe '#push_branches' do
+ let(:remote_name) { 'remote-name' }
+ let(:branch_name) { 'master' }
+ let(:cmd) { %W(git push -- #{remote_name} #{branch_name}) }
+ let(:force) { false }
+
+ subject { gl_projects.push_branches(remote_name, 600, force, [branch_name]) }
+
+ it 'executes the command' do
+ stub_spawn(cmd, 600, tmp_repo_path, success: true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'fails' do
+ stub_spawn(cmd, 600, tmp_repo_path, success: false)
+
+ is_expected.to be_falsy
+ end
+
+ context 'with --force' do
+ let(:cmd) { %W(git push --force -- #{remote_name} #{branch_name}) }
+ let(:force) { true }
+
+ it 'executes the command' do
+ stub_spawn(cmd, 600, tmp_repo_path, success: true)
+
+ is_expected.to be_truthy
+ end
+ end
+ end
+
+ describe '#fetch_remote' do
+ let(:remote_name) { 'remote-name' }
+ let(:branch_name) { 'master' }
+ let(:force) { false }
+ let(:tags) { true }
+ let(:args) { { force: force, tags: tags }.merge(extra_args) }
+ let(:extra_args) { {} }
+ let(:cmd) { %W(git fetch #{remote_name} --prune --quiet --tags) }
+
+ subject { gl_projects.fetch_remote(remote_name, 600, args) }
+
+ def stub_tempfile(name, filename, opts = {})
+ chmod = opts.delete(:chmod)
+ file = StringIO.new
+
+ allow(file).to receive(:close!)
+ allow(file).to receive(:path).and_return(name)
+
+ expect(Tempfile).to receive(:new).with(filename).and_return(file)
+ expect(file).to receive(:chmod).with(chmod) if chmod
+
+ file
+ end
+
+ context 'with default args' do
+ it 'executes the command' do
+ stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'fails' do
+ stub_spawn(cmd, 600, tmp_repo_path, {}, success: false)
+
+ is_expected.to be_falsy
+ end
+ end
+
+ context 'with --force' do
+ let(:force) { true }
+ let(:cmd) { %W(git fetch #{remote_name} --prune --quiet --force --tags) }
+
+ it 'executes the command with forced option' do
+ stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
+
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'with --no-tags' do
+ let(:tags) { false }
+ let(:cmd) { %W(git fetch #{remote_name} --prune --quiet --no-tags) }
+
+ it 'executes the command' do
+ stub_spawn(cmd, 600, tmp_repo_path, {}, success: true)
+
+ is_expected.to be_truthy
+ end
+ end
+
+ describe 'with an SSH key' do
+ let(:extra_args) { { ssh_key: 'SSH KEY' } }
+
+ it 'sets GIT_SSH to a custom script' do
+ script = stub_tempfile('scriptFile', 'gitlab-shell-ssh-wrapper', chmod: 0o755)
+ key = stub_tempfile('/tmp files/keyFile', 'gitlab-shell-key-file', chmod: 0o400)
+
+ stub_spawn(cmd, 600, tmp_repo_path, { 'GIT_SSH' => 'scriptFile' }, success: true)
+
+ is_expected.to be_truthy
+
+ expect(script.string).to eq("#!/bin/sh\nexec ssh '-oIdentityFile=\"/tmp files/keyFile\"' '-oIdentitiesOnly=\"yes\"' \"$@\"")
+ expect(key.string).to eq('SSH KEY')
+ end
+ end
+
+ describe 'with known_hosts data' do
+ let(:extra_args) { { known_hosts: 'KNOWN HOSTS' } }
+
+ it 'sets GIT_SSH to a custom script' do
+ script = stub_tempfile('scriptFile', 'gitlab-shell-ssh-wrapper', chmod: 0o755)
+ key = stub_tempfile('/tmp files/knownHosts', 'gitlab-shell-known-hosts', chmod: 0o400)
+
+ stub_spawn(cmd, 600, tmp_repo_path, { 'GIT_SSH' => 'scriptFile' }, success: true)
+
+ is_expected.to be_truthy
+
+ expect(script.string).to eq("#!/bin/sh\nexec ssh '-oStrictHostKeyChecking=\"yes\"' '-oUserKnownHostsFile=\"/tmp files/knownHosts\"' \"$@\"")
+ expect(key.string).to eq('KNOWN HOSTS')
+ end
+ end
+ end
+
+ describe '#import_project' do
+ let(:project) { create(:project) }
+ let(:import_url) { TestEnv.factory_repo_path_bare }
+ let(:cmd) { %W(git clone --bare -- #{import_url} #{tmp_repo_path}) }
+ let(:timeout) { 600 }
+
+ subject { gl_projects.import_project(import_url, timeout) }
+
+ context 'success import' do
+ it 'imports a repo' do
+ expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
+
+ message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>."
+ expect(logger).to receive(:info).with(message)
+
+ is_expected.to be_truthy
+
+ expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_truthy
+ end
+ end
+
+ context 'already exists' do
+ it "doesn't import" do
+ FileUtils.mkdir_p(tmp_repo_path)
+
+ is_expected.to be_falsy
+ end
+ end
+
+ context 'timeout' do
+ it 'does not import a repo' do
+ stub_spawn_timeout(cmd, timeout, nil)
+
+ message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed."
+ expect(logger).to receive(:error).with(message)
+
+ is_expected.to be_falsy
+
+ expect(gl_projects.output).to eq("Timed out\n")
+ expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
+ end
+ end
+ end
+
+ describe '#fork_repository' do
+ let(:dest_repos_path) { tmp_repos_path }
+ let(:dest_repo_name) { File.join('@hashed', 'aa', 'bb', 'xyz.git') }
+ let(:dest_repo) { File.join(dest_repos_path, dest_repo_name) }
+ let(:dest_namespace) { File.dirname(dest_repo) }
+
+ subject { gl_projects.fork_repository(dest_repos_path, dest_repo_name) }
+
+ before do
+ FileUtils.mkdir_p(dest_repos_path)
+ end
+
+ after do
+ FileUtils.rm_rf(dest_repos_path)
+ end
+
+ it 'forks the repository' do
+ message = "Forking repository from <#{tmp_repo_path}> to <#{dest_repo}>."
+ expect(logger).to receive(:info).with(message)
+
+ is_expected.to be_truthy
+
+ expect(File.exist?(dest_repo)).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy
+ end
+
+ it 'does not fork if a project of the same name already exists' do
+ # create a fake project at the intended destination
+ FileUtils.mkdir_p(dest_repo)
+
+ # trying to fork again should fail as the repo already exists
+ message = "fork-repository failed: destination repository <#{dest_repo}> already exists."
+ expect(logger).to receive(:error).with(message)
+
+ is_expected.to be_falsy
+ end
+
+ context 'different storages' do
+ let(:dest_repos_path) { File.join(File.dirname(tmp_repos_path), 'alternative') }
+
+ it 'forks the repo' do
+ is_expected.to be_truthy
+
+ expect(File.exist?(dest_repo)).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy
+ end
+ end
+ end
+
+ def build_gitlab_projects(*args)
+ described_class.new(
+ *args,
+ global_hooks_path: Gitlab.config.gitlab_shell.hooks_path,
+ logger: logger
+ )
+ end
+
+ def stub_spawn(*args, success: true)
+ exitstatus = success ? 0 : nil
+ expect(gl_projects).to receive(:popen_with_timeout).with(*args)
+ .and_return(["output", exitstatus])
+ end
+
+ def stub_spawn_timeout(*args)
+ expect(gl_projects).to receive(:popen_with_timeout).with(*args)
+ .and_raise(Timeout::Error)
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index e6845420f7d..03a9cc488ca 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -19,6 +19,51 @@ describe Gitlab::Git::Repository, seed_helper: true do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
+ describe '.create_hooks' do
+ let(:repo_path) { File.join(TestEnv.repos_path, 'hook-test.git') }
+ let(:hooks_dir) { File.join(repo_path, 'hooks') }
+ let(:target_hooks_dir) { Gitlab.config.gitlab_shell.hooks_path }
+ let(:existing_target) { File.join(repo_path, 'foobar') }
+
+ before do
+ FileUtils.rm_rf(repo_path)
+ FileUtils.mkdir_p(repo_path)
+ end
+
+ context 'hooks is a directory' do
+ let(:existing_file) { File.join(hooks_dir, 'my-file') }
+
+ before do
+ FileUtils.mkdir_p(hooks_dir)
+ FileUtils.touch(existing_file)
+ described_class.create_hooks(repo_path, target_hooks_dir)
+ end
+
+ it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) }
+ it { expect(Dir[File.join(repo_path, "hooks.old.*/my-file")].count).to eq(1) }
+ end
+
+ context 'hooks is a valid symlink' do
+ before do
+ FileUtils.mkdir_p existing_target
+ File.symlink(existing_target, hooks_dir)
+ described_class.create_hooks(repo_path, target_hooks_dir)
+ end
+
+ it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) }
+ end
+
+ context 'hooks is a broken symlink' do
+ before do
+ FileUtils.rm_f(existing_target)
+ File.symlink(existing_target, hooks_dir)
+ described_class.create_hooks(repo_path, target_hooks_dir)
+ end
+
+ it { expect(File.readlink(hooks_dir)).to eq(target_hooks_dir) }
+ end
+ end
+
describe "Respond to" do
subject { repository }
diff --git a/spec/lib/gitlab/metrics/method_call_spec.rb b/spec/lib/gitlab/metrics/method_call_spec.rb
index 5341addf911..78767d06462 100644
--- a/spec/lib/gitlab/metrics/method_call_spec.rb
+++ b/spec/lib/gitlab/metrics/method_call_spec.rb
@@ -20,9 +20,39 @@ describe Gitlab::Metrics::MethodCall do
context 'prometheus instrumentation is enabled' do
before do
+ allow(Feature.get(:prometheus_metrics_method_instrumentation)).to receive(:enabled?).and_call_original
+ described_class.measurement_enabled_cache_expires_at.value = Time.now.to_i - 1
Feature.get(:prometheus_metrics_method_instrumentation).enable
end
+ around do |example|
+ Timecop.freeze do
+ example.run
+ end
+ end
+
+ it 'caches subsequent invocations of feature check' do
+ 10.times do
+ method_call.measure { 'foo' }
+ end
+
+ expect(Feature.get(:prometheus_metrics_method_instrumentation)).to have_received(:enabled?).once
+ end
+
+ it 'expires feature check cache after 1 minute' do
+ method_call.measure { 'foo' }
+
+ Timecop.travel(1.minute.from_now) do
+ method_call.measure { 'foo' }
+ end
+
+ Timecop.travel(1.minute.from_now + 1.second) do
+ method_call.measure { 'foo' }
+ end
+
+ expect(Feature.get(:prometheus_metrics_method_instrumentation)).to have_received(:enabled?).twice
+ end
+
it 'observes the performance of the supplied block' do
expect(described_class.call_duration_histogram)
.to receive(:observe)
@@ -34,6 +64,8 @@ describe Gitlab::Metrics::MethodCall do
context 'prometheus instrumentation is disabled' do
before do
+ described_class.measurement_enabled_cache_expires_at.value = Time.now.to_i - 1
+
Feature.get(:prometheus_metrics_method_instrumentation).disable
end
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index eec6858a5de..dd779b04741 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -2,12 +2,19 @@ require 'spec_helper'
require 'stringio'
describe Gitlab::Shell do
- let(:project) { double('Project', id: 7, path: 'diaspora') }
+ set(:project) { create(:project, :repository) }
+
let(:gitlab_shell) { described_class.new }
let(:popen_vars) { { 'GIT_TERMINAL_PROMPT' => ENV['GIT_TERMINAL_PROMPT'] } }
+ let(:gitlab_projects) { double('gitlab_projects') }
+ let(:timeout) { Gitlab.config.gitlab_shell.git_timeout }
before do
allow(Project).to receive(:find).and_return(project)
+
+ allow(gitlab_shell).to receive(:gitlab_projects)
+ .with(project.repository_storage_path, project.disk_path + '.git')
+ .and_return(gitlab_projects)
end
it { is_expected.to respond_to :add_key }
@@ -44,38 +51,6 @@ describe Gitlab::Shell do
end
end
- describe 'projects commands' do
- let(:gitlab_shell_path) { File.expand_path('tmp/tests/gitlab-shell') }
- let(:projects_path) { File.join(gitlab_shell_path, 'bin/gitlab-projects') }
- let(:gitlab_shell_hooks_path) { File.join(gitlab_shell_path, 'hooks') }
-
- before do
- allow(Gitlab.config.gitlab_shell).to receive(:path).and_return(gitlab_shell_path)
- allow(Gitlab.config.gitlab_shell).to receive(:hooks_path).and_return(gitlab_shell_hooks_path)
- allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800)
- end
-
- describe '#mv_repository' do
- it 'executes the command' do
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [projects_path, 'mv-project', 'storage/path', 'project/path.git', 'new/path.git']
- )
- gitlab_shell.mv_repository('storage/path', 'project/path', 'new/path')
- end
- end
-
- describe '#add_key' do
- it 'removes trailing garbage' do
- allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar']
- )
-
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
- end
- end
- end
-
describe Gitlab::Shell::KeyAdder do
describe '#add_key' do
it 'removes trailing garbage' do
@@ -121,6 +96,17 @@ describe Gitlab::Shell do
allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800)
end
+ describe '#add_key' do
+ it 'removes trailing garbage' do
+ allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
+ expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
+ [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar']
+ )
+
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
+ end
+ end
+
describe '#add_repository' do
shared_examples '#add_repository' do
let(:repository_storage) { 'default' }
@@ -162,83 +148,76 @@ describe Gitlab::Shell do
end
describe '#remove_repository' do
+ subject { gitlab_shell.remove_repository(project.repository_storage_path, project.disk_path) }
+
it 'returns true when the command succeeds' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'],
- nil, popen_vars).and_return([nil, 0])
+ expect(gitlab_projects).to receive(:rm_project) { true }
- expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be true
+ is_expected.to be_truthy
end
it 'returns false when the command fails' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'rm-project', 'current/storage', 'project/path.git'],
- nil, popen_vars).and_return(["error", 1])
+ expect(gitlab_projects).to receive(:rm_project) { false }
- expect(gitlab_shell.remove_repository('current/storage', 'project/path')).to be false
+ is_expected.to be_falsy
end
end
describe '#mv_repository' do
it 'returns true when the command succeeds' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'],
- nil, popen_vars).and_return([nil, 0])
+ expect(gitlab_projects).to receive(:mv_project).with('project/newpath.git') { true }
- expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be true
+ expect(gitlab_shell.mv_repository(project.repository_storage_path, project.disk_path, 'project/newpath')).to be_truthy
end
it 'returns false when the command fails' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'mv-project', 'current/storage', 'project/path.git', 'project/newpath.git'],
- nil, popen_vars).and_return(["error", 1])
+ expect(gitlab_projects).to receive(:mv_project).with('project/newpath.git') { false }
- expect(gitlab_shell.mv_repository('current/storage', 'project/path', 'project/newpath')).to be false
+ expect(gitlab_shell.mv_repository(project.repository_storage_path, project.disk_path, 'project/newpath')).to be_falsy
end
end
describe '#fork_repository' do
+ subject do
+ gitlab_shell.fork_repository(
+ project.repository_storage_path,
+ project.disk_path,
+ 'new/storage',
+ 'fork/path'
+ )
+ end
+
it 'returns true when the command succeeds' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'],
- nil, popen_vars).and_return([nil, 0])
+ expect(gitlab_projects).to receive(:fork_repository).with('new/storage', 'fork/path.git') { true }
- expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be true
+ is_expected.to be_truthy
end
it 'return false when the command fails' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'fork-repository', 'current/storage', 'project/path.git', 'new/storage', 'fork/path.git'],
- nil, popen_vars).and_return(["error", 1])
+ expect(gitlab_projects).to receive(:fork_repository).with('new/storage', 'fork/path.git') { false }
- expect(gitlab_shell.fork_repository('current/storage', 'project/path', 'new/storage', 'fork/path')).to be false
+ is_expected.to be_falsy
end
end
shared_examples 'fetch_remote' do |gitaly_on|
- let(:project2) { create(:project, :repository) }
- let(:repository) { project2.repository }
+ let(:repository) { project.repository }
def fetch_remote(ssh_auth = nil)
- gitlab_shell.fetch_remote(repository.raw_repository, 'new/storage', ssh_auth: ssh_auth)
+ gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', ssh_auth: ssh_auth)
end
- def expect_popen(fail = false, vars = {})
- popen_args = [
- projects_path,
- 'fetch-remote',
- TestEnv.repos_path,
- repository.relative_path,
- 'new/storage',
- Gitlab.config.gitlab_shell.git_timeout.to_s
- ]
-
- return_value = fail ? ["error", 1] : [nil, 0]
+ def expect_gitlab_projects(fail = false, options = {})
+ expect(gitlab_projects).to receive(:fetch_remote).with(
+ 'remote-name',
+ timeout,
+ options
+ ).and_return(!fail)
- expect(Gitlab::Popen).to receive(:popen).with(popen_args, nil, popen_vars.merge(vars)).and_return(return_value)
+ allow(gitlab_projects).to receive(:output).and_return('error') if fail
end
- def expect_gitaly_call(fail, vars = {})
+ def expect_gitaly_call(fail, options = {})
receive_fetch_remote =
if fail
receive(:fetch_remote).and_raise(GRPC::NotFound)
@@ -250,12 +229,12 @@ describe Gitlab::Shell do
end
if gitaly_on
- def expect_call(fail, vars = {})
- expect_gitaly_call(fail, vars)
+ def expect_call(fail, options = {})
+ expect_gitaly_call(fail, options)
end
else
- def expect_call(fail, vars = {})
- expect_popen(fail, vars)
+ def expect_call(fail, options = {})
+ expect_gitlab_projects(fail, options)
end
end
@@ -271,20 +250,27 @@ describe Gitlab::Shell do
end
it 'returns true when the command succeeds' do
- expect_call(false)
+ expect_call(false, force: false, tags: true)
expect(fetch_remote).to be_truthy
end
it 'raises an exception when the command fails' do
- expect_call(true)
+ expect_call(true, force: false, tags: true)
expect { fetch_remote }.to raise_error(Gitlab::Shell::Error)
end
+ it 'allows forced and no_tags to be changed' do
+ expect_call(false, force: true, tags: false)
+
+ result = gitlab_shell.fetch_remote(repository.raw_repository, 'remote-name', forced: true, no_tags: true)
+ expect(result).to be_truthy
+ end
+
context 'SSH auth' do
it 'passes the SSH key if specified' do
- expect_call(false, 'GITLAB_SHELL_SSH_KEY' => 'foo')
+ expect_call(false, force: false, tags: true, ssh_key: 'foo')
ssh_auth = build_ssh_auth(ssh_key_auth?: true, ssh_private_key: 'foo')
@@ -292,7 +278,7 @@ describe Gitlab::Shell do
end
it 'does not pass an empty SSH key' do
- expect_call(false)
+ expect_call(false, force: false, tags: true)
ssh_auth = build_ssh_auth(ssh_key_auth: true, ssh_private_key: '')
@@ -300,7 +286,7 @@ describe Gitlab::Shell do
end
it 'does not pass the key unless SSH key auth is to be used' do
- expect_call(false)
+ expect_call(false, force: false, tags: true)
ssh_auth = build_ssh_auth(ssh_key_auth: false, ssh_private_key: 'foo')
@@ -308,7 +294,7 @@ describe Gitlab::Shell do
end
it 'passes the known_hosts data if specified' do
- expect_call(false, 'GITLAB_SHELL_KNOWN_HOSTS' => 'foo')
+ expect_call(false, force: false, tags: true, known_hosts: 'foo')
ssh_auth = build_ssh_auth(ssh_known_hosts: 'foo')
@@ -316,7 +302,7 @@ describe Gitlab::Shell do
end
it 'does not pass empty known_hosts data' do
- expect_call(false)
+ expect_call(false, force: false, tags: true)
ssh_auth = build_ssh_auth(ssh_known_hosts: '')
@@ -324,7 +310,7 @@ describe Gitlab::Shell do
end
it 'does not pass known_hosts data unless SSH is to be used' do
- expect_call(false, popen_vars)
+ expect_call(false, force: false, tags: true)
ssh_auth = build_ssh_auth(ssh_import?: false, ssh_known_hosts: 'foo')
@@ -342,20 +328,79 @@ describe Gitlab::Shell do
end
describe '#import_repository' do
+ let(:import_url) { 'https://gitlab.com/gitlab-org/gitlab-ce.git' }
+
it 'returns true when the command succeeds' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"],
- nil, popen_vars).and_return([nil, 0])
+ expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true }
- expect(gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git')).to be true
+ result = gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, import_url)
+
+ expect(result).to be_truthy
end
it 'raises an exception when the command fails' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'import-project', 'current/storage', 'project/path.git', 'https://gitlab.com/gitlab-org/gitlab-ce.git', "800"],
- nil, popen_vars).and_return(["error", 1])
+ allow(gitlab_projects).to receive(:output) { 'error' }
+ expect(gitlab_projects).to receive(:import_project) { false }
+
+ expect do
+ gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, import_url)
+ end.to raise_error(Gitlab::Shell::Error, "error")
+ end
+ end
+
+ describe '#push_remote_branches' do
+ subject(:result) do
+ gitlab_shell.push_remote_branches(
+ project.repository_storage_path,
+ project.disk_path,
+ 'downstream-remote',
+ ['master']
+ )
+ end
+
+ it 'executes the command' do
+ expect(gitlab_projects).to receive(:push_branches)
+ .with('downstream-remote', timeout, true, ['master'])
+ .and_return(true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'fails to execute the command' do
+ allow(gitlab_projects).to receive(:output) { 'error' }
+ expect(gitlab_projects).to receive(:push_branches)
+ .with('downstream-remote', timeout, true, ['master'])
+ .and_return(false)
+
+ expect { result }.to raise_error(Gitlab::Shell::Error, 'error')
+ end
+ end
+
+ describe '#delete_remote_branches' do
+ subject(:result) do
+ gitlab_shell.delete_remote_branches(
+ project.repository_storage_path,
+ project.disk_path,
+ 'downstream-remote',
+ ['master']
+ )
+ end
+
+ it 'executes the command' do
+ expect(gitlab_projects).to receive(:delete_remote_branches)
+ .with('downstream-remote', ['master'])
+ .and_return(true)
+
+ is_expected.to be_truthy
+ end
+
+ it 'fails to execute the command' do
+ allow(gitlab_projects).to receive(:output) { 'error' }
+ expect(gitlab_projects).to receive(:delete_remote_branches)
+ .with('downstream-remote', ['master'])
+ .and_return(false)
- expect { gitlab_shell.import_repository('current/storage', 'project/path', 'https://gitlab.com/gitlab-org/gitlab-ce.git') }.to raise_error(Gitlab::Shell::Error, "error")
+ expect { result }.to raise_error(Gitlab::Shell::Error, 'error')
end
end
end
diff --git a/spec/lib/gitlab/sidekiq_config_spec.rb b/spec/lib/gitlab/sidekiq_config_spec.rb
index 09f95be2213..0c66d764851 100644
--- a/spec/lib/gitlab/sidekiq_config_spec.rb
+++ b/spec/lib/gitlab/sidekiq_config_spec.rb
@@ -16,9 +16,30 @@ describe Gitlab::SidekiqConfig do
expect(queues).to include('post_receive')
expect(queues).to include('merge')
- expect(queues).to include('cronjob')
+ expect(queues).to include('cronjob:stuck_import_jobs')
expect(queues).to include('mailers')
expect(queues).to include('default')
end
end
+
+ describe '.expand_queues' do
+ it 'expands queue namespaces to concrete queue names' do
+ queues = described_class.expand_queues(%w[cronjob])
+
+ expect(queues).to include('cronjob:stuck_import_jobs')
+ expect(queues).to include('cronjob:stuck_merge_jobs')
+ end
+
+ it 'lets concrete queue names pass through' do
+ queues = described_class.expand_queues(%w[post_receive])
+
+ expect(queues).to include('post_receive')
+ end
+
+ it 'lets unknown queues pass through' do
+ queues = described_class.expand_queues(%w[unknown])
+
+ expect(queues).to include('unknown')
+ end
+ end
end
diff --git a/spec/lib/gitlab/sidekiq_versioning/manager_spec.rb b/spec/lib/gitlab/sidekiq_versioning/manager_spec.rb
new file mode 100644
index 00000000000..7debf70a16f
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_versioning/manager_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqVersioning::Manager do
+ before do
+ Sidekiq::Manager.prepend described_class
+ end
+
+ describe '#initialize' do
+ it 'listens on all expanded queues' do
+ manager = Sidekiq::Manager.new(queues: %w[post_receive repository_fork cronjob unknown])
+
+ queues = manager.options[:queues]
+
+ expect(queues).to include('post_receive')
+ expect(queues).to include('repository_fork')
+ expect(queues).to include('cronjob')
+ expect(queues).to include('cronjob:stuck_import_jobs')
+ expect(queues).to include('cronjob:stuck_merge_jobs')
+ expect(queues).to include('unknown')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_versioning_spec.rb b/spec/lib/gitlab/sidekiq_versioning_spec.rb
new file mode 100644
index 00000000000..fa6d42e730d
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_versioning_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqVersioning, :sidekiq, :redis do
+ let(:foo_worker) do
+ Class.new do
+ def self.name
+ 'FooWorker'
+ end
+
+ include ApplicationWorker
+ end
+ end
+
+ let(:bar_worker) do
+ Class.new do
+ def self.name
+ 'BarWorker'
+ end
+
+ include ApplicationWorker
+ end
+ end
+
+ before do
+ allow(Gitlab::SidekiqConfig).to receive(:workers).and_return([foo_worker, bar_worker])
+ allow(Gitlab::SidekiqConfig).to receive(:worker_queues).and_return([foo_worker.queue, bar_worker.queue])
+ end
+
+ describe '.install!' do
+ it 'prepends SidekiqVersioning::Manager into Sidekiq::Manager' do
+ described_class.install!
+
+ expect(Sidekiq::Manager).to include(Gitlab::SidekiqVersioning::Manager)
+ end
+
+ it 'registers all versionless and versioned queues with Redis' do
+ described_class.install!
+
+ queues = Sidekiq::Queue.all.map(&:name)
+ expect(queues).to include('foo')
+ expect(queues).to include('bar')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/tcp_checker_spec.rb b/spec/lib/gitlab/tcp_checker_spec.rb
new file mode 100644
index 00000000000..4acf0334496
--- /dev/null
+++ b/spec/lib/gitlab/tcp_checker_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe Gitlab::TcpChecker do
+ before do
+ @server = TCPServer.new('localhost', 0)
+ _, @port, _, @ip = @server.addr
+ end
+
+ after do
+ @server.close
+ end
+
+ subject(:checker) { described_class.new(@ip, @port) }
+
+ describe '#check' do
+ subject { checker.check }
+
+ it 'can connect to an open port' do
+ is_expected.to be_truthy
+
+ expect(checker.error).to be_nil
+ end
+
+ it 'fails to connect to a closed port' do
+ @server.close
+
+ is_expected.to be_falsy
+
+ expect(checker.error).to be_a(Errno::ECONNREFUSED)
+ end
+ end
+end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index e1d71a9573b..4d0a3942996 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -342,6 +342,46 @@ describe Notify do
end
end
+ context 'for issue notes' do
+ let(:host) { Gitlab.config.gitlab.host }
+
+ context 'in discussion' do
+ set(:first_note) { create(:discussion_note_on_issue) }
+ set(:second_note) { create(:discussion_note_on_issue, in_reply_to: first_note) }
+ set(:third_note) { create(:discussion_note_on_issue, in_reply_to: second_note) }
+
+ subject { described_class.note_issue_email(recipient.id, third_note.id) }
+
+ it 'has In-Reply-To header pointing to previous note in discussion' do
+ expect(subject.header['In-Reply-To'].message_ids).to eq(["note_#{second_note.id}@#{host}"])
+ end
+
+ it 'has References header including the notes and issue of the discussion' do
+ expect(subject.header['References'].message_ids).to include("issue_#{first_note.noteable.id}@#{host}",
+ "note_#{first_note.id}@#{host}",
+ "note_#{second_note.id}@#{host}")
+ end
+
+ it 'has X-GitLab-Discussion-ID header' do
+ expect(subject.header['X-GitLab-Discussion-ID'].value).to eq(third_note.discussion.id)
+ end
+ end
+
+ context 'individual issue comments' do
+ set(:note) { create(:note_on_issue) }
+
+ subject { described_class.note_issue_email(recipient.id, note.id) }
+
+ it 'has In-Reply-To header pointing to the issue' do
+ expect(subject.header['In-Reply-To'].message_ids).to eq(["issue_#{note.noteable.id}@#{host}"])
+ end
+
+ it 'has References header including the notes and issue of the discussion' do
+ expect(subject.header['References'].message_ids).to include("issue_#{note.noteable.id}@#{host}")
+ end
+ end
+ end
+
context 'for snippet notes' do
let(:project_snippet) { create(:project_snippet, project: project) }
let(:project_snippet_note) { create(:note_on_project_snippet, project: project, noteable: project_snippet) }
diff --git a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
index 05f281fffff..57ee2adaaff 100644
--- a/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
+++ b/spec/migrations/migrate_gcp_clusters_to_new_clusters_architectures_spec.rb
@@ -2,9 +2,10 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20171013104327_migrate_gcp_clusters_to_new_clusters_architectures.rb')
describe MigrateGcpClustersToNewClustersArchitectures, :migration do
- let(:project) { create(:project) }
+ let(:projects) { table(:projects) }
+ let(:project) { projects.create }
let(:user) { create(:user) }
- let(:service) { create(:kubernetes_service, project: project) }
+ let(:service) { create(:kubernetes_service, project_id: project.id) }
context 'when cluster is being created' do
let(:project_id) { project.id }
@@ -56,8 +57,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
expect(cluster.provider_type).to eq('gcp')
expect(cluster.platform_type).to eq('kubernetes')
- expect(cluster.project).to eq(project)
- expect(project.clusters).to include(cluster)
+ expect(cluster.project_ids).to include(project.id)
expect(cluster.provider_gcp.cluster).to eq(cluster)
expect(cluster.provider_gcp.status).to eq(status)
@@ -133,8 +133,7 @@ describe MigrateGcpClustersToNewClustersArchitectures, :migration do
expect(cluster.provider_type).to eq('gcp')
expect(cluster.platform_type).to eq('kubernetes')
- expect(cluster.project).to eq(project)
- expect(project.clusters).to include(cluster)
+ expect(cluster.project_ids).to include(project.id)
expect(cluster.provider_gcp.cluster).to eq(cluster)
expect(cluster.provider_gcp.status).to eq(status)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index c5e23532aa5..871e8b47650 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1861,9 +1861,9 @@ describe Ci::Build do
describe 'state transition: any => [:running]' do
shared_examples 'validation is active' do
context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
- it { expect { job.run! }.to raise_error(Ci::Build::MissingDependenciesError) }
+ it { expect { job.run! }.not_to raise_error(Ci::Build::MissingDependenciesError) }
end
context 'when artifacts of depended job has been expired' do
@@ -1885,11 +1885,10 @@ describe Ci::Build do
shared_examples 'validation is not active' do
context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
it { expect { job.run! }.not_to raise_error }
end
-
context 'when artifacts of depended job has been expired' do
let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 6b98d013ded..bb63abd167b 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -601,30 +601,30 @@ describe MergeRequest do
end
describe '#can_remove_source_branch?' do
- let(:user) { create(:user) }
- let(:user2) { create(:user) }
+ set(:user) { create(:user) }
+ set(:merge_request) { create(:merge_request, :simple) }
- before do
- subject.source_project.team << [user, :master]
+ subject { merge_request }
- subject.source_branch = "feature"
- subject.target_branch = "master"
- subject.save!
+ before do
+ subject.source_project.add_master(user)
end
it "can't be removed when its a protected branch" do
allow(ProtectedBranch).to receive(:protected?).and_return(true)
+
expect(subject.can_remove_source_branch?(user)).to be_falsey
end
it "can't remove a root ref" do
- subject.source_branch = "master"
- subject.target_branch = "feature"
+ subject.update(source_branch: 'master', target_branch: 'feature')
expect(subject.can_remove_source_branch?(user)).to be_falsey
end
it "is unable to remove the source branch for a project the user cannot push to" do
+ user2 = create(:user)
+
expect(subject.can_remove_source_branch?(user2)).to be_falsey
end
@@ -635,6 +635,7 @@ describe MergeRequest do
end
it "cannot be removed if the last commit is not also the head of the source branch" do
+ subject.clear_memoized_shas
subject.source_branch = "lfs"
expect(subject.can_remove_source_branch?(user)).to be_falsey
@@ -1405,6 +1406,16 @@ describe MergeRequest do
subject.reload_diff
end
+
+ context 'when using the after_update hook to update' do
+ context 'when the branches are updated' do
+ it 'uses the new heads to generate the diff' do
+ expect { subject.update!(source_branch: subject.target_branch, target_branch: subject.source_branch) }
+ .to change { subject.merge_request_diff.start_commit_sha }
+ .and change { subject.merge_request_diff.head_commit_sha }
+ end
+ end
+ end
end
describe '#update_diff_discussion_positions' do
@@ -1857,4 +1868,20 @@ describe MergeRequest do
it_behaves_like 'throttled touch' do
subject { create(:merge_request, updated_at: 1.hour.ago) }
end
+
+ context 'state machine transitions' do
+ describe '#unlock_mr' do
+ subject { create(:merge_request, state: 'locked', merge_jid: 123) }
+
+ it 'updates merge request head pipeline and sets merge_jid to nil' do
+ pipeline = create(:ci_empty_pipeline, project: subject.project, ref: subject.source_branch, sha: subject.source_branch_sha)
+
+ subject.unlock_mr
+
+ subject.reload
+ expect(subject.head_pipeline).to eq(pipeline)
+ expect(subject.merge_jid).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index e1a0c55b6a6..cefbf60b28c 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -756,6 +756,28 @@ describe Note do
end
end
+ describe '#references' do
+ context 'when part of a discussion' do
+ it 'references all earlier notes in the discussion' do
+ first_note = create(:discussion_note_on_issue)
+ second_note = create(:discussion_note_on_issue, in_reply_to: first_note)
+ third_note = create(:discussion_note_on_issue, in_reply_to: second_note)
+ create(:discussion_note_on_issue, in_reply_to: third_note)
+
+ expect(third_note.references).to eq([first_note.noteable, first_note, second_note])
+ end
+ end
+
+ context 'when not part of a discussion' do
+ subject { create(:note) }
+ let(:note) { create(:note, in_reply_to: subject) }
+
+ it 'returns the noteable' do
+ expect(note.references).to eq([note.noteable])
+ end
+ end
+ end
+
describe 'expiring ETag cache' do
let(:note) { build(:note_on_issue) }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 129fce74f45..799d99c0369 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -59,12 +59,18 @@ describe Repository do
end
describe 'tags_sorted_by' do
- context 'name' do
- subject { repository.tags_sorted_by('name').map(&:name) }
+ context 'name_desc' do
+ subject { repository.tags_sorted_by('name_desc').map(&:name) }
it { is_expected.to eq(['v1.1.0', 'v1.0.0']) }
end
+ context 'name_asc' do
+ subject { repository.tags_sorted_by('name_asc').map(&:name) }
+
+ it { is_expected.to eq(['v1.0.0', 'v1.1.0']) }
+ end
+
context 'updated' do
let(:tag_a) { repository.find_tag('v1.0.0') }
let(:tag_b) { repository.find_tag('v1.1.0') }
@@ -1007,7 +1013,7 @@ describe Repository do
it 'runs without errors' do
# old_rev is an ancestor of new_rev
- expect(repository.rugged.merge_base(old_rev, new_rev)).to eq(old_rev)
+ expect(repository.merge_base(old_rev, new_rev)).to eq(old_rev)
# old_rev is not a direct ancestor (parent) of new_rev
expect(repository.rugged.lookup(new_rev).parent_ids).not_to include(old_rev)
@@ -1029,7 +1035,7 @@ describe Repository do
it 'raises an exception' do
# The 'master' branch is NOT an ancestor of new_rev.
- expect(repository.rugged.merge_base(old_rev, new_rev)).not_to eq(old_rev)
+ expect(repository.merge_base(old_rev, new_rev)).not_to eq(old_rev)
# Updating 'master' to new_rev would lose the commits on 'master' that
# are not contained in new_rev. This should not be allowed.
@@ -2360,4 +2366,111 @@ describe Repository do
end
end
end
+
+ describe '#contributors' do
+ let(:author_a) { build(:author, email: 'tiagonbotelho@hotmail.com', name: 'tiagonbotelho') }
+ let(:author_b) { build(:author, email: 'gitlab@winniehell.de', name: 'Winnie') }
+ let(:author_c) { build(:author, email: 'douwe@gitlab.com', name: 'Douwe Maan') }
+ let(:stubbed_commits) do
+ [build(:commit, author: author_a),
+ build(:commit, author: author_a),
+ build(:commit, author: author_b),
+ build(:commit, author: author_c),
+ build(:commit, author: author_c),
+ build(:commit, author: author_c)]
+ end
+ let(:order_by) { nil }
+ let(:sort) { nil }
+
+ before do
+ allow(repository).to receive(:commits).with(nil, limit: 2000, offset: 0, skip_merges: true).and_return(stubbed_commits)
+ end
+
+ subject { repository.contributors(order_by: order_by, sort: sort) }
+
+ def expect_contributors(*contributors)
+ expect(subject.map(&:email)).to eq(contributors.map(&:email))
+ end
+
+ it 'returns the array of Gitlab::Contributor for the repository' do
+ expect_contributors(author_a, author_b, author_c)
+ end
+
+ context 'order_by email' do
+ let(:order_by) { 'email' }
+
+ context 'asc' do
+ let(:sort) { 'asc' }
+
+ it 'returns all the contributors ordered by email asc case insensitive' do
+ expect_contributors(author_c, author_b, author_a)
+ end
+ end
+
+ context 'desc' do
+ let(:sort) { 'desc' }
+
+ it 'returns all the contributors ordered by email desc case insensitive' do
+ expect_contributors(author_a, author_b, author_c)
+ end
+ end
+ end
+
+ context 'order_by name' do
+ let(:order_by) { 'name' }
+
+ context 'asc' do
+ let(:sort) { 'asc' }
+
+ it 'returns all the contributors ordered by name asc case insensitive' do
+ expect_contributors(author_c, author_a, author_b)
+ end
+ end
+
+ context 'desc' do
+ let(:sort) { 'desc' }
+
+ it 'returns all the contributors ordered by name desc case insensitive' do
+ expect_contributors(author_b, author_a, author_c)
+ end
+ end
+ end
+
+ context 'order_by commits' do
+ let(:order_by) { 'commits' }
+
+ context 'asc' do
+ let(:sort) { 'asc' }
+
+ it 'returns all the contributors ordered by commits asc' do
+ expect_contributors(author_b, author_a, author_c)
+ end
+ end
+
+ context 'desc' do
+ let(:sort) { 'desc' }
+
+ it 'returns all the contributors ordered by commits desc' do
+ expect_contributors(author_c, author_a, author_b)
+ end
+ end
+ end
+
+ context 'invalid ordering' do
+ let(:order_by) { 'unknown' }
+
+ it 'returns the contributors unsorted' do
+ expect_contributors(author_a, author_b, author_c)
+ end
+ end
+
+ context 'invalid sorting' do
+ let(:order_by) { 'name' }
+ let(:sort) { 'unknown' }
+
+ it 'returns the contributors unsorted' do
+ expect_contributors(author_a, author_b, author_c)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index 9f2ff3b5af6..741800ff61d 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -378,6 +378,28 @@ describe API::Repositories do
expect(first_contributor['additions']).to eq(0)
expect(first_contributor['deletions']).to eq(0)
end
+
+ context 'using sorting' do
+ context 'by commits desc' do
+ it 'returns the repository contribuors sorted by commits desc' do
+ get api(route, current_user), { order_by: 'commits', sort: 'desc' }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('contributors')
+ expect(json_response.first['commits']).to be > json_response.last['commits']
+ end
+ end
+
+ context 'by name desc' do
+ it 'returns the repository contribuors sorted by name asc case insensitive' do
+ get api(route, current_user), { order_by: 'name', sort: 'asc' }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('contributors')
+ expect(json_response.first['name'].downcase).to be < json_response.last['name'].downcase
+ end
+ end
+ end
end
context 'when unauthenticated', 'and project is public' do
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index 0bf7863bdc8..e2b19ad59f9 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -16,6 +16,44 @@ describe API::Tags do
describe 'GET /projects/:id/repository/tags' do
let(:route) { "/projects/#{project_id}/repository/tags" }
+ context 'sorting' do
+ let(:current_user) { user }
+
+ it 'sorts by descending order by default' do
+ get api(route, current_user)
+
+ desc_order_tags = project.repository.tags.sort_by { |tag| tag.dereferenced_target.committed_date }
+ desc_order_tags.reverse!.map! { |tag| tag.dereferenced_target.id }
+
+ expect(json_response.map { |tag| tag['commit']['id'] }).to eq(desc_order_tags)
+ end
+
+ it 'sorts by ascending order if specified' do
+ get api("#{route}?sort=asc", current_user)
+
+ asc_order_tags = project.repository.tags.sort_by { |tag| tag.dereferenced_target.committed_date }
+ asc_order_tags.map! { |tag| tag.dereferenced_target.id }
+
+ expect(json_response.map { |tag| tag['commit']['id'] }).to eq(asc_order_tags)
+ end
+
+ it 'sorts by name in descending order when requested' do
+ get api("#{route}?order_by=name", current_user)
+
+ ordered_by_name = project.repository.tags.map { |tag| tag.name }.sort.reverse
+
+ expect(json_response.map { |tag| tag['name'] }).to eq(ordered_by_name)
+ end
+
+ it 'sorts by name in ascending order when requested' do
+ get api("#{route}?order_by=name&sort=asc", current_user)
+
+ ordered_by_name = project.repository.tags.map { |tag| tag.name }.sort
+
+ expect(json_response.map { |tag| tag['name'] }).to eq(ordered_by_name)
+ end
+ end
+
shared_examples_for 'repository tags' do
it 'returns the repository tags' do
get api(route, current_user)
diff --git a/spec/rubocop/cop/include_sidekiq_worker_spec.rb b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
new file mode 100644
index 00000000000..7f406535dda
--- /dev/null
+++ b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../rubocop/cop/include_sidekiq_worker'
+
+describe RuboCop::Cop::IncludeSidekiqWorker do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ context 'when `Sidekiq::Worker` is included' do
+ let(:source) { 'include Sidekiq::Worker' }
+ let(:correct_source) { 'include ApplicationWorker' }
+
+ it 'registers an offense ' do
+ inspect_source(cop, source)
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([1])
+ expect(cop.highlights).to eq(['Sidekiq::Worker'])
+ end
+ end
+
+ it 'autocorrects to the right version' do
+ autocorrected = autocorrect_source(cop, source)
+
+ expect(autocorrected).to eq(correct_source)
+ end
+ end
+end
diff --git a/spec/rubocop/cop/sidekiq_options_queue_spec.rb b/spec/rubocop/cop/sidekiq_options_queue_spec.rb
new file mode 100644
index 00000000000..a31de381631
--- /dev/null
+++ b/spec/rubocop/cop/sidekiq_options_queue_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../rubocop/cop/sidekiq_options_queue'
+
+describe RuboCop::Cop::SidekiqOptionsQueue do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'registers an offense when `sidekiq_options` is used with the `queue` option' do
+ inspect_source(cop, 'sidekiq_options queue: "some_queue"')
+
+ aggregate_failures do
+ expect(cop.offenses.size).to eq(1)
+ expect(cop.offenses.map(&:line)).to eq([1])
+ expect(cop.highlights).to eq(['queue: "some_queue"'])
+ end
+ end
+
+ it 'does not register an offense when `sidekiq_options` is used with another option' do
+ inspect_source(cop, 'sidekiq_options retry: false')
+
+ expect(cop.offenses).to be_empty
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 41ce81e0651..267258b33a8 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -64,6 +64,18 @@ describe Ci::CreatePipelineService do
create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project)
end
+ context 'when related merge request is already merged' do
+ let!(:merged_merge_request) do
+ create(:merge_request, source_branch: 'master', target_branch: "branch_2", source_project: project, state: 'merged')
+ end
+
+ it 'does not schedule update head pipeline job' do
+ expect(UpdateHeadPipelineForMergeRequestWorker).not_to receive(:perform_async).with(merged_merge_request.id)
+
+ execute_service
+ end
+ end
+
context 'when the head pipeline sha equals merge request sha' do
it 'updates head pipeline of each merge request' do
merge_request_1
@@ -77,13 +89,13 @@ describe Ci::CreatePipelineService do
end
context 'when the head pipeline sha does not equal merge request sha' do
- it 'raises the ArgumentError error from worker and does not update the head piepeline of MRs' do
+ it 'does not update the head piepeline of MRs' do
merge_request_1
merge_request_2
allow_any_instance_of(Ci::Pipeline).to receive(:latest?).and_return(true)
- expect { execute_service(after: 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f') }.to raise_error(ArgumentError)
+ expect { execute_service(after: 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f') }.not_to raise_error
last_pipeline = Ci::Pipeline.last
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 22fb7ed7215..de8a9ce12ff 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -287,9 +287,9 @@ module Ci
shared_examples 'validation is active' do
context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
- it_behaves_like 'not pick'
+ it { expect(subject).to eq(pending_job) }
end
context 'when artifacts of depended job has been expired' do
@@ -309,7 +309,7 @@ module Ci
end
context 'when job object is staled' do
- let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
before do
allow_any_instance_of(Ci::Build).to receive(:drop!)
@@ -324,11 +324,10 @@ module Ci
shared_examples 'validation is not active' do
context 'when depended job has not been completed yet' do
- let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+ let!(:pre_stage_job) { create(:ci_build, :manual, pipeline: pipeline, name: 'test', stage_idx: 0) }
it { expect(subject).to eq(pending_job) }
end
-
context 'when artifacts of depended job has been expired' do
let!(:pre_stage_job) { create(:ci_build, :success, :expired, pipeline: pipeline, name: 'test', stage_idx: 0) }
diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb
index a7ab389b357..623b182b205 100644
--- a/spec/services/merge_requests/create_from_issue_service_spec.rb
+++ b/spec/services/merge_requests/create_from_issue_service_spec.rb
@@ -100,5 +100,17 @@ describe MergeRequests::CreateFromIssueService do
expect(result[:merge_request].target_branch).to eq(project.default_branch)
end
+
+ it 'executes quick actions if the build service sets them in the description' do
+ allow(service).to receive(:merge_request).and_wrap_original do |m, *args|
+ m.call(*args).tap do |merge_request|
+ merge_request.description = "/assign #{user.to_reference}"
+ end
+ end
+
+ result = service.execute
+
+ expect(result[:merge_request].assignee).to eq(user)
+ end
end
end
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index f86f1ac2443..c38ddf4612b 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -1,14 +1,14 @@
require 'spec_helper'
describe MergeRequests::MergeService do
- let(:user) { create(:user) }
- let(:user2) { create(:user) }
+ set(:user) { create(:user) }
+ set(:user2) { create(:user) }
let(:merge_request) { create(:merge_request, :simple, author: user2, assignee: user2) }
let(:project) { merge_request.project }
before do
- project.team << [user, :master]
- project.team << [user2, :developer]
+ project.add_master(user)
+ project.add_developer(user2)
end
describe '#execute' do
diff --git a/spec/support/batch_loader.rb b/spec/support/batch_loader.rb
new file mode 100644
index 00000000000..bb790e660a6
--- /dev/null
+++ b/spec/support/batch_loader.rb
@@ -0,0 +1,5 @@
+RSpec.configure do |config|
+ config.after do
+ BatchLoader::Executor.clear_current
+ end
+end
diff --git a/spec/support/stub_env.rb b/spec/support/stub_env.rb
index 19fbe572930..f621463e621 100644
--- a/spec/support/stub_env.rb
+++ b/spec/support/stub_env.rb
@@ -17,6 +17,7 @@ module StubENV
def add_stubbed_value(key, value)
allow(ENV).to receive(:[]).with(key).and_return(value)
+ allow(ENV).to receive(:key?).with(key).and_return(true)
allow(ENV).to receive(:fetch).with(key).and_return(value)
allow(ENV).to receive(:fetch).with(key, anything()) do |_, default_val|
value || default_val
@@ -29,6 +30,7 @@ module StubENV
def init_stub
allow(ENV).to receive(:[]).and_call_original
+ allow(ENV).to receive(:key?).and_call_original
allow(ENV).to receive(:fetch).and_call_original
add_stubbed_value(STUBBED_KEY, true)
end
diff --git a/spec/views/projects/tree/_blob_item.html.haml_spec.rb b/spec/views/projects/tree/_blob_item.html.haml_spec.rb
new file mode 100644
index 00000000000..6a477c712ff
--- /dev/null
+++ b/spec/views/projects/tree/_blob_item.html.haml_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe 'projects/tree/_blob_item' do
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository }
+ let(:blob_item) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files/ruby').first }
+
+ before do
+ assign(:project, project)
+ assign(:repository, repository)
+ assign(:id, File.join('master', ''))
+ assign(:lfs_blob_ids, [])
+ end
+
+ it 'renders blob item' do
+ render_partial(blob_item)
+
+ expect(rendered).to have_content(blob_item.name)
+ expect(rendered).not_to have_selector('.label-lfs', text: 'LFS')
+ end
+
+ describe 'LFS blob' do
+ before do
+ assign(:lfs_blob_ids, [blob_item].map(&:id))
+
+ render_partial(blob_item)
+ end
+
+ it 'renders LFS badge' do
+ expect(rendered).to have_selector('.label-lfs', text: 'LFS')
+ end
+ end
+
+ def render_partial(blob_item)
+ render partial: 'projects/tree/blob_item', locals: {
+ blob_item: blob_item,
+ type: 'blob'
+ }
+ end
+end
diff --git a/spec/views/projects/tree/show.html.haml_spec.rb b/spec/views/projects/tree/show.html.haml_spec.rb
index 3c25e341b39..44b32df0395 100644
--- a/spec/views/projects/tree/show.html.haml_spec.rb
+++ b/spec/views/projects/tree/show.html.haml_spec.rb
@@ -9,6 +9,7 @@ describe 'projects/tree/show' do
before do
assign(:project, project)
assign(:repository, repository)
+ assign(:lfs_blob_ids, [])
allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:can_collaborate_with_project?).and_return(true)
diff --git a/spec/workers/concerns/application_worker_spec.rb b/spec/workers/concerns/application_worker_spec.rb
index 0145563e0ed..901d77178bc 100644
--- a/spec/workers/concerns/application_worker_spec.rb
+++ b/spec/workers/concerns/application_worker_spec.rb
@@ -17,6 +17,14 @@ describe ApplicationWorker do
end
end
+ describe '.queue_namespace' do
+ it 'sets the queue name based on the class name' do
+ worker.queue_namespace :some_namespace
+
+ expect(worker.queue).to eq('some_namespace:foo_bar_dummy')
+ end
+ end
+
describe '.queue' do
it 'returns the queue name' do
worker.sidekiq_options queue: :some_queue
diff --git a/spec/workers/concerns/cluster_queue_spec.rb b/spec/workers/concerns/cluster_queue_spec.rb
index 5049886b55c..4118b9aa194 100644
--- a/spec/workers/concerns/cluster_queue_spec.rb
+++ b/spec/workers/concerns/cluster_queue_spec.rb
@@ -14,6 +14,6 @@ describe ClusterQueue do
it 'sets a default pipelines queue automatically' do
expect(worker.sidekiq_options['queue'])
- .to eq :gcp_cluster
+ .to eq 'gcp_cluster:dummy'
end
end
diff --git a/spec/workers/concerns/cronjob_queue_spec.rb b/spec/workers/concerns/cronjob_queue_spec.rb
index 3ae1c5f54d8..c042a52f41f 100644
--- a/spec/workers/concerns/cronjob_queue_spec.rb
+++ b/spec/workers/concerns/cronjob_queue_spec.rb
@@ -13,7 +13,7 @@ describe CronjobQueue do
end
it 'sets the queue name of a worker' do
- expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob')
+ expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob:dummy')
end
it 'disables retrying of failed jobs' do
diff --git a/spec/workers/concerns/gitlab/github_import/queue_spec.rb b/spec/workers/concerns/gitlab/github_import/queue_spec.rb
index 9c69ee32da1..a96f583aff7 100644
--- a/spec/workers/concerns/gitlab/github_import/queue_spec.rb
+++ b/spec/workers/concerns/gitlab/github_import/queue_spec.rb
@@ -11,6 +11,6 @@ describe Gitlab::GithubImport::Queue do
include Gitlab::GithubImport::Queue
end
- expect(worker.sidekiq_options['queue']).to eq('github_importer')
+ expect(worker.sidekiq_options['queue']).to eq('github_importer:dummy')
end
end
diff --git a/spec/workers/concerns/pipeline_queue_spec.rb b/spec/workers/concerns/pipeline_queue_spec.rb
index dd911760948..a312b307fce 100644
--- a/spec/workers/concerns/pipeline_queue_spec.rb
+++ b/spec/workers/concerns/pipeline_queue_spec.rb
@@ -14,15 +14,6 @@ describe PipelineQueue do
it 'sets a default pipelines queue automatically' do
expect(worker.sidekiq_options['queue'])
- .to eq 'pipeline_default'
- end
-
- describe '.enqueue_in' do
- it 'sets a custom sidekiq queue with prefix and group' do
- worker.enqueue_in(group: :processing)
-
- expect(worker.sidekiq_options['queue'])
- .to eq 'pipeline_processing'
- end
+ .to eq 'pipeline_default:dummy'
end
end
diff --git a/spec/workers/concerns/repository_check_queue_spec.rb b/spec/workers/concerns/repository_check_queue_spec.rb
index fdbbfcc90a5..d2eeecfc9a8 100644
--- a/spec/workers/concerns/repository_check_queue_spec.rb
+++ b/spec/workers/concerns/repository_check_queue_spec.rb
@@ -13,7 +13,7 @@ describe RepositoryCheckQueue do
end
it 'sets the queue name of a worker' do
- expect(worker.sidekiq_options['queue'].to_s).to eq('repository_check')
+ expect(worker.sidekiq_options['queue'].to_s).to eq('repository_check:dummy')
end
it 'disables retrying of failed jobs' do
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index 7ee0a51a263..9e3b99b3502 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -1,21 +1,36 @@
require 'spec_helper'
describe 'Every Sidekiq worker' do
- it 'includes ApplicationWorker' do
- expect(Gitlab::SidekiqConfig.workers).to all(include(ApplicationWorker))
- end
-
it 'does not use the default queue' do
expect(Gitlab::SidekiqConfig.workers.map(&:queue)).not_to include('default')
end
it 'uses the cronjob queue when the worker runs as a cronjob' do
- expect(Gitlab::SidekiqConfig.cron_workers.map(&:queue)).to all(eq('cronjob'))
+ expect(Gitlab::SidekiqConfig.cron_workers.map(&:queue)).to all(start_with('cronjob:'))
+ end
+
+ it 'has its queue in app/workers/all_queues.yml', :aggregate_failures do
+ file_worker_queues = Gitlab::SidekiqConfig.worker_queues.to_set
+
+ worker_queues = Gitlab::SidekiqConfig.workers.map(&:queue).to_set
+ worker_queues << ActionMailer::DeliveryJob.queue_name
+ worker_queues << 'default'
+
+ missing_from_file = worker_queues - file_worker_queues
+ expect(missing_from_file).to be_empty, "expected #{missing_from_file.to_a.inspect} to be in app/workers/all_queues.yml"
+
+ unncessarily_in_file = file_worker_queues - worker_queues
+ expect(unncessarily_in_file).to be_empty, "expected #{unncessarily_in_file.to_a.inspect} not to be in app/workers/all_queues.yml"
end
- it 'defines the queue in the Sidekiq configuration file' do
- config_queue_names = Gitlab::SidekiqConfig.config_queues.to_set
+ it 'has its queue or namespace in config/sidekiq_queues.yml', :aggregate_failures do
+ config_queues = Gitlab::SidekiqConfig.config_queues.to_set
+
+ Gitlab::SidekiqConfig.workers.each do |worker|
+ queue = worker.queue
+ queue_namespace = queue.split(':').first
- expect(Gitlab::SidekiqConfig.worker_queues).to all(be_in(config_queue_names))
+ expect(config_queues).to include(queue).or(include(queue_namespace))
+ end
end
end
diff --git a/spec/workers/stuck_merge_jobs_worker_spec.rb b/spec/workers/stuck_merge_jobs_worker_spec.rb
index f8b55e873df..c2c2a5f9121 100644
--- a/spec/workers/stuck_merge_jobs_worker_spec.rb
+++ b/spec/workers/stuck_merge_jobs_worker_spec.rb
@@ -14,7 +14,6 @@ describe StuckMergeJobsWorker do
mr_with_sha.reload
mr_without_sha.reload
-
expect(mr_with_sha).to be_merged
expect(mr_without_sha).to be_opened
expect(mr_with_sha.merge_jid).to be_present
@@ -24,10 +23,13 @@ describe StuckMergeJobsWorker do
it 'updates merge request to opened when locked but has not been merged' do
allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(%w(123))
merge_request = create(:merge_request, :locked, merge_jid: '123', state: :locked)
+ pipeline = create(:ci_empty_pipeline, project: merge_request.project, ref: merge_request.source_branch, sha: merge_request.source_branch_sha)
worker.perform
- expect(merge_request.reload).to be_opened
+ merge_request.reload
+ expect(merge_request).to be_opened
+ expect(merge_request.head_pipeline).to eq(pipeline)
end
it 'logs updated stuck merge job ids' do
diff --git a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
index 522e1566271..9adde5fc21a 100644
--- a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
+++ b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
@@ -22,7 +22,7 @@ describe UpdateHeadPipelineForMergeRequestWorker do
end
it 'does not update head_pipeline_id' do
- expect { subject.perform(merge_request.id) }.to raise_error(ArgumentError)
+ expect { subject.perform(merge_request.id) }.not_to raise_error
expect(merge_request.reload.head_pipeline_id).to eq(nil)
end
diff --git a/vendor/Dockerfile/CONTRIBUTING.md b/vendor/Dockerfile/CONTRIBUTING.md
index 0878db6dd9e..3e98f2e7b5b 100644
--- a/vendor/Dockerfile/CONTRIBUTING.md
+++ b/vendor/Dockerfile/CONTRIBUTING.md
@@ -1,22 +1,15 @@
-The canonical repository for `Dockerfile` templates is
-https://gitlab.com/gitlab-org/Dockerfile.
+## Developer Certificate of Origin + License
-GitLab only mirrors the templates. Please submit your merge requests to
-https://gitlab.com/gitlab-org/Dockerfile.
+By contributing to GitLab B.V., You accept and agree to the following terms and
+conditions for Your present and future Contributions submitted to GitLab B.V.
+Except for the license granted herein to GitLab B.V. and recipients of software
+distributed by GitLab B.V., You reserve all right, title, and interest in and to
+Your Contributions. All Contributions are subject to the following DCO + License
+terms.
-## Contributing
+[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md)
-Thank you for your interest in contributing to this GitLab project! We welcome
-all contributions. By participating in this project, you agree to abide by the
-[code of conduct](#code-of-conduct).
-
-## Contributor license agreement
-
-By submitting code as an individual you agree to the [individual contributor
-license agreement][individual-agreement].
-
-By submitting code as an entity you agree to the [corporate contributor license
-agreement][corporate-agreement].
+_This notice should stay as the first item in the CONTRIBUTING.md file._
## Code of conduct
diff --git a/vendor/Dockerfile/LICENSE b/vendor/Dockerfile/LICENSE
index d6c93c6fcf7..27a215686e7 100644
--- a/vendor/Dockerfile/LICENSE
+++ b/vendor/Dockerfile/LICENSE
@@ -1,6 +1,6 @@
-The MIT License (MIT)
+Copyright (c) 2011-2017 GitLab B.V.
-Copyright (c) 2016-2017 GitLab.org
+With regard to the GitLab Software:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,17 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+For all third party components incorporated into the GitLab Software, those
+components are licensed under the original license provided by the owner of the
+applicable component.
diff --git a/vendor/gitignore/Global/Matlab.gitignore b/vendor/gitignore/Global/Matlab.gitignore
index cca150a88dd..7996ad5058e 100644
--- a/vendor/gitignore/Global/Matlab.gitignore
+++ b/vendor/gitignore/Global/Matlab.gitignore
@@ -1,5 +1,5 @@
##---------------------------------------------------
-## Remove autosaves generated by the Matlab editor
+## Remove autosaves generated by the MATLAB editor
## We have git for backups!
##---------------------------------------------------
@@ -14,6 +14,7 @@
# Simulink Code Generation
slprj/
+sccprj/
# Session info
octave-workspace
diff --git a/vendor/gitignore/Go.gitignore b/vendor/gitignore/Go.gitignore
index a1338d68517..ea58090bd21 100644
--- a/vendor/gitignore/Go.gitignore
+++ b/vendor/gitignore/Go.gitignore
@@ -9,6 +9,3 @@
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
-
-# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
-.glide/
diff --git a/vendor/gitignore/Haskell.gitignore b/vendor/gitignore/Haskell.gitignore
index eee88b2f0f7..82f3a88e17b 100644
--- a/vendor/gitignore/Haskell.gitignore
+++ b/vendor/gitignore/Haskell.gitignore
@@ -17,5 +17,6 @@ cabal.sandbox.config
*.eventlog
.stack-work/
cabal.project.local
+cabal.project.local~
.HTF/
.ghc.environment.*
diff --git a/vendor/gitignore/Jekyll.gitignore b/vendor/gitignore/Jekyll.gitignore
index 5c91b60c063..2ca868298ce 100644
--- a/vendor/gitignore/Jekyll.gitignore
+++ b/vendor/gitignore/Jekyll.gitignore
@@ -1,3 +1,4 @@
_site/
.sass-cache/
+.jekyll-cache/
.jekyll-metadata
diff --git a/vendor/gitignore/ROS.gitignore b/vendor/gitignore/ROS.gitignore
index f8bcd117371..425641f2c3a 100644
--- a/vendor/gitignore/ROS.gitignore
+++ b/vendor/gitignore/ROS.gitignore
@@ -1,3 +1,5 @@
+devel/
+logs/
build/
bin/
lib/
diff --git a/vendor/gitignore/Symfony.gitignore b/vendor/gitignore/Symfony.gitignore
index 85fd714a965..d098259ffb0 100644
--- a/vendor/gitignore/Symfony.gitignore
+++ b/vendor/gitignore/Symfony.gitignore
@@ -25,6 +25,7 @@
/bin/*
!bin/console
!bin/symfony_requirements
+/vendor/
# Assets and user uploads
/web/bundles/
@@ -37,6 +38,9 @@
# Build data
/build/
+# Composer PHAR
+/composer.phar
+
# Backup entities generated with doctrine:generate:entities command
**/Entity/*~
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
index b6418e51766..9bb63365618 100644
--- a/vendor/gitignore/TeX.gitignore
+++ b/vendor/gitignore/TeX.gitignore
@@ -215,7 +215,11 @@ TSWLatexianTemp*
*~[0-9]*
# auto folder when using emacs and auctex
-/auto/*
+./auto/*
+*.el
# expex forward references with \gathertags
*-tags.tex
+
+# standalone packages
+*.sta
diff --git a/vendor/gitignore/Unity.gitignore b/vendor/gitignore/Unity.gitignore
index eb83a8f122d..75e5b1405da 100644
--- a/vendor/gitignore/Unity.gitignore
+++ b/vendor/gitignore/Unity.gitignore
@@ -1,9 +1,9 @@
-/[Ll]ibrary/
-/[Tt]emp/
-/[Oo]bj/
-/[Bb]uild/
-/[Bb]uilds/
-/Assets/AssetStoreTools*
+[Ll]ibrary/
+[Tt]emp/
+[Oo]bj/
+[Bb]uild/
+[Bb]uilds/
+Assets/AssetStoreTools*
# Visual Studio 2015 cache directory
/.vs/
@@ -25,6 +25,7 @@ ExportedObj/
# Unity3D generated meta files
*.pidb.meta
+*.pdb.meta
# Unity3D Generated File On Crash Reports
sysinfo.txt
diff --git a/vendor/gitignore/UnrealEngine.gitignore b/vendor/gitignore/UnrealEngine.gitignore
index 6c6e1c327fd..1daca8b50d9 100644
--- a/vendor/gitignore/UnrealEngine.gitignore
+++ b/vendor/gitignore/UnrealEngine.gitignore
@@ -50,6 +50,7 @@ SourceArt/**/*.tga
# Binary Files
Binaries/*
+Plugins/*/Binaries/*
# Builds
Build/*
@@ -70,6 +71,7 @@ Saved/*
# Compiled source files for the engine to use
Intermediate/*
+Plugins/*/Intermediate/*
# Cache files for the editor to use
DerivedDataCache/*
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index 509668db67a..6217e6c48e9 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -24,11 +24,14 @@ bld/
[Oo]bj/
[Ll]og/
-# Visual Studio 2015 cache/options directory
+# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
@@ -51,6 +54,10 @@ project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
@@ -247,7 +254,7 @@ FakesAssemblies/
.ntvs_analysis.dat
node_modules/
-# Typescript v1 declaration files
+# TypeScript v1 declaration files
typings/
# Visual Studio 6 build log
@@ -303,3 +310,6 @@ __pycache__/
# OpenCover UI analysis results
OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index 88261502d7f..da4d86b9a04 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -83,6 +83,16 @@ codequality:
artifacts:
paths: [codeclimate.json]
+sast:
+ image: registry.gitlab.com/gitlab-org/gl-sast:latest
+ variables:
+ POSTGRES_DB: "false"
+ allow_failure: true
+ script:
+ - /app/bin/run .
+ artifacts:
+ paths: [gl-sast-report.json]
+
review:
stage: review
script:
@@ -218,8 +228,8 @@ production:
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume /tmp/cc:/tmp/cc"
- docker run ${cc_opts} codeclimate/codeclimate init
- docker run ${cc_opts} codeclimate/codeclimate analyze -f json > codeclimate.json
+ docker run ${cc_opts} codeclimate/codeclimate:0.69.0 init
+ docker run ${cc_opts} codeclimate/codeclimate:0.69.0 analyze -f json > codeclimate.json
}
function deploy() {
@@ -345,6 +355,13 @@ production:
}
function build() {
+
+ if [[ -n "$CI_REGISTRY_USER" ]]; then
+ echo "Logging to GitLab Container Registry with CI credentials..."
+ docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
+ echo ""
+ fi
+
if [[ -f Dockerfile ]]; then
echo "Building Dockerfile-based application..."
docker build -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .
@@ -362,12 +379,6 @@ production:
echo ""
fi
- if [[ -n "$CI_REGISTRY_USER" ]]; then
- echo "Logging to GitLab Container Registry with CI credentials..."
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
- echo ""
- fi
-
echo "Pushing to GitLab Container Registry..."
docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
echo ""
@@ -402,7 +413,9 @@ production:
name="$name-$track"
fi
- helm delete "$name" || true
+ if [[ -n "$(helm ls -q "^$name$")" ]]; then
+ helm delete "$name"
+ fi
}
before_script:
diff --git a/vendor/gitlab-ci-yml/CONTRIBUTING.md b/vendor/gitlab-ci-yml/CONTRIBUTING.md
index d4c057bf9dc..d33a1f06f26 100644
--- a/vendor/gitlab-ci-yml/CONTRIBUTING.md
+++ b/vendor/gitlab-ci-yml/CONTRIBUTING.md
@@ -1,16 +1,15 @@
-## Contributing
+## Developer Certificate of Origin + License
-Thank you for your interest in contributing to this GitLab project! We welcome
-all contributions. By participating in this project, you agree to abide by the
-[code of conduct](#code-of-conduct).
+By contributing to GitLab B.V., You accept and agree to the following terms and
+conditions for Your present and future Contributions submitted to GitLab B.V.
+Except for the license granted herein to GitLab B.V. and recipients of software
+distributed by GitLab B.V., You reserve all right, title, and interest in and to
+Your Contributions. All Contributions are subject to the following DCO + License
+terms.
-## Contributor license agreement
+[DCO + License](https://gitlab.com/gitlab-org/dco/blob/master/README.md)
-By submitting code as an individual you agree to the [individual contributor
-license agreement][individual-agreement].
-
-By submitting code as an entity you agree to the [corporate contributor license
-agreement][corporate-agreement].
+_This notice should stay as the first item in the CONTRIBUTING.md file._
## Code of conduct
diff --git a/vendor/gitlab-ci-yml/Chef.gitlab-ci.yml b/vendor/gitlab-ci-yml/Chef.gitlab-ci.yml
new file mode 100644
index 00000000000..4d5b6484d6e
--- /dev/null
+++ b/vendor/gitlab-ci-yml/Chef.gitlab-ci.yml
@@ -0,0 +1,51 @@
+# This file uses Test Kitchen with the kitchen-dokken driver to
+# perform functional testing. Doing so requires that your runner be a
+# Docker runner configured for privileged mode. Please see
+# https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode
+# for help configuring your runner properly, or, if you want to switch
+# to a different driver, see http://kitchen.ci/docs/drivers
+
+image: "chef/chefdk"
+services:
+ - docker:dind
+
+variables:
+ DOCKER_HOST: "tcp://docker:2375"
+ KITCHEN_LOCAL_YAML: ".kitchen.dokken.yml"
+
+stages:
+ - lint
+ - unit
+ - functional
+
+foodcritic:
+ stage: lint
+ script:
+ - chef exec foodcritic .
+
+cookstyle:
+ stage: lint
+ script:
+ - chef exec cookstyle .
+
+chefspec:
+ stage: unit
+ script:
+ - chef exec rspec spec
+
+# Set up your test matrix here. Example:
+#verify-centos-6:
+# stage: functional
+# before_script:
+# - apt-get update
+# - apt-get -y install rsync
+# script:
+# - kitchen verify default-centos-6 --destroy=always
+#
+#verify-centos-7:
+# stage: functional
+# before_script:
+# - apt-get update
+# - apt-get -y install rsync
+# script:
+# - kitchen verify default-centos-7 --destroy=always
diff --git a/vendor/gitlab-ci-yml/Go.gitlab-ci.yml b/vendor/gitlab-ci-yml/Go.gitlab-ci.yml
index 86e4985d8d2..d572d7a1edc 100644
--- a/vendor/gitlab-ci-yml/Go.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Go.gitlab-ci.yml
@@ -11,8 +11,8 @@ variables:
# repository in /go/src/gitlab.com/namespace/project
# Thus, making a symbolic link corrects this.
before_script:
- - mkdir -p $GOPATH/src/$REPO_NAME
- - ln -svf $CI_PROJECT_DIR/* $GOPATH/src/$REPO_NAME
+ - mkdir -p $GOPATH/src/$(dirname $REPO_NAME)
+ - ln -svf $CI_PROJECT_DIR $GOPATH/src/$REPO_NAME
- cd $GOPATH/src/$REPO_NAME
stages:
diff --git a/vendor/gitlab-ci-yml/LICENSE b/vendor/gitlab-ci-yml/LICENSE
index d6c93c6fcf7..27a215686e7 100644
--- a/vendor/gitlab-ci-yml/LICENSE
+++ b/vendor/gitlab-ci-yml/LICENSE
@@ -1,6 +1,6 @@
-The MIT License (MIT)
+Copyright (c) 2011-2017 GitLab B.V.
-Copyright (c) 2016-2017 GitLab.org
+With regard to the GitLab Software:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,17 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+For all third party components incorporated into the GitLab Software, those
+components are licensed under the original license provided by the owner of the
+applicable component.
diff --git a/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml b/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml
index 6573eceaa59..1463161a04b 100644
--- a/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Rust.gitlab-ci.yml
@@ -20,4 +20,4 @@ image: "rust:latest"
test:cargo:
script:
- rustc --version && cargo --version # Print version info for debugging
- - cargo test --verbose --jobs 1 --release # Don't paralize to make errors more readable
+ - cargo test --verbose --jobs 1 --release # Don't parallelise to make errors more readable
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index 6f6ca5f8b32..b6a5c2f81a0 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -1,460 +1,78 @@
-"","","MIT,ISC,Apache 2.0,New BSD,Simplified BSD"
RedCloth,4.3.2,MIT
-abbrev,1.0.9,ISC
-abbrev,1.1.0,ISC
-accepts,1.3.3,MIT
ace-rails-ap,4.1.2,MIT
-acorn,3.3.0,MIT
-acorn,4.0.13,MIT
-acorn,5.1.1,MIT
-acorn-dynamic-import,2.0.2,MIT
-acorn-jsx,3.0.1,MIT
-actionmailer,4.2.8,MIT
-actionpack,4.2.8,MIT
-actionview,4.2.8,MIT
-activejob,4.2.8,MIT
-activemodel,4.2.8,MIT
-activerecord,4.2.8,MIT
-activesupport,4.2.8,MIT
+actionmailer,4.2.10,MIT
+actionpack,4.2.10,MIT
+actionview,4.2.10,MIT
+activejob,4.2.10,MIT
+activemodel,4.2.10,MIT
+activerecord,4.2.10,MIT
+activesupport,4.2.10,MIT
acts-as-taggable-on,4.0.0,MIT
addressable,2.5.2,Apache 2.0
-after,0.8.2,MIT
-ajv,4.11.8,MIT
-ajv,5.2.2,MIT
-ajv-keywords,1.5.1,MIT
-ajv-keywords,2.1.0,MIT
akismet,2.0.0,MIT
-align-text,0.1.4,MIT
allocations,1.0.5,MIT
-alphanum-sort,1.0.2,MIT
-amdefine,1.0.1,BSD-3-Clause OR MIT
-ansi-escapes,1.4.0,MIT
-ansi-html,0.0.5,"Apache, Version 2.0"
-ansi-html,0.0.7,Apache 2.0
-ansi-regex,2.1.1,MIT
-ansi-styles,2.2.1,MIT
-ansi-styles,3.2.0,MIT
-anymatch,1.3.2,ISC
-append-transform,0.4.0,MIT
-aproba,1.1.1,ISC
-are-we-there-yet,1.1.4,ISC
arel,6.0.4,MIT
-argparse,1.0.9,MIT
-arr-diff,2.0.0,MIT
-arr-flatten,1.0.1,MIT
-array-find,1.0.0,MIT
-array-find-index,1.0.2,MIT
-array-flatten,1.1.1,MIT
-array-flatten,2.1.1,MIT
-array-slice,0.2.3,MIT
-array-union,1.0.2,MIT
-array-uniq,1.0.3,MIT
-array-unique,0.2.1,MIT
-arraybuffer.slice,0.0.6,MIT
-arrify,1.0.1,MIT
asana,0.6.0,MIT
asciidoctor,1.5.3,MIT
asciidoctor-plantuml,0.0.7,MIT
-asn1,0.2.3,MIT
-asn1.js,4.9.1,MIT
-assert,1.4.1,MIT
-assert-plus,0.2.0,MIT
-assert-plus,1.0.0,MIT
-async,0.9.2,MIT
-async,1.5.2,MIT
-async,2.4.1,MIT
-async-each,1.0.1,MIT
-asynckit,0.4.0,MIT
+asset_sync,2.2.0,MIT
atomic,1.1.99,Apache 2.0
attr_encrypted,3.0.3,MIT
attr_required,1.0.0,MIT
-autoprefixer,6.7.7,MIT
autoprefixer-rails,6.2.3,MIT
-autosize,4.0.0,MIT
-aws-sign2,0.6.0,Apache 2.0
-aws4,1.6.0,MIT
axiom-types,0.1.1,MIT
-axios,0.16.2,MIT
-babel-code-frame,6.22.0,MIT
-babel-core,6.23.1,MIT
-babel-eslint,7.2.1,MIT
-babel-generator,6.23.0,MIT
-babel-helper-bindify-decorators,6.22.0,MIT
-babel-helper-builder-binary-assignment-operator-visitor,6.22.0,MIT
-babel-helper-call-delegate,6.22.0,MIT
-babel-helper-define-map,6.23.0,MIT
-babel-helper-explode-assignable-expression,6.22.0,MIT
-babel-helper-explode-class,6.22.0,MIT
-babel-helper-function-name,6.23.0,MIT
-babel-helper-get-function-arity,6.22.0,MIT
-babel-helper-hoist-variables,6.22.0,MIT
-babel-helper-optimise-call-expression,6.23.0,MIT
-babel-helper-regex,6.22.0,MIT
-babel-helper-remap-async-to-generator,6.22.0,MIT
-babel-helper-replace-supers,6.23.0,MIT
-babel-helpers,6.23.0,MIT
-babel-loader,7.1.1,MIT
-babel-messages,6.23.0,MIT
-babel-plugin-check-es2015-constants,6.22.0,MIT
-babel-plugin-istanbul,4.0.0,New BSD
-babel-plugin-syntax-async-functions,6.13.0,MIT
-babel-plugin-syntax-async-generators,6.13.0,MIT
-babel-plugin-syntax-class-properties,6.13.0,MIT
-babel-plugin-syntax-decorators,6.13.0,MIT
-babel-plugin-syntax-dynamic-import,6.18.0,MIT
-babel-plugin-syntax-exponentiation-operator,6.13.0,MIT
-babel-plugin-syntax-object-rest-spread,6.13.0,MIT
-babel-plugin-syntax-trailing-function-commas,6.22.0,MIT
-babel-plugin-transform-async-generator-functions,6.22.0,MIT
-babel-plugin-transform-async-to-generator,6.22.0,MIT
-babel-plugin-transform-class-properties,6.23.0,MIT
-babel-plugin-transform-decorators,6.22.0,MIT
-babel-plugin-transform-define,1.2.0,MIT
-babel-plugin-transform-es2015-arrow-functions,6.22.0,MIT
-babel-plugin-transform-es2015-block-scoped-functions,6.22.0,MIT
-babel-plugin-transform-es2015-block-scoping,6.23.0,MIT
-babel-plugin-transform-es2015-classes,6.23.0,MIT
-babel-plugin-transform-es2015-computed-properties,6.22.0,MIT
-babel-plugin-transform-es2015-destructuring,6.23.0,MIT
-babel-plugin-transform-es2015-duplicate-keys,6.22.0,MIT
-babel-plugin-transform-es2015-for-of,6.23.0,MIT
-babel-plugin-transform-es2015-function-name,6.22.0,MIT
-babel-plugin-transform-es2015-literals,6.22.0,MIT
-babel-plugin-transform-es2015-modules-amd,6.24.0,MIT
-babel-plugin-transform-es2015-modules-commonjs,6.24.0,MIT
-babel-plugin-transform-es2015-modules-systemjs,6.23.0,MIT
-babel-plugin-transform-es2015-modules-umd,6.24.0,MIT
-babel-plugin-transform-es2015-object-super,6.22.0,MIT
-babel-plugin-transform-es2015-parameters,6.23.0,MIT
-babel-plugin-transform-es2015-shorthand-properties,6.22.0,MIT
-babel-plugin-transform-es2015-spread,6.22.0,MIT
-babel-plugin-transform-es2015-sticky-regex,6.22.0,MIT
-babel-plugin-transform-es2015-template-literals,6.22.0,MIT
-babel-plugin-transform-es2015-typeof-symbol,6.23.0,MIT
-babel-plugin-transform-es2015-unicode-regex,6.22.0,MIT
-babel-plugin-transform-exponentiation-operator,6.22.0,MIT
-babel-plugin-transform-object-rest-spread,6.23.0,MIT
-babel-plugin-transform-regenerator,6.22.0,MIT
-babel-plugin-transform-strict-mode,6.22.0,MIT
-babel-preset-es2015,6.24.0,MIT
-babel-preset-es2016,6.22.0,MIT
-babel-preset-es2017,6.22.0,MIT
-babel-preset-latest,6.24.0,MIT
-babel-preset-stage-2,6.22.0,MIT
-babel-preset-stage-3,6.22.0,MIT
-babel-register,6.23.0,MIT
-babel-runtime,6.22.0,MIT
-babel-template,6.23.0,MIT
-babel-traverse,6.23.1,MIT
-babel-types,6.23.0,MIT
babosa,1.0.2,MIT
-babylon,6.16.1,MIT
-backo2,1.0.2,MIT
-balanced-match,0.4.2,MIT
-balanced-match,1.0.0,MIT
base32,0.3.2,MIT
-base64-arraybuffer,0.1.5,MIT
-base64-js,1.2.0,MIT
-base64id,1.0.0,MIT
-batch,0.6.1,MIT
+batch-loader,1.1.1,MIT
bcrypt,3.1.11,MIT
-bcrypt-pbkdf,1.0.1,New BSD
bcrypt_pbkdf,1.0.0,MIT
-better-assert,1.0.2,MIT
-big.js,3.1.3,MIT
-binary-extensions,1.10.0,MIT
bindata,2.4.1,ruby
-blob,0.0.4,unknown
-block-stream,0.0.9,ISC
-bluebird,2.11.0,MIT
-bluebird,3.5.0,MIT
-bn.js,4.11.6,MIT
-body-parser,1.17.2,MIT
-bonjour,3.5.0,MIT
-boom,2.10.1,New BSD
bootstrap-sass,3.3.6,MIT
bootstrap_form,2.7.0,MIT
-brace-expansion,1.1.7,MIT
-brace-expansion,1.1.8,MIT
-braces,0.1.5,MIT
-braces,1.8.5,MIT
-brorand,1.0.7,MIT
browser,2.2.0,MIT
-browserify-aes,1.0.6,MIT
-browserify-cipher,1.0.0,MIT
-browserify-des,1.0.0,MIT
-browserify-rsa,4.0.1,MIT
-browserify-sign,4.0.0,ISC
-browserify-zlib,0.1.4,MIT
-browserslist,1.7.7,MIT
-buffer,4.9.1,MIT
-buffer-indexof,1.1.0,MIT
-buffer-shims,1.0.0,MIT
-buffer-xor,1.0.3,MIT
builder,3.2.3,MIT
-builtin-modules,1.1.1,MIT
-builtin-status-codes,3.0.0,MIT
-bytes,2.4.0,MIT
-bytes,2.5.0,MIT
-caller-path,0.1.0,MIT
-callsite,1.0.0,unknown
-callsites,0.2.0,MIT
-camelcase,1.2.1,MIT
-camelcase,2.1.1,MIT
-camelcase,3.0.0,MIT
-camelcase,4.1.0,MIT
-camelcase-keys,2.1.0,MIT
-caniuse-api,1.6.1,MIT
-caniuse-db,1.0.30000649,CC-BY-4.0
carrierwave,1.2.1,MIT
-caseless,0.12.0,Apache 2.0
cause,0.1,MIT
-center-align,0.1.3,MIT
-chalk,1.1.3,MIT
-chalk,2.3.0,MIT
charlock_holmes,0.7.5,MIT
-chokidar,1.7.0,MIT
chronic,0.10.2,MIT
chronic_duration,0.10.6,MIT
chunky_png,1.3.5,MIT
-cipher-base,1.0.3,MIT
-circular-json,0.3.3,MIT
citrus,3.0.2,MIT
-clap,1.1.3,MIT
-cli-cursor,1.0.2,MIT
-cli-width,2.1.0,ISC
-clipboard,1.6.1,MIT
-cliui,2.1.0,ISC
-cliui,3.2.0,ISC
-clone,1.0.2,MIT
-co,4.6.0,MIT
-coa,1.0.1,MIT
-code-point-at,1.1.0,MIT
coercible,1.0.0,MIT
-color,0.11.4,MIT
-color-convert,1.9.0,MIT
-color-name,1.1.2,MIT
-color-string,0.3.0,MIT
-colormin,1.1.2,MIT
-colors,1.1.2,MIT
-combine-lists,1.0.1,MIT
-combined-stream,1.0.5,MIT
-commander,2.9.0,MIT
-commondir,1.0.1,MIT
-component-bind,1.0.0,unknown
-component-emitter,1.1.2,unknown
-component-emitter,1.2.1,MIT
-component-inherit,0.0.3,unknown
-compressible,2.0.11,MIT
-compression,1.7.0,MIT
-compression-webpack-plugin,1.0.0,MIT
-concat-map,0.0.1,MIT
-concat-stream,1.6.0,MIT
concurrent-ruby-ext,1.0.5,MIT
-config-chain,1.1.11,MIT
-configstore,1.4.0,Simplified BSD
-connect,3.6.3,MIT
-connect-history-api-fallback,1.3.0,MIT
connection_pool,2.2.1,MIT
-console-browserify,1.1.0,MIT
-console-control-strings,1.1.0,ISC
-consolidate,0.14.5,MIT
-constants-browserify,1.0.0,MIT
-contains-path,0.1.0,MIT
-content-disposition,0.5.2,MIT
-content-type,1.0.2,MIT
-convert-source-map,1.3.0,MIT
-cookie,0.3.1,MIT
-cookie-signature,1.0.6,MIT
-copy-webpack-plugin,4.0.1,MIT
-core-js,2.3.0,MIT
-core-js,2.4.1,MIT
-core-util-is,1.0.2,MIT
-cosmiconfig,2.1.1,MIT
crack,0.4.3,MIT
-create-ecdh,4.0.0,MIT
-create-hash,1.1.2,MIT
-create-hmac,1.1.4,MIT
creole,0.5.0,ruby
-cropper,2.3.0,MIT
-cross-spawn,5.1.0,MIT
-cryptiles,2.0.5,New BSD
-crypto-browserify,3.11.0,MIT
-css-color-names,0.0.4,MIT
-css-loader,0.28.0,MIT
-css-selector-tokenizer,0.6.0,MIT
-css-selector-tokenizer,0.7.0,MIT
css_parser,1.5.0,MIT
-cssesc,0.1.0,MIT
-cssnano,3.10.0,MIT
-csso,2.3.2,MIT
-currently-unhandled,0.4.1,MIT
-custom-event,1.0.1,MIT
-d,0.1.1,MIT
-d,1.0.0,MIT
-d3,3.5.11,New BSD
d3_rails,3.5.11,MIT
-dashdash,1.14.1,MIT
-date-now,0.1.4,MIT
-de-indent,1.0.2,MIT
-debug,2.2.0,MIT
-debug,2.3.3,MIT
-debug,2.6.7,MIT
-debug,2.6.8,MIT
debugger-ruby_core_source,1.3.8,MIT
-decamelize,1.2.0,MIT
deckar01-task_list,2.0.0,MIT
declarative,0.0.10,MIT
declarative-option,0.1.0,MIT
-decompress-response,3.3.0,MIT
-deep-equal,1.0.1,MIT
-deep-extend,0.4.2,MIT
-deep-is,0.1.3,MIT
-default-require-extensions,1.0.0,MIT
default_value_for,3.0.2,MIT
-defined,1.0.0,MIT
-del,2.2.2,MIT
-del,3.0.0,MIT
-delayed-stream,1.0.0,MIT
-delegate,3.1.2,MIT
-delegates,1.0.0,MIT
-depd,1.1.0,MIT
-depd,1.1.1,MIT
-des.js,1.0.0,MIT
descendants_tracker,0.0.4,MIT
-destroy,1.0.4,MIT
-detect-indent,4.0.0,MIT
-detect-node,2.0.3,ISC
devise,4.2.0,MIT
devise-two-factor,3.0.0,MIT
-di,0.0.1,MIT
diff-lcs,1.3,"MIT,Artistic-2.0,GPL-2.0+"
-diffie-hellman,5.0.2,MIT
diffy,3.1.0,MIT
-dns-equal,1.0.0,MIT
-dns-packet,1.2.2,MIT
-dns-txt,2.0.2,MIT
-doctrine,1.5.0,BSD
-doctrine,2.0.0,Apache 2.0
-document-register-element,1.3.0,MIT
-dom-serialize,2.2.1,MIT
-dom-serializer,0.1.0,MIT
-domain-browser,1.1.7,MIT
domain_name,0.5.20161021,"Simplified BSD,New BSD,Mozilla Public License 2.0"
-domelementtype,1.1.3,unknown
-domelementtype,1.3.0,unknown
-domhandler,2.3.0,unknown
-domutils,1.5.1,unknown
doorkeeper,4.2.6,MIT
doorkeeper-openid_connect,1.2.0,MIT
-dropzone,4.2.0,MIT
dropzonejs-rails,0.7.2,MIT
-duplexer,0.1.1,MIT
-duplexer3,0.1.4,New BSD
-duplexify,3.5.1,MIT
-ecc-jsbn,0.1.1,MIT
-editorconfig,0.13.2,MIT
-ee-first,1.1.1,MIT
-ejs,2.5.6,Apache 2.0
-electron-to-chromium,1.3.3,ISC
-elliptic,6.3.3,MIT
email_reply_trimmer,0.1.6,MIT
-emoji-unicode-version,0.2.1,MIT
-emojis-list,2.1.0,MIT
-encodeurl,1.0.1,MIT
encryptor,3.0.0,MIT
-end-of-stream,1.4.0,MIT
-engine.io,1.8.3,MIT
-engine.io-client,1.8.3,MIT
-engine.io-parser,1.3.2,MIT
-enhanced-resolve,0.9.1,MIT
-enhanced-resolve,3.4.1,MIT
-ent,2.2.0,MIT
-entities,1.1.1,BSD-like
equalizer,0.0.11,MIT
-errno,0.1.4,MIT
-error-ex,1.3.0,MIT
erubis,2.7.0,MIT
-es5-ext,0.10.24,MIT
-es6-iterator,2.0.1,MIT
-es6-map,0.1.5,MIT
-es6-promise,3.0.2,MIT
-es6-set,0.1.5,MIT
-es6-symbol,3.1.1,MIT
-es6-weak-map,2.0.1,MIT
-escape-html,1.0.3,MIT
-escape-string-regexp,1.0.5,MIT
escape_utils,1.1.1,MIT
-escodegen,1.8.1,Simplified BSD
-escope,3.6.0,Simplified BSD
-eslint,3.19.0,MIT
-eslint-config-airbnb-base,10.0.1,MIT
-eslint-import-resolver-node,0.2.3,MIT
-eslint-import-resolver-webpack,0.8.3,MIT
-eslint-module-utils,2.0.0,MIT
-eslint-plugin-filenames,1.1.0,MIT
-eslint-plugin-html,2.0.1,ISC
-eslint-plugin-import,2.2.0,MIT
-eslint-plugin-jasmine,2.2.0,MIT
-eslint-plugin-promise,3.5.0,ISC
-espree,3.5.0,Simplified BSD
-esprima,2.7.3,Simplified BSD
-esprima,4.0.0,Simplified BSD
-esquery,1.0.0,BSD
-esrecurse,4.1.0,Simplified BSD
-estraverse,1.9.3,BSD
-estraverse,4.1.1,Simplified BSD
-estraverse,4.2.0,Simplified BSD
-esutils,2.0.2,BSD
et-orbi,1.0.3,MIT
-etag,1.8.0,MIT
-eve-raphael,0.5.0,Apache 2.0
-event-emitter,0.3.5,MIT
-event-stream,3.3.4,MIT
-eventemitter3,1.2.0,MIT
-events,1.1.1,MIT
-eventsource,0.1.6,MIT
-evp_bytestokey,1.0.0,MIT
excon,0.57.1,MIT
-execa,0.7.0,MIT
execjs,2.6.0,MIT
-exit-hook,1.1.1,MIT
-expand-braces,0.1.2,MIT
-expand-brackets,0.1.5,MIT
-expand-range,0.1.1,MIT
-expand-range,1.8.2,MIT
-exports-loader,0.6.4,MIT
-express,4.15.4,MIT
expression_parser,0.9.0,MIT
-extend,3.0.1,MIT
-extglob,0.3.2,MIT
-extsprintf,1.0.2,MIT
faraday,0.12.2,MIT
faraday_middleware,0.11.0.1,MIT
faraday_middleware-multi_json,0.0.6,MIT
-fast-deep-equal,1.0.0,MIT
-fast-levenshtein,2.0.6,MIT
fast_gettext,1.4.0,"MIT,ruby"
-fastparse,1.1.1,MIT
-faye-websocket,0.10.0,MIT
-faye-websocket,0.11.1,MIT
-faye-websocket,0.7.3,MIT
ffi,1.9.18,New BSD
-figures,1.7.0,MIT
-file-entry-cache,2.0.0,MIT
-file-loader,0.11.1,MIT
-filename-regex,2.0.0,MIT
-fileset,2.0.3,MIT
-filesize,3.3.0,New BSD
-filesize,3.5.10,New BSD
-fill-range,2.2.3,MIT
-finalhandler,1.0.4,MIT
-find-cache-dir,1.0.0,MIT
-find-root,0.1.2,MIT
-find-up,1.1.2,MIT
-find-up,2.1.0,MIT
-flat-cache,1.2.2,MIT
-flatten,1.0.2,MIT
flipper,0.10.2,MIT
flipper-active_record,0.10.2,MIT
flowdock,0.7.1,MIT
@@ -467,392 +85,93 @@ fog-local,0.3.1,MIT
fog-openstack,0.1.21,MIT
fog-rackspace,0.1.1,MIT
fog-xml,0.1.3,MIT
-follow-redirects,1.2.3,MIT
font-awesome-rails,4.7.0.1,"MIT,SIL Open Font License"
-for-in,0.1.6,MIT
-for-own,0.1.4,MIT
-forever-agent,0.6.1,Apache 2.0
-form-data,2.1.4,MIT
formatador,0.2.5,MIT
-forwarded,0.1.0,MIT
-fresh,0.5.0,MIT
-from,0.1.7,MIT
-fs-access,1.0.1,MIT
-fs-extra,0.26.7,MIT
-fs.realpath,1.0.0,ISC
-fsevents,1.1.2,MIT
-fstream,1.0.11,ISC
-fstream-ignore,1.0.5,ISC
-function-bind,1.1.0,MIT
-fuzzaldrin-plus,0.5.0,MIT
-gauge,2.7.4,ISC
gemnasium-gitlab-service,0.2.6,MIT
gemojione,3.3.0,MIT
-generate-function,2.0.0,MIT
-generate-object-property,1.2.0,MIT
-get-caller-file,1.0.2,ISC
-get-stdin,4.0.1,MIT
-get-stream,3.0.0,MIT
get_process_mem,0.2.0,MIT
-getpass,0.1.7,MIT
gettext_i18n_rails,1.8.0,MIT
gettext_i18n_rails_js,1.2.0,MIT
-gitaly-proto,0.51.0,MIT
+gitaly-proto,0.59.0,MIT
github-linguist,4.7.6,MIT
github-markup,1.6.1,MIT
gitlab-flowdock-git-hook,1.0.1,MIT
gitlab-grit,2.8.2,MIT
gitlab-markup,1.6.3,MIT
-gitlab-svgs,1.0.4,unknown
gitlab_omniauth-ldap,2.0.4,MIT
-glob,5.0.15,ISC
-glob,6.0.4,ISC
-glob,7.1.1,ISC
-glob,7.1.2,ISC
-glob-base,0.3.0,MIT
-glob-parent,2.0.0,ISC
-globalid,0.3.7,MIT
-globals,9.18.0,MIT
-globby,5.0.0,MIT
-globby,6.1.0,MIT
+globalid,0.4.1,MIT
gollum-grit_adapter,1.0.1,MIT
gollum-lib,4.2.7,MIT
gollum-rugged_adapter,0.4.4,MIT
gon,6.1.0,MIT
-good-listener,1.2.2,MIT
google-api-client,0.13.6,Apache 2.0
google-protobuf,3.4.1.1,New BSD
-googleapis-common-protos-types,1.0.0,Apache 2.0
googleauth,0.5.3,Apache 2.0
-got,3.3.1,MIT
-got,7.1.0,MIT
gpgme,2.0.13,LGPL-2.1+
-graceful-fs,4.1.11,ISC
-graceful-readlink,1.0.1,MIT
grape,1.0.0,MIT
grape-entity,0.6.0,MIT
grape-route-helpers,2.1.0,MIT
grape_logging,1.7.0,MIT
-grpc,1.6.6,Apache 2.0
-gzip-size,3.0.0,MIT
+grpc,1.4.5,New BSD
hamlit,2.6.1,MIT
-handle-thing,1.2.5,MIT
-handlebars,4.0.6,MIT
-har-schema,1.0.5,ISC
-har-validator,4.2.1,ISC
-has,1.0.1,MIT
-has-ansi,2.0.0,MIT
-has-binary,0.1.7,MIT
-has-cors,1.1.0,MIT
-has-flag,1.0.0,MIT
-has-flag,2.0.0,MIT
-has-symbol-support-x,1.3.0,MIT
-has-to-string-tag-x,1.3.0,MIT
-has-unicode,2.0.1,ISC
-hash-sum,1.0.2,MIT
-hash.js,1.0.3,MIT
hashie,3.5.6,MIT
hashie-forbidden_attributes,0.1.1,MIT
-hawk,3.1.3,New BSD
-he,1.1.1,MIT
health_check,2.6.0,MIT
hipchat,1.5.2,MIT
-hoek,2.16.3,New BSD
-home-or-tmp,2.0.0,MIT
-hosted-git-info,2.2.0,ISC
-hpack.js,2.1.6,MIT
-html-comment-regex,1.1.1,MIT
-html-entities,1.2.0,MIT
html-pipeline,1.11.0,MIT
html2text,0.2.0,MIT
htmlentities,4.3.4,MIT
-htmlparser2,3.9.2,MIT
http,0.9.8,MIT
http-cookie,1.0.3,MIT
-http-deceiver,1.2.7,MIT
-http-errors,1.6.1,MIT
-http-errors,1.6.2,MIT
http-form_data,1.0.1,MIT
-http-proxy,1.16.2,MIT
-http-proxy-middleware,0.17.4,MIT
-http-signature,1.1.1,MIT
http_parser.rb,0.6.0,MIT
httparty,0.13.7,MIT
httpclient,2.8.2,ruby
-https-browserify,0.0.1,MIT
-i18n,0.8.6,MIT
+i18n,0.9.1,MIT
ice_nine,0.11.2,MIT
-iconv-lite,0.4.15,MIT
-icss-replace-symbols,1.0.2,ISC
-ieee754,1.1.8,New BSD
-ignore,3.3.3,MIT
-ignore-by-default,1.0.1,ISC
-immediate,3.0.6,MIT
-imports-loader,0.7.1,MIT
-imurmurhash,0.1.4,MIT
-indent-string,2.1.0,MIT
-indexes-of,1.0.1,MIT
-indexof,0.0.1,unknown
-infinity-agent,2.0.3,MIT
-inflight,1.0.6,ISC
influxdb,0.2.3,MIT
-inherits,2.0.1,ISC
-inherits,2.0.3,ISC
-ini,1.3.4,ISC
-inquirer,0.12.0,MIT
-internal-ip,1.2.0,MIT
-interpret,1.0.1,MIT
-invariant,2.2.2,New BSD
-invert-kv,1.0.0,MIT
-ip,1.1.5,MIT
-ipaddr.js,1.4.0,MIT
ipaddress,0.8.3,MIT
-is-absolute,0.2.6,MIT
-is-absolute-url,2.1.0,MIT
-is-arrayish,0.2.1,MIT
-is-binary-path,1.0.1,MIT
-is-buffer,1.1.5,MIT
-is-builtin-module,1.0.0,MIT
-is-dotfile,1.0.2,MIT
-is-equal-shallow,0.1.3,MIT
-is-extendable,0.1.1,MIT
-is-extglob,1.0.0,MIT
-is-extglob,2.1.1,MIT
-is-finite,1.0.2,MIT
-is-fullwidth-code-point,1.0.0,MIT
-is-fullwidth-code-point,2.0.0,MIT
-is-glob,2.0.1,MIT
-is-glob,3.1.0,MIT
-is-my-json-valid,2.16.0,MIT
-is-npm,1.0.0,MIT
-is-number,0.1.1,MIT
-is-number,2.1.0,MIT
-is-object,1.0.1,MIT
-is-path-cwd,1.0.0,MIT
-is-path-in-cwd,1.0.0,MIT
-is-path-inside,1.0.0,MIT
-is-plain-obj,1.1.0,MIT
-is-posix-bracket,0.1.1,MIT
-is-primitive,2.0.0,MIT
-is-property,1.0.2,MIT
-is-redirect,1.0.0,MIT
-is-relative,0.2.1,MIT
-is-resolvable,1.0.0,MIT
-is-retry-allowed,1.1.0,MIT
-is-stream,1.1.0,MIT
-is-svg,2.1.0,MIT
-is-typedarray,1.0.0,MIT
-is-unc-path,0.1.2,MIT
-is-utf8,0.2.1,MIT
-is-windows,0.2.0,MIT
-isarray,0.0.1,MIT
-isarray,1.0.0,MIT
-isbinaryfile,3.0.2,MIT
-isexe,1.1.2,ISC
-isobject,2.1.0,MIT
-isstream,0.1.2,MIT
-istanbul,0.4.5,New BSD
-istanbul-api,1.1.1,New BSD
-istanbul-lib-coverage,1.0.1,New BSD
-istanbul-lib-hook,1.0.0,New BSD
-istanbul-lib-instrument,1.4.2,New BSD
-istanbul-lib-report,1.0.0-alpha.3,New BSD
-istanbul-lib-source-maps,1.1.0,New BSD
-istanbul-reports,1.0.1,New BSD
-isurl,1.0.0,MIT
-jasmine-core,2.6.3,MIT
-jasmine-jquery,2.1.1,MIT
-jed,1.1.1,MIT
jira-ruby,1.4.1,MIT
-jodid25519,1.0.2,MIT
-jquery,2.2.1,MIT
jquery-atwho-rails,1.3.2,MIT
-jquery-rails,4.1.1,MIT
-jquery-ujs,1.2.1,MIT
-js-base64,2.1.9,BSD
-js-beautify,1.6.12,MIT
-js-cookie,2.1.3,MIT
-js-tokens,3.0.1,MIT
-js-yaml,3.7.0,MIT
-js-yaml,3.9.1,MIT
-jsbn,0.1.1,MIT
-jsesc,0.5.0,MIT
-jsesc,1.3.0,MIT
+jquery-rails,4.3.1,MIT
json,1.8.6,ruby
json-jwt,1.7.2,MIT
-json-loader,0.5.7,MIT
-json-schema,0.2.3,"AFLv2.1,BSD"
-json-schema-traverse,0.3.1,MIT
-json-stable-stringify,1.0.1,MIT
-json-stringify-safe,5.0.1,ISC
-json3,3.3.2,MIT
-json5,0.5.1,MIT
-jsonfile,2.4.0,MIT
-jsonify,0.0.0,Public Domain
-jsonpointer,4.0.1,MIT
-jsprim,1.4.0,MIT
-jszip,3.1.3,(MIT OR GPL-3.0)
-jszip-utils,0.0.2,MIT or GPLv3
jwt,1.5.6,MIT
kaminari,1.0.1,MIT
kaminari-actionview,1.0.1,MIT
kaminari-activerecord,1.0.1,MIT
kaminari-core,1.0.1,MIT
-karma,1.7.0,MIT
-karma-chrome-launcher,2.1.1,MIT
-karma-coverage-istanbul-reporter,0.2.0,MIT
-karma-jasmine,1.1.0,MIT
-karma-mocha-reporter,2.2.2,MIT
-karma-sourcemap-loader,0.3.7,MIT
-karma-webpack,2.0.4,MIT
kgio,2.10.0,LGPL-2.1+
-kind-of,3.1.0,MIT
-klaw,1.3.1,MIT
kubeclient,2.2.0,MIT
-latest-version,1.0.1,MIT
-lazy-cache,1.0.4,MIT
-lcid,1.0.0,MIT
-levn,0.3.0,MIT
licensee,8.7.0,MIT
-lie,3.1.1,MIT
little-plugger,1.1.4,MIT
-load-json-file,1.1.0,MIT
-load-json-file,2.0.0,MIT
-loader-runner,2.3.0,MIT
-loader-utils,0.2.16,MIT
-loader-utils,1.1.0,MIT
locale,2.1.2,"ruby,LGPLv3+"
-locate-path,2.0.0,MIT
-lodash,3.10.1,MIT
-lodash,4.17.4,MIT
-lodash._baseassign,3.2.0,MIT
-lodash._basecopy,3.0.1,MIT
-lodash._baseget,3.7.2,MIT
-lodash._bindcallback,3.0.1,MIT
-lodash._createassigner,3.1.1,MIT
-lodash._getnative,3.9.1,MIT
-lodash._isiterateecall,3.0.9,MIT
-lodash._topath,3.8.1,MIT
-lodash.assign,3.2.0,MIT
-lodash.camelcase,4.1.1,MIT
-lodash.camelcase,4.3.0,MIT
-lodash.capitalize,4.2.1,MIT
-lodash.cond,4.5.2,MIT
-lodash.deburr,4.1.0,MIT
-lodash.defaults,3.1.2,MIT
-lodash.get,3.7.0,MIT
-lodash.get,4.4.2,MIT
-lodash.isarguments,3.1.0,MIT
-lodash.isarray,3.0.4,MIT
-lodash.kebabcase,4.0.1,MIT
-lodash.keys,3.1.2,MIT
-lodash.memoize,4.1.2,MIT
-lodash.restparam,3.6.1,MIT
-lodash.snakecase,4.0.1,MIT
-lodash.uniq,4.5.0,MIT
-lodash.words,4.2.0,MIT
-log4js,0.6.38,Apache 2.0
logging,2.2.2,MIT
-loglevel,1.4.1,MIT
lograge,0.5.1,MIT
-longest,1.0.1,MIT
loofah,2.0.3,MIT
-loose-envify,1.3.1,MIT
-loud-rejection,1.6.0,MIT
-lowercase-keys,1.0.0,MIT
-lru-cache,2.2.4,MIT
-lru-cache,3.2.0,ISC
-lru-cache,4.0.2,ISC
-macaddress,0.2.8,MIT
-mail,2.6.6,MIT
+mail,2.7.0,MIT
mail_room,0.9.1,MIT
-make-dir,1.0.0,MIT
-map-obj,1.0.1,MIT
-map-stream,0.1.0,unknown
-marked,0.3.6,MIT
-math-expression-evaluator,1.2.16,MIT
-media-typer,0.3.0,MIT
-mem,1.1.0,MIT
memoist,0.16.0,MIT
-memory-fs,0.2.0,MIT
-memory-fs,0.4.1,MIT
-meow,3.7.0,MIT
-merge-descriptors,1.0.1,MIT
method_source,0.8.2,MIT
-methods,1.1.2,MIT
-micromatch,2.3.11,MIT
-miller-rabin,4.0.0,MIT
-mime,1.3.4,MIT
-mime-db,1.27.0,MIT
-mime-db,1.29.0,MIT
-mime-types,2.1.15,MIT
mime-types,3.1,MIT
mime-types-data,3.2016.0521,MIT
mimemagic,0.3.0,MIT
-mimic-fn,1.1.0,MIT
-mimic-response,1.0.0,MIT
+mini_mime,0.1.4,MIT
mini_portile2,2.3.0,MIT
-minimalistic-assert,1.0.0,ISC
-minimatch,3.0.3,ISC
-minimatch,3.0.4,ISC
-minimist,0.0.8,MIT
-minimist,1.2.0,MIT
-mkdirp,0.5.1,MIT
-mmap2,2.2.7,ruby
-moment,2.17.1,MIT
-monaco-editor,0.10.0,MIT
-mousetrap,1.4.6,Apache 2.0
mousetrap-rails,1.4.6,"MIT,Apache"
-ms,0.7.1,MIT
-ms,0.7.2,MIT
-ms,2.0.0,MIT
multi_json,1.12.2,MIT
multi_xml,0.6.0,MIT
-multicast-dns,6.1.1,MIT
-multicast-dns-service-types,1.1.0,MIT
multipart-post,2.0.0,MIT
mustermann,1.0.0,MIT
mustermann-grape,1.0.0,MIT
-mute-stream,0.0.5,ISC
mysql2,0.4.5,MIT
-name-all-modules-plugin,1.0.1,MIT
-nan,2.6.2,MIT
-natural-compare,1.4.0,MIT
-negotiator,0.6.1,MIT
-nested-error-stacks,1.0.2,MIT
net-ldap,0.16.0,MIT
net-ssh,4.1.0,MIT
netrc,0.11.0,MIT
-node-dir,0.1.17,MIT
-node-forge,0.6.33,BSD
-node-libs-browser,1.1.1,MIT
-node-libs-browser,2.0.0,MIT
-node-pre-gyp,0.6.36,New BSD
-node-pre-gyp,0.6.37,New BSD
-nodemon,1.11.0,MIT
nokogiri,1.8.1,MIT
-nopt,1.0.10,MIT
-nopt,3.0.6,ISC
-nopt,4.0.1,ISC
-normalize-package-data,2.4.0,Simplified BSD
-normalize-path,2.1.1,MIT
-normalize-range,0.1.2,MIT
-normalize-url,1.9.1,MIT
-npm-run-path,2.0.2,MIT
-npmlog,4.1.0,ISC
-null-check,1.0.0,MIT
-num2fraction,1.2.2,MIT
-number-is-nan,1.0.1,MIT
numerizer,0.1.1,MIT
oauth,0.5.1,MIT
-oauth-sign,0.8.2,Apache 2.0
oauth2,1.4.0,MIT
-object-assign,3.0.0,MIT
-object-assign,4.1.0,MIT
-object-assign,4.1.1,MIT
-object-component,0.0.3,unknown
-object.omit,2.0.1,MIT
-obuf,1.1.1,MIT
octokit,4.6.2,MIT
oj,2.17.5,MIT
omniauth,1.4.2,MIT
@@ -873,54 +192,10 @@ omniauth-saml,1.7.0,MIT
omniauth-shibboleth,1.2.1,MIT
omniauth-twitter,1.2.1,MIT
omniauth_crowd,2.2.3,MIT
-on-finished,2.3.0,MIT
-on-headers,1.0.1,MIT
-once,1.4.0,ISC
-onetime,1.1.0,MIT
-opener,1.4.3,(WTFPL OR MIT)
-opn,4.0.2,MIT
-optimist,0.6.1,MIT/X11
-optionator,0.8.2,MIT
-options,0.0.6,MIT
org-ruby,0.9.12,MIT
-original,1.0.0,MIT
orm_adapter,0.5.0,MIT
os,0.9.6,MIT
-os-browserify,0.2.1,MIT
-os-homedir,1.0.2,MIT
-os-locale,1.4.0,MIT
-os-locale,2.1.0,MIT
-os-tmpdir,1.0.2,MIT
-osenv,0.1.4,ISC
-p-cancelable,0.3.0,MIT
-p-finally,1.0.0,MIT
-p-limit,1.1.0,MIT
-p-locate,2.0.0,MIT
-p-map,1.1.1,MIT
-p-timeout,1.2.0,MIT
-package-json,1.2.0,MIT
-pako,0.2.9,MIT
-pako,1.0.5,(MIT AND Zlib)
paranoia,2.3.1,MIT
-parse-asn1,5.0.0,ISC
-parse-glob,3.0.4,MIT
-parse-json,2.2.0,MIT
-parsejson,0.0.3,MIT
-parseqs,0.0.5,MIT
-parseuri,0.0.5,MIT
-parseurl,1.3.1,MIT
-path-browserify,0.0.0,MIT
-path-exists,2.1.0,MIT
-path-exists,3.0.0,MIT
-path-is-absolute,1.0.1,MIT
-path-is-inside,1.0.2,(WTFPL OR MIT)
-path-key,2.0.1,MIT
-path-parse,1.0.5,MIT
-path-to-regexp,0.1.7,MIT
-path-type,1.1.0,MIT
-path-type,2.0.0,MIT
-pause-stream,0.0.11,"MIT,Apache2"
-pbkdf2,3.0.9,MIT
peek,1.0.1,MIT
peek-gc,0.0.2,MIT
peek-host,1.0.0,MIT
@@ -930,86 +205,14 @@ peek-pg,1.3.0,MIT
peek-rblineprof,0.2.0,MIT
peek-redis,1.2.0,MIT
peek-sidekiq,1.0.3,MIT
-performance-now,0.2.0,MIT
pg,0.18.4,"BSD,ruby,GPL"
-pify,2.3.0,MIT
-pify,3.0.0,MIT
-pikaday,1.6.1,MIT
-pinkie,2.0.4,MIT
-pinkie-promise,2.0.1,MIT
-pkg-dir,1.0.0,MIT
-pkg-dir,2.0.0,MIT
-pkg-up,1.0.0,MIT
-pluralize,1.2.1,MIT
po_to_json,1.0.1,MIT
-portfinder,1.0.13,MIT
posix-spawn,0.3.13,MIT
-postcss,5.2.16,MIT
-postcss-calc,5.3.1,MIT
-postcss-colormin,2.2.2,MIT
-postcss-convert-values,2.6.1,MIT
-postcss-discard-comments,2.0.4,MIT
-postcss-discard-duplicates,2.1.0,MIT
-postcss-discard-empty,2.1.0,MIT
-postcss-discard-overridden,0.1.1,MIT
-postcss-discard-unused,2.2.3,MIT
-postcss-filter-plugins,2.0.2,MIT
-postcss-load-config,1.2.0,MIT
-postcss-load-options,1.2.0,MIT
-postcss-load-plugins,2.3.0,MIT
-postcss-merge-idents,2.1.7,MIT
-postcss-merge-longhand,2.0.2,MIT
-postcss-merge-rules,2.1.2,MIT
-postcss-message-helpers,2.0.0,MIT
-postcss-minify-font-values,1.0.5,MIT
-postcss-minify-gradients,1.0.5,MIT
-postcss-minify-params,1.2.2,MIT
-postcss-minify-selectors,2.1.1,MIT
-postcss-modules-extract-imports,1.0.1,ISC
-postcss-modules-local-by-default,1.1.1,MIT
-postcss-modules-scope,1.0.2,ISC
-postcss-modules-values,1.2.2,ISC
-postcss-normalize-charset,1.1.1,MIT
-postcss-normalize-url,3.0.8,MIT
-postcss-ordered-values,2.2.3,MIT
-postcss-reduce-idents,2.4.0,MIT
-postcss-reduce-initial,1.0.1,MIT
-postcss-reduce-transforms,1.0.4,MIT
-postcss-selector-parser,2.2.3,MIT
-postcss-svgo,2.1.6,MIT
-postcss-unique-selectors,2.0.2,MIT
-postcss-value-parser,3.3.0,MIT
-postcss-zindex,2.2.0,MIT
-prelude-ls,1.1.2,MIT
premailer,1.10.4,New BSD
premailer-rails,1.9.7,MIT
-prepend-http,1.0.4,MIT
-preserve,0.2.0,MIT
-prismjs,1.6.0,MIT
-private,0.1.7,MIT
-process,0.11.9,MIT
-process-nextick-args,1.0.7,MIT
-progress,1.1.8,MIT
-prometheus-client-mmap,0.7.0.beta18,Apache 2.0
-proto-list,1.2.4,ISC
-proxy-addr,1.1.5,MIT
-prr,0.0.0,MIT
-ps-tree,1.1.0,MIT
-pseudomap,1.0.2,ISC
-public-encrypt,4.0.0,MIT
+prometheus-client-mmap,0.7.0.beta43,Apache 2.0
public_suffix,3.0.0,MIT
-punycode,1.3.2,MIT
-punycode,1.4.1,MIT
pyu-ruby-sasl,0.0.3.3,MIT
-q,1.5.0,MIT
-qjobs,1.1.5,MIT
-qs,6.4.0,New BSD
-qs,6.5.0,New BSD
-query-string,4.3.2,MIT
-querystring,0.2.0,MIT
-querystring-es3,0.2.1,MIT
-querystringify,0.0.4,MIT
-querystringify,1.0.0,MIT
rack,1.6.8,MIT
rack-accept,0.4.5,MIT
rack-attack,4.4.1,MIT
@@ -1018,88 +221,35 @@ rack-oauth2,1.2.3,MIT
rack-protection,1.5.3,MIT
rack-proxy,0.6.0,MIT
rack-test,0.6.3,MIT
-rails,4.2.8,MIT
+rails,4.2.10,MIT
rails-deprecated_sanitizer,1.0.3,MIT
rails-dom-testing,1.0.8,MIT
rails-html-sanitizer,1.0.3,MIT
rails-i18n,4.0.9,MIT
-railties,4.2.8,MIT
+railties,4.2.10,MIT
rainbow,2.2.2,MIT
raindrops,0.18.0,LGPL-2.1+
-rake,12.1.0,MIT
-randomatic,1.1.6,MIT
-randombytes,2.0.3,MIT
-range-parser,1.2.0,MIT
-raphael,2.2.7,MIT
-raven-js,3.14.0,Simplified BSD
-raw-body,2.2.0,MIT
-raw-loader,0.5.1,MIT
+rake,12.3.0,MIT
rbnacl,4.0.2,MIT
rbnacl-libsodium,1.0.11,MIT
-rc,1.2.1,(BSD-2-Clause OR MIT OR Apache-2.0)
rdoc,4.2.2,ruby
re2,1.1.1,New BSD
-react-dev-utils,0.5.2,New BSD
-read-all-stream,3.1.0,MIT
-read-pkg,1.1.0,MIT
-read-pkg,2.0.0,MIT
-read-pkg-up,1.0.1,MIT
-read-pkg-up,2.0.0,MIT
-readable-stream,1.0.34,MIT
-readable-stream,2.0.6,MIT
-readable-stream,2.2.9,MIT
-readable-stream,2.3.3,MIT
-readdirp,2.1.0,MIT
-readline2,1.0.1,MIT
recaptcha,3.0.0,MIT
-rechoir,0.6.2,MIT
recursive-open-struct,1.0.0,MIT
-recursive-readdir,2.1.1,MIT
redcarpet,3.4.0,MIT
-redent,1.0.0,MIT
redis,3.3.3,MIT
-redis-actionpack,5.0.1,MIT
-redis-activesupport,5.0.1,MIT
+redis-actionpack,5.0.2,MIT
+redis-activesupport,5.0.4,MIT
redis-namespace,1.5.2,MIT
-redis-rack,1.6.0,MIT
-redis-rails,5.0.1,MIT
-redis-store,1.2.0,MIT
-reduce-css-calc,1.3.0,MIT
-reduce-function-call,1.0.2,MIT
-regenerate,1.3.2,MIT
-regenerator-runtime,0.10.1,MIT
-regenerator-transform,0.9.8,BSD
-regex-cache,0.4.3,MIT
-regexpu-core,1.0.0,MIT
-regexpu-core,2.0.0,MIT
-registry-url,3.1.0,MIT
-regjsgen,0.2.0,MIT
-regjsparser,0.1.5,BSD
-remove-trailing-separator,1.1.0,ISC
-repeat-element,1.1.2,MIT
-repeat-string,0.2.2,MIT
-repeat-string,1.6.1,MIT
-repeating,1.1.3,MIT
-repeating,2.0.1,MIT
+redis-rack,2.0.3,MIT
+redis-rails,5.0.2,MIT
+redis-store,1.4.1,MIT
representable,3.0.4,MIT
-request,2.81.0,Apache 2.0
request_store,1.3.1,MIT
-require-directory,2.1.1,MIT
-require-from-string,1.2.1,MIT
-require-main-filename,1.0.1,ISC
-require-uncached,1.0.3,MIT
-requires-port,1.0.0,MIT
-resolve,1.1.7,MIT
-resolve,1.2.0,MIT
-resolve-from,1.0.1,MIT
responders,2.3.0,MIT
rest-client,2.0.0,MIT
-restore-cursor,1.0.1,MIT
retriable,3.1.1,MIT
-right-align,0.1.3,MIT
-rimraf,2.6.1,ISC
rinku,2.0.0,ISC
-ripemd160,1.0.1,New BSD
rotp,2.1.2,MIT
rouge,2.2.1,MIT
rqrcode,0.7.0,MIT
@@ -1112,244 +262,51 @@ rubyntlm,0.6.2,MIT
rubypants,0.2.0,BSD
rufus-scheduler,3.4.0,MIT
rugged,0.26.0,MIT
-run-async,0.1.0,MIT
-rx-lite,3.1.2,Apache 2.0
-safe-buffer,5.0.1,MIT
-safe-buffer,5.1.1,MIT
safe_yaml,1.0.4,MIT
sanitize,2.1.0,MIT
sass,3.4.22,MIT
sass-rails,5.0.6,MIT
sawyer,0.8.1,MIT
-sax,1.2.2,ISC
securecompare,1.0.0,MIT
seed-fu,2.3.6,MIT
-select,1.1.2,MIT
-select-hose,2.0.0,MIT
-select2,3.5.2-browserify,unknown
select2-rails,3.5.9.3,MIT
-selfsigned,1.10.1,MIT
-semver,4.3.6,ISC
-semver,5.3.0,ISC
-semver-diff,2.1.0,MIT
-send,0.15.4,MIT
sentry-raven,2.5.3,Apache 2.0
-serve-index,1.9.0,MIT
-serve-static,1.12.4,MIT
-set-blocking,2.0.0,ISC
-set-immediate-shim,1.0.1,MIT
-setimmediate,1.0.5,MIT
-setprototypeof,1.0.3,ISC
settingslogic,2.0.9,MIT
sexp_processor,4.9.0,MIT
-sha.js,2.4.8,MIT
-shebang-command,1.2.0,MIT
-shebang-regex,1.0.0,MIT
-shelljs,0.7.8,New BSD
sidekiq,5.0.4,LGPL
sidekiq-cron,0.6.0,MIT
sidekiq-limit_fetch,3.4.0,MIT
-sigmund,1.0.1,ISC
-signal-exit,3.0.2,ISC
signet,0.7.3,Apache 2.0
slack-notifier,1.5.1,MIT
-slash,1.0.0,MIT
-slice-ansi,0.0.4,MIT
-slide,1.1.6,ISC
-sntp,1.0.9,BSD
-socket.io,1.7.3,MIT
-socket.io-adapter,0.5.0,MIT
-socket.io-client,1.7.3,MIT
-socket.io-parser,2.3.1,MIT
-sockjs,0.3.18,MIT
-sockjs-client,1.0.1,MIT
-sockjs-client,1.1.4,MIT
-sort-keys,1.1.2,MIT
-source-list-map,0.1.8,MIT
-source-list-map,2.0.0,MIT
-source-map,0.1.43,BSD
-source-map,0.2.0,BSD
-source-map,0.4.4,New BSD
-source-map,0.5.6,New BSD
-source-map-support,0.4.11,MIT
-spdx-correct,1.0.2,Apache 2.0
-spdx-expression-parse,1.0.4,(MIT AND CC-BY-3.0)
-spdx-license-ids,1.2.2,Unlicense
-spdy,3.4.7,MIT
-spdy-transport,2.0.20,MIT
-split,0.3.3,MIT
-sprintf-js,1.0.3,New BSD
sprockets,3.7.1,MIT
-sprockets-rails,3.2.0,MIT
-sql.js,0.4.0,MIT
-sshpk,1.13.0,MIT
+sprockets-rails,3.2.1,MIT
state_machines,0.4.0,MIT
state_machines-activemodel,0.4.0,MIT
state_machines-activerecord,0.4.0,MIT
-statuses,1.3.1,MIT
-stream-browserify,2.0.1,MIT
-stream-combiner,0.0.4,MIT
-stream-http,2.6.3,MIT
-stream-shift,1.0.0,MIT
-strict-uri-encode,1.1.0,MIT
-string-length,1.0.1,MIT
-string-width,1.0.2,MIT
-string-width,2.0.0,MIT
-string_decoder,0.10.31,MIT
-string_decoder,1.0.1,MIT
-string_decoder,1.0.3,MIT
stringex,2.7.1,MIT
-stringstream,0.0.5,MIT
-strip-ansi,3.0.1,MIT
-strip-bom,2.0.0,MIT
-strip-bom,3.0.0,MIT
-strip-eof,1.0.0,MIT
-strip-indent,1.0.1,MIT
-strip-json-comments,2.0.1,MIT
-supports-color,2.0.0,MIT
-supports-color,3.2.3,MIT
-supports-color,4.2.1,MIT
-svg4everybody,2.1.9,CC0-1.0
-svgo,0.7.2,MIT
sys-filesystem,1.1.6,Artistic 2.0
-table,3.8.3,New BSD
-tapable,0.1.10,MIT
-tapable,0.2.8,MIT
-tar,2.2.1,ISC
-tar-pack,3.4.0,Simplified BSD
temple,0.7.7,MIT
-test-exclude,4.0.0,ISC
text,1.3.1,MIT
-text-table,0.2.0,MIT
thor,0.19.4,MIT
thread_safe,0.3.6,Apache 2.0
-three,0.84.0,MIT
-three-orbit-controls,82.1.0,MIT
-three-stl-loader,1.0.4,MIT
-through,2.3.8,MIT
-thunky,0.1.0,unknown
tilt,2.0.6,MIT
-timeago.js,2.0.5,MIT
-timed-out,2.0.0,MIT
-timed-out,4.0.1,MIT
-timers-browserify,1.4.2,MIT
-timers-browserify,2.0.4,MIT
timfel-krb5-auth,0.8.3,LGPL
-tiny-emitter,1.1.0,MIT
-tmp,0.0.31,MIT
-to-array,0.1.4,MIT
-to-arraybuffer,1.0.1,MIT
-to-fast-properties,1.0.2,MIT
toml-rb,0.3.15,MIT
-touch,1.0.0,ISC
-tough-cookie,2.3.2,New BSD
-traverse,0.6.6,MIT
-trim-newlines,1.0.0,MIT
-trim-right,1.0.1,MIT
truncato,0.7.10,MIT
-tryit,1.0.3,MIT
-ts-loader,3.1.1,MIT
-tty-browserify,0.0.0,MIT
-tunnel-agent,0.6.0,Apache 2.0
-tweetnacl,0.14.5,Unlicense
-type-check,0.3.2,MIT
-type-is,1.6.15,MIT
-typedarray,0.0.6,MIT
-typescript,2.6.1,Apache 2.0
-tzinfo,1.2.3,MIT
+tzinfo,1.2.4,MIT
u2f,0.2.1,MIT
uber,0.1.0,MIT
uglifier,2.7.2,MIT
-uglify-js,2.8.29,Simplified BSD
-uglify-to-browserify,1.0.2,MIT
-uglifyjs-webpack-plugin,0.4.6,MIT
-uid-number,0.0.6,ISC
-ultron,1.0.2,MIT
-ultron,1.1.0,MIT
-unc-path-regex,0.1.2,MIT
-undefsafe,0.0.3,MIT / http://rem.mit-license.org
-underscore,1.8.3,MIT
unf,0.1.4,BSD
unf_ext,0.0.7.4,MIT
unicorn,5.1.0,ruby
unicorn-worker-killer,0.4.4,ruby
-uniq,1.0.1,MIT
-uniqid,4.1.1,MIT
-uniqs,2.0.0,MIT
-unpipe,1.0.0,MIT
-update-notifier,0.5.0,Simplified BSD
-url,0.11.0,MIT
-url-loader,0.5.8,MIT
-url-parse,1.0.5,MIT
-url-parse,1.1.7,MIT
-url-parse,1.1.9,MIT
-url-parse-lax,1.0.0,MIT
-url-to-options,1.0.1,MIT
url_safe_base64,0.2.2,MIT
-user-home,2.0.0,MIT
-useragent,2.2.1,MIT
-util,0.10.3,MIT
-util-deprecate,1.0.2,MIT
-utils-merge,1.0.0,MIT
-uuid,2.0.3,MIT
-uuid,3.0.1,MIT
-validate-npm-package-license,3.0.1,Apache 2.0
validates_hostname,1.0.6,MIT
-vary,1.1.1,MIT
-vendors,1.0.1,MIT
-verror,1.3.6,MIT
version_sorter,2.1.0,MIT
virtus,1.0.5,MIT
-visibilityjs,1.2.4,MIT
-vm-browserify,0.0.4,MIT
vmstat,2.3.0,MIT
-void-elements,2.0.1,MIT
-vue,2.5.2,MIT
-vue-hot-reload-api,2.0.11,MIT
-vue-loader,11.3.4,MIT
-vue-resource,1.3.4,MIT
-vue-style-loader,2.0.5,MIT
-vue-template-compiler,2.5.2,MIT
-vue-template-es2015-compiler,1.5.1,MIT
-vuex,3.0.0,MIT
warden,1.2.6,MIT
-watchpack,1.4.0,MIT
-wbuf,1.7.2,MIT
-webpack,3.5.5,MIT
-webpack-bundle-analyzer,2.8.2,MIT
-webpack-dev-middleware,1.11.0,MIT
-webpack-dev-server,2.7.1,MIT
webpack-rails,0.9.10,MIT
-webpack-sources,1.0.1,MIT
-webpack-stats-plugin,0.1.5,MIT
-websocket-driver,0.6.5,MIT
-websocket-extensions,0.1.1,MIT
-whet.extend,0.9.9,MIT
-which,1.2.12,ISC
-which-module,1.0.0,ISC
-which-module,2.0.0,ISC
-wide-align,1.1.2,ISC
wikicloth,0.8.1,MIT
-window-size,0.1.0,MIT
-wordwrap,0.0.2,MIT/X11
-wordwrap,0.0.3,MIT
-wordwrap,1.0.0,MIT
-wrap-ansi,2.1.0,MIT
-wrappy,1.0.2,ISC
-write,0.2.1,MIT
-write-file-atomic,1.3.4,ISC
-ws,1.1.2,MIT
-ws,2.3.1,MIT
-wtf-8,1.0.0,MIT
-xdg-basedir,2.0.0,MIT
xml-simple,1.1.5,ruby
-xmlhttprequest-ssl,1.5.3,MIT
-xtend,4.0.1,MIT
-y18n,3.2.1,ISC
-yallist,2.1.2,ISC
-yargs,3.10.0,MIT
-yargs,6.6.0,MIT
-yargs,8.0.2,MIT
-yargs-parser,4.2.1,ISC
-yargs-parser,7.0.0,ISC
-yeast,0.1.2,MIT