summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/cng.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/global.gitlab-ci.yml4
-rw-r--r--.gitlab/ci/qa.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml7
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/test-metadata.gitlab-ci.yml2
-rw-r--r--.ruby-version2
-rw-r--r--app/assets/javascripts/notes/stores/actions.js17
-rw-r--r--app/assets/javascripts/notes/stores/utils.js7
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js42
-rw-r--r--app/assets/javascripts/profile/profile.js11
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue4
-rw-r--r--app/assets/stylesheets/framework/ci_variable_list.scss1
-rw-r--r--app/assets/stylesheets/framework/header.scss7
-rw-r--r--app/assets/stylesheets/framework/modal.scss6
-rw-r--r--app/assets/stylesheets/pages/notes.scss5
-rw-r--r--app/controllers/profiles/preferences_controller.rb4
-rw-r--r--app/controllers/profiles_controller.rb7
-rw-r--r--app/graphql/gitlab_schema.rb42
-rw-r--r--app/models/project_services/jira_service.rb1
-rw-r--r--app/models/repository.rb2
-rw-r--r--app/models/user.rb3
-rw-r--r--app/models/user_preference.rb4
-rw-r--r--app/policies/project_policy.rb4
-rw-r--r--app/services/git/branch_push_service.rb2
-rw-r--r--app/services/members/create_service.rb11
-rw-r--r--app/services/projects/after_import_service.rb2
-rw-r--r--app/services/projects/repository_languages_service.rb2
-rw-r--r--app/views/admin/projects/show.html.haml2
-rw-r--r--app/views/ci/variables/_index.html.haml1
-rw-r--r--app/views/ci/variables/_variable_header.html.haml16
-rw-r--r--app/views/ci/variables/_variable_row.html.haml18
-rw-r--r--app/views/groups/group_members/index.html.haml2
-rw-r--r--app/views/layouts/_piwik.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_profile.html.haml2
-rw-r--r--app/views/profiles/preferences/show.html.haml26
-rw-r--r--app/views/profiles/show.html.haml26
-rw-r--r--app/views/repository_check_mailer/notify.text.haml2
-rw-r--r--app/views/shared/issuable/form/_contribution.html.haml2
-rw-r--r--app/workers/detect_repository_languages_worker.rb7
-rw-r--r--changelogs/unreleased/27987-use-findorcreateservice-to-create-labels.yml5
-rw-r--r--changelogs/unreleased/53973-fix-subpixel-border-issue.yml5
-rw-r--r--changelogs/unreleased/57654-add-time-preferences-for-user-fe.yml5
-rw-r--r--changelogs/unreleased/58404-set-default-max-depth-for-GraphQL.yml5
-rw-r--r--changelogs/unreleased/60180-jira-service-fix-nil-on-find-call.yml5
-rw-r--r--changelogs/unreleased/60425-fix-500-when-accessing-charts-with-anonymous-user.yml5
-rw-r--r--changelogs/unreleased/61494-set-status-modal-visual-bugs.yml5
-rw-r--r--changelogs/unreleased/add-warning-to-backup-rake-task.yml5
-rw-r--r--changelogs/unreleased/antonyliu-i18n-user-profile.yml5
-rw-r--r--changelogs/unreleased/sh-fix-invited-members.yml5
-rw-r--r--changelogs/unreleased/sh-upgrade-ruby-2-6-3-ce.yml5
-rw-r--r--changelogs/unreleased/winh-notes-error-handling.yml5
-rw-r--r--config/initializers/01_secret_token.rb7
-rw-r--r--db/migrate/20190325105715_add_fields_to_user_preferences.rb24
-rw-r--r--db/migrate/20190412155659_add_merge_request_blocks.rb25
-rw-r--r--db/schema.rb14
-rw-r--r--doc/administration/integration/plantuml.md3
-rw-r--r--doc/ci/caching/index.md2
-rw-r--r--doc/development/architecture.md179
-rw-r--r--doc/install/installation.md6
-rw-r--r--doc/install/requirements.md7
-rw-r--r--doc/update/mysql_to_postgresql.md16
-rw-r--r--doc/update/upgrading_from_source.md6
-rw-r--r--doc/user/admin_area/settings/usage_statistics.md2
-rw-r--r--doc/user/project/pages/lets_encrypt_for_gitlab_pages.md4
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb2
-rw-r--r--lib/tasks/gitlab/backup.rake8
-rw-r--r--locale/gitlab.pot75
-rw-r--r--qa/Dockerfile2
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb15
-rw-r--r--spec/features/dashboard/user_filters_projects_spec.rb56
-rw-r--r--spec/features/issuables/markdown_references/jira_spec.rb2
-rw-r--r--spec/features/profiles/user_edit_preferences_spec.rb32
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb32
-rw-r--r--spec/frontend/notes/components/discussion_notes_spec.js7
-rw-r--r--spec/frontend/notes/stores/utils_spec.js17
-rw-r--r--spec/graphql/gitlab_schema_spec.rb86
-rw-r--r--spec/initializers/secret_token_spec.rb11
-rw-r--r--spec/javascripts/ci_variable_list/ajax_variable_list_spec.js2
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js45
-rw-r--r--spec/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown_spec.js77
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb40
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb4
-rw-r--r--spec/models/project_services/jira_service_spec.rb7
-rw-r--r--spec/models/user_preference_spec.rb6
-rw-r--r--spec/requests/api/graphql/gitlab_schema_spec.rb40
-rw-r--r--spec/services/members/create_service_spec.rb13
-rw-r--r--spec/services/projects/after_import_service_spec.rb2
-rw-r--r--spec/services/projects/detect_repository_languages_service_spec.rb2
-rw-r--r--spec/services/projects/repository_languages_service_spec.rb4
-rw-r--r--spec/services/users/update_service_spec.rb9
-rw-r--r--spec/support/features/variable_list_shared_examples.rb36
-rw-r--r--spec/support/helpers/graphql_helpers.rb6
-rw-r--r--spec/support/helpers/project_forks_helper.rb23
-rw-r--r--spec/workers/detect_repository_languages_worker_spec.rb11
97 files changed, 1008 insertions, 334 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 44beccd966a..c971df3ba5f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.5.3-golang-1.11-git-2.21-chrome-73.0-node-10.x-yarn-1.12-postgresql-9.6-graphicsmagick-1.3.29"
+image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.21-chrome-73.0-node-10.x-yarn-1.12-postgresql-9.6-graphicsmagick-1.3.29"
variables:
MYSQL_ALLOW_EMPTY_PASSWORD: "1"
diff --git a/.gitlab/ci/cng.gitlab-ci.yml b/.gitlab/ci/cng.gitlab-ci.yml
index c384bcdcdfc..d624e8d09f6 100644
--- a/.gitlab/ci/cng.gitlab-ci.yml
+++ b/.gitlab/ci/cng.gitlab-ci.yml
@@ -1,5 +1,5 @@
cloud-native-image:
- image: ruby:2.5-alpine
+ image: ruby:2.6-alpine
before_script: []
dependencies: []
stage: post-test
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index fbf8925e30a..986ba7558d5 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -16,7 +16,7 @@
gitlab:assets:compile:
<<: *assets-compile-cache
extends: .dedicated-no-docs-pull-cache-job
- image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.5.3-git-2.21-chrome-73.0-node-8.x-yarn-1.12-graphicsmagick-1.3.29-docker-18.06.1
+ image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-git-2.21-chrome-73.0-node-8.x-yarn-1.12-graphicsmagick-1.3.29-docker-18.06.1
dependencies:
- setup-test-env
services:
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index 466c47b37c7..cf87f5eb39c 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -9,7 +9,7 @@
- gitlab-org
.default-cache: &default-cache
- key: "debian-stretch-ruby-2.5.3-node-10.x"
+ key: "debian-stretch-ruby-2.6.3-node-10.x"
paths:
- vendor/ruby
- .yarn-cache/
@@ -47,7 +47,7 @@
.single-script-job-dedicated-runner:
extends: .dedicated-runner
- image: ruby:2.5-alpine
+ image: ruby:2.6-alpine
stage: test
cache: {}
dependencies: []
diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml
index 85c6409186e..122ed622ee2 100644
--- a/.gitlab/ci/qa.gitlab-ci.yml
+++ b/.gitlab/ci/qa.gitlab-ci.yml
@@ -1,5 +1,5 @@
package-and-qa:
- image: ruby:2.5-alpine
+ image: ruby:2.6-alpine
stage: qa
when: manual
before_script: []
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 35c5f67427e..29534e40a14 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -86,7 +86,7 @@
.rspec-metadata-pg-10: &rspec-metadata-pg-10
<<: *rspec-metadata
<<: *use-pg-10
- image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.5.3-golang-1.11-git-2.21-chrome-73.0-node-10.x-yarn-1.12-postgresql-10-graphicsmagick-1.3.29"
+ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.6.3-golang-1.11-git-2.21-chrome-73.0-node-10.x-yarn-1.12-postgresql-10-graphicsmagick-1.3.29"
.rspec-metadata-mysql: &rspec-metadata-mysql
<<: *rspec-metadata
@@ -108,7 +108,8 @@
- git fetch https://gitlab.com/gitlab-org/gitlab-ce.git v9.3.0
- git checkout -f FETCH_HEAD
- sed -i "s/gem 'oj', '~> 2.17.4'//" Gemfile
- - bundle update google-protobuf grpc
+ - sed -i "s/gem 'bootsnap', '~> 1.0.0'/gem 'bootsnap'/" Gemfile
+ - bundle update google-protobuf grpc bootsnap
- bundle install $BUNDLE_INSTALL_FLAGS
- date
- cp config/gitlab.yml.example config/gitlab.yml
@@ -183,7 +184,7 @@ static-analysis:
script:
- scripts/static-analysis
cache:
- key: "debian-stretch-ruby-2.5.3-node-10.x-and-rubocop"
+ key: "debian-stretch-ruby-2.6.3-node-10.x-and-rubocop"
paths:
- vendor/ruby
- .yarn-cache/
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index ae16549ef6b..3a024c44fe2 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -54,7 +54,7 @@ build-qa-image:
- time docker push ${QA_IMAGE}
.review-build-cng-base: &review-build-cng-base
- image: ruby:2.5-alpine
+ image: ruby:2.6-alpine
stage: test
when: manual
before_script:
diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml
index 3ba7af956b5..4b595083ec6 100644
--- a/.gitlab/ci/test-metadata.gitlab-ci.yml
+++ b/.gitlab/ci/test-metadata.gitlab-ci.yml
@@ -56,7 +56,7 @@ update-tests-metadata:
flaky-examples-check:
extends: .dedicated-runner
- image: ruby:2.5-alpine
+ image: ruby:2.6-alpine
services: []
before_script: []
variables:
diff --git a/.ruby-version b/.ruby-version
index aedc15bb0c6..ec1cf33c3f6 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.5.3
+2.6.3
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index bac124be34c..63658d49a05 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -268,11 +268,20 @@ export const saveNote = ({ commit, dispatch }, noteData) => {
const { errors } = res;
const commandsChanges = res.commands_changes;
- if (hasQuickActions && errors && Object.keys(errors).length) {
- eTagPoll.makeRequest();
+ if (errors && Object.keys(errors).length) {
+ /*
+ The following reply means that quick actions have been successfully applied:
- $('.js-gfm-input').trigger('clear-commands-cache.atwho');
- Flash(__('Commands applied'), 'notice', noteData.flashContainer);
+ {"commands_changes":{},"valid":false,"errors":{"commands_only":["Commands applied"]}}
+ */
+ if (hasQuickActions) {
+ eTagPoll.makeRequest();
+
+ $('.js-gfm-input').trigger('clear-commands-cache.atwho');
+ Flash(__('Commands applied'), 'notice', noteData.flashContainer);
+ } else {
+ throw new Error(__('Failed to save comment!'));
+ }
}
if (commandsChanges) {
diff --git a/app/assets/javascripts/notes/stores/utils.js b/app/assets/javascripts/notes/stores/utils.js
index 029fde348fb..ed4cef4a917 100644
--- a/app/assets/javascripts/notes/stores/utils.js
+++ b/app/assets/javascripts/notes/stores/utils.js
@@ -2,7 +2,8 @@ import AjaxCache from '~/lib/utils/ajax_cache';
import { trimFirstCharOfLineContent } from '~/diffs/store/utils';
import { sprintf, __ } from '~/locale';
-const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
+// factory function because global flag makes RegExp stateful
+const createQuickActionsRegex = () => /^\/\w+.*$/gm;
export const findNoteObjectById = (notes, id) => notes.filter(n => n.id === id)[0];
@@ -27,9 +28,9 @@ export const getQuickActionText = note => {
return text;
};
-export const hasQuickActions = note => REGEX_QUICK_ACTIONS.test(note);
+export const hasQuickActions = note => createQuickActionsRegex().test(note);
-export const stripQuickActions = note => note.replace(REGEX_QUICK_ACTIONS, '').trim();
+export const stripQuickActions = note => note.replace(createQuickActionsRegex(), '').trim();
export const prepareDiffLines = diffLines =>
diffLines.map(line => ({ ...trimFirstCharOfLineContent(line) }));
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js
index c1f6edf2f27..a20a0526f12 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js
@@ -1,4 +1,10 @@
-const defaultTimezone = 'UTC';
+const defaultTimezone = { name: 'UTC', offset: 0 };
+const defaults = {
+ $inputEl: null,
+ $dropdownEl: null,
+ onSelectTimezone: null,
+ displayFormat: item => item.name,
+};
export const formatUtcOffset = offset => {
const parsed = parseInt(offset, 10);
@@ -11,23 +17,28 @@ export const formatUtcOffset = offset => {
export const formatTimezone = item => `[UTC ${formatUtcOffset(item.offset)}] ${item.name}`;
-const defaults = {
- $inputEl: null,
- $dropdownEl: null,
- onSelectTimezone: null,
+export const findTimezoneByIdentifier = (tzList = [], identifier = null) => {
+ if (tzList && tzList.length && identifier && identifier.length) {
+ return tzList.find(tz => tz.identifier === identifier) || null;
+ }
+ return null;
};
export default class TimezoneDropdown {
- constructor({ $dropdownEl, $inputEl, onSelectTimezone } = defaults) {
+ constructor({ $dropdownEl, $inputEl, onSelectTimezone, displayFormat } = defaults) {
this.$dropdown = $dropdownEl;
this.$dropdownToggle = this.$dropdown.find('.dropdown-toggle-text');
this.$input = $inputEl;
this.timezoneData = this.$dropdown.data('data');
+ this.onSelectTimezone = onSelectTimezone;
+ this.displayFormat = displayFormat || defaults.displayFormat;
+
+ this.initialTimezone =
+ findTimezoneByIdentifier(this.timezoneData, this.$input.val()) || defaultTimezone;
+
this.initDefaultTimezone();
this.initDropdown();
-
- this.onSelectTimezone = onSelectTimezone;
}
initDropdown() {
@@ -35,7 +46,7 @@ export default class TimezoneDropdown {
data: this.timezoneData,
filterable: true,
selectable: true,
- toggleLabel: item => item.name,
+ toggleLabel: this.displayFormat,
search: {
fields: ['name'],
},
@@ -43,20 +54,17 @@ export default class TimezoneDropdown {
text: item => formatTimezone(item),
});
- this.setDropdownToggle();
+ this.setDropdownToggle(this.displayFormat(this.initialTimezone));
}
initDefaultTimezone() {
- const initialValue = this.$input.val();
-
- if (!initialValue) {
- this.$input.val(defaultTimezone);
+ if (!this.$input.val()) {
+ this.$input.val(defaultTimezone.name);
}
}
- setDropdownToggle() {
- const initialValue = this.$input.val();
- this.$dropdownToggle.text(initialValue);
+ setDropdownToggle(dropdownText) {
+ this.$dropdownToggle.text(dropdownText);
}
updateInputValue({ selectedObj, e }) {
diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js
index deacff5abe7..6e3800021b4 100644
--- a/app/assets/javascripts/profile/profile.js
+++ b/app/assets/javascripts/profile/profile.js
@@ -2,6 +2,9 @@ import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import flash from '../flash';
import { parseBoolean } from '~/lib/utils/common_utils';
+import TimezoneDropdown, {
+ formatTimezone,
+} from '~/pages/projects/pipeline_schedules/shared/components/timezone_dropdown';
export default class Profile {
constructor({ form } = {}) {
@@ -10,6 +13,14 @@ export default class Profile {
this.setRepoRadio();
this.bindEvents();
this.initAvatarGlCrop();
+
+ this.$inputEl = $('#user_timezone');
+
+ this.timezoneDropdown = new TimezoneDropdown({
+ $inputEl: this.$inputEl,
+ $dropdownEl: $('.js-timezone-dropdown'),
+ displayFormat: selectedItem => formatTimezone(selectedItem),
+ });
}
initAvatarGlCrop() {
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
index c0659a0173a..10e2c8453e2 100644
--- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
@@ -178,7 +178,7 @@ export default {
/>
<div ref="userStatusForm" class="form-group position-relative m-0">
<div class="input-group">
- <span class="input-group-btn">
+ <span class="input-group-prepend">
<button
ref="toggleEmojiMenuButton"
v-gl-tooltip.bottom
@@ -211,7 +211,7 @@ export default {
@keyup.enter.prevent
@click="hideEmojiMenu"
/>
- <span v-show="isDirty" class="input-group-btn">
+ <span v-show="isDirty" class="input-group-append">
<button
v-gl-tooltip.bottom
:title="s__('SetStatusModal|Clear status')"
diff --git a/app/assets/stylesheets/framework/ci_variable_list.scss b/app/assets/stylesheets/framework/ci_variable_list.scss
index d9b0e4558ad..28d7492b99c 100644
--- a/app/assets/stylesheets/framework/ci_variable_list.scss
+++ b/app/assets/stylesheets/framework/ci_variable_list.scss
@@ -47,6 +47,7 @@
display: flex;
align-items: flex-start;
width: 100%;
+ padding-bottom: $gl-padding;
@include media-breakpoint-down(xs) {
display: block;
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index a6179e2a96e..a57cd6737f8 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -581,10 +581,15 @@
.emoji-menu-toggle-button {
@include emoji-menu-toggle-button;
+ padding: $gl-vert-padding $gl-btn-padding;
}
.input-group {
- height: 34px;
+ &,
+ .input-group-prepend,
+ .input-group-append {
+ height: $input-height;
+ }
}
}
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index 323a3dbecd5..e2bb1eb67c0 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -66,12 +66,6 @@
margin-top: $grid-size;
}
}
-
- @include media-breakpoint-up(sm) {
- .btn:nth-child(1) {
- margin-left: auto;
- }
- }
}
body.modal-open {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 09f75cd827f..f2b67a693c3 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -471,6 +471,11 @@ $note-form-margin-left: 72px;
vertical-align: top;
white-space: normal;
+ // Fixes subpixel rounding issue https://gitlab.com/gitlab-org/gitlab-ce/issues/53973
+ // background-color is needed for dark code preference
+ padding-bottom: 1px;
+ background-color: $white-light;
+
&.parallel {
border-width: 1px;
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index 0e30df1b15b..62f98d9e549 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -44,7 +44,9 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:project_view,
:theme_id,
:first_day_of_week,
- :preferred_language
+ :preferred_language,
+ :time_display_relative,
+ :time_format_in_24h
]
end
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index b9c52618d4b..1d16ddb1608 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -15,7 +15,7 @@ class ProfilesController < Profiles::ApplicationController
result = Users::UpdateService.new(current_user, user_params.merge(user: @user)).execute
if result[:status] == :success
- message = "Profile was successfully updated"
+ message = s_("Profiles|Profile was successfully updated")
format.html { redirect_back_or_default(default: { action: 'show' }, options: { notice: message }) }
format.json { render json: { message: message } }
@@ -31,7 +31,7 @@ class ProfilesController < Profiles::ApplicationController
user.reset_incoming_email_token!
end
- flash[:notice] = "Incoming email token was successfully reset"
+ flash[:notice] = s_("Profiles|Incoming email token was successfully reset")
redirect_to profile_personal_access_tokens_path
end
@@ -41,7 +41,7 @@ class ProfilesController < Profiles::ApplicationController
user.reset_feed_token!
end
- flash[:notice] = 'Feed token was successfully reset'
+ flash[:notice] = s_('Profiles|Feed token was successfully reset')
redirect_to profile_personal_access_tokens_path
end
@@ -106,6 +106,7 @@ class ProfilesController < Profiles::ApplicationController
:organization,
:private_profile,
:include_private_contributions,
+ :timezone,
status: [:emoji, :message]
)
end
diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb
index a12568d5d31..897e12c1b56 100644
--- a/app/graphql/gitlab_schema.rb
+++ b/app/graphql/gitlab_schema.rb
@@ -7,6 +7,9 @@ class GitlabSchema < GraphQL::Schema
AUTHENTICATED_COMPLEXITY = 250
ADMIN_COMPLEXITY = 300
+ ANONYMOUS_MAX_DEPTH = 10
+ AUTHENTICATED_MAX_DEPTH = 15
+
use BatchLoader::GraphQL
use Gitlab::Graphql::Authorize
use Gitlab::Graphql::Present
@@ -23,21 +26,36 @@ class GitlabSchema < GraphQL::Schema
mutation(Types::MutationType)
- def self.execute(query_str = nil, **kwargs)
- kwargs[:max_complexity] ||= max_query_complexity(kwargs[:context])
+ class << self
+ def execute(query_str = nil, **kwargs)
+ kwargs[:max_complexity] ||= max_query_complexity(kwargs[:context])
+ kwargs[:max_depth] ||= max_query_depth(kwargs[:context])
- super(query_str, **kwargs)
- end
+ super(query_str, **kwargs)
+ end
+
+ private
+
+ def max_query_complexity(ctx)
+ current_user = ctx&.fetch(:current_user, nil)
+
+ if current_user&.admin
+ ADMIN_COMPLEXITY
+ elsif current_user
+ AUTHENTICATED_COMPLEXITY
+ else
+ DEFAULT_MAX_COMPLEXITY
+ end
+ end
- def self.max_query_complexity(ctx)
- current_user = ctx&.fetch(:current_user, nil)
+ def max_query_depth(ctx)
+ current_user = ctx&.fetch(:current_user, nil)
- if current_user&.admin
- ADMIN_COMPLEXITY
- elsif current_user
- AUTHENTICATED_COMPLEXITY
- else
- DEFAULT_MAX_COMPLEXITY
+ if current_user
+ AUTHENTICATED_MAX_DEPTH
+ else
+ ANONYMOUS_MAX_DEPTH
+ end
end
end
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index ebf28dc842c..7b4832b84a8 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -265,6 +265,7 @@ class JiraService < IssueTrackerService
def find_remote_link(issue, url)
links = jira_request { issue.remotelink.all }
+ return unless links
links.find { |link| link.object["url"] == url }
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index be17b54ff12..1c02e68f2f6 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -465,7 +465,7 @@ class Repository
def after_import
expire_content_cache
- DetectRepositoryLanguagesWorker.perform_async(project.id, project.owner.id)
+ DetectRepositoryLanguagesWorker.perform_async(project.id)
end
# Runs code after a new commit has been pushed.
diff --git a/app/models/user.rb b/app/models/user.rb
index 4a1bf5514fe..60f69659a6b 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -230,6 +230,9 @@ class User < ApplicationRecord
delegate :notes_filter_for, to: :user_preference
delegate :set_notes_filter, to: :user_preference
delegate :first_day_of_week, :first_day_of_week=, to: :user_preference
+ delegate :timezone, :timezone=, to: :user_preference
+ delegate :time_display_relative, :time_display_relative=, to: :user_preference
+ delegate :time_format_in_24h, :time_format_in_24h=, to: :user_preference
accepts_nested_attributes_for :user_preference, update_only: true
diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb
index 282b192167f..f1326f4c8cb 100644
--- a/app/models/user_preference.rb
+++ b/app/models/user_preference.rb
@@ -10,6 +10,10 @@ class UserPreference < ApplicationRecord
validates :issue_notes_filter, :merge_request_notes_filter, inclusion: { in: NOTES_FILTERS.values }, presence: true
+ default_value_for :timezone, value: Time.zone.tzinfo.name, allows_nil: false
+ default_value_for :time_display_relative, value: true, allows_nil: false
+ default_value_for :time_format_in_24h, value: false, allows_nil: false
+
class << self
def notes_filters
{
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 76544249688..3218c04b219 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -488,6 +488,10 @@ class ProjectPolicy < BasePolicy
def team_access_level
return -1 if @user.nil?
+ lookup_access_level!
+ end
+
+ def lookup_access_level!
# NOTE: max_member_access has its own cache
project.team.max_member_access(@user.id)
end
diff --git a/app/services/git/branch_push_service.rb b/app/services/git/branch_push_service.rb
index da053ce80c7..abf11f253f6 100644
--- a/app/services/git/branch_push_service.rb
+++ b/app/services/git/branch_push_service.rb
@@ -48,7 +48,7 @@ module Git
def enqueue_detect_repository_languages
return unless default_branch?
- DetectRepositoryLanguagesWorker.perform_async(project.id, current_user.id)
+ DetectRepositoryLanguagesWorker.perform_async(project.id)
end
# Only stop environments if the ref is a branch that is being deleted
diff --git a/app/services/members/create_service.rb b/app/services/members/create_service.rb
index f6bad74736c..d6b17ec10be 100644
--- a/app/services/members/create_service.rb
+++ b/app/services/members/create_service.rb
@@ -23,7 +23,16 @@ module Members
members.each do |member|
if member.errors.any?
- errors << "#{member.user.username}: #{member.errors.full_messages.to_sentence}"
+ current_error =
+ # Invited users may not have an associated user
+ if member.user.present?
+ "#{member.user.username}: "
+ else
+ ""
+ end
+
+ current_error += member.errors.full_messages.to_sentence
+ errors << current_error
else
after_execute(member: member)
end
diff --git a/app/services/projects/after_import_service.rb b/app/services/projects/after_import_service.rb
index afb9048e87b..bbdde4408d2 100644
--- a/app/services/projects/after_import_service.rb
+++ b/app/services/projects/after_import_service.rb
@@ -9,7 +9,7 @@ module Projects
end
def execute
- Projects::HousekeepingService.new(@project, :gc).execute do
+ Projects::HousekeepingService.new(@project).execute do
repository.delete_all_refs_except(RESERVED_REF_PREFIXES)
end
rescue Projects::HousekeepingService::LeaseTaken => e
diff --git a/app/services/projects/repository_languages_service.rb b/app/services/projects/repository_languages_service.rb
index e75851c7da4..05f43c2264b 100644
--- a/app/services/projects/repository_languages_service.rb
+++ b/app/services/projects/repository_languages_service.rb
@@ -11,7 +11,7 @@ module Projects
def perform_language_detection
if persisted_repository_languages.blank?
- ::DetectRepositoryLanguagesWorker.perform_async(project.id, current_user.id)
+ ::DetectRepositoryLanguagesWorker.perform_async(project.id)
else
project.update_column(:detected_repository_languages, true)
end
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index bc34af88928..f016a157daf 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -100,6 +100,8 @@
%span.light archived:
%strong project is read-only
+ = render_if_exists "shared_runner_status", project: @project
+
%li
%span.light access:
%strong
diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml
index dc9ccb6cc39..464b9faf282 100644
--- a/app/views/ci/variables/_index.html.haml
+++ b/app/views/ci/variables/_index.html.haml
@@ -10,6 +10,7 @@
.hide.alert.alert-danger.js-ci-variable-error-box
%ul.ci-variable-list
+ = render 'ci/variables/variable_header'
- @variables.each.each do |variable|
= render 'ci/variables/variable_row', form_field: 'variables', variable: variable
= render 'ci/variables/variable_row', form_field: 'variables'
diff --git a/app/views/ci/variables/_variable_header.html.haml b/app/views/ci/variables/_variable_header.html.haml
new file mode 100644
index 00000000000..d3b7a5ae883
--- /dev/null
+++ b/app/views/ci/variables/_variable_header.html.haml
@@ -0,0 +1,16 @@
+- only_key_value = local_assigns.fetch(:only_key_value, false)
+
+%li.ci-variable-row.m-0.d-none.d-sm-block
+ .d-flex.w-100.align-items-center.pb-2
+ .bold.table-section.section-15.append-right-10
+ = s_('CiVariables|Type')
+ .bold.table-section.section-15.append-right-10
+ = s_('CiVariables|Key')
+ .bold.table-section.section-15.append-right-10
+ = s_('CiVariables|Value')
+ - unless only_key_value
+ .bold.table-section.section-20
+ = s_('CiVariables|State')
+ .bold.table-section.section-20
+ = s_('CiVariables|Masked')
+ = render_if_exists 'ci/variables/environment_scope_header'
diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml
index 37257b3aa1c..b4930b41c09 100644
--- a/app/views/ci/variables/_variable_row.html.haml
+++ b/app/views/ci/variables/_variable_row.html.haml
@@ -20,18 +20,18 @@
- masked_input_name = "#{form_field}[variables_attributes][][masked]"
%li.js-row.ci-variable-row{ data: { is_persisted: "#{!id.nil?}" } }
- .ci-variable-row-body
+ .ci-variable-row-body.border-bottom
%input.js-ci-variable-input-id{ type: "hidden", name: id_input_name, value: id }
%input.js-ci-variable-input-destroy{ type: "hidden", name: destroy_input_name }
- %select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control{ name: variable_type_input_name }
+ %select.js-ci-variable-input-variable-type.ci-variable-body-item.form-control.select-control.table-section.section-15{ name: variable_type_input_name }
= options_for_select(ci_variable_type_options, variable_type)
- %input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control{ type: "text",
+ %input.js-ci-variable-input-key.ci-variable-body-item.qa-ci-variable-input-key.form-control.table-section.section-15{ type: "text",
name: key_input_name,
value: key,
placeholder: s_('CiVariables|Input variable key') }
- .ci-variable-body-item.gl-show-field-errors
+ .ci-variable-body-item.gl-show-field-errors.table-section.section-15.border-top-0.p-0
.form-control.js-secret-value-placeholder.qa-ci-variable-input-value{ class: ('hide' unless id) }
- = '*' * 20
+ = '*' * 17
%textarea.js-ci-variable-input-value.js-secret-value.qa-ci-variable-input-value.form-control{ class: ('hide' if id),
rows: 1,
name: value_input_name,
@@ -41,7 +41,7 @@
= s_("CiVariables|Cannot use Masked Variable with current value")
= link_to icon('question-circle'), help_page_path('ci/variables/README', anchor: 'masked-variables'), target: '_blank', rel: 'noopener noreferrer'
- unless only_key_value
- .ci-variable-body-item.ci-variable-protected-item
+ .ci-variable-body-item.ci-variable-protected-item.table-section.section-20.mr-0.border-top-0
.append-right-default
= s_("CiVariable|Protected")
%button{ type: 'button',
@@ -55,7 +55,7 @@
%span.toggle-icon
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
- .ci-variable-body-item.ci-variable-masked-item
+ .ci-variable-body-item.ci-variable-masked-item.table-section.section-20.mr-0.border-top-0
.append-right-default
= s_("CiVariable|Masked")
%button{ type: 'button',
@@ -70,5 +70,5 @@
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
= render_if_exists 'ci/variables/environment_scope', form_field: form_field, variable: variable
- %button.js-row-remove-button.ci-variable-row-remove-button{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
- = icon('minus-circle')
+ %button.js-row-remove-button.ci-variable-row-remove-button.table-section.section-5.border-top-0{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
+ = icon('minus-circle')
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 09cc713e3af..021c0b6c429 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -14,6 +14,8 @@
= render 'shared/members/requests', membership_source: @group, requesters: @requesters
+ = render_if_exists 'groups/group_members/ldap_sync'
+
.clearfix
%h5.member.existing-title
Existing members
diff --git a/app/views/layouts/_piwik.html.haml b/app/views/layouts/_piwik.html.haml
index a888e8ae187..473b14ce626 100644
--- a/app/views/layouts/_piwik.html.haml
+++ b/app/views/layouts/_piwik.html.haml
@@ -7,7 +7,7 @@
(function() {
var u="//#{extra_config.piwik_url}/";
_paq.push(['setTrackerUrl', u+'piwik.php']);
- _paq.push(['setSiteId', #{extra_config.piwik_site_id}]);
+ _paq.push(['setSiteId', "#{extra_config.piwik_site_id}"]);
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.type='text/javascript'; g.async=true; g.defer=true; g.src=u+'piwik.js'; s.parentNode.insertBefore(g,s);
})();
diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml
index 1e3bb8f1224..2061eac917f 100644
--- a/app/views/layouts/nav/sidebar/_profile.html.haml
+++ b/app/views/layouts/nav/sidebar/_profile.html.haml
@@ -4,7 +4,7 @@
= link_to profile_path, title: _('Profile Settings') do
.avatar-container.s40.settings-avatar
= image_tag avatar_icon_for_user(current_user, 40), class: "avatar s40 avatar-tile", alt: current_user.name
- .sidebar-context-title User Settings
+ .sidebar-context-title= _('User Settings')
%ul.sidebar-top-level-items
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path do
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index bfe1c3ddf33..58f2eb229ba 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -82,5 +82,31 @@
= f.label :first_day_of_week, class: 'label-bold' do
= _('First day of the week')
= f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'form-control'
+ - if Feature.enabled?(:user_time_settings)
+ .col-sm-12
+ %hr
+ .col-lg-4.profile-settings-sidebar
+ %h4.prepend-top-0= s_('Preferences|Time preferences')
+ %p= s_('Preferences|These settings will update how dates and times are displayed for you.')
+ .col-lg-8
+ .form-group
+ %h5= s_('Preferences|Time format')
+ .checkbox-icon-inline-wrapper.form-check
+ - time_format_label = capture do
+ = s_('Preferences|Display time in 24-hour format')
+ = f.check_box :time_format_in_24h, class: 'form-check-input'
+ = f.label :time_format_in_24h do
+ = time_format_label
+ %h5= s_('Preferences|Time display')
+ .checkbox-icon-inline-wrapper.form-check
+ - time_display_label = capture do
+ = s_('Preferences|Use relative times')
+ = f.check_box :time_display_relative, class: 'form-check-input'
+ = f.label :time_display_relative do
+ = time_display_label
+ .text-muted
+ = s_('Preferences|For example: 30 mins ago.')
+ .col-lg-4.profile-settings-sidebar
+ .col-lg-8
.form-group
= f.submit _('Save changes'), class: 'btn btn-success'
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 02c750a92c3..917e7acc353 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -64,6 +64,18 @@
prepend: emoji_button,
append: reset_message_button,
placeholder: s_("Profiles|What's your status?")
+ - if Feature.enabled?(:user_time_settings)
+ %hr
+ .row.user-time-preferences
+ .col-lg-4.profile-settings-sidebar
+ %h4.prepend-top-0= s_("Profiles|Time settings")
+ %p= s_("Profiles|You can set your current timezone here")
+ .col-lg-8
+ -# TODO: might need an entry in user/profile.md to describe some of these settings
+ -# https://gitlab.com/gitlab-org/gitlab-ce/issues/60070
+ %h5= ("Time zone")
+ = dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown input-lg', title: _("Select a timezone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
+ %input.hidden{ :type => 'hidden', :id => 'user_timezone', :name => 'user[timezone]', value: @user.timezone }
%hr
.row
@@ -80,8 +92,8 @@
= f.text_field :name, required: true, readonly: true, wrapper: { class: 'col-md-9 qa-full-name' },
help: s_("Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you") % { provider_label: attribute_provider_label(:name) }
- else
- = f.text_field :name, label: 'Full name', required: true, title: s_("Profiles|Using emojis in names seems fun, but please try to set a status message instead"), wrapper: { class: 'col-md-9 qa-full-name' }, help: s_("Profiles|Enter your name, so people you know can recognize you")
- = f.text_field :id, readonly: true, label: 'User ID', wrapper: { class: 'col-md-3' }
+ = f.text_field :name, label: s_('Profiles|Full name'), required: true, title: s_("Profiles|Using emojis in names seems fun, but please try to set a status message instead"), wrapper: { class: 'col-md-9 qa-full-name' }, help: s_("Profiles|Enter your name, so people you know can recognize you")
+ = f.text_field :id, readonly: true, label: s_('Profiles|User ID'), wrapper: { class: 'col-md-3' }
= render_if_exists 'profiles/email_settings', form: f
= f.text_field :skype, class: 'input-md', placeholder: s_("Profiles|username")
@@ -91,18 +103,18 @@
- if @user.read_only_attribute?(:location)
= f.text_field :location, readonly: true, help: s_("Profiles|Your location was automatically set based on your %{provider_label} account") % { provider_label: attribute_provider_label(:location) }
- else
- = f.text_field :location, class: 'input-lg', placeholder: s_("Profiles|City, country")
- = f.text_field :organization, class: 'input-md', help: s_("Profiles|Who you represent or work for")
- = f.text_area :bio, rows: 4, maxlength: 250, help: s_("Profiles|Tell us about yourself in fewer than 250 characters")
+ = f.text_field :location, label: s_('Profiles|Location'), class: 'input-lg', placeholder: s_("Profiles|City, country")
+ = f.text_field :organization, label: s_('Profiles|Organization'), class: 'input-md', help: s_("Profiles|Who you represent or work for")
+ = f.text_area :bio, label: s_('Profiles|Bio'), rows: 4, maxlength: 250, help: s_("Profiles|Tell us about yourself in fewer than 250 characters")
%hr
- %h5= ("Private profile")
+ %h5= s_("Private profile")
.checkbox-icon-inline-wrapper
- private_profile_label = capture do
= s_("Profiles|Don't display activity-related personal information on your profiles")
= f.check_box :private_profile, label: private_profile_label, inline: true, wrapper_class: 'mr-0'
= link_to icon('question-circle'), help_page_path('user/profile/index.md', anchor: 'private-profile')
%h5= s_("Profiles|Private contributions")
- = f.check_box :include_private_contributions, label: 'Include private contributions on my profile', wrapper_class: 'mb-2', inline: true
+ = f.check_box :include_private_contributions, label: s_('Profiles|Include private contributions on my profile'), wrapper_class: 'mb-2', inline: true
.help-block
= s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information")
.prepend-top-default.append-bottom-default
diff --git a/app/views/repository_check_mailer/notify.text.haml b/app/views/repository_check_mailer/notify.text.haml
index 6b64b337b0e..a2e04fa710f 100644
--- a/app/views/repository_check_mailer/notify.text.haml
+++ b/app/views/repository_check_mailer/notify.text.haml
@@ -3,3 +3,5 @@
= _("View details: %{details_url}") % { details_url: admin_projects_url(last_repository_check_failed: 1) }
= _("You are receiving this message because you are a GitLab administrator for %{url}.") % { url: Gitlab.config.gitlab.url }
+
+= render_if_exists 'repository_check_mailer/email_additional_text'
diff --git a/app/views/shared/issuable/form/_contribution.html.haml b/app/views/shared/issuable/form/_contribution.html.haml
index bc9a1edc39c..a78231b37ce 100644
--- a/app/views/shared/issuable/form/_contribution.html.haml
+++ b/app/views/shared/issuable/form/_contribution.html.haml
@@ -15,6 +15,6 @@
= form.check_box :allow_collaboration, disabled: !issuable.can_allow_collaboration?(current_user), class: 'form-check-input'
= form.label :allow_collaboration, class: 'form-check-label' do
= _('Allow commits from members who can merge to the target branch.')
- = link_to 'About this feature', help_page_path('user/project/merge_requests/allow_collaboration')
+ = link_to 'About this feature', help_page_path('user/project/merge_requests/allow_collaboration'), target: '_blank', rel: 'noopener noreferrer nofollow'
.form-text.text-muted
= allow_collaboration_unavailable_reason(issuable)
diff --git a/app/workers/detect_repository_languages_worker.rb b/app/workers/detect_repository_languages_worker.rb
index 64bc9776d48..838c3be78f0 100644
--- a/app/workers/detect_repository_languages_worker.rb
+++ b/app/workers/detect_repository_languages_worker.rb
@@ -12,13 +12,12 @@ class DetectRepositoryLanguagesWorker
attr_reader :project
# rubocop: disable CodeReuse/ActiveRecord
- def perform(project_id, user_id)
+ def perform(project_id, user_id = nil)
@project = Project.find_by(id: project_id)
- user = User.find_by(id: user_id)
- return unless project && user
+ return unless project
try_obtain_lease do
- ::Projects::DetectRepositoryLanguagesService.new(project, user).execute
+ ::Projects::DetectRepositoryLanguagesService.new(project).execute
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/changelogs/unreleased/27987-use-findorcreateservice-to-create-labels.yml b/changelogs/unreleased/27987-use-findorcreateservice-to-create-labels.yml
new file mode 100644
index 00000000000..8d3501e0171
--- /dev/null
+++ b/changelogs/unreleased/27987-use-findorcreateservice-to-create-labels.yml
@@ -0,0 +1,5 @@
+---
+title: Use FindOrCreateService to create labels and check for existing ones
+merge_request: 27987
+author: Matt Duren
+type: fixed
diff --git a/changelogs/unreleased/53973-fix-subpixel-border-issue.yml b/changelogs/unreleased/53973-fix-subpixel-border-issue.yml
new file mode 100644
index 00000000000..0dae7047236
--- /dev/null
+++ b/changelogs/unreleased/53973-fix-subpixel-border-issue.yml
@@ -0,0 +1,5 @@
+---
+title: Fix MR discussion border missing in chrome sometimes
+merge_request: 28185
+author:
+type: fixed
diff --git a/changelogs/unreleased/57654-add-time-preferences-for-user-fe.yml b/changelogs/unreleased/57654-add-time-preferences-for-user-fe.yml
new file mode 100644
index 00000000000..f4ce3a51724
--- /dev/null
+++ b/changelogs/unreleased/57654-add-time-preferences-for-user-fe.yml
@@ -0,0 +1,5 @@
+---
+title: Add time preferences for user
+merge_request: 25381
+author:
+type: added
diff --git a/changelogs/unreleased/58404-set-default-max-depth-for-GraphQL.yml b/changelogs/unreleased/58404-set-default-max-depth-for-GraphQL.yml
new file mode 100644
index 00000000000..7e95158a0e0
--- /dev/null
+++ b/changelogs/unreleased/58404-set-default-max-depth-for-GraphQL.yml
@@ -0,0 +1,5 @@
+---
+title: 58404 - setup max depth for GraphQL
+merge_request: 25737
+author: Ken Ding
+type: added
diff --git a/changelogs/unreleased/60180-jira-service-fix-nil-on-find-call.yml b/changelogs/unreleased/60180-jira-service-fix-nil-on-find-call.yml
new file mode 100644
index 00000000000..6891a9ca83c
--- /dev/null
+++ b/changelogs/unreleased/60180-jira-service-fix-nil-on-find-call.yml
@@ -0,0 +1,5 @@
+---
+title: 'Resolved JIRA service: NoMethodError: undefined method ''find'' for nil:NilClass'
+merge_request: 28206
+author:
+type: fixed
diff --git a/changelogs/unreleased/60425-fix-500-when-accessing-charts-with-anonymous-user.yml b/changelogs/unreleased/60425-fix-500-when-accessing-charts-with-anonymous-user.yml
new file mode 100644
index 00000000000..4274dc5918c
--- /dev/null
+++ b/changelogs/unreleased/60425-fix-500-when-accessing-charts-with-anonymous-user.yml
@@ -0,0 +1,5 @@
+---
+title: "Fix 500 error when accessing charts with an anonymous user"
+merge_request: 28091
+author: Diego Silva
+type: fixed
diff --git a/changelogs/unreleased/61494-set-status-modal-visual-bugs.yml b/changelogs/unreleased/61494-set-status-modal-visual-bugs.yml
new file mode 100644
index 00000000000..4126b8f93c1
--- /dev/null
+++ b/changelogs/unreleased/61494-set-status-modal-visual-bugs.yml
@@ -0,0 +1,5 @@
+---
+title: Fix visual issues in set status modal
+merge_request: 28147
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-warning-to-backup-rake-task.yml b/changelogs/unreleased/add-warning-to-backup-rake-task.yml
new file mode 100644
index 00000000000..7ddeae3f9fd
--- /dev/null
+++ b/changelogs/unreleased/add-warning-to-backup-rake-task.yml
@@ -0,0 +1,5 @@
+---
+title: Add warning that gitlab-secrets isn't included in backup
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/antonyliu-i18n-user-profile.yml b/changelogs/unreleased/antonyliu-i18n-user-profile.yml
new file mode 100644
index 00000000000..f9065ee5697
--- /dev/null
+++ b/changelogs/unreleased/antonyliu-i18n-user-profile.yml
@@ -0,0 +1,5 @@
+---
+title: 'i18n: externalize strings from user profile settings'
+merge_request: 28088
+author: Antony Liu
+type: other
diff --git a/changelogs/unreleased/sh-fix-invited-members.yml b/changelogs/unreleased/sh-fix-invited-members.yml
new file mode 100644
index 00000000000..96e43e1aa53
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-invited-members.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Error 500 when inviting user already present
+merge_request: 28198
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-upgrade-ruby-2-6-3-ce.yml b/changelogs/unreleased/sh-upgrade-ruby-2-6-3-ce.yml
new file mode 100644
index 00000000000..9ad5c9ebb64
--- /dev/null
+++ b/changelogs/unreleased/sh-upgrade-ruby-2-6-3-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade Ruby version to 2.6.3
+merge_request: 28117
+author:
+type: performance
diff --git a/changelogs/unreleased/winh-notes-error-handling.yml b/changelogs/unreleased/winh-notes-error-handling.yml
new file mode 100644
index 00000000000..6f23dd459d4
--- /dev/null
+++ b/changelogs/unreleased/winh-notes-error-handling.yml
@@ -0,0 +1,5 @@
+---
+title: Handle errors in successful notes reply
+merge_request: 28082
+author:
+type: fixed
diff --git a/config/initializers/01_secret_token.rb b/config/initializers/01_secret_token.rb
index 02bded43083..4328ca509ba 100644
--- a/config/initializers/01_secret_token.rb
+++ b/config/initializers/01_secret_token.rb
@@ -28,7 +28,8 @@ def create_tokens
secret_key_base: file_secret_key || generate_new_secure_token,
otp_key_base: env_secret_key || file_secret_key || generate_new_secure_token,
db_key_base: generate_new_secure_token,
- openid_connect_signing_key: generate_new_rsa_private_key
+ openid_connect_signing_key: generate_new_rsa_private_key,
+ lets_encrypt_private_key: generate_lets_encrypt_private_key
}
missing_secrets = set_missing_keys(defaults)
@@ -49,6 +50,10 @@ def generate_new_rsa_private_key
OpenSSL::PKey::RSA.new(2048).to_pem
end
+def generate_lets_encrypt_private_key
+ OpenSSL::PKey::RSA.new(4096).to_pem
+end
+
def warn_missing_secret(secret)
warn "Missing Rails.application.secrets.#{secret} for #{Rails.env} environment. The secret will be generated and stored in config/secrets.yml."
end
diff --git a/db/migrate/20190325105715_add_fields_to_user_preferences.rb b/db/migrate/20190325105715_add_fields_to_user_preferences.rb
new file mode 100644
index 00000000000..9ea3b4f9cd8
--- /dev/null
+++ b/db/migrate/20190325105715_add_fields_to_user_preferences.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddFieldsToUserPreferences < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_column(:user_preferences, :timezone, :string)
+ add_column(:user_preferences, :time_display_relative, :boolean)
+ add_column(:user_preferences, :time_format_in_24h, :boolean)
+ end
+
+ def down
+ remove_column(:user_preferences, :timezone)
+ remove_column(:user_preferences, :time_display_relative)
+ remove_column(:user_preferences, :time_format_in_24h)
+ end
+end
diff --git a/db/migrate/20190412155659_add_merge_request_blocks.rb b/db/migrate/20190412155659_add_merge_request_blocks.rb
new file mode 100644
index 00000000000..9e7f370d1cf
--- /dev/null
+++ b/db/migrate/20190412155659_add_merge_request_blocks.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class AddMergeRequestBlocks < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :merge_request_blocks, id: :bigserial do |t|
+ t.references :blocking_merge_request,
+ index: false, null: false,
+ foreign_key: { to_table: :merge_requests, on_delete: :cascade }
+
+ t.references :blocked_merge_request,
+ index: true, null: false,
+ foreign_key: { to_table: :merge_requests, on_delete: :cascade }
+
+ t.index [:blocking_merge_request_id, :blocked_merge_request_id],
+ unique: true,
+ name: 'index_mr_blocks_on_blocking_and_blocked_mr_ids'
+
+ t.timestamps_with_timezone
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index deaf406fe3d..159e7e03cf4 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1227,6 +1227,15 @@ ActiveRecord::Schema.define(version: 20190506135400) do
t.index ["user_id"], name: "index_merge_request_assignees_on_user_id", using: :btree
end
+ create_table "merge_request_blocks", force: :cascade do |t|
+ t.integer "blocking_merge_request_id", null: false
+ t.integer "blocked_merge_request_id", null: false
+ t.datetime_with_timezone "created_at", null: false
+ t.datetime_with_timezone "updated_at", null: false
+ t.index ["blocked_merge_request_id"], name: "index_merge_request_blocks_on_blocked_merge_request_id", using: :btree
+ t.index ["blocking_merge_request_id", "blocked_merge_request_id"], name: "index_mr_blocks_on_blocking_and_blocked_mr_ids", unique: true, using: :btree
+ end
+
create_table "merge_request_diff_commits", id: false, force: :cascade do |t|
t.datetime_with_timezone "authored_date"
t.datetime_with_timezone "committed_date"
@@ -2246,6 +2255,9 @@ ActiveRecord::Schema.define(version: 20190506135400) do
t.integer "first_day_of_week"
t.string "issues_sort"
t.string "merge_requests_sort"
+ t.string "timezone"
+ t.boolean "time_display_relative"
+ t.boolean "time_format_in_24h"
t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true, using: :btree
end
@@ -2511,6 +2523,8 @@ ActiveRecord::Schema.define(version: 20190506135400) do
add_foreign_key "members", "users", name: "fk_2e88fb7ce9", on_delete: :cascade
add_foreign_key "merge_request_assignees", "merge_requests", on_delete: :cascade
add_foreign_key "merge_request_assignees", "users", on_delete: :cascade
+ add_foreign_key "merge_request_blocks", "merge_requests", column: "blocked_merge_request_id", on_delete: :cascade
+ add_foreign_key "merge_request_blocks", "merge_requests", column: "blocking_merge_request_id", on_delete: :cascade
add_foreign_key "merge_request_diff_commits", "merge_request_diffs", on_delete: :cascade
add_foreign_key "merge_request_diff_files", "merge_request_diffs", on_delete: :cascade
add_foreign_key "merge_request_diffs", "merge_requests", name: "fk_8483f3258f", on_delete: :cascade
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index d383d1efe70..b7b820abb40 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -57,8 +57,7 @@ you can change these defaults by editing the `/etc/tomcat7/server.xml` file.
You need to enable PlantUML integration from Settings under Admin Area. To do
that, login with an Admin account and do following:
- - in GitLab go to **Admin Area** and then **Settings**
- - scroll to bottom of the page until PlantUML section
+ - in GitLab go to **Admin Area**->**Settings**->**Integrations**->**PlantUML**
- check **Enable PlantUML** checkbox
- set the PlantUML instance as **PlantUML URL**
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index e079483e2b5..3f72fe3e9eb 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -289,7 +289,7 @@ jobs inherit it. Gems are installed in `vendor/ruby/` and are cached per-branch:
#
# https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
#
-image: ruby:2.5
+image: ruby:2.6
# Cache gems in between builds
cache:
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 7945a48fe40..88c16a8db22 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -1,6 +1,3 @@
-<script src="https://unpkg.com/mermaid@8.0.0/dist/mermaid.min.js"></script>
-<script>mermaid.initialize({startOnLoad:true});</script>
-
# GitLab Architecture Overview
## Software delivery
@@ -213,107 +210,75 @@ To serve repositories over SSH there's an add-on application called gitlab-shell
### Components
-<div class="mermaid">
+```mermaid
graph TB
- HTTP[HTTP/HTTPS] -- TCP 80, 443 --> NGINX(NGINX)
- SSH --TCP 22 --> GitLabShell(GitLab Shell)
- SMTP(SMTP Gateway)
- Geo(GitLab Geo Node) -- TCP 22, 80, 443 --> NGINX
-
- subgraph GitLab
- GitLabShell --TCP 8080 -->Unicorn["Unicorn (GitLab Rails)"]
- GitLabShell --> Gitaly
- GitLabShell --> Redis
- Unicorn --> PgBouncer(PgBouncer)
- Unicorn --> Redis
- Unicorn --> Gitaly
- Redis --> Sidekiq
- Sidekiq("Sidekiq (GitLab Rails, ES Indexer)") --> PgBouncer
- GitLabWorkhorse(GitLab Workhorse) --> Unicorn
- GitLabWorkhorse --> Redis
- GitLabWorkhorse --> Gitaly
- Gitaly --> Redis
- NGINX --> GitLabWorkhorse
- NGINX -- TCP 8090 --> GitLabPages(GitLab Pages)
- NGINX --> Grafana(Grafana)
- Grafana -- TCP 9090 --> Prometheus(Prometheus)
- Prometheus -- TCP 80, 443 --> Unicorn
- RedisExporter(Redis Exporter) --> Redis
- Prometheus -- TCP 9121 --> RedisExporter
- PostgreSQLExporter(PostgreSQL Exporter) --> PostgreSQL
- PgBouncerExporter(PgBouncer Exporter) --> PgBouncer
- Prometheus -- TCP 9187 --> PostgreSQLExporter
- Prometheus -- TCP 9100 --> NodeExporter(Node Exporter)
- Prometheus -- TCP 9168 --> GitLabMonitor(GitLab Monitor)
- Prometheus -- TCP 9127 --> PgBouncerExporter
- GitLabMonitor --> PostgreSQL
- GitLabMonitor --> GitLabShell
- GitLabMonitor --> Sidekiq
- PgBouncer --> Consul(Consul)
- PostgreSQL --> Consul
- PgBouncer --> PostgreSQL
- NGINX --> Registry(Registry)
- Unicorn --> Registry
- NGINX --> Mattermost(Mattermost)
- Mattermost --- Unicorn
- Prometheus --> Alertmanager
- Migrations --> PostgreSQL
- Runner(Runner) --> NGINX
- Unicorn -- TCP 9200 --> ElasticSearch
- Sidekiq -- TCP 9200 --> ElasticSearch
- Sidekiq -- TCP 80, 443 --> Sentry
- Unicorn -- TCP 80, 443 --> Sentry
- Sidekiq -- UDP 6831 --> Jaeger
- Unicorn -- UDP 6831 --> Jaeger
- Gitaly -- UDP 6831 --> Jaeger
- GitLabShell -- UDP 6831 --> Jaeger
- GitLabWorkhorse -- UDP 6831 --> Jaeger
- Alertmanager -- TCP 25 --> SMTP
- Sidekiq -- TCP 25 --> SMTP
- Unicorn -- TCP 25 --> SMTP
- Unicorn -- TCP 369 --> LDAP
- Sidekiq -- TCP 369 --> LDAP
- Unicorn -- TCP 443 --> ObjectStorage("Object Storage")
- Sidekiq -- TCP 443 --> ObjectStorage
- GitLabWorkhorse -- TCP 443 --> ObjectStorage
- Registry -- TCP 443 --> ObjectStorage
- Geo -- TCP 5432 --> PostgreSQL
- end
-
- HTTPK8s(HTTP/HTTPS) -- TCP 80, 443 --> LoadBalancerK8s(Load Balancer)
- LoadBalancerK8s -- TCP 80, 443 --> nginx-ingressK8s
- subgraph Kubernetes
- PrometheusK8s(Prometheus)
- TillerK8s(Tiller)
- nginx-ingressK8s(NGINX Ingress)
- Cert-ManagerK8s(Cert-Manager)
- GitLabRunnerK8s(GitLab Runner)
- GitLabRunnerK8s --> NGINX
- JupyterHubK8s(JupyterHub)
- nginx-ingressK8s --> JupyterHubK8s
- KnativeK8s(Knative)
- end
-
-classDef defaultoff stroke-dasharray: 5, 5
-class ElasticSearch defaultoff
-class Grafana defaultoff
-class PrometheusK8s defaultoff
-class TillerK8s defaultoff
-class nginx-ingressK8s defaultoff
-class Cert-ManagerK8s defaultoff
-class GitLabRunnerK8s defaultoff
-class JupyterHubK8s defaultoff
-class KnativeK8s defaultoff
-class HTTPK8s defaultoff
-class LoadBalancerK8s defaultoff
-class Sentry defaultoff
-class Jaeger defaultoff
-class Alertmanager defaultoff
-class SMTP defaultoff
-class ObjectStorage defaultoff
-class Geo defaultoff
-</div>
+ HTTP[HTTP/HTTPS] -- TCP 80, 443 --> NGINX[NGINX]
+ SSH -- TCP 22 --> GitLabShell[GitLab Shell]
+ SMTP[SMTP Gateway]
+ Geo[GitLab Geo Node] -- TCP 22, 80, 443 --> NGINX
+
+ GitLabShell --TCP 8080 -->Unicorn["Unicorn (GitLab Rails)"]
+ GitLabShell --> Gitaly
+ GitLabShell --> Redis
+ Unicorn --> PgBouncer[PgBouncer]
+ Unicorn --> Redis
+ Unicorn --> Gitaly
+ Redis --> Sidekiq
+ Sidekiq["Sidekiq (GitLab Rails, ES Indexer)"] --> PgBouncer
+ GitLabWorkhorse[GitLab Workhorse] --> Unicorn
+ GitLabWorkhorse --> Redis
+ GitLabWorkhorse --> Gitaly
+ Gitaly --> Redis
+ NGINX --> GitLabWorkhorse
+ NGINX -- TCP 8090 --> GitLabPages[GitLab Pages]
+ NGINX --> Grafana[Grafana]
+ Grafana -- TCP 9090 --> Prometheus[Prometheus]
+ Prometheus -- TCP 80, 443 --> Unicorn
+ RedisExporter[Redis Exporter] --> Redis
+ Prometheus -- TCP 9121 --> RedisExporter
+ PostgreSQLExporter[PostgreSQL Exporter] --> PostgreSQL
+ PgBouncerExporter[PgBouncer Exporter] --> PgBouncer
+ Prometheus -- TCP 9187 --> PostgreSQLExporter
+ Prometheus -- TCP 9100 --> NodeExporter[Node Exporter]
+ Prometheus -- TCP 9168 --> GitLabMonito[GitLab Monitor]
+ Prometheus -- TCP 9127 --> PgBouncerExporter
+ GitLabMonitor --> PostgreSQL
+ GitLabMonitor --> GitLabShell
+ GitLabMonitor --> Sidekiq
+ PgBouncer --> Consul
+ PostgreSQL --> Consul
+ PgBouncer --> PostgreSQL
+ NGINX --> Registry
+ Unicorn --> Registry
+ NGINX --> Mattermost
+ Mattermost --- Unicorn
+ Prometheus --> Alertmanager
+ Migrations --> PostgreSQL
+ Runner -- TCP 443 --> NGINX
+ Unicorn -- TCP 9200 --> ElasticSearch
+ Sidekiq -- TCP 9200 --> ElasticSearch
+ Sidekiq -- TCP 80, 443 --> Sentry
+ Unicorn -- TCP 80, 443 --> Sentry
+ Sidekiq -- UDP 6831 --> Jaeger
+ Unicorn -- UDP 6831 --> Jaeger
+ Gitaly -- UDP 6831 --> Jaeger
+ GitLabShell -- UDP 6831 --> Jaeger
+ GitLabWorkhorse -- UDP 6831 --> Jaeger
+ Alertmanager -- TCP 25 --> SMTP
+ Sidekiq -- TCP 25 --> SMTP
+ Unicorn -- TCP 25 --> SMTP
+ Unicorn -- TCP 369 --> LDAP
+ Sidekiq -- TCP 369 --> LDAP
+ Unicorn -- TCP 443 --> ObjectStorage["Object Storage"]
+ Sidekiq -- TCP 443 --> ObjectStorage
+ GitLabWorkhorse -- TCP 443 --> ObjectStorage
+ Registry -- TCP 443 --> ObjectStorage
+ Geo -- TCP 5432 --> PostgreSQL
+
+```
+
+---
**Legend**:
@@ -324,7 +289,7 @@ class Geo defaultoff
| Component | Description | [Omnibus GitLab](https://docs.gitlab.com/omnibus/README.html) | [GitLab chart](https://docs.gitlab.com/charts/) | [Minikube Minimal](https://docs.gitlab.com/charts/development/minikube/#deploying-gitlab-with-minimal-settings) | [GitLab.com](https://gitlab.com) | CE/EE |
| --------- | ----------- |:--------------------:|:------------------:|:-----:|:--------:|:--------:|
-| NGINX | Routes requests to appropriate components, terminates SSL | [✅](https://docs.gitlab.com/omnibus/settings/nginx.html) | [✅](https://docs.gitlab.com/charts/charts/nginx/index.html) | [⚙](https://docs.gitlab.com/charts/charts/nginx/index.html) | [✅](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) |CE & EE |
+| NGINX | Routes requests to appropriate components, terminates SSL | [✅](https://docs.gitlab.com/omnibus/settings/nginx.html) | [✅](https://docs.gitlab.com/charts/charts/nginx/index.html) | [⚙](https://docs.gitlab.com/charts/charts/nginx/index.html) | [✅](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | CE & EE |
| Unicorn (GitLab Rails) | Handles requests for the web interface and API | [✅](https://docs.gitlab.com/omnibus/settings/unicorn.html) | [✅](https://docs.gitlab.com/charts/charts/gitlab/unicorn/index.html) | [✅](https://docs.gitlab.com/charts/charts/gitlab/unicorn/index.html) | [✅](https://docs.gitlab.com/ee/user/gitlab_com/#unicorn) | CE & EE |
| Sidekiq | Background jobs processor | [✅](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template) | [✅](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/index.html) | [✅](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/index.html) | [✅](https://docs.gitlab.com/ee/user/gitlab_com/#sidekiq) | CE & EE |
| Gitaly | Git RPC service for handling all git calls made by GitLab | [✅](https://docs.gitlab.com/ee/administration/gitaly/) | [✅](https://docs.gitlab.com/charts/charts/gitlab/gitaly/index.html) | [✅](https://docs.gitlab.com/charts/charts/gitlab/gitaly/index.html) | [✅](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#service-architecture) | CE & EE |
@@ -342,18 +307,18 @@ class Geo defaultoff
| Redis Exporter | Prometheus endpoint with Redis metrics | [✅](https://docs.gitlab.com/ee/administration/monitoring/prometheus/redis_exporter.html) | [✅](https://docs.gitlab.com/charts/charts/redis/index.html) | [✅](https://docs.gitlab.com/charts/charts/redis/index.html) | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE |
| PostgreSQL Exporter | Prometheus endpoint with PostgreSQL metrics | [✅](https://docs.gitlab.com/ee/administration/monitoring/prometheus/postgres_exporter.html) | [✅](https://github.com/helm/charts/tree/master/stable/postgresql) | [✅](https://github.com/helm/charts/tree/master/stable/postgresql) | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE |
| PgBouncer Exporter | Prometheus endpoint with PgBouncer metrics | [⚙](https://docs.gitlab.com/ee/administration/monitoring/prometheus/pgbouncer_exporter.html) | [❌](https://docs.gitlab.com/charts/installation/deployment.html#postgresql) | [❌](https://docs.gitlab.com/charts/installation/deployment.html#postgresql) | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE |
-| GitLab Monitor | Tracks a variety of GitLab metrics | [✅](https://docs.gitlab.com/ee/administration/monitoring/prometheus/gitlab_monitor_exporter.html) | [❌](https://gitlab.com/charts/gitlab/issues/319) | [❌](https://gitlab.com/charts/gitlab/issues/319) | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE |
+| GitLab Monitor | Generates a variety of GitLab metrics | [✅](https://docs.gitlab.com/ee/administration/monitoring/prometheus/gitlab_monitor_exporter.html) | [❌](https://gitlab.com/charts/gitlab/issues/319) | [❌](https://gitlab.com/charts/gitlab/issues/319) | [✅](https://about.gitlab.com/handbook/engineering/monitoring/) | CE & EE |
| Mattermost | Open-source Slack alternative | [⚙](https://docs.gitlab.com/omnibus/gitlab-mattermost/) | [⤓](https://docs.mattermost.com/install/install-mmte-helm-gitlab-helm.html) | [⤓](https://docs.mattermost.com/install/install-mmte-helm-gitlab-helm.html) | [⤓](https://docs.gitlab.com/ee/user/project/integrations/mattermost_slash_commands.html#manual-configuration), [⤓](https://docs.gitlab.com/ee/user/project/integrations/mattermost.html) | CE & EE |
-| Minio | Object storage service | [⤓](https://min.io/download) | [✅](https://docs.gitlab.com/charts/charts/minio/index.html) | [✅](https://docs.gitlab.com/charts/charts/minio/index.html) | [❌](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#storage-architecture) | CE & EE |
+| Minio | Object storage service | [⤓](https://min.io/download) | [✅](https://docs.gitlab.com/charts/charts/minio/index.html) | [✅](https://docs.gitlab.com/charts/charts/minio/index.html) | [✅](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#storage-architecture) | CE & EE |
| Runner | Executes GitLab CI jobs | [⤓](https://docs.gitlab.com/runner/) | [✅](https://docs.gitlab.com/runner/) | [⚙](https://docs.gitlab.com/runner/) | [✅](https://docs.gitlab.com/ee/user/gitlab_com/#shared-runners) | CE & EE |
-| Migrations | Database migrations | [✅](https://docs.gitlab.com/omnibus/settings/database.html#disabling-automatic-database-migration) | [✅](https://docs.gitlab.com/charts/charts/gitlab/migrations/index.html) | [✅](https://docs.gitlab.com/charts/charts/gitlab/migrations/index.html) | [✅](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#database-architecture) | CE & EE |
+| DB Migrations | Database migrations | [✅](https://docs.gitlab.com/omnibus/settings/database.html#disabling-automatic-database-migration) | [✅](https://docs.gitlab.com/charts/charts/gitlab/migrations/index.html) | [✅](https://docs.gitlab.com/charts/charts/gitlab/migrations/index.html) | [✅](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#database-architecture) | CE & EE |
| Certificate Management | TLS Settings, Let's Encrypt | [✅](https://docs.gitlab.com/omnibus/settings/ssl.html) | [✅](https://docs.gitlab.com/charts/installation/tls.html) | [⚙](https://docs.gitlab.com/charts/installation/tls.html) | [✅](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/#secrets-management) | CE & EE |
| GitLab Geo Node | Geographically distributed GitLab nodes | [⚙](https://docs.gitlab.com/ee/administration/geo/replication/index.html#setup-instructions) | [❌](https://gitlab.com/charts/gitlab/issues/8) | [❌](https://gitlab.com/charts/gitlab/issues/8) | ✅ | EE Only |
| LDAP Authentication | Authenticate users against centralized LDAP directory | [⤓](https://docs.gitlab.com/ee/administration/auth/ldap.html) | [⤓](https://docs.gitlab.com/charts/charts/globals.html#ldap) | [⤓](https://docs.gitlab.com/charts/charts/globals.html#ldap) | [❌](https://about.gitlab.com/pricing/#gitlab-com) | CE & EE |
| Outbound email (SMTP) | Send email messages to users | [⤓](https://docs.gitlab.com/omnibus/settings/smtp.html) | [⤓](https://docs.gitlab.com/charts/installation/command-line-options.html#outgoing-email-configuration) | [⤓](https://docs.gitlab.com/charts/installation/command-line-options.html#outgoing-email-configuration) | [✅](https://docs.gitlab.com/ee/user/gitlab_com/#mail-configuration) | CE & EE |
| Inbound email (SMTP) | Receive messages to update issues | [⤓](https://docs.gitlab.com/ee/administration/incoming_email.html) | [⤓](https://docs.gitlab.com/charts/installation/command-line-options.html#incoming-email-configuration) | [⤓](https://docs.gitlab.com/charts/installation/command-line-options.html#incoming-email-configuration) | [✅](https://docs.gitlab.com/ee/user/gitlab_com/#mail-configuration) | CE & EE |
| ElasticSearch | Improved search within GitLab | [⤓](https://docs.gitlab.com/ee/integration/elasticsearch.html) | [⤓](https://docs.gitlab.com/ee/integration/elasticsearch.html) | [⤓](https://docs.gitlab.com/ee/integration/elasticsearch.html) | [❌](https://gitlab.com/groups/gitlab-org/-/epics/153) | EE Only |
-| Sentry: GitLab instance | Tracking errors generated by the GitLab instance | [⤓](https://docs.gitlab.com/omnibus/settings/configuration.html#error-reporting-and-logging-with-sentry) | [❌](https://gitlab.com/charts/gitlab/issues/1319) | [❌](https://gitlab.com/charts/gitlab/issues/1319) | [✅](https://about.gitlab.com/handbook/support/workflows/services/gitlab_com/500_errors.html#searching-sentry) | CE & EE |
+| Sentry: GitLab instance | Track errors generated by the GitLab instance | [⤓](https://docs.gitlab.com/omnibus/settings/configuration.html#error-reporting-and-logging-with-sentry) | [❌](https://gitlab.com/charts/gitlab/issues/1319) | [❌](https://gitlab.com/charts/gitlab/issues/1319) | [✅](https://about.gitlab.com/handbook/support/workflows/services/gitlab_com/500_errors.html#searching-sentry) | CE & EE |
| Jaeger: GitLab instance | View traces generated by the GitLab instance | [❌](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4104) | [❌](https://gitlab.com/charts/gitlab/issues/1320) | [❌](https://gitlab.com/charts/gitlab/issues/1320) | [❌](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4104) | CE & EE |
| Sentry: deployed apps | Error tracking for deployed apps | [⤓](https://docs.gitlab.com/ee/user/project/operations/error_tracking.html) | [⤓](https://docs.gitlab.com/ee/user/project/operations/error_tracking.html) | [⤓](https://docs.gitlab.com/ee/user/project/operations/error_tracking.html) | [⤓](https://docs.gitlab.com/ee/user/project/operations/error_tracking.html) | CE & EE |
| Jaeger: deployed apps | Distributed tracing for deployed apps | [⤓](https://docs.gitlab.com/ee/user/project/operations/tracing.html) | [⤓](https://docs.gitlab.com/ee/user/project/operations/tracing.html) | [⤓](https://docs.gitlab.com/ee/user/project/operations/tracing.html) | [⤓](https://docs.gitlab.com/ee/user/project/operations/tracing.html) | EE Only |
diff --git a/doc/install/installation.md b/doc/install/installation.md
index c694f0ed691..f16bc04af34 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -163,9 +163,9 @@ Download Ruby and compile it:
```sh
mkdir /tmp/ruby && cd /tmp/ruby
-curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.3.tar.gz
-echo 'f919a9fbcdb7abecd887157b49833663c5c15fda ruby-2.5.3.tar.gz' | shasum -c - && tar xzf ruby-2.5.3.tar.gz
-cd ruby-2.5.3
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.3.tar.gz
+echo '2347ed6ca5490a104ebd5684d2b9b5eefa6cd33c ruby-2.6.3.tar.gz' | shasum -c - && tar xzf ruby-2.6.3.tar.gz
+cd ruby-2.6.3
./configure --disable-install-rdoc
make
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 672723aaf12..f6a52205a0e 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -169,7 +169,7 @@ So for a machine with 2 cores, 3 unicorn workers is ideal.
For all machines that have 2GB and up we recommend a minimum of three unicorn workers.
If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive swapping.
-To change the Unicorn workers when you have the Omnibus package (which defaults to the recommendation above) please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings).
+To change the Unicorn workers when you have the Omnibus package (which defaults to the recommendation above) please see [the Unicorn settings in the Omnibus GitLab documentation](https://docs.gitlab.com/omnibus/settings/unicorn.html).
## Redis and Sidekiq
@@ -201,14 +201,13 @@ you decide to run GitLab Runner and the GitLab Rails application on the same
machine.
It is also not safe to install everything on a single machine, because of the
-[security reasons] - especially when you plan to use shell executor with GitLab
+[security reasons](https://docs.gitlab.com/runner/security/)
+- especially when you plan to use shell executor with GitLab
Runner.
We recommend using a separate machine for each GitLab Runner, if you plan to
use the CI features.
-[security reasons]: https://gitlab.com/gitlab-org/gitlab-runner/blob/master/docs/security/index.md
-
## Supported web browsers
We support the current and the previous major release of:
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index e607dbceeea..b83abcd36f7 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -45,6 +45,22 @@ For other distributions, follow the instructions in PostgreSQL's
[download page](https://www.postgresql.org/download/) to add their repository
and then install `pgloader`.
+If you are migrating to a Docker based installation, you will need to install
+pgloader within the container as it is not included in the container image.
+
+1. Start a shell session in the context of the running container:
+
+ ``` bash
+ docker exec -it gitlab bash
+ ```
+
+1. Install pgloader:
+
+ ``` bash
+ apt-get update
+ apt-get -y install pgloader
+ ```
+
## Omnibus GitLab installations
For [Omnibus GitLab packages](https://about.gitlab.com/install/), you'll first
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index fea89669831..5118726cb0a 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -52,9 +52,9 @@ Download Ruby and compile it:
```bash
mkdir /tmp/ruby && cd /tmp/ruby
-curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.3.tar.gz
-echo 'f919a9fbcdb7abecd887157b49833663c5c15fda ruby-2.5.3.tar.gz' | shasum -c - && tar xzf ruby-2.5.3.tar.gz
-cd ruby-2.5.3
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.3.tar.gz
+echo '2347ed6ca5490a104ebd5684d2b9b5eefa6cd33c ruby-2.6.3.tar.gz' | shasum -c - && tar xzf ruby-2.6.3.tar.gz
+cd ruby-2.6.3
./configure --disable-install-rdoc
make
diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md
index e165a120162..8b5d80efb0d 100644
--- a/doc/user/admin_area/settings/usage_statistics.md
+++ b/doc/user/admin_area/settings/usage_statistics.md
@@ -48,6 +48,8 @@ You can view the exact JSON payload in the administration panel. To view the pay
1. Expand **Settings** in the left sidebar and click on **Metrics and profiling**.
1. Expand **Usage statistics** and click on the **Preview payload** button.
+You can see how [the usage ping data maps to different stages of the product](https://gitlab.com/gitlab-data/analytics/blob/master/transform/snowflake-dbt/data/ping_metrics_to_stage_mapping_data.csv).
+
### Deactivate the usage ping
The usage ping is opt-out. If you want to deactivate this feature, go to
diff --git a/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md b/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md
index ea22f3e905b..da1b7c59c8e 100644
--- a/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md
+++ b/doc/user/project/pages/lets_encrypt_for_gitlab_pages.md
@@ -145,8 +145,8 @@ Now that your certificate has been issued, let's add it to your Pages site:
1. Visit your website at `https://example.com`.
To force `https` connections on your site, navigate to your
-project's **Settings > Pages** and check **Force domains with SSL
-certificates to use HTTPS**.
+project's **Settings > Pages** and check **Force HTTPS (requires
+valid certificates)**.
## Renewal
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index 769d3279f91..c9f0ed66a54 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -135,7 +135,7 @@ module Gitlab
def create_labels
LABELS.each do |label_params|
- label = ::Labels::CreateService.new(label_params).execute(project: project)
+ label = ::Labels::FindOrCreateService.new(nil, project, label_params).execute(skip_authorization: true)
if label.valid?
@labels[label_params[:title]] = label
else
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index 3977fc7ad8c..c531eb1d216 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -20,6 +20,11 @@ namespace :gitlab do
backup.pack
backup.cleanup
backup.remove_old
+
+ puts "Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data \n" \
+ "and are not included in this backup. You will need these files to restore a backup.\n" \
+ "Please back them up manually.".color(:red)
+ puts "Backup task is done."
end
# Restore backup of GitLab system
@@ -68,6 +73,9 @@ namespace :gitlab do
Rake::Task['cache:clear'].invoke
backup.cleanup
+ puts "Warning: Your gitlab.rb and gitlab-secrets.json files contain sensitive data \n" \
+ "and are not included in this backup. You will need to restore these files manually.".color(:red)
+ puts "Restore task is done."
end
namespace :repo do
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 2907430bd51..71ec8bcb9ba 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1894,9 +1894,24 @@ msgstr ""
msgid "CiVariables|Input variable value"
msgstr ""
+msgid "CiVariables|Key"
+msgstr ""
+
+msgid "CiVariables|Masked"
+msgstr ""
+
msgid "CiVariables|Remove variable row"
msgstr ""
+msgid "CiVariables|State"
+msgstr ""
+
+msgid "CiVariables|Type"
+msgstr ""
+
+msgid "CiVariables|Value"
+msgstr ""
+
msgid "CiVariable|* (All environments)"
msgstr ""
@@ -4196,6 +4211,9 @@ msgstr ""
msgid "Failed to remove user key."
msgstr ""
+msgid "Failed to save comment!"
+msgstr ""
+
msgid "Failed to save merge conflicts resolutions. Please try again!"
msgstr ""
@@ -6974,12 +6992,33 @@ msgstr ""
msgid "Preferences saved."
msgstr ""
+msgid "Preferences|Display time in 24-hour format"
+msgstr ""
+
+msgid "Preferences|For example: 30 mins ago."
+msgstr ""
+
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|These settings will update how dates and times are displayed for you."
+msgstr ""
+
msgid "Preferences|This feature is experimental and translations are not complete yet"
msgstr ""
+msgid "Preferences|Time display"
+msgstr ""
+
+msgid "Preferences|Time format"
+msgstr ""
+
+msgid "Preferences|Time preferences"
+msgstr ""
+
+msgid "Preferences|Use relative times"
+msgstr ""
+
msgid "Press %{key}-C to copy"
msgstr ""
@@ -7022,6 +7061,9 @@ msgstr ""
msgid "Private - The group and its projects can only be viewed by members."
msgstr ""
+msgid "Private profile"
+msgstr ""
+
msgid "Private projects can be created in your personal namespace with:"
msgstr ""
@@ -7061,6 +7103,9 @@ msgstr ""
msgid "Profiles|Avatar will be removed. Are you sure?"
msgstr ""
+msgid "Profiles|Bio"
+msgstr ""
+
msgid "Profiles|Change username"
msgstr ""
@@ -7121,6 +7166,18 @@ msgstr ""
msgid "Profiles|Enter your name, so people you know can recognize you"
msgstr ""
+msgid "Profiles|Feed token was successfully reset"
+msgstr ""
+
+msgid "Profiles|Full name"
+msgstr ""
+
+msgid "Profiles|Include private contributions on my profile"
+msgstr ""
+
+msgid "Profiles|Incoming email token was successfully reset"
+msgstr ""
+
msgid "Profiles|Increase your account's security by enabling Two-Factor Authentication (2FA)"
msgstr ""
@@ -7133,6 +7190,9 @@ msgstr ""
msgid "Profiles|Learn more"
msgstr ""
+msgid "Profiles|Location"
+msgstr ""
+
msgid "Profiles|Made a private contribution"
msgstr ""
@@ -7142,6 +7202,9 @@ msgstr ""
msgid "Profiles|No file chosen"
msgstr ""
+msgid "Profiles|Organization"
+msgstr ""
+
msgid "Profiles|Path"
msgstr ""
@@ -7151,6 +7214,9 @@ msgstr ""
msgid "Profiles|Private contributions"
msgstr ""
+msgid "Profiles|Profile was successfully updated"
+msgstr ""
+
msgid "Profiles|Public Avatar"
msgstr ""
@@ -7190,6 +7256,9 @@ msgstr ""
msgid "Profiles|This information will appear on your profile"
msgstr ""
+msgid "Profiles|Time settings"
+msgstr ""
+
msgid "Profiles|Two-Factor Authentication"
msgstr ""
@@ -7211,6 +7280,9 @@ msgstr ""
msgid "Profiles|Use a private email - %{email}"
msgstr ""
+msgid "Profiles|User ID"
+msgstr ""
+
msgid "Profiles|Username change failed - %{message}"
msgstr ""
@@ -7232,6 +7304,9 @@ msgstr ""
msgid "Profiles|You can change your avatar here or remove the current avatar to revert to %{gravatar_link}"
msgstr ""
+msgid "Profiles|You can set your current timezone here"
+msgstr ""
+
msgid "Profiles|You can upload your avatar here"
msgstr ""
diff --git a/qa/Dockerfile b/qa/Dockerfile
index ca7f9accb70..74be373b8e8 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -1,4 +1,4 @@
-FROM ruby:2.5-stretch
+FROM ruby:2.6-stretch
LABEL maintainer "Grzegorz Bizon <grzegorz@gitlab.com>"
ENV DEBIAN_FRONTEND noninteractive
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index d390e84c9b0..b5248c7f0c8 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -28,6 +28,21 @@ describe Projects::GraphsController do
end
describe 'charts' do
+ context 'with an anonymous user' do
+ let(:project) { create(:project, :repository, :public) }
+
+ before do
+ sign_out(user)
+ end
+
+ it 'renders charts with 200 status code' do
+ get(:charts, params: { namespace_id: project.namespace.path, project_id: project.path, id: 'master' })
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:charts)
+ end
+ end
+
context 'when languages were previously detected' do
let(:project) { create(:project, :repository, detected_repository_languages: true) }
let!(:repository_language) { create(:repository_language, project: project) }
diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb
index 5b17c49db2d..4410c8f887f 100644
--- a/spec/features/dashboard/user_filters_projects_spec.rb
+++ b/spec/features/dashboard/user_filters_projects_spec.rb
@@ -151,34 +151,22 @@ describe 'Dashboard > User filters projects' do
end
describe 'Sorting' do
- before do
- [
- { name: 'Red ribbon army', created_at: 2.days.ago },
- { name: 'Cell saga', created_at: Time.now },
- { name: 'Frieza saga', created_at: 10.days.ago }
- ].each do |item|
- project = create(:project, name: item[:name], namespace: user.namespace, created_at: item[:created_at])
- project.add_developer(user)
- end
+ let(:desc_sorted_project_names) { %w[Treasure Victorialand] }
+ before do
user.toggle_star(project)
user.toggle_star(project2)
user2.toggle_star(project2)
end
- it 'includes sorting direction' do
+ it 'has all sorting options', :js do
sorting_dropdown = page.find('.filtered-search-block #filtered-search-sorting-dropdown')
expect(sorting_dropdown).to have_css '.reverse-sort-btn'
- end
-
- it 'has all sorting options', :js do
- sorting_dropdown = page.find('.filtered-search-block #filtered-search-sorting-dropdown')
- sorting_option_labels = ['Last updated', 'Created date', 'Name', 'Stars']
sorting_dropdown.click
- sorting_option_labels.each do |label|
+ ['Last updated', 'Created date', 'Name', 'Stars'].each do |label|
expect(sorting_dropdown).to have_content(label)
end
end
@@ -194,16 +182,11 @@ describe 'Dashboard > User filters projects' do
it 'sorts the project list' do
select_dropdown_option '#filtered-search-sorting-dropdown', 'Name'
- desc = ['Victorialand', 'Treasure', 'Red ribbon army', 'Frieza saga', 'Cell saga']
- asc = ['Cell saga', 'Frieza saga', 'Red ribbon army', 'Treasure', 'Victorialand']
-
- click_sort_direction
-
- expect_to_see_projects(desc)
+ expect_to_see_projects(desc_sorted_project_names)
click_sort_direction
- expect_to_see_projects(asc)
+ expect_to_see_projects(desc_sorted_project_names.reverse)
end
end
@@ -211,16 +194,11 @@ describe 'Dashboard > User filters projects' do
it 'sorts the project list' do
select_dropdown_option '#filtered-search-sorting-dropdown', 'Last updated'
- desc = ["Frieza saga", "Red ribbon army", "Victorialand", "Treasure", "Cell saga"]
- asc = ["Cell saga", "Treasure", "Victorialand", "Red ribbon army", "Frieza saga"]
+ expect_to_see_projects(desc_sorted_project_names)
click_sort_direction
- expect_to_see_projects(desc)
-
- click_sort_direction
-
- expect_to_see_projects(asc)
+ expect_to_see_projects(desc_sorted_project_names.reverse)
end
end
@@ -228,16 +206,11 @@ describe 'Dashboard > User filters projects' do
it 'sorts the project list' do
select_dropdown_option '#filtered-search-sorting-dropdown', 'Created date'
- desc = ["Frieza saga", "Red ribbon army", "Victorialand", "Treasure", "Cell saga"]
- asc = ["Cell saga", "Treasure", "Victorialand", "Red ribbon army", "Frieza saga"]
-
- click_sort_direction
-
- expect_to_see_projects(desc)
+ expect_to_see_projects(desc_sorted_project_names)
click_sort_direction
- expect_to_see_projects(asc)
+ expect_to_see_projects(desc_sorted_project_names.reverse)
end
end
@@ -245,16 +218,11 @@ describe 'Dashboard > User filters projects' do
it 'sorts the project list' do
select_dropdown_option '#filtered-search-sorting-dropdown', 'Stars'
- desc = ["Red ribbon army", "Cell saga", "Frieza saga", "Victorialand", "Treasure"]
- asc = ["Treasure", "Victorialand", "Red ribbon army", "Cell saga", "Frieza saga"]
-
- click_sort_direction
-
- expect_to_see_projects(desc)
+ expect_to_see_projects(desc_sorted_project_names)
click_sort_direction
- expect_to_see_projects(asc)
+ expect_to_see_projects(desc_sorted_project_names.reverse)
end
end
end
diff --git a/spec/features/issuables/markdown_references/jira_spec.rb b/spec/features/issuables/markdown_references/jira_spec.rb
index cc04798248c..8eaccfc0949 100644
--- a/spec/features/issuables/markdown_references/jira_spec.rb
+++ b/spec/features/issuables/markdown_references/jira_spec.rb
@@ -1,6 +1,6 @@
require "rails_helper"
-describe "Jira", :js, :quarantine do
+describe "Jira", :js do
let(:user) { create(:user) }
let(:actual_project) { create(:project, :public, :repository) }
let(:merge_request) { create(:merge_request, target_project: actual_project, source_project: actual_project) }
diff --git a/spec/features/profiles/user_edit_preferences_spec.rb b/spec/features/profiles/user_edit_preferences_spec.rb
new file mode 100644
index 00000000000..2d2da222998
--- /dev/null
+++ b/spec/features/profiles/user_edit_preferences_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe 'User edit preferences profile' do
+ let(:user) { create(:user) }
+
+ before do
+ stub_feature_flags(user_time_settings: true)
+ sign_in(user)
+ visit(profile_preferences_path)
+ end
+
+ it 'allows the user to toggle their time format preference' do
+ field = page.find_field("user[time_format_in_24h]")
+
+ expect(field).not_to be_checked
+
+ field.click
+
+ expect(field).to be_checked
+ end
+
+ it 'allows the user to toggle their time display preference' do
+ field = page.find_field("user[time_display_relative]")
+
+ expect(field).to be_checked
+
+ field.click
+
+ expect(field).not_to be_checked
+ end
+end
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index b43711f6ef6..a53da94ef7d 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -327,5 +327,37 @@ describe 'User edit profile' do
end
end
end
+
+ context 'User time preferences', :js do
+ let(:issue) { create(:issue, project: project)}
+ let(:project) { create(:project) }
+
+ before do
+ stub_feature_flags(user_time_settings: true)
+ end
+
+ it 'shows the user time preferences form' do
+ expect(page).to have_content('Time settings')
+ end
+
+ it 'allows the user to select a time zone from a dropdown list of options' do
+ expect(page.find('.user-time-preferences .dropdown')).not_to have_css('.show')
+
+ page.find('.user-time-preferences .js-timezone-dropdown').click
+
+ expect(page.find('.user-time-preferences .dropdown')).to have_css('.show')
+
+ page.find("a", text: "Nuku'alofa").click
+
+ tz = page.find('.user-time-preferences #user_timezone', visible: false)
+
+ expect(tz.value).to eq('Pacific/Tongatapu')
+ end
+
+ it 'timezone defaults to servers default' do
+ timezone_name = Time.zone.tzinfo.name
+ expect(page.find('.user-time-preferences #user_timezone', visible: false).value).to eq(timezone_name)
+ end
+ end
end
end
diff --git a/spec/frontend/notes/components/discussion_notes_spec.js b/spec/frontend/notes/components/discussion_notes_spec.js
index 392c1b6533e..7e037beca9d 100644
--- a/spec/frontend/notes/components/discussion_notes_spec.js
+++ b/spec/frontend/notes/components/discussion_notes_spec.js
@@ -1,4 +1,4 @@
-import { mount, createLocalVue } from '@vue/test-utils';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
import '~/behaviors/markdown/render_gfm';
import { SYSTEM_NOTE } from '~/notes/constants';
import DiscussionNotes from '~/notes/components/discussion_notes.vue';
@@ -8,6 +8,7 @@ import PlaceholderSystemNote from '~/vue_shared/components/notes/placeholder_sys
import SystemNote from '~/vue_shared/components/notes/system_note.vue';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import createStore from '~/notes/stores';
+import { setTestTimeout } from 'helpers/timeout';
import {
noteableDataMock,
discussionMock,
@@ -17,6 +18,8 @@ import {
const localVue = createLocalVue();
describe('DiscussionNotes', () => {
+ setTestTimeout(500);
+
let wrapper;
const createComponent = props => {
@@ -24,7 +27,7 @@ describe('DiscussionNotes', () => {
store.dispatch('setNoteableData', noteableDataMock);
store.dispatch('setNotesData', notesDataMock);
- wrapper = mount(DiscussionNotes, {
+ wrapper = shallowMount(DiscussionNotes, {
localVue,
store,
propsData: {
diff --git a/spec/frontend/notes/stores/utils_spec.js b/spec/frontend/notes/stores/utils_spec.js
new file mode 100644
index 00000000000..b31b7491334
--- /dev/null
+++ b/spec/frontend/notes/stores/utils_spec.js
@@ -0,0 +1,17 @@
+import { hasQuickActions } from '~/notes/stores/utils';
+
+describe('hasQuickActions', () => {
+ it.each`
+ input | expected
+ ${'some comment'} | ${false}
+ ${'/quickaction'} | ${true}
+ ${'some comment with\n/quickaction'} | ${true}
+ `('returns $expected for $input', ({ input, expected }) => {
+ expect(hasQuickActions(input)).toBe(expected);
+ });
+
+ it('is stateless', () => {
+ expect(hasQuickActions('some comment')).toBe(hasQuickActions('some comment'));
+ expect(hasQuickActions('/quickaction')).toBe(hasQuickActions('/quickaction'));
+ });
+});
diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb
index 05f10fb40f0..c138c87c4ac 100644
--- a/spec/graphql/gitlab_schema_spec.rb
+++ b/spec/graphql/gitlab_schema_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
describe GitlabSchema do
+ let(:user) { build :user }
+
it 'uses batch loading' do
expect(field_instrumenters).to include(BatchLoader::GraphQL)
end
@@ -33,43 +35,75 @@ describe GitlabSchema do
expect(connection).to eq(Gitlab::Graphql::Connections::KeysetConnection)
end
- context 'for different types of users' do
- it 'returns DEFAULT_MAX_COMPLEXITY for no context' do
- expect(GraphQL::Schema)
- .to receive(:execute)
- .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
+ describe '.execute' do
+ context 'for different types of users' do
+ context 'when no context' do
+ it 'returns DEFAULT_MAX_COMPLEXITY' do
+ expect(GraphQL::Schema)
+ .to receive(:execute)
+ .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
- described_class.execute('query')
- end
+ described_class.execute('query')
+ end
+ end
- it 'returns DEFAULT_MAX_COMPLEXITY for no user' do
- expect(GraphQL::Schema)
- .to receive(:execute)
- .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
+ context 'when no user' do
+ it 'returns DEFAULT_MAX_COMPLEXITY' do
+ expect(GraphQL::Schema)
+ .to receive(:execute)
+ .with('query', hash_including(max_complexity: GitlabSchema::DEFAULT_MAX_COMPLEXITY))
- described_class.execute('query', context: {})
- end
+ described_class.execute('query', context: {})
+ end
- it 'returns AUTHENTICATED_COMPLEXITY for a logged in user' do
- user = build :user
+ it 'returns ANONYMOUS_MAX_DEPTH' do
+ expect(GraphQL::Schema)
+ .to receive(:execute)
+ .with('query', hash_including(max_depth: GitlabSchema::ANONYMOUS_MAX_DEPTH))
- expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: GitlabSchema::AUTHENTICATED_COMPLEXITY))
+ described_class.execute('query', context: {})
+ end
+ end
- described_class.execute('query', context: { current_user: user })
- end
+ context 'when a logged in user' do
+ it 'returns AUTHENTICATED_COMPLEXITY' do
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: GitlabSchema::AUTHENTICATED_COMPLEXITY))
- it 'returns ADMIN_COMPLEXITY for an admin user' do
- user = build :user, :admin
+ described_class.execute('query', context: { current_user: user })
+ end
- expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: GitlabSchema::ADMIN_COMPLEXITY))
+ it 'returns AUTHENTICATED_MAX_DEPTH' do
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_depth: GitlabSchema::AUTHENTICATED_MAX_DEPTH))
- described_class.execute('query', context: { current_user: user })
- end
+ described_class.execute('query', context: { current_user: user })
+ end
+ end
+
+ context 'when an admin user' do
+ it 'returns ADMIN_COMPLEXITY' do
+ user = build :user, :admin
+
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: GitlabSchema::ADMIN_COMPLEXITY))
+
+ described_class.execute('query', context: { current_user: user })
+ end
+ end
+
+ context 'when max_complexity passed on the query' do
+ it 'returns what was passed on the query' do
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_complexity: 1234))
+
+ described_class.execute('query', max_complexity: 1234)
+ end
+ end
- it 'returns what was passed on the query' do
- expect(GraphQL::Schema).to receive(:execute).with('query', { max_complexity: 1234 })
+ context 'when max_depth passed on the query' do
+ it 'returns what was passed on the query' do
+ expect(GraphQL::Schema).to receive(:execute).with('query', hash_including(max_depth: 1234))
- described_class.execute('query', max_complexity: 1234)
+ described_class.execute('query', max_depth: 1234)
+ end
+ end
end
end
diff --git a/spec/initializers/secret_token_spec.rb b/spec/initializers/secret_token_spec.rb
index 726ce07a2d1..77bc28a6b07 100644
--- a/spec/initializers/secret_token_spec.rb
+++ b/spec/initializers/secret_token_spec.rb
@@ -45,11 +45,21 @@ describe 'create_tokens' do
expect(keys).to all(match(RSA_KEY))
end
+ it "generates private key for Let's Encrypt" do
+ create_tokens
+
+ keys = secrets.values_at(:lets_encrypt_private_key)
+
+ expect(keys.uniq).to eq(keys)
+ expect(keys).to all(match(RSA_KEY))
+ end
+
it 'warns about the secrets to add to secrets.yml' do
expect(self).to receive(:warn_missing_secret).with('secret_key_base')
expect(self).to receive(:warn_missing_secret).with('otp_key_base')
expect(self).to receive(:warn_missing_secret).with('db_key_base')
expect(self).to receive(:warn_missing_secret).with('openid_connect_signing_key')
+ expect(self).to receive(:warn_missing_secret).with('lets_encrypt_private_key')
create_tokens
end
@@ -78,6 +88,7 @@ describe 'create_tokens' do
before do
secrets.db_key_base = 'db_key_base'
secrets.openid_connect_signing_key = 'openid_connect_signing_key'
+ secrets.lets_encrypt_private_key = 'lets_encrypt_private_key'
allow(File).to receive(:exist?).with('.secret').and_return(true)
allow(File).to receive(:read).with('.secret').and_return('file_key')
diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
index 481b1a4d4b0..2839922fbd3 100644
--- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
@@ -113,7 +113,7 @@ describe('AjaxFormVariableList', () => {
it('hides secret values', done => {
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, {});
- const row = container.querySelector('.js-row:first-child');
+ const row = container.querySelector('.js-row');
const valueInput = row.querySelector('.js-ci-variable-input-value');
const valuePlaceholder = row.querySelector('.js-secret-value-placeholder');
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index 39901276b8c..7a9f32ddcff 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -794,6 +794,51 @@ describe('Actions Notes Store', () => {
});
});
+ describe('saveNote', () => {
+ const payload = { endpoint: TEST_HOST, data: { 'note[note]': 'some text' } };
+
+ describe('if response contains errors', () => {
+ const res = { errors: { something: ['went wrong'] } };
+
+ it('throws an error', done => {
+ actions
+ .saveNote(
+ {
+ commit() {},
+ dispatch: () => Promise.resolve(res),
+ },
+ payload,
+ )
+ .then(() => done.fail('Expected error to be thrown!'))
+ .catch(error => {
+ expect(error.message).toBe('Failed to save comment!');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('if response contains no errors', () => {
+ const res = { valid: true };
+
+ it('returns the response', done => {
+ actions
+ .saveNote(
+ {
+ commit() {},
+ dispatch: () => Promise.resolve(res),
+ },
+ payload,
+ )
+ .then(data => {
+ expect(data).toBe(res);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
describe('submitSuggestion', () => {
const discussionId = 'discussion-id';
const noteId = 'note-id';
diff --git a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown_spec.js
index a89952ee435..5f4dba5ecb9 100644
--- a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown_spec.js
+++ b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown_spec.js
@@ -3,6 +3,7 @@ import GLDropdown from '~/gl_dropdown'; // eslint-disable-line no-unused-vars
import TimezoneDropdown, {
formatUtcOffset,
formatTimezone,
+ findTimezoneByIdentifier,
} from '~/pages/projects/pipeline_schedules/shared/components/timezone_dropdown';
describe('Timezone Dropdown', function() {
@@ -12,6 +13,7 @@ describe('Timezone Dropdown', function() {
let $dropdownEl = null;
let $wrapper = null;
const tzListSel = '.dropdown-content ul li a.is-active';
+ const tzDropdownToggleText = '.dropdown-toggle-text';
describe('Initialize', () => {
describe('with dropdown already loaded', () => {
@@ -94,6 +96,36 @@ describe('Timezone Dropdown', function() {
expect(onSelectTimezone).toHaveBeenCalled();
});
+
+ it('will correctly set the dropdown label if a timezone identifier is set on the inputEl', () => {
+ $inputEl.val('America/St_Johns');
+
+ // eslint-disable-next-line no-new
+ new TimezoneDropdown({
+ $inputEl,
+ $dropdownEl,
+ displayFormat: selectedItem => formatTimezone(selectedItem),
+ });
+
+ expect($wrapper.find(tzDropdownToggleText).html()).toEqual('[UTC - 2.5] Newfoundland');
+ });
+
+ it('will call a provided `displayFormat` handler to format the dropdown value', () => {
+ const displayFormat = jasmine.createSpy('displayFormat');
+ // eslint-disable-next-line no-new
+ new TimezoneDropdown({
+ $inputEl,
+ $dropdownEl,
+ displayFormat,
+ });
+
+ $wrapper
+ .find(tzListSel)
+ .first()
+ .trigger('click');
+
+ expect(displayFormat).toHaveBeenCalled();
+ });
});
});
@@ -164,4 +196,49 @@ describe('Timezone Dropdown', function() {
).toEqual('[UTC 0] Accra');
});
});
+
+ describe('findTimezoneByIdentifier', () => {
+ const tzList = [
+ {
+ identifier: 'Asia/Tokyo',
+ name: 'Sapporo',
+ offset: 32400,
+ },
+ {
+ identifier: 'Asia/Hong_Kong',
+ name: 'Hong Kong',
+ offset: 28800,
+ },
+ {
+ identifier: 'Asia/Dhaka',
+ name: 'Dhaka',
+ offset: 21600,
+ },
+ ];
+
+ const identifier = 'Asia/Dhaka';
+ it('returns the correct object if the identifier exists', () => {
+ const res = findTimezoneByIdentifier(tzList, identifier);
+
+ expect(res).toBeTruthy();
+ expect(res).toBe(tzList[2]);
+ });
+
+ it('returns null if it doesnt find the identifier', () => {
+ const res = findTimezoneByIdentifier(tzList, 'Australia/Melbourne');
+
+ expect(res).toBeNull();
+ });
+
+ it('returns null if there is no identifier given', () => {
+ expect(findTimezoneByIdentifier(tzList)).toBeNull();
+ expect(findTimezoneByIdentifier(tzList, '')).toBeNull();
+ });
+
+ it('returns null if there is an empty or invalid array given', () => {
+ expect(findTimezoneByIdentifier([], identifier)).toBeNull();
+ expect(findTimezoneByIdentifier(null, identifier)).toBeNull();
+ expect(findTimezoneByIdentifier(undefined, identifier)).toBeNull();
+ });
+ });
});
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index e1a2bae5fe8..a02c00e3340 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -222,6 +222,46 @@ describe Gitlab::BitbucketImport::Importer do
body: {}.to_json)
end
+ context 'creating labels on project' do
+ before do
+ allow(importer).to receive(:import_wiki)
+ end
+
+ it 'creates labels as expected' do
+ expect { importer.execute }.to change { Label.count }.from(0).to(Gitlab::BitbucketImport::Importer::LABELS.size)
+ end
+
+ it 'does not fail if label is already existing' do
+ label = Gitlab::BitbucketImport::Importer::LABELS.first
+ ::Labels::CreateService.new(label).execute(project: project)
+
+ expect { importer.execute }.not_to raise_error
+ end
+
+ it 'does not create new labels' do
+ Gitlab::BitbucketImport::Importer::LABELS.each do |label|
+ create(:label, project: project, title: label[:title])
+ end
+
+ expect { importer.execute }.not_to change { Label.count }
+ end
+
+ it 'does not update existing ones' do
+ label_title = Gitlab::BitbucketImport::Importer::LABELS.first[:title]
+ existing_label = create(:label, project: project, title: label_title)
+ # Reload label from database so we avoid timestamp comparison issues related to time precision when comparing
+ # attributes later.
+ existing_label.reload
+
+ Timecop.freeze(Time.now + 1.minute) do
+ importer.execute
+
+ label_after_import = project.labels.find(existing_label.id)
+ expect(label_after_import.attributes).to eq(existing_label.attributes)
+ end
+ end
+ end
+
it 'maps statuses to open or closed' do
allow(importer).to receive(:import_wiki)
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 4e910e67ac2..aa975c8bb0b 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -35,7 +35,7 @@ describe Gitlab::UsageData do
subject { described_class.data }
it "gathers usage data" do
- expect(subject.keys).to match_array(%i(
+ expect(subject.keys).to include(*%i(
active_user_count
counts
recorded_at
@@ -68,7 +68,7 @@ describe Gitlab::UsageData do
expect(count_data[:boards]).to eq(1)
expect(count_data[:projects]).to eq(3)
- expect(count_data.keys).to match_array(%i(
+ expect(count_data.keys).to include(*%i(
assignee_lists
boards
ci_builds
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 4a7eee1fbf3..04ae9390436 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -166,6 +166,13 @@ describe JiraService do
).once
end
+ it 'does not fail if remote_link.all on issue returns nil' do
+ allow(JIRA::Resource::Remotelink).to receive(:all).and_return(nil)
+
+ expect { @jira_service.close_issue(resource, ExternalIssue.new('JIRA-123', project)) }
+ .not_to raise_error(NoMethodError)
+ end
+
# Check https://developer.atlassian.com/jiradev/jira-platform/guides/other/guide-jira-remote-issue-links/fields-in-remote-issue-links
# for more information
it 'creates Remote Link reference in JIRA for comment' do
diff --git a/spec/models/user_preference_spec.rb b/spec/models/user_preference_spec.rb
index b2ef17a81d4..e09c91e874a 100644
--- a/spec/models/user_preference_spec.rb
+++ b/spec/models/user_preference_spec.rb
@@ -73,4 +73,10 @@ describe UserPreference do
it_behaves_like 'a sort_by preference'
end
end
+
+ describe '#timezone' do
+ it 'returns server time as default' do
+ expect(user_preference.timezone).to eq(Time.zone.tzinfo.name)
+ end
+ end
end
diff --git a/spec/requests/api/graphql/gitlab_schema_spec.rb b/spec/requests/api/graphql/gitlab_schema_spec.rb
index b63b4fb34df..dd518274f82 100644
--- a/spec/requests/api/graphql/gitlab_schema_spec.rb
+++ b/spec/requests/api/graphql/gitlab_schema_spec.rb
@@ -3,15 +3,43 @@ require 'spec_helper'
describe 'GitlabSchema configurations' do
include GraphqlHelpers
- it 'shows an error if complexity is too high' do
- project = create(:project, :repository)
- query = graphql_query_for('project', { 'fullPath' => project.full_path }, %w(id name description))
+ let(:project) { create(:project, :repository) }
+ let(:query) { graphql_query_for('project', { 'fullPath' => project.full_path }, %w(id name description)) }
+ let(:current_user) { create(:user) }
- allow(GitlabSchema).to receive(:max_query_complexity).and_return 1
+ describe '#max_complexity' do
+ context 'when complexity is too high' do
+ it 'shows an error' do
+ allow(GitlabSchema).to receive(:max_query_complexity).and_return 1
- post_graphql(query, current_user: nil)
+ post_graphql(query, current_user: nil)
- expect(graphql_errors.first['message']).to include('which exceeds max complexity of 1')
+ expect(graphql_errors.first['message']).to include('which exceeds max complexity of 1')
+ end
+ end
+ end
+
+ describe '#max_depth' do
+ context 'when query depth is too high' do
+ it 'shows error' do
+ errors = [{ "message" => "Query has depth of 2, which exceeds max depth of 1" }]
+ allow(GitlabSchema).to receive(:max_query_depth).and_return 1
+
+ post_graphql(query)
+
+ expect(graphql_errors).to eq(errors)
+ end
+ end
+
+ context 'when query depth is within range' do
+ it 'has no error' do
+ allow(GitlabSchema).to receive(:max_query_depth).and_return 5
+
+ post_graphql(query)
+
+ expect(graphql_errors).to be_nil
+ end
+ end
end
context 'when IntrospectionQuery' do
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index 2c4fb131ed9..674fe0f666e 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -44,7 +44,18 @@ describe Members::CreateService do
result = described_class.new(user, params).execute(project)
expect(result[:status]).to eq(:error)
- expect(result[:message]).to include(project_user.username)
+ expect(result[:message]).to include("#{project_user.username}: Access level is not included in the list")
expect(project.users).not_to include project_user
end
+
+ it 'does not add a member with an existing invite' do
+ invited_member = create(:project_member, :invited, project: project)
+
+ params = { user_ids: invited_member.invite_email,
+ access_level: Gitlab::Access::GUEST }
+ result = described_class.new(user, params).execute(project)
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq('Invite email has already been taken')
+ end
end
diff --git a/spec/services/projects/after_import_service_spec.rb b/spec/services/projects/after_import_service_spec.rb
index 95c11f71c5e..51d3fd18881 100644
--- a/spec/services/projects/after_import_service_spec.rb
+++ b/spec/services/projects/after_import_service_spec.rb
@@ -15,7 +15,7 @@ describe Projects::AfterImportService do
describe '#execute' do
before do
allow(Projects::HousekeepingService)
- .to receive(:new).with(project, :gc).and_return(housekeeping_service)
+ .to receive(:new).with(project).and_return(housekeeping_service)
allow(housekeeping_service)
.to receive(:execute).and_yield
diff --git a/spec/services/projects/detect_repository_languages_service_spec.rb b/spec/services/projects/detect_repository_languages_service_spec.rb
index e3e561c971c..df5eed18ac0 100644
--- a/spec/services/projects/detect_repository_languages_service_spec.rb
+++ b/spec/services/projects/detect_repository_languages_service_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Projects::DetectRepositoryLanguagesService, :clean_gitlab_redis_shared_state do
set(:project) { create(:project, :repository) }
- subject { described_class.new(project, project.owner) }
+ subject { described_class.new(project) }
describe '#execute' do
context 'without previous detection' do
diff --git a/spec/services/projects/repository_languages_service_spec.rb b/spec/services/projects/repository_languages_service_spec.rb
index 09c61363ad2..46c5095327d 100644
--- a/spec/services/projects/repository_languages_service_spec.rb
+++ b/spec/services/projects/repository_languages_service_spec.rb
@@ -10,7 +10,7 @@ describe Projects::RepositoryLanguagesService do
context 'when a project is without detected programming languages' do
it 'schedules a worker and returns the empty result' do
- expect(::DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id, project.owner.id)
+ expect(::DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id)
expect(service.execute).to eq([])
end
end
@@ -19,7 +19,7 @@ describe Projects::RepositoryLanguagesService do
let!(:repository_language) { create(:repository_language, project: project) }
it 'does not schedule a worker and returns the detected languages' do
- expect(::DetectRepositoryLanguagesWorker).not_to receive(:perform_async).with(project.id, project.owner.id)
+ expect(::DetectRepositoryLanguagesWorker).not_to receive(:perform_async).with(project.id)
languages = service.execute
diff --git a/spec/services/users/update_service_spec.rb b/spec/services/users/update_service_spec.rb
index e04c05418b0..9384287f98a 100644
--- a/spec/services/users/update_service_spec.rb
+++ b/spec/services/users/update_service_spec.rb
@@ -13,6 +13,15 @@ describe Users::UpdateService do
expect(user.name).to eq('New Name')
end
+ it 'updates time preferences' do
+ result = update_user(user, timezone: 'Europe/Warsaw', time_display_relative: true, time_format_in_24h: false)
+
+ expect(result).to eq(status: :success)
+ expect(user.reload.timezone).to eq('Europe/Warsaw')
+ expect(user.time_display_relative).to eq(true)
+ expect(user.time_format_in_24h).to eq(false)
+ end
+
it 'returns an error result when record cannot be updated' do
result = {}
expect do
diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb
index 693b796fbdc..92a19dd22a2 100644
--- a/spec/support/features/variable_list_shared_examples.rb
+++ b/spec/support/features/variable_list_shared_examples.rb
@@ -17,7 +17,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value')
end
@@ -38,7 +38,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value')
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
@@ -59,7 +59,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value')
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true')
@@ -116,19 +116,19 @@ shared_examples 'variable list' do
page.within('.js-ci-variable-list-section') do
expect(first('.js-ci-variable-input-key').value).to eq(variable.key)
expect(first('.js-ci-variable-input-value', visible: false).value).to eq(variable.value)
- expect(page).to have_content('*' * 20)
+ expect(page).to have_content('*' * 17)
click_button('Reveal value')
expect(first('.js-ci-variable-input-key').value).to eq(variable.key)
expect(first('.js-ci-variable-input-value').value).to eq(variable.value)
- expect(page).not_to have_content('*' * 20)
+ expect(page).not_to have_content('*' * 17)
click_button('Hide value')
expect(first('.js-ci-variable-input-key').value).to eq(variable.key)
expect(first('.js-ci-variable-input-value', visible: false).value).to eq(variable.value)
- expect(page).to have_content('*' * 20)
+ expect(page).to have_content('*' * 17)
end
end
@@ -149,7 +149,7 @@ shared_examples 'variable list' do
page.within('.js-ci-variable-list-section') do
click_button('Reveal value')
- page.within('.js-row:nth-child(1)') do
+ page.within('.js-row:nth-child(2)') do
find('.js-ci-variable-input-key').set('new_key')
find('.js-ci-variable-input-value').set('new_value')
end
@@ -159,7 +159,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-row:nth-child(1)') do
+ page.within('.js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('new_key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('new_value')
end
@@ -181,7 +181,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(3)') do
find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
@@ -193,7 +193,7 @@ shared_examples 'variable list' do
visit page_path
# We check the first row because it re-sorts to alphabetical order on refresh
- page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(3)') do
expect(find('.js-ci-variable-input-key').value).to eq('unprotected_key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('unprotected_value')
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
@@ -215,7 +215,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false')
@@ -226,7 +226,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-key').value).to eq('protected_key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('protected_value')
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false')
@@ -234,7 +234,7 @@ shared_examples 'variable list' do
end
it 'edits variable to be unmasked' do
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true')
find('.ci-variable-masked-item .js-project-feature-toggle').click
@@ -247,13 +247,13 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false')
end
end
it 'edits variable to be masked' do
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true')
find('.ci-variable-masked-item .js-project-feature-toggle').click
@@ -266,7 +266,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('false')
find('.ci-variable-masked-item .js-project-feature-toggle').click
@@ -279,7 +279,7 @@ shared_examples 'variable list' do
visit page_path
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
+ page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do
expect(find('.js-ci-variable-input-masked', visible: false).value).to eq('true')
end
end
@@ -302,7 +302,7 @@ shared_examples 'variable list' do
expect(page).to have_selector('.js-row', count: 4)
# Remove the `akey` variable
- page.within('.js-row:nth-child(2)') do
+ page.within('.js-row:nth-child(3)') do
first('.js-row-remove-button').click
end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index b49d743fb9a..f15944652fd 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -102,6 +102,7 @@ module GraphqlHelpers
def all_graphql_fields_for(class_name, parent_types = Set.new)
allow_unlimited_graphql_complexity
+ allow_unlimited_graphql_depth
type = GitlabSchema.types[class_name.to_s]
return "" unless type
@@ -190,4 +191,9 @@ module GraphqlHelpers
allow_any_instance_of(GitlabSchema).to receive(:max_complexity).and_return nil
allow(GitlabSchema).to receive(:max_query_complexity).with(any_args).and_return nil
end
+
+ def allow_unlimited_graphql_depth
+ allow_any_instance_of(GitlabSchema).to receive(:max_depth).and_return nil
+ allow(GitlabSchema).to receive(:max_query_depth).with(any_args).and_return nil
+ end
end
diff --git a/spec/support/helpers/project_forks_helper.rb b/spec/support/helpers/project_forks_helper.rb
index 9a86560da2a..bcb11a09b36 100644
--- a/spec/support/helpers/project_forks_helper.rb
+++ b/spec/support/helpers/project_forks_helper.rb
@@ -1,5 +1,11 @@
module ProjectForksHelper
def fork_project(project, user = nil, params = {})
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ fork_project_direct(project, user, params)
+ end
+ end
+
+ def fork_project_direct(project, user = nil, params = {})
# Load the `fork_network` for the project to fork as there might be one that
# wasn't loaded yet.
project.reload unless project.fork_network
@@ -44,11 +50,16 @@ module ProjectForksHelper
end
def fork_project_with_submodules(project, user = nil, params = {})
- forked_project = fork_project(project, user, params)
- TestEnv.copy_repo(forked_project,
- bare_repo: TestEnv.forked_repo_path_bare,
- refs: TestEnv::FORKED_BRANCH_SHA)
- forked_project.repository.after_import
- forked_project
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ forked_project = fork_project_direct(project, user, params)
+ TestEnv.copy_repo(
+ forked_project,
+ bare_repo: TestEnv.forked_repo_path_bare,
+ refs: TestEnv::FORKED_BRANCH_SHA
+ )
+ forked_project.repository.after_import
+
+ forked_project
+ end
end
end
diff --git a/spec/workers/detect_repository_languages_worker_spec.rb b/spec/workers/detect_repository_languages_worker_spec.rb
index dbf32555985..755eb8dbf6b 100644
--- a/spec/workers/detect_repository_languages_worker_spec.rb
+++ b/spec/workers/detect_repository_languages_worker_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
describe DetectRepositoryLanguagesWorker do
set(:project) { create(:project) }
- let(:user) { project.owner }
subject { described_class.new }
@@ -14,19 +13,13 @@ describe DetectRepositoryLanguagesWorker do
allow(::Projects::DetectRepositoryLanguagesService).to receive(:new).and_return(service)
expect(service).to receive(:execute)
- subject.perform(project.id, user.id)
+ subject.perform(project.id)
end
context 'when invalid ids are used' do
it 'does not raise when the project could not be found' do
expect do
- subject.perform(-1, user.id)
- end.not_to raise_error
- end
-
- it 'does not raise when the user could not be found' do
- expect do
- subject.perform(project.id, -1)
+ subject.perform(-1)
end.not_to raise_error
end
end