Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Lopez <james@gitlab.com>2017-12-14 14:28:00 +0300
committerJames Lopez <james@gitlab.com>2017-12-14 14:28:00 +0300
commitc425722d74ed44b6e5e71af038acc81954b00d5a (patch)
treec657b0254f733939ea2280d05572ad0e7f321b68
parent83e01aba58826ab0775d3ecf73717470da81bd2a (diff)
parent7b16c193a16eb387096f4d18c1d7c91072b33b87 (diff)
Merge branch '10-3-stable-prepare-rc1' into '10-3-stable'
Prepare 10.3 RC1 release See merge request gitlab-org/gitlab-ce!15907
-rw-r--r--CHANGELOG.md33
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/commit/image_file.js4
-rw-r--r--app/assets/javascripts/contextual_sidebar.js8
-rw-r--r--app/assets/javascripts/fly_out_nav.js2
-rw-r--r--app/assets/javascripts/issue.js13
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue88
-rw-r--r--app/assets/javascripts/issue_show/components/description.vue24
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/graph.vue24
-rw-r--r--app/assets/javascripts/monitoring/components/graph/deployment.vue101
-rw-r--r--app/assets/javascripts/monitoring/mixins/monitoring_mixins.js2
-rw-r--r--app/assets/javascripts/monitoring/utils/date_time_formatters.js1
-rw-r--r--app/assets/javascripts/notes/components/issue_note.vue3
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js24
-rw-r--r--app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue11
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue9
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js71
-rw-r--r--app/assets/javascripts/sidebar/sidebar_bundle.js5
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js1
-rw-r--r--app/assets/javascripts/sidebar/stores/sidebar_store.js50
-rw-r--r--app/assets/javascripts/vue_shared/components/icon.vue30
-rw-r--r--app/assets/javascripts/vue_shared/components/popup_dialog.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/recaptcha_dialog.vue85
-rw-r--r--app/assets/javascripts/vue_shared/mixins/recaptcha_dialog_implementor.js36
-rw-r--r--app/assets/stylesheets/framework/contextual-sidebar.scss38
-rw-r--r--app/assets/stylesheets/framework/modal.scss7
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss5
-rw-r--r--app/assets/stylesheets/framework/variables.scss3
-rw-r--r--app/assets/stylesheets/pages/boards.scss6
-rw-r--r--app/assets/stylesheets/pages/environments.scss19
-rw-r--r--app/assets/stylesheets/pages/issuable.scss5
-rw-r--r--app/controllers/concerns/issuable_actions.rb10
-rw-r--r--app/controllers/concerns/spammable_actions.rb17
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/helpers/blob_helper.rb14
-rw-r--r--app/helpers/diff_helper.rb16
-rw-r--r--app/helpers/preferences_helper.rb2
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/issue.rb3
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/models/namespace.rb11
-rw-r--r--app/models/project.rb3
-rw-r--r--app/models/redirect_route.rb28
-rw-r--r--app/models/repository.rb3
-rw-r--r--app/models/route.rb26
-rw-r--r--app/models/user.rb15
-rw-r--r--app/services/ci/create_pipeline_service.rb27
-rw-r--r--app/services/ci/register_job_service.rb17
-rw-r--r--app/views/layouts/_recaptcha_verification.html.haml15
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_profile.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml2
-rw-r--r--app/views/projects/blob/viewers/_image.html.haml3
-rw-r--r--app/views/projects/diffs/_replaced_image_diff.html.haml16
-rw-r--r--app/views/projects/diffs/_single_image_diff.html.haml6
-rw-r--r--app/views/projects/environments/metrics.html.haml8
-rw-r--r--app/views/shared/_recaptcha_form.html.haml19
-rw-r--r--app/workers/stuck_merge_jobs_worker.rb7
-rw-r--r--app/workers/update_head_pipeline_for_merge_request_worker.rb13
-rw-r--r--changelogs/unreleased/29483-no-feedback-when-checking-on-checklist-if-potential-spam-was-detected.yml5
-rw-r--r--changelogs/unreleased/35385-allow-git-pull-push-on-project-redirects.yml5
-rw-r--r--changelogs/unreleased/35724-animate-sidebar.yml5
-rw-r--r--changelogs/unreleased/38032-deploy-markers-should-be-more-verbose.yml5
-rw-r--r--changelogs/unreleased/40285-prometheus-loading-screen-no-longer-seems-to-appear.yml5
-rw-r--r--changelogs/unreleased/dm-image-blob-diff-full-url.yml5
-rw-r--r--changelogs/unreleased/fix-event-target-author-preloading.yml5
-rw-r--r--changelogs/unreleased/mk-fix-schema-dump-of-untracked-files-for-uploads.yml5
-rw-r--r--changelogs/unreleased/optimize-projects-for-imported-projects.yml6
-rw-r--r--changelogs/unreleased/sh-fix-import-rake-task.yml5
-rw-r--r--changelogs/unreleased/sh-remove-allocation-tracking-influxdb.yml5
-rw-r--r--config/initializers/active_record_schema_ignore_tables.rb2
-rw-r--r--db/migrate/20171122131600_add_new_project_guidelines_to_appearances.rb6
-rw-r--r--db/migrate/20171204204233_add_permanent_to_redirect_route.rb18
-rw-r--r--db/migrate/20171206221519_add_permanent_index_to_redirect_route.rb19
-rw-r--r--db/post_migrate/20171106154015_remove_issues_branch_name.rb (renamed from db/migrate/20171106154015_remove_issues_branch_name.rb)0
-rw-r--r--db/schema.rb4
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md3
-rw-r--r--doc/ci/docker/using_docker_images.md61
-rw-r--r--doc/install/installation.md4
-rw-r--r--doc/update/10.2-to-10.3.md360
-rw-r--r--doc/user/project/merge_requests/img/merge_request_diff_file_navigation.pngbin76052 -> 244736 bytes
-rw-r--r--doc/user/project/merge_requests/index.md3
-rw-r--r--lib/api/entities.rb17
-rw-r--r--lib/api/internal.rb13
-rw-r--r--lib/api/issues.rb2
-rw-r--r--lib/gitlab/bare_repository_import/importer.rb1
-rw-r--r--lib/gitlab/bare_repository_import/repository.rb2
-rw-r--r--lib/gitlab/checks/project_moved.rb65
-rw-r--r--lib/gitlab/ci/pipeline/chain/base.rb7
-rw-r--r--lib/gitlab/ci/pipeline/chain/build.rb42
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb61
-rw-r--r--lib/gitlab/ci/pipeline/chain/helpers.rb12
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/abilities.rb12
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/repository.rb7
-rw-r--r--lib/gitlab/email/handler/create_merge_request_handler.rb4
-rw-r--r--lib/gitlab/git/repository.rb36
-rw-r--r--lib/gitlab/git/repository_mirroring.rb41
-rw-r--r--lib/gitlab/git/storage/checker.rb24
-rw-r--r--lib/gitlab/git_access.rb17
-rw-r--r--lib/gitlab/identifier.rb5
-rw-r--r--lib/gitlab/metrics/samplers/influx_sampler.rb24
-rw-r--r--lib/gitlab/metrics/samplers/ruby_sampler.rb27
-rwxr-xr-xscripts/trigger-build-omnibus2
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb23
-rw-r--r--spec/factories/users.rb4
-rw-r--r--spec/features/groups/members/manage_members.rb21
-rw-r--r--spec/features/merge_requests/image_diff_notes.rb12
-rw-r--r--spec/fixtures/emails/valid_new_merge_request.eml2
-rw-r--r--spec/fixtures/emails/valid_new_merge_request_no_description.eml18
-rw-r--r--spec/helpers/preferences_helper_spec.rb74
-rw-r--r--spec/javascripts/fly_out_nav_spec.js4
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js49
-rw-r--r--spec/javascripts/issue_show/components/description_spec.js30
-rw-r--r--spec/javascripts/monitoring/graph/deployment_spec.js29
-rw-r--r--spec/javascripts/monitoring/graph_spec.js14
-rw-r--r--spec/javascripts/monitoring/mock_data.js6
-rw-r--r--spec/javascripts/notes/components/issue_note_spec.js15
-rw-r--r--spec/javascripts/sidebar/sidebar_assignees_spec.js45
-rw-r--r--spec/javascripts/sidebar/sidebar_subscriptions_spec.js9
-rw-r--r--spec/javascripts/vue_shared/components/popup_dialog_spec.js12
-rw-r--r--spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb17
-rw-r--r--spec/lib/gitlab/bare_repository_import/importer_spec.rb17
-rw-r--r--spec/lib/gitlab/bare_repository_import/repository_spec.rb7
-rw-r--r--spec/lib/gitlab/checks/project_moved_spec.rb81
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/build_spec.rb57
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/command_spec.rb185
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb9
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb5
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb26
-rw-r--r--spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb12
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb21
-rw-r--r--spec/lib/gitlab/git_access_spec.rb60
-rw-r--r--spec/lib/gitlab/identifier_spec.rb4
-rw-r--r--spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb23
-rw-r--r--spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb23
-rw-r--r--spec/models/merge_request_spec.rb16
-rw-r--r--spec/models/namespace_spec.rb30
-rw-r--r--spec/models/route_spec.rb104
-rw-r--r--spec/models/user_spec.rb59
-rw-r--r--spec/requests/api/groups_spec.rb62
-rw-r--r--spec/requests/api/internal_spec.rb38
-rw-r--r--spec/requests/api/issues_spec.rb14
-rw-r--r--spec/requests/git_http_spec.rb8
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb31
-rw-r--r--spec/services/ci/register_job_service_spec.rb13
-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
173 files changed, 2807 insertions, 1795 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6088a1b3515..adf097b52f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 10.2.4 (2017-12-07)
+
+### Security (5 changes)
+
+- Fix e-mail address disclosure through member search fields
+- Prevent creating issues through API when user does not have permissions
+- Prevent an information disclosure in the Groups API
+- Fix user without access to private Wiki being able to see it on the project page
+- Fix Cross-Site Scripting (XSS) vulnerability while editing a comment
+
+
## 10.2.3 (2017-11-30)
### Fixed (7 changes)
@@ -237,6 +248,17 @@ entry.
- Add Gitaly metrics to the performance bar.
+## 10.1.5 (2017-12-07)
+
+### Security (5 changes)
+
+- Fix e-mail address disclosure through member search fields
+- Prevent creating issues through API when user does not have permissions
+- Prevent an information disclosure in the Groups API
+- Fix user without access to private Wiki being able to see it on the project page
+- Fix Cross-Site Scripting (XSS) vulnerability while editing a comment
+
+
## 10.1.4 (2017-11-14)
### Fixed (4 changes)
@@ -485,6 +507,17 @@ entry.
- creation of keys moved to services. !13331 (haseebeqx)
- Add username as GL_USERNAME in hooks.
+## 10.0.7 (2017-12-07)
+
+### Security (5 changes)
+
+- Fix e-mail address disclosure through member search fields
+- Prevent creating issues through API when user does not have permissions
+- Prevent an information disclosure in the Groups API
+- Fix user without access to private Wiki being able to see it on the project page
+- Fix Cross-Site Scripting (XSS) vulnerability while editing a comment
+
+
## 10.0.5 (2017-11-03)
- [FIXED] Fix incorrect X-axis labels in Prometheus graphs. !14258
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 4e32c7b1caf..269fb5dfe2c 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-5.10.1
+5.10.2
diff --git a/Gemfile b/Gemfile
index 3187b0e5ae9..ec1b9699275 100644
--- a/Gemfile
+++ b/Gemfile
@@ -283,7 +283,7 @@ group :metrics do
gem 'influxdb', '~> 0.2', require: false
# Prometheus
- gem 'prometheus-client-mmap', '~> 0.7.0.beta39'
+ gem 'prometheus-client-mmap', '~> 0.7.0.beta43'
gem 'raindrops', '~> 0.18'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 6213167ae0b..60c29ac65c9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1107,7 +1107,7 @@ DEPENDENCIES
peek-sidekiq (~> 1.0.3)
pg (~> 0.18.2)
premailer-rails (~> 1.9.7)
- prometheus-client-mmap (~> 0.7.0.beta39)
+ prometheus-client-mmap (~> 0.7.0.beta43)
pry-byebug (~> 3.4.1)
pry-rails (~> 0.3.4)
rack-attack (~> 4.4.1)
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index 50d0cb5c86d..5662802525e 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -121,7 +121,7 @@ export default class ImageFile {
return $('.swipe.view', this.file).each((function(_this) {
return function(index, view) {
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
- ref = this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
+ ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$swipeFrame = $('.swipe-frame', view);
$swipeWrap = $('.swipe-wrap', view);
$swipeBar = $('.swipe-bar', view);
@@ -158,7 +158,7 @@ export default class ImageFile {
return $('.onion-skin.view', this.file).each((function(_this) {
return function(index, view) {
var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
- ref = this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
+ ref = _this.prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
$frame = $('.onion-skin-frame', view);
$frameAdded = $('.frame.added', view);
$track = $('.drag-track', view);
diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js
index 46b68ebe158..cd20dde2951 100644
--- a/app/assets/javascripts/contextual_sidebar.js
+++ b/app/assets/javascripts/contextual_sidebar.js
@@ -28,7 +28,7 @@ export default class ContextualSidebar {
this.$closeSidebar.on('click', () => this.toggleSidebarNav(false));
this.$overlay.on('click', () => this.toggleSidebarNav(false));
this.$sidebarToggle.on('click', () => {
- const value = !this.$sidebar.hasClass('sidebar-icons-only');
+ const value = !this.$sidebar.hasClass('sidebar-collapsed-desktop');
this.toggleCollapsedSidebar(value);
});
@@ -43,16 +43,16 @@ export default class ContextualSidebar {
}
toggleSidebarNav(show) {
- this.$sidebar.toggleClass('nav-sidebar-expanded', show);
+ this.$sidebar.toggleClass('sidebar-expanded-mobile', show);
this.$overlay.toggleClass('mobile-nav-open', show);
- this.$sidebar.removeClass('sidebar-icons-only');
+ this.$sidebar.removeClass('sidebar-collapsed-desktop');
}
toggleCollapsedSidebar(collapsed) {
const breakpoint = bp.getBreakpointSize();
if (this.$sidebar.length) {
- this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
+ this.$sidebar.toggleClass('sidebar-collapsed-desktop', collapsed);
this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
}
ContextualSidebar.setCollapsedCookie(collapsed);
diff --git a/app/assets/javascripts/fly_out_nav.js b/app/assets/javascripts/fly_out_nav.js
index 98837c3b2a0..6110d961609 100644
--- a/app/assets/javascripts/fly_out_nav.js
+++ b/app/assets/javascripts/fly_out_nav.js
@@ -21,7 +21,7 @@ let headerHeight = 50;
export const getHeaderHeight = () => headerHeight;
-export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-icons-only');
+export const isSidebarCollapsed = () => sidebar && sidebar.classList.contains('sidebar-collapsed-desktop');
export const canShowActiveSubItems = (el) => {
if (el.classList.contains('active') && !isSidebarCollapsed()) {
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 7de07e9403d..91b5ef1c10a 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -8,18 +8,7 @@ import IssuablesHelper from './helpers/issuables_helper';
export default class Issue {
constructor() {
- if ($('a.btn-close').length) {
- this.taskList = new TaskList({
- dataType: 'issue',
- fieldName: 'description',
- selector: '.detail-page-description',
- onSuccess: (result) => {
- document.querySelector('#task_status').innerText = result.task_status;
- document.querySelector('#task_status_short').innerText = result.task_status_short;
- }
- });
- this.initIssueBtnEventListeners();
- }
+ if ($('a.btn-close').length) this.initIssueBtnEventListeners();
Issue.$btnNewBranch = $('#new-branch');
Issue.createMrDropdownWrap = document.querySelector('.create-mr-dropdown-wrap');
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index 5bdc7c99503..c7ce16bb623 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -9,6 +9,7 @@ import descriptionComponent from './description.vue';
import editedComponent from './edited.vue';
import formComponent from './form.vue';
import '../../lib/utils/url_utility';
+import RecaptchaDialogImplementor from '../../vue_shared/mixins/recaptcha_dialog_implementor';
export default {
props: {
@@ -149,6 +150,11 @@ export default {
editedComponent,
formComponent,
},
+
+ mixins: [
+ RecaptchaDialogImplementor,
+ ],
+
methods: {
openForm() {
if (!this.showForm) {
@@ -164,9 +170,11 @@ export default {
closeForm() {
this.showForm = false;
},
+
updateIssuable() {
this.service.updateIssuable(this.store.formState)
.then(res => res.json())
+ .then(data => this.checkForSpam(data))
.then((data) => {
if (location.pathname !== data.web_url) {
gl.utils.visitUrl(data.web_url);
@@ -179,11 +187,24 @@ export default {
this.store.updateState(data);
eventHub.$emit('close.form');
})
- .catch(() => {
- eventHub.$emit('close.form');
- window.Flash(`Error updating ${this.issuableType}`);
+ .catch((error) => {
+ if (error && error.name === 'SpamError') {
+ this.openRecaptcha();
+ } else {
+ eventHub.$emit('close.form');
+ window.Flash(`Error updating ${this.issuableType}`);
+ }
});
},
+
+ closeRecaptchaDialog() {
+ this.store.setFormState({
+ updateLoading: false,
+ });
+
+ this.closeRecaptcha();
+ },
+
deleteIssuable() {
this.service.deleteIssuable()
.then(res => res.json())
@@ -237,9 +258,9 @@ export default {
</script>
<template>
- <div>
+<div>
+ <div v-if="canUpdate && showForm">
<form-component
- v-if="canUpdate && showForm"
:form-state="formState"
:can-destroy="canDestroy"
:issuable-templates="issuableTemplates"
@@ -251,30 +272,37 @@ export default {
:can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete"
/>
- <div v-else>
- <title-component
- :issuable-ref="issuableRef"
- :can-update="canUpdate"
- :title-html="state.titleHtml"
- :title-text="state.titleText"
- :show-inline-edit-button="showInlineEditButton"
- />
- <description-component
- v-if="state.descriptionHtml"
- :can-update="canUpdate"
- :description-html="state.descriptionHtml"
- :description-text="state.descriptionText"
- :updated-at="state.updatedAt"
- :task-status="state.taskStatus"
- :issuable-type="issuableType"
- :update-url="updateEndpoint"
- />
- <edited-component
- v-if="hasUpdated"
- :updated-at="state.updatedAt"
- :updated-by-name="state.updatedByName"
- :updated-by-path="state.updatedByPath"
- />
- </div>
+
+ <recaptcha-dialog
+ v-show="showRecaptcha"
+ :html="recaptchaHTML"
+ @close="closeRecaptchaDialog"
+ />
+ </div>
+ <div v-else>
+ <title-component
+ :issuable-ref="issuableRef"
+ :can-update="canUpdate"
+ :title-html="state.titleHtml"
+ :title-text="state.titleText"
+ :show-inline-edit-button="showInlineEditButton"
+ />
+ <description-component
+ v-if="state.descriptionHtml"
+ :can-update="canUpdate"
+ :description-html="state.descriptionHtml"
+ :description-text="state.descriptionText"
+ :updated-at="state.updatedAt"
+ :task-status="state.taskStatus"
+ :issuable-type="issuableType"
+ :update-url="updateEndpoint"
+ />
+ <edited-component
+ v-if="hasUpdated"
+ :updated-at="state.updatedAt"
+ :updated-by-name="state.updatedByName"
+ :updated-by-path="state.updatedByPath"
+ />
</div>
+</div>
</template>
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index b7559ced946..feb73481422 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -1,9 +1,14 @@
<script>
import animateMixin from '../mixins/animate';
import TaskList from '../../task_list';
+ import RecaptchaDialogImplementor from '../../vue_shared/mixins/recaptcha_dialog_implementor';
export default {
- mixins: [animateMixin],
+ mixins: [
+ animateMixin,
+ RecaptchaDialogImplementor,
+ ],
+
props: {
canUpdate: {
type: Boolean,
@@ -51,6 +56,7 @@
this.updateTaskStatusText();
},
},
+
methods: {
renderGFM() {
$(this.$refs['gfm-content']).renderGFM();
@@ -61,9 +67,19 @@
dataType: this.issuableType,
fieldName: 'description',
selector: '.detail-page-description',
+ onSuccess: this.taskListUpdateSuccess.bind(this),
});
}
},
+
+ taskListUpdateSuccess(data) {
+ try {
+ this.checkForSpam(data);
+ } catch (error) {
+ if (error && error.name === 'SpamError') this.openRecaptcha();
+ }
+ },
+
updateTaskStatusText() {
const taskRegexMatches = this.taskStatus.match(/(\d+) of ((?!0)\d+)/);
const $issuableHeader = $('.issuable-meta');
@@ -109,5 +125,11 @@
:data-update-url="updateUrl"
>
</textarea>
+
+ <recaptcha-dialog
+ v-show="showRecaptcha"
+ :html="recaptchaHTML"
+ @close="closeRecaptcha"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index cbe24c0915b..8da723ced03 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -21,6 +21,8 @@
hasMetrics: convertPermissionToBoolean(metricsData.hasMetrics),
documentationPath: metricsData.documentationPath,
settingsPath: metricsData.settingsPath,
+ tagsPath: metricsData.tagsPath,
+ projectPath: metricsData.projectPath,
metricsEndpoint: metricsData.additionalMetrics,
deploymentEndpoint: metricsData.deploymentEndpoint,
emptyGettingStartedSvgPath: metricsData.emptyGettingStartedSvgPath,
@@ -112,6 +114,8 @@
:hover-data="hoverData"
:update-aspect-ratio="updateAspectRatio"
:deployment-data="store.deploymentData"
+ :project-path="projectPath"
+ :tags-path="tagsPath"
/>
</graph-group>
</div>
diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue
index f8782fde927..cdae287658b 100644
--- a/app/assets/javascripts/monitoring/components/graph.vue
+++ b/app/assets/javascripts/monitoring/components/graph.vue
@@ -30,6 +30,14 @@
required: false,
default: () => ({}),
},
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ tagsPath: {
+ type: String,
+ required: true,
+ },
},
mixins: [MonitoringMixin],
@@ -251,6 +259,14 @@
:line-color="path.lineColor"
:area-color="path.areaColor"
/>
+ <rect
+ class="prometheus-graph-overlay"
+ :width="(graphWidth - 70)"
+ :height="(graphHeight - 100)"
+ transform="translate(-5, 20)"
+ ref="graphOverlay"
+ @mousemove="handleMouseOverGraph($event)">
+ </rect>
<graph-deployment
:show-deploy-info="showDeployInfo"
:deployment-data="reducedDeploymentData"
@@ -267,14 +283,6 @@
:graph-height-offset="graphHeightOffset"
:show-flag-content="showFlagContent"
/>
- <rect
- class="prometheus-graph-overlay"
- :width="(graphWidth - 70)"
- :height="(graphHeight - 100)"
- transform="translate(-5, 20)"
- ref="graphOverlay"
- @mousemove="handleMouseOverGraph($event)">
- </rect>
</svg>
</svg>
</div>
diff --git a/app/assets/javascripts/monitoring/components/graph/deployment.vue b/app/assets/javascripts/monitoring/components/graph/deployment.vue
index e3b8be0c7fb..026e2fd0c49 100644
--- a/app/assets/javascripts/monitoring/components/graph/deployment.vue
+++ b/app/assets/javascripts/monitoring/components/graph/deployment.vue
@@ -1,5 +1,6 @@
<script>
- import { dateFormat, timeFormat } from '../../utils/date_time_formatters';
+ import { dateFormatWithName, timeFormat } from '../../utils/date_time_formatters';
+ import Icon from '../../../vue_shared/components/icon.vue';
export default {
props: {
@@ -25,6 +26,10 @@
},
},
+ components: {
+ Icon,
+ },
+
computed: {
calculatedHeight() {
return this.graphHeight - this.graphHeightOffset;
@@ -33,7 +38,7 @@
methods: {
refText(d) {
- return d.tag ? d.ref : d.sha.slice(0, 6);
+ return d.tag ? d.ref : d.sha.slice(0, 8);
},
formatTime(deploymentTime) {
@@ -41,7 +46,7 @@
},
formatDate(deploymentTime) {
- return dateFormat(deploymentTime);
+ return dateFormatWithName(deploymentTime);
},
nameDeploymentClass(deployment) {
@@ -54,11 +59,19 @@
positionFlag(deployment) {
let xPosition = 3;
- if (deployment.xPos > (this.graphWidth - 200)) {
- xPosition = -97;
+ if (deployment.xPos > (this.graphWidth - 225)) {
+ xPosition = -142;
}
return xPosition;
},
+
+ svgContainerHeight(tag) {
+ let svgHeight = 80;
+ if (!tag) {
+ svgHeight -= 20;
+ }
+ return svgHeight;
+ },
},
};
</script>
@@ -91,35 +104,75 @@
class="js-deploy-info-box"
:x="positionFlag(deployment)"
y="0"
- width="92"
- height="60">
+ width="134"
+ :height="svgContainerHeight(deployment.tag)">
<rect
class="rect-text-metric deploy-info-rect rect-metric"
x="1"
y="1"
rx="2"
- width="90"
- height="58">
+ width="132"
+ :height="svgContainerHeight(deployment.tag) - 2">
</rect>
- <g
- transform="translate(5, 2)">
- <text
- class="deploy-info-text text-metric-bold">
- {{refText(deployment)}}
- </text>
- </g>
- <text
- class="deploy-info-text"
- y="18"
- transform="translate(5, 2)">
- {{formatDate(deployment.time)}}
- </text>
<text
class="deploy-info-text text-metric-bold"
- y="38"
transform="translate(5, 2)">
- {{formatTime(deployment.time)}}
+ Deployed
</text>
+ <!--The date info-->
+ <g transform="translate(5, 20)">
+ <text class="deploy-info-text">
+ {{formatDate(deployment.time)}}
+ </text>
+ <text
+ class="deploy-info-text text-metric-bold"
+ x="62">
+ {{formatTime(deployment.time)}}
+ </text>
+ </g>
+ <line
+ class="divider-line"
+ x1="0"
+ y1="38"
+ x2="132"
+ :y2="38"
+ stroke="#000">
+ </line>
+ <!--Commit information-->
+ <g transform="translate(5, 40)">
+ <icon
+ name="commit"
+ :width="12"
+ :height="12"
+ :y="3">
+ </icon>
+ <a :xlink:href="deployment.commitUrl">
+ <text
+ class="deploy-info-text deploy-info-text-link"
+ transform="translate(20, 2)">
+ {{refText(deployment)}}
+ </text>
+ </a>
+ </g>
+ <!--Tag information-->
+ <g
+ transform="translate(5, 55)"
+ v-if="deployment.tag">
+ <icon
+ name="label"
+ :width="12"
+ :height="12"
+ :y="5">
+ </icon>
+ <a :xlink:href="deployment.tagUrl">
+ <text
+ class="deploy-info-text deploy-info-text-link"
+ transform="translate(20, 2)"
+ y="2">
+ {{deployment.tag}}
+ </text>
+ </a>
+ </g>
</svg>
</g>
<svg
diff --git a/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js b/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js
index 31f38aca5d6..cbca14ede02 100644
--- a/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js
+++ b/app/assets/javascripts/monitoring/mixins/monitoring_mixins.js
@@ -33,7 +33,9 @@ const mixins = {
id: deployment.id,
time,
sha: deployment.sha,
+ commitUrl: `${this.projectPath}/commit/${deployment.sha}`,
tag: deployment.tag,
+ tagUrl: `${this.tagsPath}/${deployment.tag}`,
ref: deployment.ref.name,
xPos,
showDeploymentFlag: false,
diff --git a/app/assets/javascripts/monitoring/utils/date_time_formatters.js b/app/assets/javascripts/monitoring/utils/date_time_formatters.js
index c4c6b1ac1f5..ad07a8465e2 100644
--- a/app/assets/javascripts/monitoring/utils/date_time_formatters.js
+++ b/app/assets/javascripts/monitoring/utils/date_time_formatters.js
@@ -1,6 +1,7 @@
import d3 from 'd3';
export const dateFormat = d3.time.format('%b %-d, %Y');
+export const dateFormatWithName = d3.time.format('%a, %b %-d');
export const timeFormat = d3.time.format('%-I:%M%p');
export const bisectDate = d3.bisector(d => d.time).left;
diff --git a/app/assets/javascripts/notes/components/issue_note.vue b/app/assets/javascripts/notes/components/issue_note.vue
index 8c81c5d6df3..3ceb961f58e 100644
--- a/app/assets/javascripts/notes/components/issue_note.vue
+++ b/app/assets/javascripts/notes/components/issue_note.vue
@@ -1,5 +1,6 @@
<script>
import { mapGetters, mapActions } from 'vuex';
+ import { escape } from 'underscore';
import Flash from '../../flash';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import noteHeader from './note_header.vue';
@@ -85,7 +86,7 @@
};
this.isRequesting = true;
this.oldContent = this.note.note_html;
- this.note.note_html = noteText;
+ this.note.note_html = escape(noteText);
this.updateNote(data)
.then(() => {
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
index 74c17bc14a2..9e47039d920 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.js
@@ -1,22 +1,32 @@
import Flash from '../../../flash';
import AssigneeTitle from './assignee_title';
import Assignees from './assignees';
-
import Store from '../../stores/sidebar_store';
-import Mediator from '../../sidebar_mediator';
-
import eventHub from '../../event_hub';
export default {
name: 'SidebarAssignees',
data() {
return {
- mediator: new Mediator(),
store: new Store(),
loading: false,
- field: '',
};
},
+ props: {
+ mediator: {
+ type: Object,
+ required: true,
+ },
+ field: {
+ type: String,
+ required: true,
+ },
+ signedIn: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
components: {
'assignee-title': AssigneeTitle,
assignees: Assignees,
@@ -61,10 +71,6 @@ export default {
eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
},
- beforeMount() {
- this.field = this.$el.dataset.field;
- this.signedIn = typeof this.$el.dataset.signedIn !== 'undefined';
- },
template: `
<div>
<assignee-title
diff --git a/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue b/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue
index c1296b28db7..6fcd2f95309 100644
--- a/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/sidebar_participants.vue
@@ -1,15 +1,19 @@
<script>
import Store from '../../stores/sidebar_store';
-import Mediator from '../../sidebar_mediator';
import participants from './participants.vue';
export default {
data() {
return {
- mediator: new Mediator(),
store: new Store(),
};
},
+ props: {
+ mediator: {
+ type: Object,
+ required: true,
+ },
+ },
components: {
participants,
},
@@ -21,6 +25,7 @@ export default {
<participants
:loading="store.isFetching.participants"
:participants="store.participants"
- :number-of-less-participants="7" />
+ :number-of-less-participants="7"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue
index 25acc099699..f4bae1d3dd5 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions.vue
@@ -1,6 +1,5 @@
<script>
import Store from '../../stores/sidebar_store';
-import Mediator from '../../sidebar_mediator';
import eventHub from '../../event_hub';
import Flash from '../../../flash';
import { __ } from '../../../locale';
@@ -9,11 +8,15 @@ import subscriptions from './subscriptions.vue';
export default {
data() {
return {
- mediator: new Mediator(),
store: new Store(),
};
},
-
+ props: {
+ mediator: {
+ type: Object,
+ required: true,
+ },
+ },
components: {
subscriptions,
},
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 4032f156b15..56cc78ca0ca 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -10,6 +10,27 @@ import Translate from '../vue_shared/translate';
Vue.use(Translate);
+function mountAssigneesComponent(mediator) {
+ const el = document.getElementById('js-vue-sidebar-assignees');
+
+ if (!el) return;
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ components: {
+ SidebarAssignees,
+ },
+ render: createElement => createElement('sidebar-assignees', {
+ props: {
+ mediator,
+ field: el.dataset.field,
+ signedIn: el.hasAttribute('data-signed-in'),
+ },
+ }),
+ });
+}
+
function mountConfidentialComponent(mediator) {
const el = document.getElementById('js-confidential-entry-point');
@@ -49,9 +70,10 @@ function mountLockComponent(mediator) {
}).$mount(el);
}
-function mountParticipantsComponent() {
+function mountParticipantsComponent(mediator) {
const el = document.querySelector('.js-sidebar-participants-entry-point');
+ // eslint-disable-next-line no-new
if (!el) return;
// eslint-disable-next-line no-new
@@ -60,11 +82,15 @@ function mountParticipantsComponent() {
components: {
sidebarParticipants,
},
- render: createElement => createElement('sidebar-participants', {}),
+ render: createElement => createElement('sidebar-participants', {
+ props: {
+ mediator,
+ },
+ }),
});
}
-function mountSubscriptionsComponent() {
+function mountSubscriptionsComponent(mediator) {
const el = document.querySelector('.js-sidebar-subscriptions-entry-point');
if (!el) return;
@@ -75,22 +101,35 @@ function mountSubscriptionsComponent() {
components: {
sidebarSubscriptions,
},
- render: createElement => createElement('sidebar-subscriptions', {}),
+ render: createElement => createElement('sidebar-subscriptions', {
+ props: {
+ mediator,
+ },
+ }),
});
}
-function mount(mediator) {
- const sidebarAssigneesEl = document.getElementById('js-vue-sidebar-assignees');
- // Only create the sidebarAssignees vue app if it is found in the DOM
- // We currently do not use sidebarAssignees for the MR page
- if (sidebarAssigneesEl) {
- new Vue(SidebarAssignees).$mount(sidebarAssigneesEl);
- }
+function mountTimeTrackingComponent() {
+ const el = document.getElementById('issuable-time-tracker');
+ if (!el) return;
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ components: {
+ SidebarTimeTracking,
+ },
+ render: createElement => createElement('sidebar-time-tracking', {}),
+ });
+}
+
+export function mountSidebar(mediator) {
+ mountAssigneesComponent(mediator);
mountConfidentialComponent(mediator);
mountLockComponent(mediator);
- mountParticipantsComponent();
- mountSubscriptionsComponent();
+ mountParticipantsComponent(mediator);
+ mountSubscriptionsComponent(mediator);
new SidebarMoveIssue(
mediator,
@@ -98,7 +137,9 @@ function mount(mediator) {
$('.js-move-issue-confirmation-button'),
).init();
- new Vue(SidebarTimeTracking).$mount('#issuable-time-tracker');
+ mountTimeTrackingComponent();
}
-export default mount;
+export function getSidebarOptions() {
+ return JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
+}
diff --git a/app/assets/javascripts/sidebar/sidebar_bundle.js b/app/assets/javascripts/sidebar/sidebar_bundle.js
index f78287e504b..04c39d7b6b5 100644
--- a/app/assets/javascripts/sidebar/sidebar_bundle.js
+++ b/app/assets/javascripts/sidebar/sidebar_bundle.js
@@ -1,9 +1,8 @@
import Mediator from './sidebar_mediator';
-import mountSidebar from './mount_sidebar';
+import { mountSidebar, getSidebarOptions } from './mount_sidebar';
function domContentLoaded() {
- const sidebarOptions = JSON.parse(document.querySelector('.js-sidebar-options').innerHTML);
- const mediator = new Mediator(sidebarOptions);
+ const mediator = new Mediator(getSidebarOptions());
mediator.fetch();
mountSidebar(mediator);
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index d4c07a188b3..abf6909a9b4 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -7,7 +7,6 @@ export default class SidebarMediator {
if (!SidebarMediator.singleton) {
this.initSingleton(options);
}
-
return SidebarMediator.singleton;
}
diff --git a/app/assets/javascripts/sidebar/stores/sidebar_store.js b/app/assets/javascripts/sidebar/stores/sidebar_store.js
index 73eb25e2333..f20cc6d8cca 100644
--- a/app/assets/javascripts/sidebar/stores/sidebar_store.js
+++ b/app/assets/javascripts/sidebar/stores/sidebar_store.js
@@ -1,33 +1,37 @@
export default class SidebarStore {
- constructor(store) {
+ constructor(options) {
if (!SidebarStore.singleton) {
- const { currentUser, rootPath, editable } = store;
- this.currentUser = currentUser;
- this.rootPath = rootPath;
- this.editable = editable;
- this.timeEstimate = 0;
- this.totalTimeSpent = 0;
- this.humanTimeEstimate = '';
- this.humanTimeSpent = '';
- this.assignees = [];
- this.isFetching = {
- assignees: true,
- participants: true,
- subscriptions: true,
- };
- this.isLoading = {};
- this.autocompleteProjects = [];
- this.moveToProjectId = 0;
- this.isLockDialogOpen = false;
- this.participants = [];
- this.subscribed = null;
-
- SidebarStore.singleton = this;
+ this.initSingleton(options);
}
return SidebarStore.singleton;
}
+ initSingleton(options) {
+ const { currentUser, rootPath, editable } = options;
+ this.currentUser = currentUser;
+ this.rootPath = rootPath;
+ this.editable = editable;
+ this.timeEstimate = 0;
+ this.totalTimeSpent = 0;
+ this.humanTimeEstimate = '';
+ this.humanTimeSpent = '';
+ this.assignees = [];
+ this.isFetching = {
+ assignees: true,
+ participants: true,
+ subscriptions: true,
+ };
+ this.isLoading = {};
+ this.autocompleteProjects = [];
+ this.moveToProjectId = 0;
+ this.isLockDialogOpen = false;
+ this.participants = [];
+ this.subscribed = null;
+
+ SidebarStore.singleton = this;
+ }
+
setAssigneeData(data) {
this.isFetching.assignees = false;
if (data.assignees) {
diff --git a/app/assets/javascripts/vue_shared/components/icon.vue b/app/assets/javascripts/vue_shared/components/icon.vue
index 4216660da8c..365229ea274 100644
--- a/app/assets/javascripts/vue_shared/components/icon.vue
+++ b/app/assets/javascripts/vue_shared/components/icon.vue
@@ -36,6 +36,30 @@
required: false,
default: '',
},
+
+ width: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+
+ height: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+
+ y: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+
+ x: {
+ type: Number,
+ required: false,
+ default: null,
+ },
},
computed: {
@@ -51,7 +75,11 @@
<template>
<svg
- :class="[iconSizeClass, cssClasses]">
+ :class="[iconSizeClass, cssClasses]"
+ :width="width"
+ :height="height"
+ :x="x"
+ :y="y">
<use
v-bind="{'xlink:href':spriteHref}"/>
</svg>
diff --git a/app/assets/javascripts/vue_shared/components/popup_dialog.vue b/app/assets/javascripts/vue_shared/components/popup_dialog.vue
index 47efee64c6e..6d15bbd84ba 100644
--- a/app/assets/javascripts/vue_shared/components/popup_dialog.vue
+++ b/app/assets/javascripts/vue_shared/components/popup_dialog.vue
@@ -38,7 +38,8 @@ export default {
},
primaryButtonLabel: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
submitDisabled: {
type: Boolean,
@@ -113,8 +114,9 @@ export default {
{{ closeButtonLabel }}
</button>
<button
+ v-if="primaryButtonLabel"
type="button"
- class="btn pull-right"
+ class="btn pull-right js-primary-button"
:disabled="submitDisabled"
:class="btnKindClass"
@click="emitSubmit(true)">
diff --git a/app/assets/javascripts/vue_shared/components/recaptcha_dialog.vue b/app/assets/javascripts/vue_shared/components/recaptcha_dialog.vue
new file mode 100644
index 00000000000..3ec50f14eb4
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/recaptcha_dialog.vue
@@ -0,0 +1,85 @@
+<script>
+import PopupDialog from './popup_dialog.vue';
+
+export default {
+ name: 'recaptcha-dialog',
+
+ props: {
+ html: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+
+ data() {
+ return {
+ script: {},
+ scriptSrc: 'https://www.google.com/recaptcha/api.js',
+ };
+ },
+
+ components: {
+ PopupDialog,
+ },
+
+ methods: {
+ appendRecaptchaScript() {
+ this.removeRecaptchaScript();
+
+ const script = document.createElement('script');
+ script.src = this.scriptSrc;
+ script.classList.add('js-recaptcha-script');
+ script.async = true;
+ script.defer = true;
+
+ this.script = script;
+
+ document.body.appendChild(script);
+ },
+
+ removeRecaptchaScript() {
+ if (this.script instanceof Element) this.script.remove();
+ },
+
+ close() {
+ this.removeRecaptchaScript();
+ this.$emit('close');
+ },
+
+ submit() {
+ this.$el.querySelector('form').submit();
+ },
+ },
+
+ watch: {
+ html() {
+ this.appendRecaptchaScript();
+ },
+ },
+
+ mounted() {
+ window.recaptchaDialogCallback = this.submit.bind(this);
+ },
+};
+</script>
+
+<template>
+<popup-dialog
+ kind="warning"
+ class="recaptcha-dialog js-recaptcha-dialog"
+ :hide-footer="true"
+ :title="__('Please solve the reCAPTCHA')"
+ @toggle="close"
+>
+ <div slot="body">
+ <p>
+ {{__('We want to be sure it is you, please confirm you are not a robot.')}}
+ </p>
+ <div
+ ref="recaptcha"
+ v-html="html"
+ ></div>
+ </div>
+</popup-dialog>
+</template>
diff --git a/app/assets/javascripts/vue_shared/mixins/recaptcha_dialog_implementor.js b/app/assets/javascripts/vue_shared/mixins/recaptcha_dialog_implementor.js
new file mode 100644
index 00000000000..ef70f9432e3
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/mixins/recaptcha_dialog_implementor.js
@@ -0,0 +1,36 @@
+import RecaptchaDialog from '../components/recaptcha_dialog.vue';
+
+export default {
+ data() {
+ return {
+ showRecaptcha: false,
+ recaptchaHTML: '',
+ };
+ },
+
+ components: {
+ RecaptchaDialog,
+ },
+
+ methods: {
+ openRecaptcha() {
+ this.showRecaptcha = true;
+ },
+
+ closeRecaptcha() {
+ this.showRecaptcha = false;
+ },
+
+ checkForSpam(data) {
+ if (!data.recaptcha_html) return data;
+
+ this.recaptchaHTML = data.recaptcha_html;
+
+ const spamError = new Error(data.error_message);
+ spamError.name = 'SpamError';
+ spamError.message = 'SpamError';
+
+ throw spamError;
+ },
+ },
+};
diff --git a/app/assets/stylesheets/framework/contextual-sidebar.scss b/app/assets/stylesheets/framework/contextual-sidebar.scss
index b73932eb7e1..26a2db99e0a 100644
--- a/app/assets/stylesheets/framework/contextual-sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual-sidebar.scss
@@ -1,4 +1,6 @@
.page-with-contextual-sidebar {
+ transition: padding-left $sidebar-transition-duration;
+
@media (min-width: $screen-md-min) {
padding-left: $contextual-sidebar-collapsed-width;
}
@@ -27,8 +29,10 @@
.context-header {
position: relative;
margin-right: 2px;
+ width: $contextual-sidebar-width;
a {
+ transition: padding $sidebar-transition-duration;
font-weight: $gl-font-weight-bold;
display: flex;
align-items: center;
@@ -63,10 +67,10 @@
}
.nav-sidebar {
+ transition: width $sidebar-transition-duration, left $sidebar-transition-duration;
position: fixed;
z-index: 400;
width: $contextual-sidebar-width;
- transition: left $sidebar-transition-duration;
top: $header-height;
bottom: 0;
left: 0;
@@ -74,16 +78,15 @@
box-shadow: inset -2px 0 0 $border-color;
transform: translate3d(0, 0, 0);
- &:not(.sidebar-icons-only) {
+ &:not(.sidebar-collapsed-desktop) {
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
box-shadow: inset -2px 0 0 $border-color,
2px 1px 3px $dropdown-shadow-color;
}
}
- &.sidebar-icons-only {
- width: auto;
- min-width: $contextual-sidebar-collapsed-width;
+ &.sidebar-collapsed-desktop {
+ width: $contextual-sidebar-collapsed-width;
.nav-sidebar-inner-scroll {
overflow-x: hidden;
@@ -108,12 +111,11 @@
}
}
- &.nav-sidebar-expanded {
+ &.sidebar-expanded-mobile {
left: 0;
}
a {
- transition: none;
text-decoration: none;
}
@@ -126,9 +128,10 @@
white-space: nowrap;
a {
+ transition: padding $sidebar-transition-duration;
display: flex;
align-items: center;
- padding: 12px 16px;
+ padding: 12px 15px;
color: $gl-text-color-secondary;
}
@@ -288,7 +291,8 @@
> a {
margin-left: 4px;
- padding-left: 12px;
+ // Subtract width of left border on active element
+ padding-left: 11px;
}
.badge {
@@ -313,6 +317,7 @@
.toggle-sidebar-button,
.close-nav-button {
width: $contextual-sidebar-width - 2px;
+ transition: width $sidebar-transition-duration;
position: fixed;
bottom: 0;
padding: 16px;
@@ -343,20 +348,21 @@
}
}
+.collapse-text {
+ white-space: nowrap;
+ overflow: hidden;
+}
-.sidebar-icons-only {
+.sidebar-collapsed-desktop {
.context-header {
- height: 61px;
+ height: 60px;
+ width: $contextual-sidebar-collapsed-width;
a {
padding: 10px 4px;
}
}
- li a {
- padding: 12px 15px;
- }
-
.sidebar-top-level-items > li {
&.active a {
padding-left: 12px;
@@ -374,8 +380,8 @@
}
.toggle-sidebar-button {
- width: $contextual-sidebar-collapsed-width - 2px;
padding: 16px;
+ width: $contextual-sidebar-collapsed-width - 2px;
.collapse-text,
.icon-angle-double-left {
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index 5c9838c1029..ce551e6b7ce 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -48,3 +48,10 @@ body.modal-open {
display: block;
}
+.recaptcha-dialog .recaptcha-form {
+ display: inline-block;
+
+ .recaptcha {
+ margin: 0;
+ }
+}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 792981fdc48..3ebba4f9efb 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -50,6 +50,11 @@
&:not(.disabled) {
cursor: pointer;
}
+
+ svg {
+ width: $gl-padding;
+ height: $gl-padding;
+ }
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 4f99c27eff1..a6fa96d834c 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -5,10 +5,9 @@ $grid-size: 8px;
$gutter_collapsed_width: 62px;
$gutter_width: 290px;
$gutter_inner_width: 250px;
-$sidebar-transition-duration: .15s;
+$sidebar-transition-duration: .3s;
$sidebar-breakpoint: 1024px;
$default-transition-duration: .15s;
-$right-sidebar-transition-duration: .3s;
$contextual-sidebar-width: 220px;
$contextual-sidebar-collapsed-width: 50px;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 3683afa07de..862ea379cbc 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -57,7 +57,7 @@
position: relative;
@media (min-width: $screen-sm-min) {
- transition: width $right-sidebar-transition-duration;
+ transition: width $sidebar-transition-duration;
width: 100%;
&.is-compact {
@@ -453,8 +453,8 @@
.right-sidebar.right-sidebar-expanded {
&.boards-sidebar-slide-enter-active,
&.boards-sidebar-slide-leave-active {
- transition: width $right-sidebar-transition-duration,
- padding $right-sidebar-transition-duration;
+ transition: width $sidebar-transition-duration,
+ padding $sidebar-transition-duration;
}
&.boards-sidebar-slide-enter,
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index b0795353ec1..a5a6b7461a3 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -201,8 +201,9 @@
stroke-width: 1;
}
-.deploy-info-text {
- dominant-baseline: text-before-edge;
+.divider-line {
+ stroke-width: 1;
+ stroke: $gray-darkest;
}
.prometheus-state {
@@ -312,6 +313,20 @@
stroke: $gray-darker;
}
+ .deploy-info-text {
+ dominant-baseline: text-before-edge;
+ font-size: 12px;
+ }
+
+ .deploy-info-text-link {
+ font-family: $monospace_font;
+ fill: $gl-link-color;
+
+ &:hover {
+ fill: $gl-link-hover-color;
+ }
+ }
+
@media (max-width: $screen-sm-max) {
.label-axis-text,
.text-metric-usage,
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 11ee1232bfe..2e26f2b5bbd 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -126,7 +126,7 @@
top: $header-height;
bottom: 0;
right: 0;
- transition: width $right-sidebar-transition-duration;
+ transition: width $sidebar-transition-duration;
background: $gray-light;
z-index: 200;
overflow: hidden;
@@ -470,7 +470,8 @@
}
}
- .milestone-title span {
+ .milestone-title span,
+ .collapse-truncated-title {
@include str-truncated(100%);
display: block;
margin: 0 4px;
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index 744e448e8df..281756af57a 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -21,11 +21,11 @@ module IssuableActions
respond_to do |format|
format.html do
- recaptcha_check_with_fallback { render :edit }
+ recaptcha_check_if_spammable { render :edit }
end
format.json do
- render_entity_json
+ recaptcha_check_if_spammable(false) { render_entity_json }
end
end
@@ -80,6 +80,12 @@ module IssuableActions
private
+ def recaptcha_check_if_spammable(should_redirect = true, &block)
+ return yield unless @issuable.is_a? Spammable
+
+ recaptcha_check_with_fallback(should_redirect, &block)
+ end
+
def render_conflict_response
respond_to do |format|
format.html do
diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb
index ada0dde87fb..03d8e188093 100644
--- a/app/controllers/concerns/spammable_actions.rb
+++ b/app/controllers/concerns/spammable_actions.rb
@@ -23,8 +23,8 @@ module SpammableActions
@spam_config_loaded = Gitlab::Recaptcha.load_configurations!
end
- def recaptcha_check_with_fallback(&fallback)
- if spammable.valid?
+ def recaptcha_check_with_fallback(should_redirect = true, &fallback)
+ if should_redirect && spammable.valid?
redirect_to spammable_path
elsif render_recaptcha?
ensure_spam_config_loaded!
@@ -33,7 +33,18 @@ module SpammableActions
flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
end
- render :verify
+ respond_to do |format|
+ format.html do
+ render :verify
+ end
+
+ format.json do
+ locals = { spammable: spammable, script: false, has_submit: false }
+ recaptcha_html = render_to_string(partial: 'shared/recaptcha_form', formats: :html, locals: locals)
+
+ render json: { recaptcha_html: recaptcha_html }
+ end
+ end
else
yield
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 3882fa4791d..8e9d6766d80 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -272,7 +272,7 @@ class ProjectsController < Projects::ApplicationController
render 'projects/empty' if @project.empty_repo?
else
- if @project.wiki_enabled?
+ if can?(current_user, :read_wiki, @project)
@project_wiki = @project.wiki
@wiki_home = @project_wiki.find_page('home', params[:version_id])
elsif @project.feature_available?(:issues, current_user)
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 18075ee8be7..556ed233ccf 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -118,20 +118,24 @@ module BlobHelper
icon("#{file_type_icon_class('file', mode, name)} fw")
end
- def blob_raw_path
+ def blob_raw_url(only_path: false)
if @build && @entry
- raw_project_job_artifacts_path(@project, @build, path: @entry.path)
+ raw_project_job_artifacts_url(@project, @build, path: @entry.path, only_path: only_path)
elsif @snippet
if @snippet.project_id
- raw_project_snippet_path(@project, @snippet)
+ raw_project_snippet_url(@project, @snippet, only_path: only_path)
else
- raw_snippet_path(@snippet)
+ raw_snippet_url(@snippet, only_path: only_path)
end
elsif @blob
- project_raw_path(@project, @id)
+ project_raw_url(@project, @id, only_path: only_path)
end
end
+ def blob_raw_path
+ blob_raw_url(only_path: true)
+ end
+
# SVGs can contain malicious JavaScript; only include whitelisted
# elements and attributes. Note that this whitelist is by no means complete
# and may omit some elements.
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index e82136f0177..1ce487e6592 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -104,15 +104,23 @@ module DiffHelper
].join(' ').html_safe
end
- def diff_file_blob_raw_path(diff_file)
- project_raw_path(@project, tree_join(diff_file.content_sha, diff_file.file_path))
+ def diff_file_blob_raw_url(diff_file, only_path: false)
+ project_raw_url(@project, tree_join(diff_file.content_sha, diff_file.file_path), only_path: only_path)
end
- def diff_file_old_blob_raw_path(diff_file)
+ def diff_file_old_blob_raw_url(diff_file, only_path: false)
sha = diff_file.old_content_sha
return unless sha
- project_raw_path(@project, tree_join(diff_file.old_content_sha, diff_file.old_path))
+ project_raw_url(@project, tree_join(diff_file.old_content_sha, diff_file.old_path), only_path: only_path)
+ end
+
+ def diff_file_blob_raw_path(diff_file)
+ diff_file_blob_raw_url(diff_file, only_path: true)
+ end
+
+ def diff_file_old_blob_raw_path(diff_file)
+ diff_file_old_blob_raw_url(diff_file, only_path: true)
end
def diff_file_html_data(project, diff_file_path, diff_commit_id)
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index 8e822ed0ea2..aaee6eaeedd 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -58,7 +58,7 @@ module PreferencesHelper
user_view
elsif user_view == "activity"
"activity"
- elsif @project.wiki_enabled?
+ elsif can?(current_user, :read_wiki, @project)
"wiki"
elsif @project.feature_available?(:issues, current_user)
"projects/issues/issues"
diff --git a/app/models/event.rb b/app/models/event.rb
index 0997b056c6a..6053594fab5 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -72,7 +72,7 @@ class Event < ActiveRecord::Base
# We're using preload for "push_event_payload" as otherwise the association
# is not always available (depending on the query being built).
includes(:author, :project, project: :namespace)
- .preload(:target, :push_event_payload)
+ .preload(:push_event_payload, target: :author)
end
scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
diff --git a/app/models/issue.rb b/app/models/issue.rb
index d6ef58d150b..2dedfc348fb 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -9,6 +9,9 @@ class Issue < ActiveRecord::Base
include FasterCacheKeys
include RelativePositioning
include TimeTrackable
+ include IgnorableColumn
+
+ ignore_column :branch_name
DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index d7faf22fc46..cba524e6e3b 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -82,6 +82,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
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 901dbf2ba69..0ff169d4531 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -40,6 +40,7 @@ class Namespace < ActiveRecord::Base
namespace_path: true
validate :nesting_level_allowed
+ validate :allowed_path_by_redirects
delegate :name, to: :owner, allow_nil: true, prefix: true
@@ -257,4 +258,14 @@ class Namespace < ActiveRecord::Base
Namespace.where(id: descendants.select(:id))
.update_all(share_with_group_lock: true)
end
+
+ def allowed_path_by_redirects
+ return if path.nil?
+
+ errors.add(:path, "#{path} has been taken before. Please use another one") if namespace_previously_created_with_same_path?
+ end
+
+ def namespace_previously_created_with_same_path?
+ RedirectRoute.permanent.exists?(path: path)
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 6ae15a0a50f..3440c01b356 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -659,7 +659,8 @@ class Project < ActiveRecord::Base
end
def import_started?
- import? && import_status == 'started'
+ # import? does SQL work so only run it if it looks like there's an import running
+ import_status == 'started' && import?
end
def import_scheduled?
diff --git a/app/models/redirect_route.rb b/app/models/redirect_route.rb
index 31de204d824..20532527346 100644
--- a/app/models/redirect_route.rb
+++ b/app/models/redirect_route.rb
@@ -17,4 +17,32 @@ class RedirectRoute < ActiveRecord::Base
where(wheres, path, "#{sanitize_sql_like(path)}/%")
end
+
+ scope :permanent, -> do
+ if column_permanent_exists?
+ where(permanent: true)
+ else
+ none
+ end
+ end
+
+ scope :temporary, -> do
+ if column_permanent_exists?
+ where(permanent: [false, nil])
+ else
+ all
+ end
+ end
+
+ default_value_for :permanent, false
+
+ def permanent=(value)
+ if self.class.column_permanent_exists?
+ super
+ end
+ end
+
+ def self.column_permanent_exists?
+ ActiveRecord::Base.connection.column_exists?(:redirect_routes, :permanent)
+ end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 751306188a0..d1ae3304e4a 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -971,8 +971,7 @@ class Repository
tmp_remote_name = true
end
- add_remote(remote_name, url)
- set_remote_as_mirror(remote_name, refmap: refmap)
+ add_remote(remote_name, url, mirror_refmap: refmap)
fetch_remote(remote_name, forced: forced)
ensure
remove_remote(remote_name) if tmp_remote_name
diff --git a/app/models/route.rb b/app/models/route.rb
index 97e8a6ad9e9..7ba3ec06041 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -8,6 +8,8 @@ class Route < ActiveRecord::Base
presence: true,
uniqueness: { case_sensitive: false }
+ validate :ensure_permanent_paths
+
after_create :delete_conflicting_redirects
after_update :delete_conflicting_redirects, if: :path_changed?
after_update :create_redirect_for_old_path
@@ -40,7 +42,7 @@ class Route < ActiveRecord::Base
# We are not calling route.delete_conflicting_redirects here, in hopes
# of avoiding deadlocks. The parent (self, in this method) already
# called it, which deletes conflicts for all descendants.
- route.create_redirect(old_path) if attributes[:path]
+ route.create_redirect(old_path, permanent: permanent_redirect?) if attributes[:path]
end
end
end
@@ -50,16 +52,30 @@ class Route < ActiveRecord::Base
end
def conflicting_redirects
- RedirectRoute.matching_path_and_descendants(path)
+ RedirectRoute.temporary.matching_path_and_descendants(path)
end
- def create_redirect(path)
- RedirectRoute.create(source: source, path: path)
+ def create_redirect(path, permanent: false)
+ RedirectRoute.create(source: source, path: path, permanent: permanent)
end
private
def create_redirect_for_old_path
- create_redirect(path_was) if path_changed?
+ create_redirect(path_was, permanent: permanent_redirect?) if path_changed?
+ end
+
+ def permanent_redirect?
+ source_type != "Project"
+ end
+
+ def ensure_permanent_paths
+ return if path.nil?
+
+ errors.add(:path, "#{path} has been taken before. Please use another one") if conflicting_redirect_exists?
+ end
+
+ def conflicting_redirect_exists?
+ RedirectRoute.permanent.matching_path_and_descendants(path).exists?
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index af1c36d9c93..ccf0e043aed 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -315,6 +315,8 @@ class User < ActiveRecord::Base
#
# Returns an ActiveRecord::Relation.
def search(query)
+ query = query.downcase
+
order = <<~SQL
CASE
WHEN users.name = %{query} THEN 0
@@ -324,8 +326,11 @@ class User < ActiveRecord::Base
END
SQL
- fuzzy_search(query, [:name, :email, :username])
- .reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name)
+ where(
+ fuzzy_arel_match(:name, query)
+ .or(fuzzy_arel_match(:username, query))
+ .or(arel_table[:email].eq(query))
+ ).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name)
end
# searches user by given pattern
@@ -333,15 +338,17 @@ class User < ActiveRecord::Base
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
def search_with_secondary_emails(query)
+ query = query.downcase
+
email_table = Email.arel_table
matched_by_emails_user_ids = email_table
.project(email_table[:user_id])
- .where(Email.fuzzy_arel_match(:email, query))
+ .where(email_table[:email].eq(query))
where(
fuzzy_arel_match(:name, query)
- .or(fuzzy_arel_match(:email, query))
.or(fuzzy_arel_match(:username, query))
+ .or(arel_table[:email].eq(query))
.or(arel_table[:id].in(matched_by_emails_user_ids))
)
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 1e5f2ed4dd2..c8b112132b3 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -12,18 +12,19 @@ module Ci
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, &block)
@pipeline = Ci::Pipeline.new
- command = OpenStruct.new(source: source,
- origin_ref: params[:ref],
- checkout_sha: params[:checkout_sha],
- after_sha: params[:after],
- before_sha: params[:before],
- trigger_request: trigger_request,
- schedule: schedule,
- ignore_skip_ci: ignore_skip_ci,
- save_incompleted: save_on_errors,
- seeds_block: block,
- project: project,
- current_user: current_user)
+ command = Gitlab::Ci::Pipeline::Chain::Command.new(
+ source: source,
+ origin_ref: params[:ref],
+ checkout_sha: params[:checkout_sha],
+ after_sha: params[:after],
+ before_sha: params[:before],
+ trigger_request: trigger_request,
+ schedule: schedule,
+ ignore_skip_ci: ignore_skip_ci,
+ save_incompleted: save_on_errors,
+ seeds_block: block,
+ project: project,
+ current_user: current_user)
sequence = Gitlab::Ci::Pipeline::Chain::Sequence
.new(pipeline, command, SEQUENCE)
@@ -80,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/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index c8b6450c9b5..f832b79ef21 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -38,11 +38,15 @@ module Ci
begin
# In case when 2 runners try to assign the same build, second runner will be declined
# with StateMachines::InvalidTransition or StaleObjectError when doing run! or save method.
- build.runner_id = runner.id
- build.run!
- register_success(build)
-
- return Result.new(build, true)
+ begin
+ build.runner_id = runner.id
+ build.run!
+ register_success(build)
+
+ return Result.new(build, true)
+ rescue Ci::Build::MissingDependenciesError
+ build.drop!(:missing_dependency_failure)
+ end
rescue StateMachines::InvalidTransition, ActiveRecord::StaleObjectError
# We are looping to find another build that is not conflicting
# It also indicates that this build can be picked and passed to runner.
@@ -54,9 +58,6 @@ module Ci
# we still have to return 409 in the end,
# to make sure that this is properly handled by runner.
valid = false
- rescue Ci::Build::MissingDependenciesError
- build.drop!(:missing_dependency_failure)
- valid = false
end
end
diff --git a/app/views/layouts/_recaptcha_verification.html.haml b/app/views/layouts/_recaptcha_verification.html.haml
index 77c77dc6754..e6f87ddd383 100644
--- a/app/views/layouts/_recaptcha_verification.html.haml
+++ b/app/views/layouts/_recaptcha_verification.html.haml
@@ -1,5 +1,4 @@
- humanized_resource_name = spammable.class.model_name.human.downcase
-- resource_name = spammable.class.model_name.singular
%h3.page-title
Anti-spam verification
@@ -8,16 +7,4 @@
%p
#{"We detected potential spam in the #{humanized_resource_name}. Please solve the reCAPTCHA to proceed."}
-= form_for form do |f|
- .recaptcha
- - params[resource_name].each do |field, value|
- = hidden_field(resource_name, field, value: value)
- = hidden_field_tag(:spam_log_id, spammable.spam_log.id)
- = hidden_field_tag(:recaptcha_verification, true)
- = recaptcha_tags
-
- -# Yields a block with given extra params.
- = yield
-
- .row-content-block.footer-block
- = f.submit "Submit #{humanized_resource_name}", class: 'btn btn-create'
+= render 'shared/recaptcha_form', spammable: spammable
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 0ec07605631..cb8db306b56 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -1,4 +1,4 @@
-.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
+.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header
= link_to admin_root_path, title: 'Admin Overview' do
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 0bf318b0b66..0c27b09f7b1 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -1,7 +1,7 @@
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
-.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
+.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header
= link_to group_path(@group), title: @group.name do
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index 7e23f9c1f05..a5a62a0695f 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -1,4 +1,4 @@
-.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
+.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
.context-header
= link_to profile_path, title: 'Profile Settings' do
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 53a9162b703..be39f577ba7 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -1,4 +1,4 @@
-.nav-sidebar{ class: ("sidebar-icons-only" if collapsed_sidebar?) }
+.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
- can_edit = can?(current_user, :admin_project, @project)
.context-header
diff --git a/app/views/projects/blob/viewers/_image.html.haml b/app/views/projects/blob/viewers/_image.html.haml
index 26ea028c5d7..2a8cefac005 100644
--- a/app/views/projects/blob/viewers/_image.html.haml
+++ b/app/views/projects/blob/viewers/_image.html.haml
@@ -1,2 +1,3 @@
.file-content.image_file
- = image_tag(blob_raw_path, alt: viewer.blob.name)
+ -# Uses the full URL rather than the path, to prevent it from getting prefixed with the asset host.
+ = image_tag(blob_raw_url, alt: viewer.blob.name)
diff --git a/app/views/projects/diffs/_replaced_image_diff.html.haml b/app/views/projects/diffs/_replaced_image_diff.html.haml
index 8fc232b464e..6dffc7c4390 100644
--- a/app/views/projects/diffs/_replaced_image_diff.html.haml
+++ b/app/views/projects/diffs/_replaced_image_diff.html.haml
@@ -1,7 +1,7 @@
- blob = diff_file.blob
- old_blob = diff_file.old_blob
-- blob_raw_path = diff_file_blob_raw_path(diff_file)
-- old_blob_raw_path = diff_file_old_blob_raw_path(diff_file)
+- blob_raw_url = diff_file_blob_raw_url(diff_file)
+- old_blob_raw_url = diff_file_old_blob_raw_url(diff_file)
- click_to_comment = local_assigns.fetch(:click_to_comment, true)
- diff_view_data = local_assigns.fetch(:diff_view_data, '')
- class_name = ''
@@ -13,7 +13,7 @@
.two-up.view
.wrap
.frame.deleted
- = image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false)
+ = image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false)
%p.image-info.hide
%span.meta-filesize= number_to_human_size(old_blob.size)
|
@@ -23,7 +23,7 @@
%strong H:
%span.meta-height
.wrap
- = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.new_path }
+ = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path }
%p.image-info.hide
%span.meta-filesize= number_to_human_size(blob.size)
|
@@ -36,9 +36,9 @@
.swipe.view.hide
.swipe-frame
.frame.deleted
- = image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false)
+ = image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false)
.swipe-wrap
- = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.new_path }
+ = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path }
%span.swipe-bar
%span.top-handle
%span.bottom-handle
@@ -46,8 +46,8 @@
.onion-skin.view.hide
.onion-skin-frame
.frame.deleted
- = image_tag(old_blob_raw_path, alt: diff_file.old_path, lazy: false)
- = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.new_path }
+ = image_tag(old_blob_raw_url, alt: diff_file.old_path, lazy: false)
+ = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "added js-image-frame #{class_name}", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.new_path }
.controls
.transparent
.drag-track
diff --git a/app/views/projects/diffs/_single_image_diff.html.haml b/app/views/projects/diffs/_single_image_diff.html.haml
index 6b0c6bbe48f..12be8beab39 100644
--- a/app/views/projects/diffs/_single_image_diff.html.haml
+++ b/app/views/projects/diffs/_single_image_diff.html.haml
@@ -1,7 +1,7 @@
- blob = diff_file.blob
- old_blob = diff_file.old_blob
-- blob_raw_path = diff_file_blob_raw_path(diff_file)
-- old_blob_raw_path = diff_file_old_blob_raw_path(diff_file)
+- blob_raw_url = diff_file_blob_raw_url(diff_file)
+- old_blob_raw_url = diff_file_old_blob_raw_url(diff_file)
- click_to_comment = local_assigns.fetch(:click_to_comment, true)
- diff_view_data = local_assigns.fetch(:diff_view_data, '')
- class_name = ''
@@ -12,5 +12,5 @@
.image.js-single-image{ data: diff_view_data }
.wrap
- single_class_name = diff_file.deleted_file? ? 'deleted' : 'added'
- = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "#{single_class_name} #{class_name} js-image-frame", position: position, note_type: DiffNote.name, image_path: blob_raw_path, alt: diff_file.file_path }
+ = render partial: "projects/diffs/image_diff_frame", locals: { class_name: "#{single_class_name} #{class_name} js-image-frame", position: position, note_type: DiffNote.name, image_path: blob_raw_url, alt: diff_file.file_path }
%p.image-info= number_to_human_size(blob.size)
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index e0aedcac5e1..ad94113fffd 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -15,8 +15,10 @@
#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),
"has-metrics": "#{@environment.has_metrics?}", deployment_endpoint: project_environment_deployments_path(@project, @environment, format: :json) } }
diff --git a/app/views/shared/_recaptcha_form.html.haml b/app/views/shared/_recaptcha_form.html.haml
new file mode 100644
index 00000000000..0e816870f15
--- /dev/null
+++ b/app/views/shared/_recaptcha_form.html.haml
@@ -0,0 +1,19 @@
+- resource_name = spammable.class.model_name.singular
+- humanized_resource_name = spammable.class.model_name.human.downcase
+- script = local_assigns.fetch(:script, true)
+- has_submit = local_assigns.fetch(:has_submit, true)
+
+= form_for resource_name, method: :post, html: { class: 'recaptcha-form js-recaptcha-form' } do |f|
+ .recaptcha
+ - params[resource_name].each do |field, value|
+ = hidden_field(resource_name, field, value: value)
+ = hidden_field_tag(:spam_log_id, spammable.spam_log.id)
+ = hidden_field_tag(:recaptcha_verification, true)
+ = recaptcha_tags script: script, callback: 'recaptchaDialogCallback'
+
+ -# Yields a block with given extra params.
+ = yield
+
+ - if has_submit
+ .row-content-block.footer-block
+ = f.submit "Submit #{humanized_resource_name}", class: 'btn btn-create'
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..68c71a2b7a7 100644
--- a/app/workers/update_head_pipeline_for_merge_request_worker.rb
+++ b/app/workers/update_head_pipeline_for_merge_request_worker.rb
@@ -8,8 +8,19 @@ class UpdateHeadPipelineForMergeRequestWorker
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/29483-no-feedback-when-checking-on-checklist-if-potential-spam-was-detected.yml b/changelogs/unreleased/29483-no-feedback-when-checking-on-checklist-if-potential-spam-was-detected.yml
new file mode 100644
index 00000000000..6bfcc5e70de
--- /dev/null
+++ b/changelogs/unreleased/29483-no-feedback-when-checking-on-checklist-if-potential-spam-was-detected.yml
@@ -0,0 +1,5 @@
+---
+title: Add recaptcha modal to issue updates detected as spam
+merge_request: 15408
+author:
+type: fixed
diff --git a/changelogs/unreleased/35385-allow-git-pull-push-on-project-redirects.yml b/changelogs/unreleased/35385-allow-git-pull-push-on-project-redirects.yml
new file mode 100644
index 00000000000..31450287caf
--- /dev/null
+++ b/changelogs/unreleased/35385-allow-git-pull-push-on-project-redirects.yml
@@ -0,0 +1,5 @@
+---
+title: Allow git pull/push on group/user/project redirects
+merge_request: 15670
+author:
+type: added
diff --git a/changelogs/unreleased/35724-animate-sidebar.yml b/changelogs/unreleased/35724-animate-sidebar.yml
new file mode 100644
index 00000000000..5d0b46a23c8
--- /dev/null
+++ b/changelogs/unreleased/35724-animate-sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: Animate contextual sidebar on collapse/expand
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/38032-deploy-markers-should-be-more-verbose.yml b/changelogs/unreleased/38032-deploy-markers-should-be-more-verbose.yml
new file mode 100644
index 00000000000..a1f28b3ba0f
--- /dev/null
+++ b/changelogs/unreleased/38032-deploy-markers-should-be-more-verbose.yml
@@ -0,0 +1,5 @@
+---
+title: Changed the deploy markers on the prometheus dashboard to be more verbose
+merge_request: 38032
+author:
+type: changed
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/dm-image-blob-diff-full-url.yml b/changelogs/unreleased/dm-image-blob-diff-full-url.yml
new file mode 100644
index 00000000000..db44a5a16b5
--- /dev/null
+++ b/changelogs/unreleased/dm-image-blob-diff-full-url.yml
@@ -0,0 +1,5 @@
+---
+title: Use app host instead of asset host when rendering image blob or diff
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-event-target-author-preloading.yml b/changelogs/unreleased/fix-event-target-author-preloading.yml
new file mode 100644
index 00000000000..c6154cc0835
--- /dev/null
+++ b/changelogs/unreleased/fix-event-target-author-preloading.yml
@@ -0,0 +1,5 @@
+---
+title: Fix N+1 query when displaying events
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/mk-fix-schema-dump-of-untracked-files-for-uploads.yml b/changelogs/unreleased/mk-fix-schema-dump-of-untracked-files-for-uploads.yml
new file mode 100644
index 00000000000..2691e85320c
--- /dev/null
+++ b/changelogs/unreleased/mk-fix-schema-dump-of-untracked-files-for-uploads.yml
@@ -0,0 +1,5 @@
+---
+title: Fix error during schema dump.
+merge_request: 15866
+author:
+type: fixed
diff --git a/changelogs/unreleased/optimize-projects-for-imported-projects.yml b/changelogs/unreleased/optimize-projects-for-imported-projects.yml
new file mode 100644
index 00000000000..13186fa36d5
--- /dev/null
+++ b/changelogs/unreleased/optimize-projects-for-imported-projects.yml
@@ -0,0 +1,6 @@
+---
+title: check the import_status field before doing SQL operations to check the import
+ url
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-fix-import-rake-task.yml b/changelogs/unreleased/sh-fix-import-rake-task.yml
new file mode 100644
index 00000000000..9cd6d7e4a72
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-import-rake-task.yml
@@ -0,0 +1,5 @@
+---
+title: Fix gitlab:import:repos Rake task moving repositories into the wrong location
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-remove-allocation-tracking-influxdb.yml b/changelogs/unreleased/sh-remove-allocation-tracking-influxdb.yml
new file mode 100644
index 00000000000..b98573df303
--- /dev/null
+++ b/changelogs/unreleased/sh-remove-allocation-tracking-influxdb.yml
@@ -0,0 +1,5 @@
+---
+title: Remove allocation tracking code from InfluxDB sampler for performance
+merge_request:
+author:
+type: performance
diff --git a/config/initializers/active_record_schema_ignore_tables.rb b/config/initializers/active_record_schema_ignore_tables.rb
new file mode 100644
index 00000000000..661135f8ade
--- /dev/null
+++ b/config/initializers/active_record_schema_ignore_tables.rb
@@ -0,0 +1,2 @@
+# Ignore table used temporarily in background migration
+ActiveRecord::SchemaDumper.ignore_tables = ["untracked_files_for_uploads"]
diff --git a/db/migrate/20171122131600_add_new_project_guidelines_to_appearances.rb b/db/migrate/20171122131600_add_new_project_guidelines_to_appearances.rb
index f141c442d97..328cc65a549 100644
--- a/db/migrate/20171122131600_add_new_project_guidelines_to_appearances.rb
+++ b/db/migrate/20171122131600_add_new_project_guidelines_to_appearances.rb
@@ -4,6 +4,12 @@ class AddNewProjectGuidelinesToAppearances < ActiveRecord::Migration
DOWNTIME = false
def change
+ # Clears the current Appearance cache otherwise it breaks since
+ # new_project_guidelines_html would be missing. See
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/41041
+ # We're not using Appearance#flush_redis_cache on purpose here.
+ Rails.cache.delete('current_appearance')
+
change_table :appearances do |t|
t.text :new_project_guidelines
t.text :new_project_guidelines_html
diff --git a/db/migrate/20171204204233_add_permanent_to_redirect_route.rb b/db/migrate/20171204204233_add_permanent_to_redirect_route.rb
new file mode 100644
index 00000000000..f3ae471201e
--- /dev/null
+++ b/db/migrate/20171204204233_add_permanent_to_redirect_route.rb
@@ -0,0 +1,18 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddPermanentToRedirectRoute < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def up
+ add_column(:redirect_routes, :permanent, :boolean)
+ end
+
+ def down
+ remove_column(:redirect_routes, :permanent)
+ end
+end
diff --git a/db/migrate/20171206221519_add_permanent_index_to_redirect_route.rb b/db/migrate/20171206221519_add_permanent_index_to_redirect_route.rb
new file mode 100644
index 00000000000..33ce7e1aa68
--- /dev/null
+++ b/db/migrate/20171206221519_add_permanent_index_to_redirect_route.rb
@@ -0,0 +1,19 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddPermanentIndexToRedirectRoute < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index(:redirect_routes, :permanent)
+ end
+
+ def down
+ remove_concurrent_index(:redirect_routes, :permanent) if index_exists?(:redirect_routes, :permanent)
+ end
+end
diff --git a/db/migrate/20171106154015_remove_issues_branch_name.rb b/db/post_migrate/20171106154015_remove_issues_branch_name.rb
index 3d08225c96d..3d08225c96d 100644
--- a/db/migrate/20171106154015_remove_issues_branch_name.rb
+++ b/db/post_migrate/20171106154015_remove_issues_branch_name.rb
diff --git a/db/schema.rb b/db/schema.rb
index 4c697a4a384..c0a141885ad 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: 20171205190711) do
+ActiveRecord::Schema.define(version: 20171206221519) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -1526,10 +1526,12 @@ ActiveRecord::Schema.define(version: 20171205190711) do
t.string "path", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.boolean "permanent"
end
add_index "redirect_routes", ["path"], name: "index_redirect_routes_on_path", unique: true, using: :btree
add_index "redirect_routes", ["path"], name: "index_redirect_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"}
+ add_index "redirect_routes", ["permanent"], name: "index_redirect_routes_on_permanent", using: :btree
add_index "redirect_routes", ["source_type", "source_id"], name: "index_redirect_routes_on_source_type_and_source_id", using: :btree
create_table "releases", force: :cascade do |t|
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 11d5e077a36..f495990d9a4 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -45,8 +45,9 @@ In this experimental phase, only a few metrics are available:
| redis_ping_success | Gauge | 9.4 | Whether or not the last redis ping succeeded |
| redis_ping_latency_seconds | Gauge | 9.4 | Round trip time of the redis ping |
| user_session_logins_total | Counter | 9.4 | Counter of how many users have logged in |
-| filesystem_circuitbreaker_latency_seconds | Histogram | 9.5 | Latency of the stat check the circuitbreaker uses to probe a shard |
+| filesystem_circuitbreaker_latency_seconds | Gauge | 9.5 | Time spent validating if a storage is accessible |
| filesystem_circuitbreaker | Gauge | 9.5 | Wether or not the circuit for a certain shard is broken or not |
+| circuitbreaker_storage_check_duration_seconds | Histogram | 10.3 | Time a single storage probe took |
## Metrics shared directory
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index ecb8f15c851..fb5bfe26bb0 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -319,45 +319,62 @@ As you can see, the syntax of `command` is similar to [Dockerfile's `CMD`][cmd].
> Introduced in GitLab and GitLab Runner 9.4. Read more about the [extended
configuration options](#extended-docker-configuration-options).
+Before showing the available entrypoint override methods, let's describe shortly
+how the Runner starts and uses a Docker image for the containers used in the
+CI jobs:
+
+1. The Runner starts a Docker container using the defined entrypoint (default
+ from `Dockerfile` that may be overridden in `.gitlab-ci.yml`)
+1. The Runner attaches itself to a running container.
+1. The Runner prepares a script (the combination of
+ [`before_script`](../yaml/README.md#before_script),
+ [`script`](../yaml/README.md#script),
+ and [`after_script`](../yaml/README.md#after_script)).
+1. The Runner sends the script to the container's shell STDIN and receives the
+ output.
+
+To override the entrypoint of a Docker image, the recommended solution is to
+define an empty `entrypoint` in `.gitlab-ci.yml`, so the Runner doesn't start
+a useless shell layer. However, that will not work for all Docker versions, and
+you should check which one your Runner is using. Specifically:
+
+- If Docker 17.06 or later is used, the `entrypoint` can be set to an empty value.
+- If Docker 17.03 or previous versions are used, the `entrypoint` can be set to
+ `/bin/sh -c`, `/bin/bash -c` or an equivalent shell available in the image.
+
+The syntax of `image:entrypoint` is similar to [Dockerfile's `ENTRYPOINT`][entrypoint].
+
+----
+
Let's assume you have a `super/sql:experimental` image with some SQL database
inside it and you would like to use it as a base image for your job because you
want to execute some tests with this database binary. Let's also assume that
this image is configured with `/usr/bin/super-sql run` as an entrypoint. That
-means, that when starting the container without additional options, it will run
+means that when starting the container without additional options, it will run
the database's process, while Runner expects that the image will have no
-entrypoint or at least will start with a shell as its entrypoint.
-
-Before the new extended Docker configuration options, you would need to create
-your own image based on the `super/sql:experimental` image, set the entrypoint
-to a shell and then use it in job's configuration, like:
+entrypoint or that the entrypoint is prepared to start a shell command.
-```Dockerfile
-# my-super-sql:experimental image's Dockerfile
+With the extended Docker configuration options, instead of creating your
+own image based on `super/sql:experimental`, setting the `ENTRYPOINT`
+to a shell, and then using the new image in your CI job, you can now simply
+define an `entrypoint` in `.gitlab-ci.yml`.
-FROM super/sql:experimental
-ENTRYPOINT ["/bin/sh"]
-```
+**For Docker 17.06+:**
```yaml
-# .gitlab-ci.yml
-
-image: my-super-sql:experimental
+image:
+ name: super/sql:experimental
+ entrypoint: [""]
```
-After the new extended Docker configuration options, you can now simply
-set an `entrypoint` in `.gitlab-ci.yml`, like:
+**For Docker =< 17.03:**
```yaml
-# .gitlab-ci.yml
-
image:
name: super/sql:experimental
- entrypoint: ["/bin/sh"]
+ entrypoint: ["/bin/sh", "-c"]
```
-As you can see the syntax of `entrypoint` is similar to
-[Dockerfile's `ENTRYPOINT`][entrypoint].
-
## Define image and services in `config.toml`
Look for the `[runners.docker]` section:
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/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/project/merge_requests/img/merge_request_diff_file_navigation.png b/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png
index 9b8aee47411..4eee734ff8d 100644
--- a/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png
+++ b/doc/user/project/merge_requests/img/merge_request_diff_file_navigation.png
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index d76ea259301..b5c3f74a113 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -138,7 +138,8 @@ You can create a new merge request by sending an email to a user-specific email
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.
+will be the default branch for the project. The message body (if not empty)
+will be used as the merge request description.
## Revert changes
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index d96e7f2770f..928706dfda7 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -248,8 +248,21 @@ module API
end
class GroupDetail < Group
- expose :projects, using: Entities::Project
- expose :shared_projects, using: Entities::Project
+ expose :projects, using: Entities::Project do |group, options|
+ GroupProjectsFinder.new(
+ group: group,
+ current_user: options[:current_user],
+ options: { only_owned: true }
+ ).execute
+ end
+
+ expose :shared_projects, using: Entities::Project do |group, options|
+ GroupProjectsFinder.new(
+ group: group,
+ current_user: options[:current_user],
+ options: { only_shared: true }
+ ).execute
+ end
end
class Commit < Grape::Entity
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 451121a4cea..ccaaeca10d4 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -4,6 +4,7 @@ module API
before { authenticate_by_gitlab_shell_token! }
helpers ::API::Helpers::InternalHelpers
+ helpers ::Gitlab::Identifier
namespace 'internal' do
# Check if git command is allowed to project
@@ -176,17 +177,25 @@ module API
post '/post_receive' do
status 200
-
PostReceive.perform_async(params[:gl_repository], params[:identifier],
params[:changes])
broadcast_message = BroadcastMessage.current&.last&.message
reference_counter_decreased = Gitlab::ReferenceCounter.new(params[:gl_repository]).decrease
- {
+ output = {
merge_request_urls: merge_request_urls,
broadcast_message: broadcast_message,
reference_counter_decreased: reference_counter_decreased
}
+
+ project = Gitlab::GlRepository.parse(params[:gl_repository]).first
+ user = identify(params[:identifier])
+ redirect_message = Gitlab::Checks::ProjectMoved.fetch_redirect_message(user.id, project.id)
+ if redirect_message
+ output[:redirected_message] = redirect_message
+ end
+
+ output
end
end
end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index e60e00d7956..5f943ba27d1 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -161,6 +161,8 @@ module API
use :issue_params
end
post ':id/issues' do
+ authorize! :create_issue, user_project
+
# Setting created_at time only allowed for admins and project owners
unless current_user.admin? || user_project.owner == current_user
params.delete(:created_at)
diff --git a/lib/gitlab/bare_repository_import/importer.rb b/lib/gitlab/bare_repository_import/importer.rb
index 196de667805..298409d8b5a 100644
--- a/lib/gitlab/bare_repository_import/importer.rb
+++ b/lib/gitlab/bare_repository_import/importer.rb
@@ -55,6 +55,7 @@ module Gitlab
name: project_name,
path: project_name,
skip_disk_validation: true,
+ import_type: 'gitlab_project',
namespace_id: group&.id).execute
if project.persisted? && mv_repo(project)
diff --git a/lib/gitlab/bare_repository_import/repository.rb b/lib/gitlab/bare_repository_import/repository.rb
index 8574ac6eb30..fa7891c8dcc 100644
--- a/lib/gitlab/bare_repository_import/repository.rb
+++ b/lib/gitlab/bare_repository_import/repository.rb
@@ -7,6 +7,8 @@ module Gitlab
@root_path = root_path
@repo_path = repo_path
+ @root_path << '/' unless root_path.ends_with?('/')
+
# Split path into 'all/the/namespaces' and 'project_name'
@group_path, _, @project_name = repo_relative_path.rpartition('/')
end
diff --git a/lib/gitlab/checks/project_moved.rb b/lib/gitlab/checks/project_moved.rb
new file mode 100644
index 00000000000..3a1c0a3455e
--- /dev/null
+++ b/lib/gitlab/checks/project_moved.rb
@@ -0,0 +1,65 @@
+module Gitlab
+ module Checks
+ class ProjectMoved
+ REDIRECT_NAMESPACE = "redirect_namespace".freeze
+
+ def initialize(project, user, redirected_path, protocol)
+ @project = project
+ @user = user
+ @redirected_path = redirected_path
+ @protocol = protocol
+ end
+
+ def self.fetch_redirect_message(user_id, project_id)
+ redirect_key = redirect_message_key(user_id, project_id)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ message = redis.get(redirect_key)
+ redis.del(redirect_key)
+ message
+ end
+ end
+
+ def add_redirect_message
+ Gitlab::Redis::SharedState.with do |redis|
+ key = self.class.redirect_message_key(user.id, project.id)
+ redis.setex(key, 5.minutes, redirect_message)
+ end
+ end
+
+ def redirect_message(rejected: false)
+ <<~MESSAGE.strip_heredoc
+ Project '#{redirected_path}' was moved to '#{project.full_path}'.
+
+ Please update your Git remote:
+
+ #{remote_url_message(rejected)}
+ MESSAGE
+ end
+
+ def permanent_redirect?
+ RedirectRoute.permanent.exists?(path: redirected_path)
+ end
+
+ private
+
+ attr_reader :project, :redirected_path, :protocol, :user
+
+ def self.redirect_message_key(user_id, project_id)
+ "#{REDIRECT_NAMESPACE}:#{user_id}:#{project_id}"
+ end
+
+ def remote_url_message(rejected)
+ if rejected
+ "git remote set-url origin #{url} and try again."
+ else
+ "git remote set-url origin #{url}"
+ end
+ end
+
+ def url
+ protocol == 'ssh' ? project.ssh_url_to_repo : project.http_url_to_repo
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/base.rb b/lib/gitlab/ci/pipeline/chain/base.rb
index 8d82e1b288d..efed19da21c 100644
--- a/lib/gitlab/ci/pipeline/chain/base.rb
+++ b/lib/gitlab/ci/pipeline/chain/base.rb
@@ -3,14 +3,13 @@ module Gitlab
module Pipeline
module Chain
class Base
- attr_reader :pipeline, :project, :current_user
+ attr_reader :pipeline, :command
+
+ delegate :project, :current_user, to: :command
def initialize(pipeline, command)
@pipeline = pipeline
@command = command
-
- @project = command.project
- @current_user = command.current_user
end
def perform!
diff --git a/lib/gitlab/ci/pipeline/chain/build.rb b/lib/gitlab/ci/pipeline/chain/build.rb
index a126dded1ae..70732d26bbd 100644
--- a/lib/gitlab/ci/pipeline/chain/build.rb
+++ b/lib/gitlab/ci/pipeline/chain/build.rb
@@ -3,20 +3,18 @@ module Gitlab
module Pipeline
module Chain
class Build < Chain::Base
- include Chain::Helpers
-
def perform!
@pipeline.assign_attributes(
source: @command.source,
- project: @project,
- ref: ref,
- sha: sha,
- before_sha: before_sha,
- tag: tag_exists?,
+ project: @command.project,
+ ref: @command.ref,
+ sha: @command.sha,
+ before_sha: @command.before_sha,
+ tag: @command.tag_exists?,
trigger_requests: Array(@command.trigger_request),
- user: @current_user,
+ user: @command.current_user,
pipeline_schedule: @command.schedule,
- protected: protected_ref?
+ protected: @command.protected_ref?
)
@pipeline.set_config_source
@@ -25,32 +23,6 @@ module Gitlab
def break?
false
end
-
- private
-
- def ref
- @ref ||= Gitlab::Git.ref_name(origin_ref)
- end
-
- def sha
- @project.commit(origin_sha || origin_ref).try(:id)
- end
-
- def origin_ref
- @command.origin_ref
- end
-
- def origin_sha
- @command.checkout_sha || @command.after_sha
- end
-
- def before_sha
- @command.checkout_sha || @command.before_sha || Gitlab::Git::BLANK_SHA
- end
-
- def protected_ref?
- @project.protected_for?(ref)
- end
end
end
end
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
new file mode 100644
index 00000000000..7b19b10e05b
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -0,0 +1,61 @@
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ Command = Struct.new(
+ :source, :project, :current_user,
+ :origin_ref, :checkout_sha, :after_sha, :before_sha,
+ :trigger_request, :schedule,
+ :ignore_skip_ci, :save_incompleted,
+ :seeds_block
+ ) do
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(**params)
+ params.each do |key, value|
+ self[key] = value
+ end
+ end
+
+ def branch_exists?
+ strong_memoize(:is_branch) do
+ project.repository.branch_exists?(ref)
+ end
+ end
+
+ def tag_exists?
+ strong_memoize(:is_tag) do
+ project.repository.tag_exists?(ref)
+ end
+ end
+
+ def ref
+ strong_memoize(:ref) do
+ Gitlab::Git.ref_name(origin_ref)
+ end
+ end
+
+ def sha
+ strong_memoize(:sha) do
+ project.commit(origin_sha || origin_ref).try(:id)
+ end
+ end
+
+ def origin_sha
+ checkout_sha || after_sha
+ end
+
+ def before_sha
+ self[:before_sha] || checkout_sha || Gitlab::Git::BLANK_SHA
+ end
+
+ def protected_ref?
+ strong_memoize(:protected_ref) do
+ project.protected_for?(ref)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/helpers.rb b/lib/gitlab/ci/pipeline/chain/helpers.rb
index 02d81286f21..bf1380a1da9 100644
--- a/lib/gitlab/ci/pipeline/chain/helpers.rb
+++ b/lib/gitlab/ci/pipeline/chain/helpers.rb
@@ -3,18 +3,6 @@ module Gitlab
module Pipeline
module Chain
module Helpers
- def branch_exists?
- return @is_branch if defined?(@is_branch)
-
- @is_branch = project.repository.branch_exists?(pipeline.ref)
- end
-
- def tag_exists?
- return @is_tag if defined?(@is_tag)
-
- @is_tag = project.repository.tag_exists?(pipeline.ref)
- end
-
def error(message)
pipeline.errors.add(:base, message)
end
diff --git a/lib/gitlab/ci/pipeline/chain/validate/abilities.rb b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
index 4913a604079..13c6fedd831 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
@@ -14,7 +14,7 @@ module Gitlab
unless allowed_to_trigger_pipeline?
if can?(current_user, :create_pipeline, project)
- return error("Insufficient permissions for protected ref '#{pipeline.ref}'")
+ return error("Insufficient permissions for protected ref '#{command.ref}'")
else
return error('Insufficient permissions to create a new pipeline')
end
@@ -29,7 +29,7 @@ module Gitlab
if current_user
allowed_to_create?
else # legacy triggers don't have a corresponding user
- !project.protected_for?(@pipeline.ref)
+ !@command.protected_ref?
end
end
@@ -38,10 +38,10 @@ module Gitlab
access = Gitlab::UserAccess.new(current_user, project: project)
- if branch_exists?
- access.can_update_branch?(@pipeline.ref)
- elsif tag_exists?
- access.can_create_tag?(@pipeline.ref)
+ if @command.branch_exists?
+ access.can_update_branch?(@command.ref)
+ elsif @command.tag_exists?
+ access.can_create_tag?(@command.ref)
else
true # Allow it for now and we'll reject when we check ref existence
end
diff --git a/lib/gitlab/ci/pipeline/chain/validate/repository.rb b/lib/gitlab/ci/pipeline/chain/validate/repository.rb
index 70a4cfdbdea..9699c24e5b6 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/repository.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/repository.rb
@@ -7,14 +7,11 @@ module Gitlab
include Chain::Helpers
def perform!
- unless branch_exists? || tag_exists?
+ unless @command.branch_exists? || @command.tag_exists?
return error('Reference not found')
end
- ## TODO, we check commit in the service, that is why
- # there is no repository access here.
- #
- unless pipeline.sha
+ unless @command.sha
return error('Commit not found')
end
end
diff --git a/lib/gitlab/email/handler/create_merge_request_handler.rb b/lib/gitlab/email/handler/create_merge_request_handler.rb
index c63666b98c1..e2f7c1d0257 100644
--- a/lib/gitlab/email/handler/create_merge_request_handler.rb
+++ b/lib/gitlab/email/handler/create_merge_request_handler.rb
@@ -55,11 +55,13 @@ module Gitlab
end
def merge_request_params
- {
+ params = {
source_project_id: project.id,
source_branch: mail.subject,
target_project_id: project.id
}
+ params[:description] = message if message.present?
+ params
end
end
end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 73889328f36..fbdd646ea13 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -20,6 +20,7 @@ module Gitlab
SEARCH_CONTEXT_LINES = 3
REBASE_WORKTREE_PREFIX = 'rebase'.freeze
SQUASH_WORKTREE_PREFIX = 'squash'.freeze
+ GITALY_INTERNAL_URL = 'ssh://gitaly/internal.git'.freeze
NoRepository = Class.new(StandardError)
InvalidBlobName = Class.new(StandardError)
@@ -887,8 +888,11 @@ module Gitlab
end
end
- def add_remote(remote_name, url)
+ # If `mirror_refmap` is present the remote is set as mirror with that mapping
+ def add_remote(remote_name, url, mirror_refmap: nil)
rugged.remotes.create(remote_name, url)
+
+ set_remote_as_mirror(remote_name, refmap: mirror_refmap) if mirror_refmap
rescue Rugged::ConfigError
remote_update(remote_name, url: url)
end
@@ -1128,12 +1132,24 @@ module Gitlab
!has_visible_content?
end
- # Like all public `Gitlab::Git::Repository` methods, this method is part
- # of `Repository`'s interface through `method_missing`.
- # `Repository` has its own `fetch_remote` which uses `gitlab-shell` and
- # takes some extra attributes, so we qualify this method name to prevent confusion.
- def fetch_remote_without_shell(remote = 'origin')
- run_git(['fetch', remote]).last.zero?
+ def fetch_repository_as_mirror(repository)
+ remote_name = "tmp-#{SecureRandom.hex}"
+
+ # Notice that this feature flag is not for `fetch_repository_as_mirror`
+ # as a whole but for the fetching mechanism (file path or gitaly-ssh).
+ url, env = gitaly_migrate(:fetch_internal) do |is_enabled|
+ if is_enabled
+ repository = RemoteRepository.new(repository) unless repository.is_a?(RemoteRepository)
+ [GITALY_INTERNAL_URL, repository.fetch_env]
+ else
+ [repository.path, nil]
+ end
+ end
+
+ add_remote(remote_name, url, mirror_refmap: :all_refs)
+ fetch_remote(remote_name, env: env)
+ ensure
+ remove_remote(remote_name)
end
def blob_at(sha, path)
@@ -1851,7 +1867,7 @@ module Gitlab
end
def gitaly_fetch_ref(source_repository, source_ref:, target_ref:)
- args = %W(fetch --no-tags -f ssh://gitaly/internal.git #{source_ref}:#{target_ref})
+ args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref})
run_git(args, env: source_repository.fetch_env)
end
@@ -1871,6 +1887,10 @@ module Gitlab
rescue Rugged::ReferenceError
raise ArgumentError, 'Invalid merge source'
end
+
+ def fetch_remote(remote_name = 'origin', env: nil)
+ run_git(['fetch', remote_name], env: env).last.zero?
+ end
end
end
end
diff --git a/lib/gitlab/git/repository_mirroring.rb b/lib/gitlab/git/repository_mirroring.rb
index 392bef69e80..effb1f0ca19 100644
--- a/lib/gitlab/git/repository_mirroring.rb
+++ b/lib/gitlab/git/repository_mirroring.rb
@@ -17,33 +17,6 @@ module Gitlab
rugged.config["remote.#{remote_name}.prune"] = true
end
- def set_remote_refmap(remote_name, refmap)
- Array(refmap).each_with_index do |refspec, i|
- refspec = REFMAPS[refspec] || refspec
-
- # We need multiple `fetch` entries, but Rugged only allows replacing a config, not adding to it.
- # To make sure we start from scratch, we set the first using rugged, and use `git` for any others
- if i == 0
- rugged.config["remote.#{remote_name}.fetch"] = refspec
- else
- run_git(%W[config --add remote.#{remote_name}.fetch #{refspec}])
- end
- end
- end
-
- # Like all_refs public `Gitlab::Git::Repository` methods, this method is part
- # of `Repository`'s interface through `method_missing`.
- # `Repository` has its own `fetch_as_mirror` which uses `gitlab-shell` and
- # takes some extra attributes, so we qualify this method name to prevent confusion.
- def fetch_as_mirror_without_shell(url)
- remote_name = "tmp-#{SecureRandom.hex}"
- add_remote(remote_name, url)
- set_remote_as_mirror(remote_name)
- fetch_remote_without_shell(remote_name)
- ensure
- remove_remote(remote_name) if remote_name
- end
-
def remote_tags(remote)
# Each line has this format: "dc872e9fa6963f8f03da6c8f6f264d0845d6b092\trefs/tags/v1.10.0\n"
# We want to convert it to: [{ 'v1.10.0' => 'dc872e9fa6963f8f03da6c8f6f264d0845d6b092' }, ...]
@@ -85,6 +58,20 @@ module Gitlab
private
+ def set_remote_refmap(remote_name, refmap)
+ Array(refmap).each_with_index do |refspec, i|
+ refspec = REFMAPS[refspec] || refspec
+
+ # We need multiple `fetch` entries, but Rugged only allows replacing a config, not adding to it.
+ # To make sure we start from scratch, we set the first using rugged, and use `git` for any others
+ if i == 0
+ rugged.config["remote.#{remote_name}.fetch"] = refspec
+ else
+ run_git(%W[config --add remote.#{remote_name}.fetch #{refspec}])
+ end
+ end
+ end
+
def list_remote_tags(remote)
tag_list, exit_code, error = nil
cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path} ls-remote --tags #{remote})
diff --git a/lib/gitlab/git/storage/checker.rb b/lib/gitlab/git/storage/checker.rb
index de63cb4b40c..d3c37f82101 100644
--- a/lib/gitlab/git/storage/checker.rb
+++ b/lib/gitlab/git/storage/checker.rb
@@ -5,6 +5,8 @@ module Gitlab
include CircuitBreakerSettings
attr_reader :storage_path, :storage, :hostname, :logger
+ METRICS_MUTEX = Mutex.new
+ STORAGE_TIMING_BUCKETS = [0.1, 0.15, 0.25, 0.33, 0.5, 1, 1.5, 2.5, 5, 10, 15].freeze
def self.check_all(logger = Rails.logger)
threads = Gitlab.config.repositories.storages.keys.map do |storage_name|
@@ -19,6 +21,17 @@ module Gitlab
end
end
+ def self.check_histogram
+ @check_histogram ||=
+ METRICS_MUTEX.synchronize do
+ @check_histogram || Gitlab::Metrics.histogram(:circuitbreaker_storage_check_duration_seconds,
+ 'Storage check time in seconds',
+ {},
+ STORAGE_TIMING_BUCKETS
+ )
+ end
+ end
+
def initialize(storage, logger = Rails.logger)
@storage = storage
config = Gitlab.config.repositories.storages[@storage]
@@ -45,7 +58,7 @@ module Gitlab
end
def check
- if Gitlab::Git::Storage::ForkedStorageCheck.storage_available?(storage_path, storage_timeout, access_retries)
+ if perform_access_check
track_storage_accessible
true
else
@@ -57,6 +70,15 @@ module Gitlab
private
+ def perform_access_check
+ start_time = Gitlab::Metrics::System.monotonic_time
+
+ Gitlab::Git::Storage::ForkedStorageCheck.storage_available?(storage_path, storage_timeout, access_retries)
+ ensure
+ execution_time = Gitlab::Metrics::System.monotonic_time - start_time
+ self.class.check_histogram.observe({ storage: storage }, execution_time)
+ end
+
def track_storage_inaccessible
first_failure = current_failure_info.first_failure || Time.now
last_failure = Time.now
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 9d7d921bb9c..56f6febe86d 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -102,18 +102,15 @@ module Gitlab
end
def check_project_moved!
- return unless redirected_path
+ return if redirected_path.nil?
- url = protocol == 'ssh' ? project.ssh_url_to_repo : project.http_url_to_repo
- message = <<-MESSAGE.strip_heredoc
- Project '#{redirected_path}' was moved to '#{project.full_path}'.
+ project_moved = Checks::ProjectMoved.new(project, user, redirected_path, protocol)
- Please update your Git remote and try again:
-
- git remote set-url origin #{url}
- MESSAGE
-
- raise ProjectMovedError, message
+ if project_moved.permanent_redirect?
+ project_moved.add_redirect_message
+ else
+ raise ProjectMovedError, project_moved.redirect_message(rejected: true)
+ end
end
def check_command_disabled!(cmd)
diff --git a/lib/gitlab/identifier.rb b/lib/gitlab/identifier.rb
index 94678b6ec40..3f3f10596c5 100644
--- a/lib/gitlab/identifier.rb
+++ b/lib/gitlab/identifier.rb
@@ -2,9 +2,8 @@
# key-13 or user-36 or last commit
module Gitlab
module Identifier
- def identify(identifier, project, newrev)
+ def identify(identifier, project = nil, newrev = nil)
if identifier.blank?
- # Local push from gitlab
identify_using_commit(project, newrev)
elsif identifier =~ /\Auser-\d+\Z/
# git push over http
@@ -17,6 +16,8 @@ module Gitlab
# Tries to identify a user based on a commit SHA.
def identify_using_commit(project, ref)
+ return if project.nil? && ref.nil?
+
commit = project.commit(ref)
return if !commit || !commit.author_email
diff --git a/lib/gitlab/metrics/samplers/influx_sampler.rb b/lib/gitlab/metrics/samplers/influx_sampler.rb
index f4f9b5ca792..5a0f7f28fc8 100644
--- a/lib/gitlab/metrics/samplers/influx_sampler.rb
+++ b/lib/gitlab/metrics/samplers/influx_sampler.rb
@@ -27,7 +27,6 @@ module Gitlab
def sample
sample_memory_usage
sample_file_descriptors
- sample_objects
sample_gc
flush
@@ -48,29 +47,6 @@ module Gitlab
add_metric('file_descriptors', value: System.file_descriptor_count)
end
- if Metrics.mri?
- def sample_objects
- sample = Allocations.to_hash
- counts = sample.each_with_object({}) do |(klass, count), hash|
- name = klass.name
-
- next unless name
-
- hash[name] = count
- end
-
- # Symbols aren't allocated so we'll need to add those manually.
- counts['Symbol'] = Symbol.all_symbols.length
-
- counts.each do |name, count|
- add_metric('object_counts', { count: count }, type: name)
- end
- end
- else
- def sample_objects
- end
- end
-
def sample_gc
time = GC::Profiler.total_time * 1000.0
stats = GC.stat.merge(total_time: time)
diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb
index f4901be9581..b68800417a2 100644
--- a/lib/gitlab/metrics/samplers/ruby_sampler.rb
+++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb
@@ -48,7 +48,6 @@ module Gitlab
def sample
start_time = System.monotonic_time
sample_gc
- sample_objects
metrics[:memory_usage].set(labels, System.memory_usage)
metrics[:file_descriptors].set(labels, System.file_descriptor_count)
@@ -68,32 +67,6 @@ module Gitlab
end
end
- def sample_objects
- list_objects.each do |name, count|
- metrics[:objects_total].set(labels.merge(class: name), count)
- end
- end
-
- if Metrics.mri?
- def list_objects
- sample = Allocations.to_hash
- counts = sample.each_with_object({}) do |(klass, count), hash|
- name = klass.name
-
- next unless name
-
- hash[name] = count
- end
-
- # Symbols aren't allocated so we'll need to add those manually.
- counts['Symbol'] = Symbol.all_symbols.length
- counts
- end
- else
- def list_objects
- end
- end
-
def worker_label
return {} unless defined?(Unicorn::Worker)
diff --git a/scripts/trigger-build-omnibus b/scripts/trigger-build-omnibus
index 3c5c22c9372..4ff0e8e10b7 100755
--- a/scripts/trigger-build-omnibus
+++ b/scripts/trigger-build-omnibus
@@ -66,7 +66,7 @@ module Omnibus
raise 'Pipeline timeout!' if timeout?
case status
- when :pending, :running
+ when :created, :pending, :running
puts "Waiting another #{INTERVAL} seconds ..."
sleep INTERVAL
when :success
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 4dbbaecdd6d..c5d08cb0b9d 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -272,6 +272,20 @@ describe Projects::IssuesController do
expect(response).to have_http_status(:ok)
expect(issue.reload.title).to eq('New title')
end
+
+ context 'when Akismet is enabled and the issue is identified as spam' do
+ before do
+ stub_application_setting(recaptcha_enabled: true)
+ allow_any_instance_of(SpamService).to receive(:check_for_spam?).and_return(true)
+ allow_any_instance_of(AkismetService).to receive(:spam?).and_return(true)
+ end
+
+ it 'renders json with recaptcha_html' do
+ subject
+
+ expect(JSON.parse(response.body)).to have_key('recaptcha_html')
+ end
+ end
end
context 'when user does not have access to update issue' do
@@ -504,17 +518,16 @@ describe Projects::IssuesController do
expect(spam_logs.first.recaptcha_verified).to be_falsey
end
- it 'renders json errors' do
+ it 'renders recaptcha_html json response' do
update_issue
- expect(json_response)
- .to eql("errors" => ["Your issue has been recognized as spam. Please, change the content or solve the reCAPTCHA to proceed."])
+ expect(json_response).to have_key('recaptcha_html')
end
- it 'returns 422 status' do
+ it 'returns 200 status' do
update_issue
- expect(response).to have_gitlab_http_status(422)
+ expect(response).to have_gitlab_http_status(200)
end
end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index 4000cd085b7..8ace424f8af 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -58,6 +58,10 @@ FactoryGirl.define do
end
end
+ trait :readme do
+ project_view :readme
+ end
+
factory :omniauth_user do
transient do
extern_uid '123456'
diff --git a/spec/features/groups/members/manage_members.rb b/spec/features/groups/members/manage_members.rb
index da1e17225db..21f7b4999ad 100644
--- a/spec/features/groups/members/manage_members.rb
+++ b/spec/features/groups/members/manage_members.rb
@@ -38,6 +38,27 @@ feature 'Groups > Members > Manage members' do
end
end
+ scenario 'do not disclose email addresses', :js do
+ group.add_owner(user1)
+ create(:user, email: 'undisclosed_email@gitlab.com', name: "Jane 'invisible' Doe")
+
+ visit group_group_members_path(group)
+
+ find('.select2-container').click
+ select_input = find('.select2-input')
+
+ select_input.send_keys('@gitlab.com')
+ wait_for_requests
+
+ expect(page).to have_content('No matches found')
+
+ select_input.native.clear
+ select_input.send_keys('undisclosed_email@gitlab.com')
+ wait_for_requests
+
+ expect(page).to have_content("Jane 'invisible' Doe")
+ end
+
scenario 'remove user from group', :js do
group.add_owner(user1)
group.add_developer(user2)
diff --git a/spec/features/merge_requests/image_diff_notes.rb b/spec/features/merge_requests/image_diff_notes.rb
index 3c53b51e330..021c4e03428 100644
--- a/spec/features/merge_requests/image_diff_notes.rb
+++ b/spec/features/merge_requests/image_diff_notes.rb
@@ -185,6 +185,18 @@ feature 'image diff notes', :js do
expect(page).to have_content(diff_note.note)
end
end
+
+ describe 'image view modes' do
+ before do
+ visit project_commit_path(project, '2f63565e7aac07bcdadb654e253078b727143ec4')
+ end
+
+ it 'resizes image in onion skin view mode' do
+ find('.view-modes-menu .onion-skin').click
+
+ expect(find('.onion-skin-frame')['style']).to match('width: 228px; height: 240px;')
+ end
+ end
end
def create_image_diff_note
diff --git a/spec/fixtures/emails/valid_new_merge_request.eml b/spec/fixtures/emails/valid_new_merge_request.eml
index 480675a6d7e..729df674604 100644
--- a/spec/fixtures/emails/valid_new_merge_request.eml
+++ b/spec/fixtures/emails/valid_new_merge_request.eml
@@ -16,3 +16,5 @@ X-Sieve: CMU Sieve 2.2
X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
13 Jun 2013 14:03:48 -0700 (PDT)
X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
+
+Merge request description
diff --git a/spec/fixtures/emails/valid_new_merge_request_no_description.eml b/spec/fixtures/emails/valid_new_merge_request_no_description.eml
new file mode 100644
index 00000000000..480675a6d7e
--- /dev/null
+++ b/spec/fixtures/emails/valid_new_merge_request_no_description.eml
@@ -0,0 +1,18 @@
+Return-Path: <jake@adventuretime.ooo>
+Received: from iceking.adventuretime.ooo ([unix socket]) by iceking (Cyrus v2.2.13-Debian-2.2.13-19+squeeze3) with LMTPA; Thu, 13 Jun 2013 17:03:50 -0400
+Received: from mail-ie0-x234.google.com (mail-ie0-x234.google.com [IPv6:2607:f8b0:4001:c03::234]) by iceking.adventuretime.ooo (8.14.3/8.14.3/Debian-9.4) with ESMTP id r5DL3nFJ016967 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=NOT) for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 17:03:50 -0400
+Received: by mail-ie0-f180.google.com with SMTP id f4so21977375iea.25 for <incoming+gitlabhq/gitlabhq@appmail.adventuretime.ooo>; Thu, 13 Jun 2013 14:03:48 -0700
+Received: by 10.0.0.1 with HTTP; Thu, 13 Jun 2013 14:03:48 -0700
+Date: Thu, 13 Jun 2013 17:03:48 -0400
+From: Jake the Dog <jake@adventuretime.ooo>
+To: incoming+gitlabhq/gitlabhq+merge-request+auth_token@appmail.adventuretime.ooo
+Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com>
+Subject: feature
+Mime-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+X-Sieve: CMU Sieve 2.2
+X-Received: by 10.0.0.1 with SMTP id n7mr11234144ipb.85.1371157428600; Thu,
+ 13 Jun 2013 14:03:48 -0700 (PDT)
+X-Scanned-By: MIMEDefang 2.69 on IPv6:2001:470:1d:165::1
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 8b8080563d3..749aa25e632 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -77,15 +77,6 @@ describe PreferencesHelper do
end
end
- def stub_user(messages = {})
- if messages.empty?
- allow(helper).to receive(:current_user).and_return(nil)
- else
- allow(helper).to receive(:current_user)
- .and_return(double('user', messages))
- end
- end
-
describe '#default_project_view' do
context 'user not signed in' do
before do
@@ -125,5 +116,70 @@ describe PreferencesHelper do
end
end
end
+
+ context 'user signed in' do
+ let(:user) { create(:user, :readme) }
+ let(:project) { create(:project, :public, :repository) }
+
+ before do
+ helper.instance_variable_set(:@project, project)
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ context 'when the user is allowed to see the code' do
+ it 'returns the project view' do
+ allow(helper).to receive(:can?).with(user, :download_code, project).and_return(true)
+
+ expect(helper.default_project_view).to eq('readme')
+ end
+ end
+
+ context 'with wikis enabled and the right policy for the user' do
+ before do
+ project.project_feature.update_attribute(:issues_access_level, 0)
+ allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
+ end
+
+ it 'returns wiki if the user has the right policy' do
+ allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(true)
+
+ expect(helper.default_project_view).to eq('wiki')
+ end
+
+ it 'returns customize_workflow if the user does not have the right policy' do
+ allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(helper.default_project_view).to eq('customize_workflow')
+ end
+ end
+
+ context 'with issues as a feature available' do
+ it 'return issues' do
+ allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(helper.default_project_view).to eq('projects/issues/issues')
+ end
+ end
+
+ context 'with no activity, no wikies and no issues' do
+ it 'returns customize_workflow as default' do
+ project.project_feature.update_attribute(:issues_access_level, 0)
+ allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(helper.default_project_view).to eq('customize_workflow')
+ end
+ end
+ end
+ end
+
+ def stub_user(messages = {})
+ if messages.empty?
+ allow(helper).to receive(:current_user).and_return(nil)
+ else
+ allow(helper).to receive(:current_user)
+ .and_return(double('user', messages))
+ end
end
end
diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js
index 4f20e31f511..a3fa07d5bc2 100644
--- a/spec/javascripts/fly_out_nav_spec.js
+++ b/spec/javascripts/fly_out_nav_spec.js
@@ -253,7 +253,7 @@ describe('Fly out sidebar navigation', () => {
it('shows collapsed only sub-items if icon only sidebar', () => {
const subItems = el.querySelector('.sidebar-sub-level-items');
const sidebar = document.createElement('div');
- sidebar.classList.add('sidebar-icons-only');
+ sidebar.classList.add('sidebar-collapsed-desktop');
subItems.classList.add('is-fly-out-only');
setSidebar(sidebar);
@@ -343,7 +343,7 @@ describe('Fly out sidebar navigation', () => {
it('returns true when active & collapsed sidebar', () => {
const sidebar = document.createElement('div');
- sidebar.classList.add('sidebar-icons-only');
+ sidebar.classList.add('sidebar-collapsed-desktop');
el.classList.add('active');
setSidebar(sidebar);
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index b47a8bf705f..53b8a368d28 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -4,6 +4,7 @@ import '~/render_gfm';
import issuableApp from '~/issue_show/components/app.vue';
import eventHub from '~/issue_show/event_hub';
import issueShowData from '../mock_data';
+import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
function formatText(text) {
return text.trim().replace(/\s\s+/g, ' ');
@@ -55,6 +56,8 @@ describe('Issuable output', () => {
Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
vm.poll.stop();
+
+ vm.$destroy();
});
it('should render a title/description/edited and update title/description/edited on update', (done) => {
@@ -268,6 +271,52 @@ describe('Issuable output', () => {
});
});
+ it('opens recaptcha dialog 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
+
+ recaptchaChild.scriptSrc = '//scriptsrc';
+ }
+
+ let modal;
+ const promise = new Promise((resolve) => {
+ resolve({
+ json() {
+ return {
+ recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
+ };
+ },
+ });
+ });
+
+ spyOn(vm.service, 'updateIssuable').and.returnValue(promise);
+
+ vm.canUpdate = true;
+ vm.showForm = true;
+
+ vm.$nextTick()
+ .then(() => mockScriptSrc())
+ .then(() => vm.updateIssuable())
+ .then(promise)
+ .then(() => setTimeoutPromise())
+ .then(() => {
+ modal = vm.$el.querySelector('.js-recaptcha-dialog');
+
+ expect(modal.style.display).not.toEqual('none');
+ expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
+ expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc');
+ })
+ .then(() => modal.querySelector('.close').click())
+ .then(() => vm.$nextTick())
+ .then(() => {
+ expect(modal.style.display).toEqual('none');
+ expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
describe('deleteIssuable', () => {
it('changes URL when deleted', (done) => {
spyOn(gl.utils, 'visitUrl');
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js
index 163e5cdd062..2e000a1063f 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/javascripts/issue_show/components/description_spec.js
@@ -51,6 +51,35 @@ 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
+
+ recaptchaChild.scriptSrc = '//scriptsrc';
+
+ vm.taskListUpdateSuccess({
+ recaptcha_html: '<div class="g-recaptcha">recaptcha_html</div>',
+ });
+
+ vm.$nextTick()
+ .then(() => {
+ modal = vm.$el.querySelector('.js-recaptcha-dialog');
+
+ expect(modal.style.display).not.toEqual('none');
+ expect(modal.querySelector('.g-recaptcha').textContent).toEqual('recaptcha_html');
+ expect(document.body.querySelector('.js-recaptcha-script').src).toMatch('//scriptsrc');
+ })
+ .then(() => modal.querySelector('.close').click())
+ .then(() => vm.$nextTick())
+ .then(() => {
+ expect(modal.style.display).toEqual('none');
+ expect(document.body.querySelector('.js-recaptcha-script')).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
describe('TaskList', () => {
beforeEach(() => {
vm = mountComponent(DescriptionComponent, Object.assign({}, props, {
@@ -86,6 +115,7 @@ describe('Description component', () => {
dataType: 'issuableType',
fieldName: 'description',
selector: '.detail-page-description',
+ onSuccess: jasmine.any(Function),
});
done();
});
diff --git a/spec/javascripts/monitoring/graph/deployment_spec.js b/spec/javascripts/monitoring/graph/deployment_spec.js
index dea42d755d4..bf6ada8185e 100644
--- a/spec/javascripts/monitoring/graph/deployment_spec.js
+++ b/spec/javascripts/monitoring/graph/deployment_spec.js
@@ -118,7 +118,7 @@ describe('MonitoringDeployment', () => {
).not.toEqual('display: none;');
});
- it('shows the refText inside a text element with the deploy-info-text class', () => {
+ it('contains date, refs and the "deployed" text', () => {
reducedDeploymentData[0].showDeploymentFlag = true;
const component = createComponent({
showDeployInfo: true,
@@ -129,8 +129,31 @@ describe('MonitoringDeployment', () => {
});
expect(
- component.$el.querySelector('.deploy-info-text').firstChild.nodeValue.trim(),
- ).toEqual(component.refText(reducedDeploymentData[0]));
+ component.$el.querySelectorAll('.deploy-info-text'),
+ ).toContainText('Deployed');
+
+ expect(
+ component.$el.querySelectorAll('.deploy-info-text'),
+ ).toContainText('Wed, May 31');
+
+ expect(
+ component.$el.querySelectorAll('.deploy-info-text'),
+ ).toContainText(component.refText(reducedDeploymentData[0]));
+ });
+
+ it('contains a link to the commit contents', () => {
+ reducedDeploymentData[0].showDeploymentFlag = true;
+ const component = createComponent({
+ showDeployInfo: true,
+ deploymentData: reducedDeploymentData,
+ graphHeight: 300,
+ graphWidth: 440,
+ graphHeightOffset: 120,
+ });
+
+ expect(
+ component.$el.querySelectorAll('.deploy-info-text-link')[0].parentElement.getAttribute('xlink:href'),
+ ).not.toEqual('');
});
it('should contain a hidden gradient', () => {
diff --git a/spec/javascripts/monitoring/graph_spec.js b/spec/javascripts/monitoring/graph_spec.js
index fd79abe241a..b1d69752bad 100644
--- a/spec/javascripts/monitoring/graph_spec.js
+++ b/spec/javascripts/monitoring/graph_spec.js
@@ -4,6 +4,8 @@ import MonitoringMixins from '~/monitoring/mixins/monitoring_mixins';
import eventHub from '~/monitoring/event_hub';
import { deploymentData, convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from './mock_data';
+const tagsPath = 'http://test.host/frontend-fixtures/environments-project/tags';
+const projectPath = 'http://test.host/frontend-fixtures/environments-project';
const createComponent = (propsData) => {
const Component = Vue.extend(Graph);
@@ -25,6 +27,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
expect(component.$el.querySelector('.text-center').innerText.trim()).toBe(component.graphData.title);
@@ -37,6 +41,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
const transformedHeight = `${component.graphHeight - 100}`;
@@ -50,6 +56,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
const viewBoxArray = component.outerViewBox.split(' ');
@@ -65,6 +73,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
spyOn(eventHub, '$emit');
@@ -81,6 +91,8 @@ describe('Graph', () => {
classType: 'col-md-6',
updateAspectRatio: false,
deploymentData,
+ tagsPath,
+ projectPath,
});
expect(component.yAxisLabel).toEqual(component.graphData.y_label);
@@ -98,6 +110,8 @@ describe('Graph', () => {
hoveredDate: new Date('Sun Aug 27 2017 06:11:51 GMT-0500 (CDT)'),
currentDeployXPos: null,
},
+ tagsPath,
+ projectPath,
});
component.positionFlag();
diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js
index 6b34855b8b2..1f4e858e731 100644
--- a/spec/javascripts/monitoring/mock_data.js
+++ b/spec/javascripts/monitoring/mock_data.js
@@ -2430,33 +2430,39 @@ export const deploymentData = [
id: 111,
iid: 3,
sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
+ commitUrl: 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
ref: {
name: 'master'
},
created_at: '2017-05-31T21:23:37.881Z',
tag: false,
+ tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false',
'last?': true
},
{
id: 110,
iid: 2,
sha: 'f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
+ commitUrl: 'http://test.host/frontend-fixtures/environments-project/commit/f5bcd1d9dac6fa4137e2510b9ccd134ef2e84187',
ref: {
name: 'master'
},
created_at: '2017-05-30T20:08:04.629Z',
tag: false,
+ tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false',
'last?': false
},
{
id: 109,
iid: 1,
sha: '6511e58faafaa7ad2228990ec57f19d66f7db7c2',
+ commitUrl: 'http://test.host/frontend-fixtures/environments-project/commit/6511e58faafaa7ad2228990ec57f19d66f7db7c2',
ref: {
name: 'update2-readme'
},
created_at: '2017-05-30T17:42:38.409Z',
tag: false,
+ tagUrl: 'http://test.host/frontend-fixtures/environments-project/tags/false',
'last?': false
}
];
diff --git a/spec/javascripts/notes/components/issue_note_spec.js b/spec/javascripts/notes/components/issue_note_spec.js
index 73fd188dbe5..bd888b2cbae 100644
--- a/spec/javascripts/notes/components/issue_note_spec.js
+++ b/spec/javascripts/notes/components/issue_note_spec.js
@@ -41,4 +41,19 @@ describe('issue_note', () => {
it('should render issue body', () => {
expect(vm.$el.querySelector('.note-text').innerHTML).toEqual(note.note_html);
});
+
+ it('prevents note preview xss', (done) => {
+ const imgSrc = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
+ const noteBody = `<img src="${imgSrc}" onload="alert(1)" />`;
+ const alertSpy = spyOn(window, 'alert');
+ vm.updateNote = () => new Promise($.noop);
+
+ vm.formUpdateHandler(noteBody, null, $.noop);
+
+ setTimeout(() => {
+ expect(alertSpy).not.toHaveBeenCalled();
+ expect(vm.note.note_html).toEqual(_.escape(noteBody));
+ done();
+ }, 0);
+ });
});
diff --git a/spec/javascripts/sidebar/sidebar_assignees_spec.js b/spec/javascripts/sidebar/sidebar_assignees_spec.js
index 929ba75e67d..b97e24d9dcf 100644
--- a/spec/javascripts/sidebar/sidebar_assignees_spec.js
+++ b/spec/javascripts/sidebar/sidebar_assignees_spec.js
@@ -4,20 +4,29 @@ import SidebarMediator from '~/sidebar/sidebar_mediator';
import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarStore from '~/sidebar/stores/sidebar_store';
import Mock from './mock_data';
+import mountComponent from '../helpers/vue_mount_component_helper';
describe('sidebar assignees', () => {
- let component;
- let SidebarAssigneeComponent;
+ let vm;
+ let mediator;
+ let sidebarAssigneesEl;
preloadFixtures('issues/open-issue.html.raw');
beforeEach(() => {
Vue.http.interceptors.push(Mock.sidebarMockInterceptor);
- SidebarAssigneeComponent = Vue.extend(SidebarAssignees);
- spyOn(SidebarMediator.prototype, 'saveAssignees').and.callThrough();
- spyOn(SidebarMediator.prototype, 'assignYourself').and.callThrough();
- this.mediator = new SidebarMediator(Mock.mediator);
+
loadFixtures('issues/open-issue.html.raw');
- this.sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees');
+
+ mediator = new SidebarMediator(Mock.mediator);
+ spyOn(mediator, 'saveAssignees').and.callThrough();
+ spyOn(mediator, 'assignYourself').and.callThrough();
+
+ const SidebarAssigneeComponent = Vue.extend(SidebarAssignees);
+ sidebarAssigneesEl = document.querySelector('#js-vue-sidebar-assignees');
+ vm = mountComponent(SidebarAssigneeComponent, {
+ mediator,
+ field: sidebarAssigneesEl.dataset.field,
+ }, sidebarAssigneesEl);
});
afterEach(() => {
@@ -28,30 +37,24 @@ describe('sidebar assignees', () => {
});
it('calls the mediator when saves the assignees', () => {
- component = new SidebarAssigneeComponent()
- .$mount(this.sidebarAssigneesEl);
- component.saveAssignees();
-
- expect(SidebarMediator.prototype.saveAssignees).toHaveBeenCalled();
+ vm.saveAssignees();
+ expect(mediator.saveAssignees).toHaveBeenCalled();
});
it('calls the mediator when "assignSelf" method is called', () => {
- component = new SidebarAssigneeComponent()
- .$mount(this.sidebarAssigneesEl);
- component.assignSelf();
+ vm.assignSelf();
- expect(SidebarMediator.prototype.assignYourself).toHaveBeenCalled();
- expect(this.mediator.store.assignees.length).toEqual(1);
+ expect(mediator.assignYourself).toHaveBeenCalled();
+ expect(mediator.store.assignees.length).toEqual(1);
});
it('hides assignees until fetched', (done) => {
- component = new SidebarAssigneeComponent().$mount(this.sidebarAssigneesEl);
- const currentAssignee = this.sidebarAssigneesEl.querySelector('.value');
+ const currentAssignee = sidebarAssigneesEl.querySelector('.value');
expect(currentAssignee).toBe(null);
- component.store.isFetching.assignees = false;
+ vm.store.isFetching.assignees = false;
Vue.nextTick(() => {
- expect(component.$el.querySelector('.value')).toBeVisible();
+ expect(vm.$el.querySelector('.value')).toBeVisible();
done();
});
});
diff --git a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
index 7adf22b0f1f..a6113cb0bae 100644
--- a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
+++ b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
@@ -26,11 +26,14 @@ describe('Sidebar Subscriptions', function () {
});
it('calls the mediator toggleSubscription on event', () => {
- spyOn(SidebarMediator.prototype, 'toggleSubscription').and.returnValue(Promise.resolve());
- vm = mountComponent(SidebarSubscriptions, {});
+ const mediator = new SidebarMediator();
+ spyOn(mediator, 'toggleSubscription').and.returnValue(Promise.resolve());
+ vm = mountComponent(SidebarSubscriptions, {
+ mediator,
+ });
eventHub.$emit('toggleSubscription');
- expect(SidebarMediator.prototype.toggleSubscription).toHaveBeenCalled();
+ expect(mediator.toggleSubscription).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/vue_shared/components/popup_dialog_spec.js b/spec/javascripts/vue_shared/components/popup_dialog_spec.js
new file mode 100644
index 00000000000..5c1d2a196f4
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/popup_dialog_spec.js
@@ -0,0 +1,12 @@
+import Vue from 'vue';
+import PopupDialog from '~/vue_shared/components/popup_dialog.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('PopupDialog', () => {
+ it('does not render a primary button if no primaryButtonLabel', () => {
+ const popupDialog = Vue.extend(PopupDialog);
+ const vm = mountComponent(popupDialog);
+
+ expect(vm.$el.querySelector('.js-primary-button')).toBeNull();
+ });
+});
diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
index b80df6956b0..be45c00dfe6 100644
--- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
@@ -182,13 +182,22 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do
end
context 'for a pre-Markdown Note attachment file path' do
- class Note < ActiveRecord::Base
- has_many :uploads, as: :model, dependent: :destroy
+ let(:model) { create(:note, :with_attachment) }
+ let!(:expected_upload_attrs) { Upload.where(model_type: 'Note', model_id: model.id).first.attributes.slice('path', 'uploader', 'size', 'checksum') }
+ let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) }
+
+ before do
+ Upload.where(model_type: 'Note', model_id: model.id).delete_all
end
- let(:model) { create(:note, :with_attachment) }
+ # Can't use the shared example because Note doesn't have an `uploads` association
+ it 'creates an Upload record' do
+ expect do
+ subject.perform(1, untracked_files_for_uploads.last.id)
+ end.to change { Upload.where(model_type: 'Note', model_id: model.id).count }.from(0).to(1)
- it_behaves_like 'non_markdown_file'
+ expect(Upload.where(model_type: 'Note', model_id: model.id).first.attributes).to include(expected_upload_attrs)
+ end
end
context 'for a user avatar file path' do
diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
index 7f3bf5fc41c..8a83e446935 100644
--- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
@@ -132,6 +132,23 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
expect(File).to exist(File.join(project.repository_storage_path, project.disk_path + '.git'))
end
+
+ it 'moves an existing project to the correct path' do
+ # This is a quick way to get a valid repository instead of copying an
+ # existing one. Since it's not persisted, the importer will try to
+ # create the project.
+ project = build(:project, :repository)
+ original_commit_count = project.repository.commit_count
+
+ bare_repo = Gitlab::BareRepositoryImport::Repository.new(project.repository_storage_path, project.repository.path)
+ gitlab_importer = described_class.new(admin, bare_repo)
+
+ expect(gitlab_importer).to receive(:create_project).and_call_original
+
+ new_project = gitlab_importer.create_project_if_needed
+
+ expect(new_project.repository.commit_count).to eq(original_commit_count)
+ end
end
context 'with Wiki' do
diff --git a/spec/lib/gitlab/bare_repository_import/repository_spec.rb b/spec/lib/gitlab/bare_repository_import/repository_spec.rb
index 2db737f5fb6..61b73abcba4 100644
--- a/spec/lib/gitlab/bare_repository_import/repository_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/repository_spec.rb
@@ -46,6 +46,13 @@ describe ::Gitlab::BareRepositoryImport::Repository do
describe '#project_full_path' do
it 'returns the project full path' do
expect(project_repo_path.repo_path).to eq('/full/path/to/repo.git')
+ expect(project_repo_path.project_full_path).to eq('to/repo')
+ end
+
+ it 'with no trailing slash in the root path' do
+ repo_path = described_class.new('/full/path', '/full/path/to/repo.git')
+
+ expect(repo_path.project_full_path).to eq('to/repo')
end
end
end
diff --git a/spec/lib/gitlab/checks/project_moved_spec.rb b/spec/lib/gitlab/checks/project_moved_spec.rb
new file mode 100644
index 00000000000..fa1575e2177
--- /dev/null
+++ b/spec/lib/gitlab/checks/project_moved_spec.rb
@@ -0,0 +1,81 @@
+require 'rails_helper'
+
+describe Gitlab::Checks::ProjectMoved, :clean_gitlab_redis_shared_state do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ describe '.fetch_redirct_message' do
+ context 'with a redirect message queue' do
+ it 'should return the redirect message' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ project_moved.add_redirect_message
+
+ expect(described_class.fetch_redirect_message(user.id, project.id)).to eq(project_moved.redirect_message)
+ end
+
+ it 'should delete the redirect message from redis' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ project_moved.add_redirect_message
+
+ expect(Gitlab::Redis::SharedState.with { |redis| redis.get("redirect_namespace:#{user.id}:#{project.id}") }).not_to be_nil
+ described_class.fetch_redirect_message(user.id, project.id)
+ expect(Gitlab::Redis::SharedState.with { |redis| redis.get("redirect_namespace:#{user.id}:#{project.id}") }).to be_nil
+ end
+ end
+
+ context 'with no redirect message queue' do
+ it 'should return nil' do
+ expect(described_class.fetch_redirect_message(1, 2)).to be_nil
+ end
+ end
+ end
+
+ describe '#add_redirect_message' do
+ it 'should queue a redirect message' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ expect(project_moved.add_redirect_message).to eq("OK")
+ end
+ end
+
+ describe '#redirect_message' do
+ context 'when the push is rejected' do
+ it 'should return a redirect message telling the user to try again' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ message = "Project 'foo/bar' was moved to '#{project.full_path}'." +
+ "\n\nPlease update your Git remote:" +
+ "\n\n git remote set-url origin #{project.http_url_to_repo} and try again.\n"
+
+ expect(project_moved.redirect_message(rejected: true)).to eq(message)
+ end
+ end
+
+ context 'when the push is not rejected' do
+ it 'should return a redirect message' do
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ message = "Project 'foo/bar' was moved to '#{project.full_path}'." +
+ "\n\nPlease update your Git remote:" +
+ "\n\n git remote set-url origin #{project.http_url_to_repo}\n"
+
+ expect(project_moved.redirect_message).to eq(message)
+ end
+ end
+ end
+
+ describe '#permanent_redirect?' do
+ context 'with a permanent RedirectRoute' do
+ it 'should return true' do
+ project.route.create_redirect('foo/bar', permanent: true)
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ expect(project_moved.permanent_redirect?).to be_truthy
+ end
+ end
+
+ context 'without a permanent RedirectRoute' do
+ it 'should return false' do
+ project.route.create_redirect('foo/bar')
+ project_moved = described_class.new(project, user, 'foo/bar', 'http')
+ expect(project_moved.permanent_redirect?).to be_falsy
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
index 0f1d72080c5..3ae7053a995 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
@@ -6,46 +6,81 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
let(:pipeline) { Ci::Pipeline.new }
let(:command) do
- double('command', source: :push,
- origin_ref: 'master',
- checkout_sha: project.commit.id,
- after_sha: nil,
- before_sha: nil,
- trigger_request: nil,
- schedule: nil,
- project: project,
- current_user: user)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ source: :push,
+ origin_ref: 'master',
+ checkout_sha: project.commit.id,
+ after_sha: nil,
+ before_sha: nil,
+ trigger_request: nil,
+ schedule: nil,
+ project: project,
+ current_user: user)
end
let(:step) { described_class.new(pipeline, command) }
before do
stub_repository_ci_yaml_file(sha: anything)
-
- step.perform!
end
it 'never breaks the chain' do
+ step.perform!
+
expect(step.break?).to be false
end
it 'fills pipeline object with data' do
+ step.perform!
+
expect(pipeline.sha).not_to be_empty
expect(pipeline.sha).to eq project.commit.id
expect(pipeline.ref).to eq 'master'
+ expect(pipeline.tag).to be false
expect(pipeline.user).to eq user
expect(pipeline.project).to eq project
end
it 'sets a valid config source' do
+ step.perform!
+
expect(pipeline.repository_source?).to be true
end
it 'returns a valid pipeline' do
+ step.perform!
+
expect(pipeline).to be_valid
end
it 'does not persist a pipeline' do
+ step.perform!
+
expect(pipeline).not_to be_persisted
end
+
+ context 'when pipeline is running for a tag' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ source: :push,
+ origin_ref: 'mytag',
+ checkout_sha: project.commit.id,
+ after_sha: nil,
+ before_sha: nil,
+ trigger_request: nil,
+ schedule: nil,
+ project: project,
+ current_user: user)
+ end
+
+ before do
+ allow_any_instance_of(Repository).to receive(:tag_exists?).with('mytag').and_return(true)
+
+ step.perform!
+ end
+
+ it 'correctly indicated that this is a tagged pipeline' do
+ expect(pipeline).to be_tag
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
new file mode 100644
index 00000000000..75a177d2d1f
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
@@ -0,0 +1,185 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::Command do
+ set(:project) { create(:project, :repository) }
+
+ describe '#initialize' do
+ subject do
+ described_class.new(origin_ref: 'master')
+ end
+
+ it 'properly initialises object from hash' do
+ expect(subject.origin_ref).to eq('master')
+ end
+ end
+
+ context 'handling of origin_ref' do
+ let(:command) { described_class.new(project: project, origin_ref: origin_ref) }
+
+ describe '#branch_exists?' do
+ subject { command.branch_exists? }
+
+ context 'for existing branch' do
+ let(:origin_ref) { 'master' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'for invalid branch' do
+ let(:origin_ref) { 'something' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#tag_exists?' do
+ subject { command.tag_exists? }
+
+ context 'for existing ref' do
+ let(:origin_ref) { 'v1.0.0' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'for invalid ref' do
+ let(:origin_ref) { 'something' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#ref' do
+ subject { command.ref }
+
+ context 'for regular ref' do
+ let(:origin_ref) { 'master' }
+
+ it { is_expected.to eq('master') }
+ end
+
+ context 'for branch ref' do
+ let(:origin_ref) { 'refs/heads/master' }
+
+ it { is_expected.to eq('master') }
+ end
+
+ context 'for tag ref' do
+ let(:origin_ref) { 'refs/tags/1.0.0' }
+
+ it { is_expected.to eq('1.0.0') }
+ end
+
+ context 'for other refs' do
+ let(:origin_ref) { 'refs/merge-requests/11/head' }
+
+ it { is_expected.to eq('refs/merge-requests/11/head') }
+ end
+ end
+ end
+
+ describe '#sha' do
+ subject { command.sha }
+
+ context 'when invalid checkout_sha is specified' do
+ let(:command) { described_class.new(project: project, checkout_sha: 'aaa') }
+
+ it 'returns empty value' do
+ is_expected.to be_nil
+ end
+ end
+
+ context 'when a valid checkout_sha is specified' do
+ let(:command) { described_class.new(project: project, checkout_sha: project.commit.id) }
+
+ it 'returns checkout_sha' do
+ is_expected.to eq(project.commit.id)
+ end
+ end
+
+ context 'when a valid after_sha is specified' do
+ let(:command) { described_class.new(project: project, after_sha: project.commit.id) }
+
+ it 'returns after_sha' do
+ is_expected.to eq(project.commit.id)
+ end
+ end
+
+ context 'when a valid origin_ref is specified' do
+ let(:command) { described_class.new(project: project, origin_ref: 'HEAD') }
+
+ it 'returns SHA for given ref' do
+ is_expected.to eq(project.commit.id)
+ end
+ end
+ end
+
+ describe '#origin_sha' do
+ subject { command.origin_sha }
+
+ context 'when using checkout_sha and after_sha' do
+ let(:command) { described_class.new(project: project, checkout_sha: 'aaa', after_sha: 'bbb') }
+
+ it 'uses checkout_sha' do
+ is_expected.to eq('aaa')
+ end
+ end
+
+ context 'when using after_sha only' do
+ let(:command) { described_class.new(project: project, after_sha: 'bbb') }
+
+ it 'uses after_sha' do
+ is_expected.to eq('bbb')
+ end
+ end
+ end
+
+ describe '#before_sha' do
+ subject { command.before_sha }
+
+ context 'when using checkout_sha and before_sha' do
+ let(:command) { described_class.new(project: project, checkout_sha: 'aaa', before_sha: 'bbb') }
+
+ it 'uses before_sha' do
+ is_expected.to eq('bbb')
+ end
+ end
+
+ context 'when using checkout_sha only' do
+ let(:command) { described_class.new(project: project, checkout_sha: 'aaa') }
+
+ it 'uses checkout_sha' do
+ is_expected.to eq('aaa')
+ end
+ end
+
+ context 'when checkout_sha and before_sha are empty' do
+ let(:command) { described_class.new(project: project) }
+
+ it 'uses BLANK_SHA' do
+ is_expected.to eq(Gitlab::Git::BLANK_SHA)
+ end
+ end
+ end
+
+ describe '#protected_ref?' do
+ let(:command) { described_class.new(project: project, origin_ref: 'my-branch') }
+
+ subject { command.protected_ref? }
+
+ context 'when a ref is protected' do
+ before do
+ expect_any_instance_of(Project).to receive(:protected_for?).with('my-branch').and_return(true)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when a ref is unprotected' do
+ before do
+ expect_any_instance_of(Project).to receive(:protected_for?).with('my-branch').and_return(false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
index f54e2326b06..1b03227d67b 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
@@ -10,9 +10,9 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
end
let(:command) do
- double('command', project: project,
- current_user: user,
- seeds_block: nil)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user, seeds_block: nil)
end
let(:step) { described_class.new(pipeline, command) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
index e165e0fac2a..eca23694a2b 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::Ci::Pipeline::Chain::Sequence do
set(:user) { create(:user) }
let(:pipeline) { build_stubbed(:ci_pipeline) }
- let(:command) { double('command' ) }
+ let(:command) { Gitlab::Ci::Pipeline::Chain::Command.new }
let(:first_step) { spy('first step') }
let(:second_step) { spy('second step') }
let(:sequence) { [first_step, second_step] }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb
index 32bd5de829b..dc13cae961c 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/skip_spec.rb
@@ -6,10 +6,11 @@ describe Gitlab::Ci::Pipeline::Chain::Skip do
set(:pipeline) { create(:ci_pipeline, project: project) }
let(:command) do
- double('command', project: project,
- current_user: user,
- ignore_skip_ci: false,
- save_incompleted: true)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user,
+ ignore_skip_ci: false,
+ save_incompleted: true)
end
let(:step) { described_class.new(pipeline, command) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
index 0bbdd23f4d6..a973ccda8de 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/abilities_spec.rb
@@ -5,11 +5,12 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Abilities do
set(:user) { create(:user) }
let(:pipeline) do
- build_stubbed(:ci_pipeline, ref: ref, project: project)
+ build_stubbed(:ci_pipeline, project: project)
end
let(:command) do
- double('command', project: project, current_user: user)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, origin_ref: ref)
end
let(:step) { described_class.new(pipeline, command) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
index 8357af38f92..5c12c6e6392 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
@@ -5,9 +5,10 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Config do
set(:user) { create(:user) }
let(:command) do
- double('command', project: project,
- current_user: user,
- save_incompleted: true)
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user,
+ save_incompleted: true)
end
let!(:step) { described_class.new(pipeline, command) }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
index bb356efe9ad..fb1b53fc55c 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb
@@ -3,10 +3,7 @@ require 'spec_helper'
describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
set(:project) { create(:project, :repository) }
set(:user) { create(:user) }
-
- let(:command) do
- double('command', project: project, current_user: user)
- end
+ let(:pipeline) { build_stubbed(:ci_pipeline) }
let!(:step) { described_class.new(pipeline, command) }
@@ -14,9 +11,10 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
step.perform!
end
- context 'when pipeline ref and sha exists' do
- let(:pipeline) do
- build_stubbed(:ci_pipeline, ref: 'master', sha: '123', project: project)
+ context 'when ref and sha exists' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, origin_ref: 'master', checkout_sha: project.commit.id)
end
it 'does not break the chain' do
@@ -28,9 +26,10 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
end
end
- context 'when pipeline ref does not exist' do
- let(:pipeline) do
- build_stubbed(:ci_pipeline, ref: 'something', project: project)
+ context 'when ref does not exist' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, origin_ref: 'something')
end
it 'breaks the chain' do
@@ -43,9 +42,10 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Repository do
end
end
- context 'when pipeline does not have SHA set' do
- let(:pipeline) do
- build_stubbed(:ci_pipeline, ref: 'master', sha: nil, project: project)
+ context 'when does not have existing SHA set' do
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project, current_user: user, origin_ref: 'master', checkout_sha: 'something')
end
it 'breaks the chain' do
diff --git a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
index 51ce3116880..dc1a93367a4 100644
--- a/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb
@@ -49,6 +49,7 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
expect(merge_request.author).to eq(user)
expect(merge_request.source_branch).to eq('feature')
expect(merge_request.title).to eq('Feature added')
+ expect(merge_request.description).to eq('Merge request description')
expect(merge_request.target_branch).to eq(project.default_branch)
end
end
@@ -79,6 +80,17 @@ describe Gitlab::Email::Handler::CreateMergeRequestHandler do
expect { receiver.execute }.to raise_error(Gitlab::Email::InvalidMergeRequestError)
end
end
+
+ context "when the message body is blank" do
+ let(:email_raw) { fixture_file("emails/valid_new_merge_request_no_description.eml") }
+
+ it "creates a new merge request with description set from the last commit" do
+ expect { receiver.execute }.to change { project.merge_requests.count }.by(1)
+ merge_request = project.merge_requests.last
+
+ expect(merge_request.description).to eq('Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>')
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index f19b65a5f71..e6845420f7d 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -588,12 +588,12 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#fetch_as_mirror_without_shell' do
+ describe '#fetch_repository_as_mirror' do
let(:new_repository) do
Gitlab::Git::Repository.new('default', 'my_project.git', '')
end
- subject { new_repository.fetch_as_mirror_without_shell(repository.path) }
+ subject { new_repository.fetch_repository_as_mirror(repository) }
before do
Gitlab::Shell.new.add_repository('default', 'my_project')
@@ -603,7 +603,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
Gitlab::Shell.new.remove_repository(TestEnv.repos_path, 'my_project')
end
- it 'fetches a url as a mirror remote' do
+ it 'fetches a repository as a mirror remote' do
subject
expect(refs(new_repository.path)).to eq(refs(repository.path))
@@ -1662,21 +1662,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe '#fetch_remote_without_shell' do
- let(:git_path) { Gitlab.config.git.bin_path }
- let(:remote_name) { 'my_remote' }
-
- subject { repository.fetch_remote_without_shell(remote_name) }
-
- it 'fetches the remote and returns true if the command was successful' do
- expect(repository).to receive(:popen)
- .with(%W(#{git_path} fetch #{remote_name}), repository.path, {})
- .and_return(['', 0])
-
- expect(subject).to be(true)
- end
- end
-
describe '#merge' do
let(:repository) do
Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index c9643c5da47..2db560c2cec 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -193,7 +193,15 @@ describe Gitlab::GitAccess do
let(:actor) { build(:rsa_deploy_key_2048, user: user) }
end
- describe '#check_project_moved!' do
+ shared_examples 'check_project_moved' do
+ it 'enqueues a redirected message' do
+ push_access_check
+
+ expect(Gitlab::Checks::ProjectMoved.fetch_redirect_message(user.id, project.id)).not_to be_nil
+ end
+ end
+
+ describe '#check_project_moved!', :clean_gitlab_redis_shared_state do
before do
project.add_master(user)
end
@@ -207,7 +215,40 @@ describe Gitlab::GitAccess do
end
end
- context 'when a redirect was followed to find the project' do
+ context 'when a permanent redirect and ssh protocol' do
+ let(:redirected_path) { 'some/other-path' }
+
+ before do
+ allow_any_instance_of(Gitlab::Checks::ProjectMoved).to receive(:permanent_redirect?).and_return(true)
+ end
+
+ it 'allows push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ end
+ end
+
+ it_behaves_like 'check_project_moved'
+ end
+
+ context 'with a permanent redirect and http protocol' do
+ let(:redirected_path) { 'some/other-path' }
+ let(:protocol) { 'http' }
+
+ before do
+ allow_any_instance_of(Gitlab::Checks::ProjectMoved).to receive(:permanent_redirect?).and_return(true)
+ end
+
+ it 'allows_push and pull access' do
+ aggregate_failures do
+ expect { push_access_check }.not_to raise_error
+ end
+ end
+
+ it_behaves_like 'check_project_moved'
+ end
+
+ context 'with a temporal redirect and ssh protocol' do
let(:redirected_path) { 'some/other-path' }
it 'blocks push and pull access' do
@@ -219,16 +260,15 @@ describe Gitlab::GitAccess do
expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.ssh_url_to_repo}/)
end
end
+ end
- context 'http protocol' do
- let(:protocol) { 'http' }
+ context 'with a temporal redirect and http protocol' do
+ let(:redirected_path) { 'some/other-path' }
+ let(:protocol) { 'http' }
- it 'includes the path to the project using HTTP' do
- aggregate_failures do
- expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
- expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
- end
- end
+ it 'does not allow to push and pull access' do
+ expect { push_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
+ expect { pull_access_check }.to raise_error(described_class::ProjectMovedError, /git remote set-url origin #{project.http_url_to_repo}/)
end
end
end
diff --git a/spec/lib/gitlab/identifier_spec.rb b/spec/lib/gitlab/identifier_spec.rb
index cfaeb1f0d4f..0385dd762c2 100644
--- a/spec/lib/gitlab/identifier_spec.rb
+++ b/spec/lib/gitlab/identifier_spec.rb
@@ -70,6 +70,10 @@ describe Gitlab::Identifier do
expect(identifier.identify_using_commit(project, '123')).to eq(user)
end
end
+
+ it 'returns nil if the project & ref are not present' do
+ expect(identifier.identify_using_commit(nil, nil)).to be_nil
+ end
end
describe '#identify_using_user' do
diff --git a/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb
index 667e4747897..f66451c5188 100644
--- a/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/influx_sampler_spec.rb
@@ -21,7 +21,6 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do
it 'samples various statistics' do
expect(sampler).to receive(:sample_memory_usage)
expect(sampler).to receive(:sample_file_descriptors)
- expect(sampler).to receive(:sample_objects)
expect(sampler).to receive(:sample_gc)
expect(sampler).to receive(:flush)
@@ -72,28 +71,6 @@ describe Gitlab::Metrics::Samplers::InfluxSampler do
end
end
- if Gitlab::Metrics.mri?
- describe '#sample_objects' do
- it 'adds a metric containing the amount of allocated objects' do
- expect(sampler).to receive(:add_metric)
- .with(/object_counts/, an_instance_of(Hash), an_instance_of(Hash))
- .at_least(:once)
- .and_call_original
-
- sampler.sample_objects
- end
-
- it 'ignores classes without a name' do
- expect(Allocations).to receive(:to_hash).and_return({ Class.new => 4 })
-
- expect(sampler).not_to receive(:add_metric)
- .with('object_counts', an_instance_of(Hash), type: nil)
-
- sampler.sample_objects
- end
- end
- end
-
describe '#sample_gc' do
it 'adds a metric containing garbage collection statistics' do
expect(GC::Profiler).to receive(:total_time).and_return(0.24)
diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
index 53699327da1..375cbf8a9ca 100644
--- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb
@@ -11,7 +11,6 @@ describe Gitlab::Metrics::Samplers::RubySampler do
it 'samples various statistics' do
expect(Gitlab::Metrics::System).to receive(:memory_usage)
expect(Gitlab::Metrics::System).to receive(:file_descriptor_count)
- expect(sampler).to receive(:sample_objects)
expect(sampler).to receive(:sample_gc)
sampler.sample
@@ -65,26 +64,4 @@ describe Gitlab::Metrics::Samplers::RubySampler do
sampler.sample
end
end
-
- if Gitlab::Metrics.mri?
- describe '#sample_objects' do
- it 'adds a metric containing the amount of allocated objects' do
- expect(sampler.metrics[:objects_total]).to receive(:set)
- .with(include(class: anything), be > 0)
- .at_least(:once)
- .and_call_original
-
- sampler.sample
- end
-
- it 'ignores classes without a name' do
- expect(Allocations).to receive(:to_hash).and_return({ Class.new => 4 })
-
- expect(sampler.metrics[:objects_total]).not_to receive(:set)
- .with(include(class: 'object_counts'), anything)
-
- sampler.sample
- end
- end
- end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 81eb62f1801..9ab72ade40d 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1851,4 +1851,20 @@ describe MergeRequest do
.to change { project.open_merge_requests_count }.from(1).to(0)
end
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/namespace_spec.rb b/spec/models/namespace_spec.rb
index 3817f20bfe7..b7c6286fd83 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -559,4 +559,34 @@ describe Namespace do
end
end
end
+
+ describe "#allowed_path_by_redirects" do
+ let(:namespace1) { create(:namespace, path: 'foo') }
+
+ context "when the path has been taken before" do
+ before do
+ namespace1.path = 'bar'
+ namespace1.save!
+ end
+
+ it 'should be invalid' do
+ namespace2 = build(:group, path: 'foo')
+ expect(namespace2).to be_invalid
+ end
+
+ it 'should return an error on path' do
+ namespace2 = build(:group, path: 'foo')
+ namespace2.valid?
+ expect(namespace2.errors.messages[:path].first).to eq('foo has been taken before. Please use another one')
+ end
+ end
+
+ context "when the path has not been taken before" do
+ it 'should be valid' do
+ expect(RedirectRoute.count).to eq(0)
+ namespace = build(:namespace)
+ expect(namespace).to be_valid
+ end
+ end
+ end
end
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index fece370c03f..ddad6862a63 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -87,6 +87,7 @@ describe Route do
end
context 'when conflicting redirects exist' do
+ let(:route) { create(:project).route }
let!(:conflicting_redirect1) { route.create_redirect('bar/test') }
let!(:conflicting_redirect2) { route.create_redirect('bar/test/foo') }
let!(:conflicting_redirect3) { route.create_redirect('gitlab-org') }
@@ -141,11 +142,50 @@ describe Route do
expect(redirect_route.source).to eq(route.source)
expect(redirect_route.path).to eq('foo')
end
+
+ context 'when the source is a Project' do
+ it 'creates a temporal RedirectRoute' do
+ project = create(:project)
+ route = project.route
+ redirect_route = route.create_redirect('foo')
+ expect(redirect_route.permanent?).to be_falsy
+ end
+ end
+
+ context 'when the source is not a project' do
+ it 'creates a permanent RedirectRoute' do
+ redirect_route = route.create_redirect('foo', permanent: true)
+ expect(redirect_route.permanent?).to be_truthy
+ end
+ end
end
describe '#delete_conflicting_redirects' do
+ context 'with permanent redirect' do
+ it 'does not delete the redirect' do
+ route.create_redirect("#{route.path}/foo", permanent: true)
+
+ expect do
+ route.delete_conflicting_redirects
+ end.not_to change { RedirectRoute.count }
+ end
+ end
+
+ context 'with temporal redirect' do
+ let(:route) { create(:project).route }
+
+ it 'deletes the redirect' do
+ route.create_redirect("#{route.path}/foo")
+
+ expect do
+ route.delete_conflicting_redirects
+ end.to change { RedirectRoute.count }.by(-1)
+ end
+ end
+
context 'when a redirect route with the same path exists' do
context 'when the redirect route has matching case' do
+ let(:route) { create(:project).route }
let!(:redirect1) { route.create_redirect(route.path) }
it 'deletes the redirect' do
@@ -169,6 +209,7 @@ describe Route do
end
context 'when the redirect route is differently cased' do
+ let(:route) { create(:project).route }
let!(:redirect1) { route.create_redirect(route.path.upcase) }
it 'deletes the redirect' do
@@ -185,7 +226,32 @@ describe Route do
expect(route.conflicting_redirects).to be_an(ActiveRecord::Relation)
end
+ context 'with permanent redirects' do
+ it 'does not return anything' do
+ route.create_redirect("#{route.path}/foo", permanent: true)
+ route.create_redirect("#{route.path}/foo/bar", permanent: true)
+ route.create_redirect("#{route.path}/baz/quz", permanent: true)
+
+ expect(route.conflicting_redirects).to be_empty
+ end
+ end
+
+ context 'with temporal redirects' do
+ let(:route) { create(:project).route }
+
+ it 'returns the redirect routes' do
+ route = create(:project).route
+ redirect1 = route.create_redirect("#{route.path}/foo")
+ redirect2 = route.create_redirect("#{route.path}/foo/bar")
+ redirect3 = route.create_redirect("#{route.path}/baz/quz")
+
+ expect(route.conflicting_redirects).to match_array([redirect1, redirect2, redirect3])
+ end
+ end
+
context 'when a redirect route with the same path exists' do
+ let(:route) { create(:project).route }
+
context 'when the redirect route has matching case' do
let!(:redirect1) { route.create_redirect(route.path) }
@@ -214,4 +280,42 @@ describe Route do
end
end
end
+
+ describe "#conflicting_redirect_exists?" do
+ context 'when a conflicting redirect exists' do
+ let(:group1) { create(:group, path: 'foo') }
+ let(:group2) { create(:group, path: 'baz') }
+
+ it 'should not be saved' do
+ group1.path = 'bar'
+ group1.save
+
+ group2.path = 'foo'
+
+ expect(group2.save).to be_falsy
+ end
+
+ it 'should return an error on path' do
+ group1.path = 'bar'
+ group1.save
+
+ group2.path = 'foo'
+ group2.valid?
+ expect(group2.errors["route.path"].first).to eq('foo has been taken before. Please use another one')
+ end
+ end
+
+ context 'when a conflicting redirect does not exist' do
+ let(:project1) { create(:project, path: 'foo') }
+ let(:project2) { create(:project, path: 'baz') }
+
+ it 'should be saved' do
+ project1.path = 'bar'
+ project1.save
+
+ project2.path = 'foo'
+ expect(project2.save).to be_truthy
+ end
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 03c96a8f5aa..4687d9dfa00 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -913,11 +913,11 @@ describe User do
describe 'email matching' do
it 'returns users with a matching Email' do
- expect(described_class.search(user.email)).to eq([user, user2])
+ expect(described_class.search(user.email)).to eq([user])
end
- it 'returns users with a partially matching Email' do
- expect(described_class.search(user.email[0..2])).to eq([user, user2])
+ it 'does not return users with a partially matching Email' do
+ expect(described_class.search(user.email[0..2])).not_to include(user, user2)
end
it 'returns users with a matching Email regardless of the casing' do
@@ -973,8 +973,8 @@ describe User do
expect(search_with_secondary_emails(user.email)).to eq([user])
end
- it 'returns users with a partially matching email' do
- expect(search_with_secondary_emails(user.email[0..2])).to eq([user])
+ it 'does not return users with a partially matching email' do
+ expect(search_with_secondary_emails(user.email[0..2])).not_to include([user])
end
it 'returns users with a matching email regardless of the casing' do
@@ -997,29 +997,8 @@ describe User do
expect(search_with_secondary_emails(email.email)).to eq([email.user])
end
- it 'returns users with a matching part of secondary email' do
- expect(search_with_secondary_emails(email.email[1..4])).to eq([email.user])
- end
-
- it 'return users with a matching part of secondary email regardless of case' do
- expect(search_with_secondary_emails(email.email[1..4].upcase)).to eq([email.user])
- expect(search_with_secondary_emails(email.email[1..4].downcase)).to eq([email.user])
- expect(search_with_secondary_emails(email.email[1..4].capitalize)).to eq([email.user])
- end
-
- it 'returns multiple users with matching secondary emails' do
- email1 = create(:email, email: '1_testemail@example.com')
- email2 = create(:email, email: '2_testemail@example.com')
- email3 = create(:email, email: 'other@email.com')
- email3.user.update_attributes!(email: 'another@mail.com')
-
- expect(
- search_with_secondary_emails('testemail@example.com').map(&:id)
- ).to include(email1.user.id, email2.user.id)
-
- expect(
- search_with_secondary_emails('testemail@example.com').map(&:id)
- ).not_to include(email3.user.id)
+ it 'does not return users with a matching part of secondary email' do
+ expect(search_with_secondary_emails(email.email[1..4])).not_to include([email.user])
end
end
@@ -2592,4 +2571,28 @@ describe User do
include_examples 'max member access for groups'
end
end
+
+ describe "#username_previously_taken?" do
+ let(:user1) { create(:user, username: 'foo') }
+
+ context 'when the username has been taken before' do
+ before do
+ user1.username = 'bar'
+ user1.save!
+ end
+
+ it 'should raise an ActiveRecord::RecordInvalid exception' do
+ user2 = build(:user, username: 'foo')
+ expect { user2.save! }.to raise_error(ActiveRecord::RecordInvalid, /Path foo has been taken before/)
+ end
+ end
+
+ context 'when the username has not been taken before' do
+ it 'should be valid' do
+ expect(RedirectRoute.count).to eq(0)
+ user2 = build(:user, username: 'baz')
+ expect(user2).to be_valid
+ end
+ end
+ end
end
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index 554723d6b1e..6330c140246 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -173,6 +173,28 @@ describe API::Groups do
end
describe "GET /groups/:id" do
+ # Given a group, create one project for each visibility level
+ #
+ # group - Group to add projects to
+ # share_with - If provided, each project will be shared with this Group
+ #
+ # Returns a Hash of visibility_level => Project pairs
+ def add_projects_to_group(group, share_with: nil)
+ projects = {
+ public: create(:project, :public, namespace: group),
+ internal: create(:project, :internal, namespace: group),
+ private: create(:project, :private, namespace: group)
+ }
+
+ if share_with
+ create(:project_group_link, project: projects[:public], group: share_with)
+ create(:project_group_link, project: projects[:internal], group: share_with)
+ create(:project_group_link, project: projects[:private], group: share_with)
+ end
+
+ projects
+ end
+
context 'when unauthenticated' do
it 'returns 404 for a private group' do
get api("/groups/#{group2.id}")
@@ -183,6 +205,26 @@ describe API::Groups do
get api("/groups/#{group1.id}")
expect(response).to have_gitlab_http_status(200)
end
+
+ it 'returns only public projects in the group' do
+ public_group = create(:group, :public)
+ projects = add_projects_to_group(public_group)
+
+ get api("/groups/#{public_group.id}")
+
+ expect(json_response['projects'].map { |p| p['id'].to_i })
+ .to contain_exactly(projects[:public].id)
+ end
+
+ it 'returns only public projects shared with the group' do
+ public_group = create(:group, :public)
+ projects = add_projects_to_group(public_group, share_with: group1)
+
+ get api("/groups/#{group1.id}")
+
+ expect(json_response['shared_projects'].map { |p| p['id'].to_i })
+ .to contain_exactly(projects[:public].id)
+ end
end
context "when authenticated as user" do
@@ -222,6 +264,26 @@ describe API::Groups do
expect(response).to have_gitlab_http_status(404)
end
+
+ it 'returns only public and internal projects in the group' do
+ public_group = create(:group, :public)
+ projects = add_projects_to_group(public_group)
+
+ get api("/groups/#{public_group.id}", user2)
+
+ expect(json_response['projects'].map { |p| p['id'].to_i })
+ .to contain_exactly(projects[:public].id, projects[:internal].id)
+ end
+
+ it 'returns only public and internal projects shared with the group' do
+ public_group = create(:group, :public)
+ projects = add_projects_to_group(public_group, share_with: group1)
+
+ get api("/groups/#{group1.id}", user2)
+
+ expect(json_response['shared_projects'].map { |p| p['id'].to_i })
+ .to contain_exactly(projects[:public].id, projects[:internal].id)
+ end
end
context "when authenticated as admin" do
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 67e1539cbc3..3c31980b273 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -537,16 +537,7 @@ describe API::Internal do
context 'the project path was changed' do
let!(:old_path_to_repo) { project.repository.path_to_repo }
- let!(:old_full_path) { project.full_path }
- let(:project_moved_message) do
- <<-MSG.strip_heredoc
- Project '#{old_full_path}' was moved to '#{project.full_path}'.
-
- Please update your Git remote and try again:
-
- git remote set-url origin #{project.ssh_url_to_repo}
- MSG
- end
+ let!(:repository) { project.repository }
before do
project.team << [user, :developer]
@@ -555,19 +546,17 @@ describe API::Internal do
end
it 'rejects the push' do
- push_with_path(key, old_path_to_repo)
+ push(key, project)
expect(response).to have_gitlab_http_status(200)
- expect(json_response['status']).to be_falsey
- expect(json_response['message']).to eq(project_moved_message)
+ expect(json_response['status']).to be_falsy
end
it 'rejects the SSH pull' do
- pull_with_path(key, old_path_to_repo)
+ pull(key, project)
expect(response).to have_gitlab_http_status(200)
- expect(json_response['status']).to be_falsey
- expect(json_response['message']).to eq(project_moved_message)
+ expect(json_response['status']).to be_falsy
end
end
end
@@ -695,7 +684,7 @@ describe API::Internal do
# end
# end
- describe 'POST /internal/post_receive' do
+ describe 'POST /internal/post_receive', :clean_gitlab_redis_shared_state do
let(:identifier) { 'key-123' }
let(:valid_params) do
@@ -713,6 +702,8 @@ describe API::Internal do
before do
project.team << [user, :developer]
+ allow(described_class).to receive(:identify).and_return(user)
+ allow_any_instance_of(Gitlab::Identifier).to receive(:identify).and_return(user)
end
it 'enqueues a PostReceive worker job' do
@@ -780,6 +771,19 @@ describe API::Internal do
expect(json_response['broadcast_message']).to eq(nil)
end
end
+
+ context 'with a redirected data' do
+ it 'returns redirected message on the response' do
+ project_moved = Gitlab::Checks::ProjectMoved.new(project, user, 'foo/baz', 'http')
+ project_moved.add_redirect_message
+
+ post api("/internal/post_receive"), valid_params
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response["redirected_message"]).to be_present
+ expect(json_response["redirected_message"]).to eq(project_moved.redirect_message)
+ end
+ end
end
describe 'POST /internal/pre_receive' do
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 99525cd0a6a..3f5070a1fd2 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -860,6 +860,20 @@ describe API::Issues, :mailer do
end
end
+ context 'user does not have permissions to create issue' do
+ let(:not_member) { create(:user) }
+
+ before do
+ project.project_feature.update(issues_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'renders 403' do
+ post api("/projects/#{project.id}/issues", not_member), title: 'new issue'
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
it 'creates a new project issue' do
post api("/projects/#{project.id}/issues", user),
title: 'new issue', labels: 'label, label2', weight: 3,
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index a16f98bec36..fa02fffc82a 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -324,9 +324,9 @@ describe 'Git HTTP requests' do
<<-MSG.strip_heredoc
Project '#{redirect.path}' was moved to '#{project.full_path}'.
- Please update your Git remote and try again:
+ Please update your Git remote:
- git remote set-url origin #{project.http_url_to_repo}
+ git remote set-url origin #{project.http_url_to_repo} and try again.
MSG
end
@@ -533,9 +533,9 @@ describe 'Git HTTP requests' do
<<-MSG.strip_heredoc
Project '#{redirect.path}' was moved to '#{project.full_path}'.
- Please update your Git remote and try again:
+ Please update your Git remote:
- git remote set-url origin #{project.http_url_to_repo}
+ git remote set-url origin #{project.http_url_to_repo} and try again.
MSG
end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index b0de8d447a2..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
@@ -518,5 +530,20 @@ describe Ci::CreatePipelineService do
end
end
end
+
+ context 'when pipeline is running for a tag' do
+ before do
+ config = YAML.dump(test: { script: 'test', only: ['branches'] },
+ deploy: { script: 'deploy', only: ['tags'] })
+
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'creates a tagged pipeline' do
+ pipeline = execute_service(ref: 'v1.0.0')
+
+ expect(pipeline.tag?).to be true
+ end
+ end
end
end
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 3ee59014b5b..22fb7ed7215 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -307,6 +307,19 @@ module Ci
it_behaves_like 'not pick'
end
+
+ context 'when job object is staled' do
+ let!(:pre_stage_job) { create(:ci_build, :running, pipeline: pipeline, name: 'test', stage_idx: 0) }
+
+ before do
+ allow_any_instance_of(Ci::Build).to receive(:drop!)
+ .and_raise(ActiveRecord::StaleObjectError.new(pending_job, :drop!))
+ end
+
+ it 'does not drop nor pick' do
+ expect(subject).to be_nil
+ end
+ end
end
shared_examples 'validation is not active' do
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