diff options
130 files changed, 1081 insertions, 451 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a1fa39b41d..6056e18595d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -133,7 +133,7 @@ Most issues will have labels for at least one of the following: - Type: ~"feature proposal", ~bug, ~customer, etc. - Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc. -- Team: ~"CI/CD", ~Discussion, ~Quality, ~Platform, etc. +- Team: ~"CI/CD", ~Plan, ~Quality, ~Platform, etc. - Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release" - Priority: ~P1, ~P2, ~P3, ~P4 - Severity: ~S1, ~S2, ~S3, ~S4 @@ -187,12 +187,13 @@ The current team labels are: - ~Configuration - ~"CI/CD" -- ~Discussion +- ~Create - ~Distribution - ~Documentation - ~Geo - ~Gitaly - ~Monitoring +- ~Plan - ~Platform - ~Quality - ~Release @@ -323,6 +323,7 @@ group :development do end group :development, :test do + gem 'bootsnap', '~> 1.3' gem 'bullet', '~> 5.5.0', require: !!ENV['ENABLE_BULLET'] gem 'pry-byebug', '~> 3.4.1', platform: :mri gem 'pry-rails', '~> 0.3.4' diff --git a/Gemfile.lock b/Gemfile.lock index 6415f9e6132..1d9c75f154c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -87,6 +87,8 @@ GEM binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) blankslate (2.1.2.4) + bootsnap (1.3.1) + msgpack (~> 1.0) bootstrap_form (2.7.0) brakeman (4.2.1) browser (2.2.0) @@ -500,6 +502,7 @@ GEM mini_portile2 (2.3.0) minitest (5.7.0) mousetrap-rails (1.4.6) + msgpack (1.2.4) multi_json (1.13.1) multi_xml (0.6.0) multipart-post (2.0.0) @@ -986,6 +989,7 @@ DEPENDENCIES benchmark-ips (~> 2.3.0) better_errors (~> 2.1.0) binding_of_caller (~> 0.7.2) + bootsnap (~> 1.3) bootstrap_form (~> 2.7.0) brakeman (~> 4.2) browser (~> 2.2) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 50ca0d5a729..2d12faa28d9 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -90,6 +90,8 @@ GEM binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) blankslate (2.1.2.4) + bootsnap (1.3.1) + msgpack (~> 1.0) bootstrap_form (2.7.0) brakeman (4.2.1) browser (2.2.0) @@ -503,6 +505,7 @@ GEM mini_portile2 (2.3.0) minitest (5.7.0) mousetrap-rails (1.4.6) + msgpack (1.2.4) multi_json (1.13.1) multi_xml (0.6.0) multipart-post (2.0.0) @@ -996,6 +999,7 @@ DEPENDENCIES benchmark-ips (~> 2.3.0) better_errors (~> 2.1.0) binding_of_caller (~> 0.7.2) + bootsnap (~> 1.3) bootstrap_form (~> 2.7.0) brakeman (~> 4.2) browser (~> 2.2) diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue index ec23b1e7c11..271c6eac81a 100644 --- a/app/assets/javascripts/boards/components/board_new_issue.vue +++ b/app/assets/javascripts/boards/components/board_new_issue.vue @@ -105,7 +105,7 @@ export default { </div> <label :for="list.id + '-title'" - class="label-light" + class="label-bold" > Title </label> diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue index eb335f352d3..dc887db1e73 100644 --- a/app/assets/javascripts/boards/components/project_select.vue +++ b/app/assets/javascripts/boards/components/project_select.vue @@ -68,7 +68,7 @@ export default { <template> <div> - <label class="label-light prepend-top-10"> + <label class="label-bold prepend-top-10"> Project </label> <div diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js index d62d3c23654..f595f3c3187 100644 --- a/app/assets/javascripts/commons/polyfills.js +++ b/app/assets/javascripts/commons/polyfills.js @@ -14,6 +14,7 @@ import 'core-js/es6/weak-map'; // Browser polyfills import 'classlist-polyfill'; +import 'formdata-polyfill'; import './polyfills/custom_event'; import './polyfills/element'; import './polyfills/event'; diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue index 20483161033..e64d5511d78 100644 --- a/app/assets/javascripts/diffs/components/diff_discussions.vue +++ b/app/assets/javascripts/diffs/components/diff_discussions.vue @@ -30,6 +30,7 @@ export default { :render-header="false" :render-diff-file="false" :always-expanded="true" + :discussions-by-diff-order="true" /> </ul> </div> diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index 1867b7980d2..833c4b027df 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -66,7 +66,7 @@ export default { <div class="form-group row" > - <label class="label-light col-form-label col-sm-3"> + <label class="label-bold col-form-label col-sm-3"> {{ __('Name') }} </label> <div class="col-sm-9"> diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue index 6385b75e557..ad6e7cf501d 100644 --- a/app/assets/javascripts/notes/components/discussion_counter.vue +++ b/app/assets/javascripts/notes/components/discussion_counter.vue @@ -5,19 +5,20 @@ import resolvedSvg from 'icons/_icon_status_success_solid.svg'; import mrIssueSvg from 'icons/_icon_mr_issue.svg'; import nextDiscussionSvg from 'icons/_next_discussion.svg'; import { pluralize } from '../../lib/utils/text_utility'; -import { scrollToElement } from '../../lib/utils/common_utils'; +import discussionNavigation from '../mixins/discussion_navigation'; import tooltip from '../../vue_shared/directives/tooltip'; export default { directives: { tooltip, }, + mixins: [discussionNavigation], computed: { ...mapGetters([ 'getUserData', 'getNoteableData', 'discussionCount', - 'unresolvedDiscussions', + 'firstUnresolvedDiscussionId', 'resolvedDiscussionCount', ]), isLoggedIn() { @@ -35,11 +36,6 @@ export default { resolveAllDiscussionsIssuePath() { return this.getNoteableData.create_issue_to_resolve_discussions_path; }, - firstUnresolvedDiscussionId() { - const item = this.unresolvedDiscussions[0] || {}; - - return item.id; - }, }, created() { this.resolveSvg = resolveSvg; @@ -50,22 +46,10 @@ export default { methods: { ...mapActions(['expandDiscussion']), jumpToFirstUnresolvedDiscussion() { - const discussionId = this.firstUnresolvedDiscussionId; - if (!discussionId) { - return; - } - - const el = document.querySelector(`[data-discussion-id="${discussionId}"]`); - const activeTab = window.mrTabs.currentAction; - - if (activeTab === 'commits' || activeTab === 'pipelines') { - window.mrTabs.activateTab('show'); - } + const diffTab = window.mrTabs.currentAction === 'diffs'; + const discussionId = this.firstUnresolvedDiscussionId(diffTab); - if (el) { - this.expandDiscussion({ discussionId }); - scrollToElement(el); - } + this.jumpToDiscussion(discussionId); }, }, }; diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 2f1a68731c7..0fe1c16854a 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -1,9 +1,8 @@ <script> -import _ from 'underscore'; import { mapActions, mapGetters } from 'vuex'; import resolveDiscussionsSvg from 'icons/_icon_mr_issue.svg'; import nextDiscussionsSvg from 'icons/_next_discussion.svg'; -import { convertObjectPropsToCamelCase, scrollToElement } from '~/lib/utils/common_utils'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { truncateSha } from '~/lib/utils/text_utility'; import systemNote from '~/vue_shared/components/notes/system_note.vue'; import { s__ } from '~/locale'; @@ -21,6 +20,7 @@ import placeholderSystemNote from '../../vue_shared/components/notes/placeholder import autosave from '../mixins/autosave'; import noteable from '../mixins/noteable'; import resolvable from '../mixins/resolvable'; +import discussionNavigation from '../mixins/discussion_navigation'; import tooltip from '../../vue_shared/directives/tooltip'; export default { @@ -40,7 +40,7 @@ export default { directives: { tooltip, }, - mixins: [autosave, noteable, resolvable], + mixins: [autosave, noteable, resolvable, discussionNavigation], props: { discussion: { type: Object, @@ -61,6 +61,11 @@ export default { required: false, default: false, }, + discussionsByDiffOrder: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -75,7 +80,12 @@ export default { 'discussionCount', 'resolvedDiscussionCount', 'allDiscussions', + 'unresolvedDiscussionsIdsByDiff', + 'unresolvedDiscussionsIdsByDate', 'unresolvedDiscussions', + 'unresolvedDiscussionsIdsOrdered', + 'nextUnresolvedDiscussionId', + 'isLastUnresolvedDiscussion', ]), transformedDiscussion() { return { @@ -126,6 +136,10 @@ export default { hasMultipleUnresolvedDiscussions() { return this.unresolvedDiscussions.length > 1; }, + showJumpToNextDiscussion() { + return this.hasMultipleUnresolvedDiscussions && + !this.isLastUnresolvedDiscussion(this.discussion.id, this.discussionsByDiffOrder); + }, shouldRenderDiffs() { const { diffDiscussion, diffFile } = this.transformedDiscussion; @@ -242,21 +256,10 @@ Please check your network connection and try again.`; }); }, jumpToNextDiscussion() { - const discussionIds = this.allDiscussions.map(d => d.id); - const unresolvedIds = this.unresolvedDiscussions.map(d => d.id); - const currentIndex = discussionIds.indexOf(this.discussion.id); - const remainingAfterCurrent = discussionIds.slice(currentIndex + 1); - const nextIndex = _.findIndex(remainingAfterCurrent, id => unresolvedIds.indexOf(id) > -1); - - if (nextIndex > -1) { - const nextId = remainingAfterCurrent[nextIndex]; - const el = document.querySelector(`[data-discussion-id="${nextId}"]`); + const nextId = + this.nextUnresolvedDiscussionId(this.discussion.id, this.discussionsByDiffOrder); - if (el) { - this.expandDiscussion({ discussionId: nextId }); - scrollToElement(el); - } - } + this.jumpToDiscussion(nextId); }, }, }; @@ -398,7 +401,7 @@ Please check your network connection and try again.`; </a> </div> <div - v-if="hasMultipleUnresolvedDiscussions" + v-if="showJumpToNextDiscussion" class="btn-group" role="group"> <button diff --git a/app/assets/javascripts/notes/mixins/discussion_navigation.js b/app/assets/javascripts/notes/mixins/discussion_navigation.js new file mode 100644 index 00000000000..f7c4deee1f8 --- /dev/null +++ b/app/assets/javascripts/notes/mixins/discussion_navigation.js @@ -0,0 +1,29 @@ +import { scrollToElement } from '~/lib/utils/common_utils'; + +export default { + methods: { + jumpToDiscussion(id) { + if (id) { + const activeTab = window.mrTabs.currentAction; + const selector = + activeTab === 'diffs' + ? `ul.notes[data-discussion-id="${id}"]` + : `div.discussion[data-discussion-id="${id}"]`; + const el = document.querySelector(selector); + + if (activeTab === 'commits' || activeTab === 'pipelines') { + window.mrTabs.activateTab('show'); + } + + if (el) { + this.expandDiscussion({ discussionId: id }); + + scrollToElement(el); + return true; + } + } + + return false; + }, + }, +}; diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js index e9e95dd4219..0d8d197bf71 100644 --- a/app/assets/javascripts/notes/stores/getters.js +++ b/app/assets/javascripts/notes/stores/getters.js @@ -70,6 +70,9 @@ export const allDiscussions = (state, getters) => { return Object.values(resolved).concat(unresolved); }; +export const allResolvableDiscussions = (state, getters) => + getters.allDiscussions.filter(d => !d.individual_note && d.resolvable); + export const resolvedDiscussionsById = state => { const map = {}; @@ -86,6 +89,51 @@ export const resolvedDiscussionsById = state => { return map; }; +// Gets Discussions IDs ordered by the date of their initial note +export const unresolvedDiscussionsIdsByDate = (state, getters) => + getters.allResolvableDiscussions + .filter(d => !d.resolved) + .sort((a, b) => { + const aDate = new Date(a.notes[0].created_at); + const bDate = new Date(b.notes[0].created_at); + + if (aDate < bDate) { + return -1; + } + + return aDate === bDate ? 0 : 1; + }) + .map(d => d.id); + +// Gets Discussions IDs ordered by their position in the diff +// +// Sorts the array of resolvable yet unresolved discussions by +// comparing file names first. If file names are the same, compares +// line numbers. +export const unresolvedDiscussionsIdsByDiff = (state, getters) => + getters.allResolvableDiscussions + .filter(d => !d.resolved) + .sort((a, b) => { + if (!a.diff_file || !b.diff_file) { + return 0; + } + + // Get file names comparison result + const filenameComparison = a.diff_file.file_path.localeCompare(b.diff_file.file_path); + + // Get the line numbers, to compare within the same file + const aLines = [a.position.formatter.new_line, a.position.formatter.old_line]; + const bLines = [b.position.formatter.new_line, b.position.formatter.old_line]; + + return filenameComparison < 0 || + (filenameComparison === 0 && + // .max() because one of them might be zero (if removed/added) + Math.max(aLines[0], aLines[1]) < Math.max(bLines[0], bLines[1])) + ? -1 + : 1; + }) + .map(d => d.id); + export const resolvedDiscussionCount = (state, getters) => { const resolvedMap = getters.resolvedDiscussionsById; @@ -102,5 +150,42 @@ export const discussionTabCounter = state => { return all.length; }; +// Returns the list of discussion IDs ordered according to given parameter +// @param {Boolean} diffOrder - is ordered by diff? +export const unresolvedDiscussionsIdsOrdered = (state, getters) => diffOrder => { + if (diffOrder) { + return getters.unresolvedDiscussionsIdsByDiff; + } + return getters.unresolvedDiscussionsIdsByDate; +}; + +// Checks if a given discussion is the last in the current order (diff or date) +// @param {Boolean} discussionId - id of the discussion +// @param {Boolean} diffOrder - is ordered by diff? +export const isLastUnresolvedDiscussion = (state, getters) => (discussionId, diffOrder) => { + const idsOrdered = getters.unresolvedDiscussionsIdsOrdered(diffOrder); + const lastDiscussionId = idsOrdered[idsOrdered.length - 1]; + + return lastDiscussionId === discussionId; +}; + +// Gets the ID of the discussion following the one provided, respecting order (diff or date) +// @param {Boolean} discussionId - id of the current discussion +// @param {Boolean} diffOrder - is ordered by diff? +export const nextUnresolvedDiscussionId = (state, getters) => (discussionId, diffOrder) => { + const idsOrdered = getters.unresolvedDiscussionsIdsOrdered(diffOrder); + const currentIndex = idsOrdered.indexOf(discussionId); + + return idsOrdered.slice(currentIndex + 1, currentIndex + 2)[0]; +}; + +// @param {Boolean} diffOrder - is ordered by diff? +export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => { + if (diffOrder) { + return getters.unresolvedDiscussionsIdsByDiff[0]; + } + return getters.unresolvedDiscussionsIdsByDate[0]; +}; + // prevent babel-plugin-rewire from generating an invalid default during karma tests export default () => {}; diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue index d0613804067..0d05668b285 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue @@ -68,7 +68,7 @@ :name="inputNameAttribute" :value="cronInterval" :checked="isEditable" - class="label-light" + class="label-bold" type="radio" @click="toggleCustomInput(true)" /> @@ -93,13 +93,13 @@ v-model="cronInterval" :name="inputNameAttribute" :value="cronIntervalPresets.everyDay" - class="label-light" + class="label-bold" type="radio" @click="toggleCustomInput(false)" /> <label - class="label-light" + class="label-bold" for="every-day" > {{ __('Every day (at 4:00am)') }} @@ -112,13 +112,13 @@ v-model="cronInterval" :name="inputNameAttribute" :value="cronIntervalPresets.everyWeek" - class="label-light" + class="label-bold" type="radio" @click="toggleCustomInput(false)" /> <label - class="label-light" + class="label-bold" for="every-week" > {{ __('Every week (Sundays at 4:00am)') }} @@ -131,13 +131,13 @@ v-model="cronInterval" :name="inputNameAttribute" :value="cronIntervalPresets.everyMonth" - class="label-light" + class="label-bold" type="radio" @click="toggleCustomInput(false)" /> <label - class="label-light" + class="label-bold" for="every-month" > {{ __('Every month (on the 1st at 4:00am)') }} diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue index 17b91479ea5..83437363af5 100644 --- a/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue +++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_setting_row.vue @@ -24,7 +24,7 @@ <div class="project-feature-row"> <label v-if="label" - class="label-light" + class="label-bold" > {{ label }} <a diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js index 8cf7f2f23d0..e49c67ffb5c 100644 --- a/app/assets/javascripts/profile/profile.js +++ b/app/assets/javascripts/profile/profile.js @@ -49,13 +49,15 @@ export default class Profile { saveForm() { const self = this; - const formData = new FormData(this.form[0]); + const formData = new FormData(this.form.get(0)); const avatarBlob = this.avatarGlCrop.getBlob(); if (avatarBlob != null) { formData.append('user[avatar]', avatarBlob, 'avatar.png'); } + formData.delete('user[avatar]-trigger'); + axios({ method: this.form.attr('method'), url: this.form.attr('action'), diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss index a22454c24e2..a10ff3eecb3 100644 --- a/app/assets/stylesheets/framework/forms.scss +++ b/app/assets/stylesheets/framework/forms.scss @@ -31,7 +31,7 @@ label { margin: 0; } - &.label-light { + &.label-bold { font-weight: $gl-font-weight-bold; } } diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 9bc774b7636..1cba0011304 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -10,7 +10,6 @@ class Projects::RawController < Projects::ApplicationController def show @blob = @repository.blob_at(@commit.id, @path) - if @blob headers['X-Content-Type-Options'] = 'nosniff' @@ -19,7 +18,7 @@ class Projects::RawController < Projects::ApplicationController if @blob.stored_externally? send_lfs_object else - send_git_blob @repository, @blob + send_git_blob @repository, @blob, inline: (params[:inline] != 'false') end else render_404 diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 3db28fd6da3..7eb45ddd117 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -114,22 +114,22 @@ module BlobHelper icon("#{file_type_icon_class('file', mode, name)} fw") end - def blob_raw_url(only_path: false) + def blob_raw_url(**kwargs) if @build && @entry - raw_project_job_artifacts_url(@project, @build, path: @entry.path, only_path: only_path) + raw_project_job_artifacts_url(@project, @build, path: @entry.path, **kwargs) elsif @snippet if @snippet.project_id - raw_project_snippet_url(@project, @snippet, only_path: only_path) + raw_project_snippet_url(@project, @snippet, **kwargs) else - raw_snippet_url(@snippet, only_path: only_path) + raw_snippet_url(@snippet, **kwargs) end elsif @blob - project_raw_url(@project, @id, only_path: only_path) + project_raw_url(@project, @id, **kwargs) end end - def blob_raw_path - blob_raw_url(only_path: true) + def blob_raw_path(**kwargs) + blob_raw_url(**kwargs, only_path: true) end # SVGs can contain malicious JavaScript; only include whitelisted @@ -226,16 +226,17 @@ module BlobHelper def open_raw_blob_button(blob) return if blob.empty? + return if blob.raw_binary? || blob.stored_externally? - if blob.raw_binary? || blob.stored_externally? - icon = sprite_icon('download') - title = 'Download' - else - icon = icon('file-code-o') - title = 'Open raw' - end + title = 'Open raw' + link_to icon('file-code-o'), blob_raw_path, class: 'btn btn-sm has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: title, data: { container: 'body' } + end + + def download_blob_button(blob) + return if blob.empty? - link_to icon, blob_raw_path, class: 'btn btn-sm has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: title, data: { container: 'body' } + title = 'Download' + link_to sprite_icon('download'), blob_raw_path(inline: false), download: @path, class: 'btn btn-sm has-tooltip', target: '_blank', rel: 'noopener noreferrer', title: title, data: { container: 'body' } end def blob_render_error_reason(viewer) diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb index a82271ce0ee..fd1d78bd9b8 100644 --- a/app/helpers/workhorse_helper.rb +++ b/app/helpers/workhorse_helper.rb @@ -2,9 +2,9 @@ # Workhorse will also serve files when using `send_file`. module WorkhorseHelper # Send a Git blob through Workhorse - def send_git_blob(repository, blob) + def send_git_blob(repository, blob, inline: true) headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob)) - headers['Content-Disposition'] = 'inline' + headers['Content-Disposition'] = inline ? 'inline' : 'attachment' headers['Content-Type'] = safe_content_type(blob) render plain: "" end diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb index 71b0c3468b9..5ff7b41b82b 100644 --- a/app/models/concerns/protected_ref_access.rb +++ b/app/models/concerns/protected_ref_access.rb @@ -17,6 +17,11 @@ module ProtectedRefAccess scope :master, -> { maintainer } # @deprecated scope :maintainer, -> { where(access_level: Gitlab::Access::MAINTAINER) } scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) } + scope :by_user, -> (user) { where(user_id: user ) } + scope :by_group, -> (group) { where(group_id: group ) } + scope :for_role, -> { where(user_id: nil, group_id: nil) } + scope :for_user, -> { where.not(user_id: nil) } + scope :for_group, -> { where.not(group_id: nil) } validates :access_level, presence: true, if: :role?, inclusion: { in: ALLOWED_ACCESS_LEVELS diff --git a/app/views/admin/application_settings/_abuse.html.haml b/app/views/admin/application_settings/_abuse.html.haml index 91993838fc8..4e9465fc75d 100644 --- a/app/views/admin/application_settings/_abuse.html.haml +++ b/app/views/admin/application_settings/_abuse.html.haml @@ -3,7 +3,7 @@ %fieldset .form-group - = f.label :admin_notification_email, 'Abuse reports notification email', class: 'label-light' + = f.label :admin_notification_email, 'Abuse reports notification email', class: 'label-bold' = f.text_field :admin_notification_email, class: 'form-control' .form-text.text-muted Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area. diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml index f40939747f4..ca242b0ede9 100644 --- a/app/views/admin/application_settings/_account_and_limit.html.haml +++ b/app/views/admin/application_settings/_account_and_limit.html.haml @@ -8,23 +8,23 @@ = f.label :gravatar_enabled, class: 'form-check-label' do Gravatar enabled .form-group - = f.label :default_projects_limit, class: 'label-light' + = f.label :default_projects_limit, class: 'label-bold' = f.number_field :default_projects_limit, class: 'form-control' .form-group - = f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'label-light' + = f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'label-bold' = f.number_field :max_attachment_size, class: 'form-control' .form-group - = f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-light' + = f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-bold' = f.number_field :session_expire_delay, class: 'form-control' %span.form-text.text-muted#session_expire_delay_help_block GitLab restart is required to apply changes .form-group - = f.label :user_oauth_applications, 'User OAuth applications', class: 'label-light' + = f.label :user_oauth_applications, 'User OAuth applications', class: 'label-bold' .form-check = f.check_box :user_oauth_applications, class: 'form-check-input' = f.label :user_oauth_applications, class: 'form-check-label' do Allow users to register any application to use GitLab as an OAuth provider .form-group - = f.label :user_default_external, 'New users set to external', class: 'label-light' + = f.label :user_default_external, 'New users set to external', class: 'label-bold' .form-check = f.check_box :user_default_external, class: 'form-check-input' = f.label :user_default_external, class: 'form-check-label' do diff --git a/app/views/admin/application_settings/_background_jobs.html.haml b/app/views/admin/application_settings/_background_jobs.html.haml index fd8e695ed49..02d761576e3 100644 --- a/app/views/admin/application_settings/_background_jobs.html.haml +++ b/app/views/admin/application_settings/_background_jobs.html.haml @@ -14,12 +14,12 @@ .form-text.text-muted Limit the amount of resources slow running jobs are assigned. .form-group - = f.label :sidekiq_throttling_queues, 'Sidekiq queues to throttle', class: 'label-light' + = f.label :sidekiq_throttling_queues, 'Sidekiq queues to throttle', class: 'label-bold' = f.select :sidekiq_throttling_queues, sidekiq_queue_options_for_select, { include_hidden: false }, multiple: true, class: 'select2 select-wide', data: { field: 'sidekiq_throttling_queues' } .form-text.text-muted Choose which queues you wish to throttle. .form-group - = f.label :sidekiq_throttling_factor, 'Throttling Factor', class: 'label-light' + = f.label :sidekiq_throttling_factor, 'Throttling Factor', class: 'label-bold' = f.number_field :sidekiq_throttling_factor, class: 'form-control', min: '0.01', max: '0.99', step: '0.01' .form-text.text-muted The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive. diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml index 7c16cafe13f..7a304ef2884 100644 --- a/app/views/admin/application_settings/_ci_cd.html.haml +++ b/app/views/admin/application_settings/_ci_cd.html.haml @@ -11,7 +11,7 @@ It will automatically build, test, and deploy applications based on a predefined CI/CD configuration = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md') .form-group - = f.label :auto_devops_domain, class: 'label-light' + = f.label :auto_devops_domain, class: 'label-bold' = f.text_field :auto_devops_domain, class: 'form-control', placeholder: 'domain.com' .form-text.text-muted = s_("AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages.") @@ -21,17 +21,17 @@ = f.label :shared_runners_enabled, class: 'form-check-label' do Enable shared runners for new projects .form-group - = f.label :shared_runners_text, class: 'label-light' + = f.label :shared_runners_text, class: 'label-bold' = f.text_area :shared_runners_text, class: 'form-control', rows: 4 .form-text.text-muted Markdown enabled .form-group - = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'label-light' + = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'label-bold' = f.number_field :max_artifacts_size, class: 'form-control' .form-text.text-muted Set the maximum file size for each job's artifacts = link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size') .form-group - = f.label :default_artifacts_expire_in, 'Default artifacts expiration', class: 'label-light' + = f.label :default_artifacts_expire_in, 'Default artifacts expiration', class: 'label-bold' = f.text_field :default_artifacts_expire_in, class: 'form-control' .form-text.text-muted Set the default expiration time for each job's artifacts. diff --git a/app/views/admin/application_settings/_gitaly.html.haml b/app/views/admin/application_settings/_gitaly.html.haml index 0b4001c0824..4ade9ee975a 100644 --- a/app/views/admin/application_settings/_gitaly.html.haml +++ b/app/views/admin/application_settings/_gitaly.html.haml @@ -3,20 +3,20 @@ %fieldset .form-group - = f.label :gitaly_timeout_default, 'Default Timeout Period', class: 'label-light' + = f.label :gitaly_timeout_default, 'Default Timeout Period', class: 'label-bold' = f.number_field :gitaly_timeout_default, class: 'form-control' .form-text.text-muted Timeout for Gitaly calls from the GitLab application (in seconds). This timeout is not enforced for git fetch/push operations or Sidekiq jobs. .form-group - = f.label :gitaly_timeout_fast, 'Fast Timeout Period', class: 'label-light' + = f.label :gitaly_timeout_fast, 'Fast Timeout Period', class: 'label-bold' = f.number_field :gitaly_timeout_fast, class: 'form-control' .form-text.text-muted Fast operation timeout (in seconds). Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. .form-group - = f.label :gitaly_timeout_medium, 'Medium Timeout Period', class: 'label-light' + = f.label :gitaly_timeout_medium, 'Medium Timeout Period', class: 'label-bold' = f.number_field :gitaly_timeout_medium, class: 'form-control' .form-text.text-muted Medium operation timeout (in seconds). This should be a value between the Fast and the Default timeout. diff --git a/app/views/admin/application_settings/_help_page.html.haml b/app/views/admin/application_settings/_help_page.html.haml index 1f402fcb786..73f8d7bb77c 100644 --- a/app/views/admin/application_settings/_help_page.html.haml +++ b/app/views/admin/application_settings/_help_page.html.haml @@ -3,7 +3,7 @@ %fieldset .form-group - = f.label :help_page_text, class: 'label-light' + = f.label :help_page_text, class: 'label-bold' = f.text_area :help_page_text, class: 'form-control', rows: 4 .form-text.text-muted Markdown enabled .form-group @@ -12,7 +12,7 @@ = f.label :help_page_hide_commercial_content, class: 'form-check-label' do Hide marketing-related entries from help .form-group - = f.label :help_page_support_url, 'Support page URL', class: 'label-light' + = f.label :help_page_support_url, 'Support page URL', class: 'label-bold' = f.text_field :help_page_support_url, class: 'form-control', placeholder: 'http://company.example.com/getting-help', :'aria-describedby' => 'support_help_block' %span.form-text.text-muted#support_help_block Alternate support URL for help page diff --git a/app/views/admin/application_settings/_influx.html.haml b/app/views/admin/application_settings/_influx.html.haml index 61e8e3199a9..2f6d6ddf23d 100644 --- a/app/views/admin/application_settings/_influx.html.haml +++ b/app/views/admin/application_settings/_influx.html.haml @@ -14,10 +14,10 @@ = f.label :metrics_enabled, class: 'form-check-label' do Enable InfluxDB Metrics .form-group - = f.label :metrics_host, 'InfluxDB host', class: 'label-light' + = f.label :metrics_host, 'InfluxDB host', class: 'label-bold' = f.text_field :metrics_host, class: 'form-control', placeholder: 'influxdb.example.com' .form-group - = f.label :metrics_port, 'InfluxDB port', class: 'label-light' + = f.label :metrics_port, 'InfluxDB port', class: 'label-bold' = f.text_field :metrics_port, class: 'form-control', placeholder: '8089' .form-text.text-muted The UDP port to use for connecting to InfluxDB. InfluxDB requires that @@ -25,7 +25,7 @@ sending messages to this port, without it metrics data will not be saved. .form-group - = f.label :metrics_pool_size, 'Connection pool size', class: 'label-light' + = f.label :metrics_pool_size, 'Connection pool size', class: 'label-bold' = f.number_field :metrics_pool_size, class: 'form-control' .form-text.text-muted The amount of InfluxDB connections to open. Connections are opened @@ -33,25 +33,25 @@ enough connections are available (at minimum the amount of application server threads). .form-group - = f.label :metrics_timeout, 'Connection timeout', class: 'label-light' + = f.label :metrics_timeout, 'Connection timeout', class: 'label-bold' = f.number_field :metrics_timeout, class: 'form-control' .form-text.text-muted The amount of seconds after which an InfluxDB connection will time out. .form-group - = f.label :metrics_method_call_threshold, 'Method Call Threshold (ms)', class: 'label-light' + = f.label :metrics_method_call_threshold, 'Method Call Threshold (ms)', class: 'label-bold' = f.number_field :metrics_method_call_threshold, class: 'form-control' .form-text.text-muted A method call is only tracked when it takes longer to complete than the given amount of milliseconds. .form-group - = f.label :metrics_sample_interval, 'Sampler Interval (sec)', class: 'label-light' + = f.label :metrics_sample_interval, 'Sampler Interval (sec)', class: 'label-bold' = f.number_field :metrics_sample_interval, class: 'form-control' .form-text.text-muted The sampling interval in seconds. Sampled data includes memory usage, retained Ruby objects, file descriptors and so on. .form-group - = f.label :metrics_packet_size, 'Metrics per packet', class: 'label-light' + = f.label :metrics_packet_size, 'Metrics per packet', class: 'label-bold' = f.number_field :metrics_packet_size, class: 'form-control' .form-text.text-muted The amount of points to store in a single UDP packet. More points diff --git a/app/views/admin/application_settings/_ip_limits.html.haml b/app/views/admin/application_settings/_ip_limits.html.haml index 73d570a5fee..44118777467 100644 --- a/app/views/admin/application_settings/_ip_limits.html.haml +++ b/app/views/admin/application_settings/_ip_limits.html.haml @@ -10,10 +10,10 @@ %span.form-text.text-muted Helps reduce request volume (e.g. from crawlers or abusive bots) .form-group - = f.label :throttle_unauthenticated_requests_per_period, 'Max requests per period per IP', class: 'label-light' + = f.label :throttle_unauthenticated_requests_per_period, 'Max requests per period per IP', class: 'label-bold' = f.number_field :throttle_unauthenticated_requests_per_period, class: 'form-control' .form-group - = f.label :throttle_unauthenticated_period_in_seconds, 'Rate limit period in seconds', class: 'label-light' + = f.label :throttle_unauthenticated_period_in_seconds, 'Rate limit period in seconds', class: 'label-bold' = f.number_field :throttle_unauthenticated_period_in_seconds, class: 'form-control' .form-group .form-check @@ -23,10 +23,10 @@ %span.form-text.text-muted Helps reduce request volume (e.g. from crawlers or abusive bots) .form-group - = f.label :throttle_authenticated_api_requests_per_period, 'Max requests per period per user', class: 'label-light' + = f.label :throttle_authenticated_api_requests_per_period, 'Max requests per period per user', class: 'label-bold' = f.number_field :throttle_authenticated_api_requests_per_period, class: 'form-control' .form-group - = f.label :throttle_authenticated_api_period_in_seconds, 'Rate limit period in seconds', class: 'label-light' + = f.label :throttle_authenticated_api_period_in_seconds, 'Rate limit period in seconds', class: 'label-bold' = f.number_field :throttle_authenticated_api_period_in_seconds, class: 'form-control' .form-group .form-check @@ -36,10 +36,10 @@ %span.form-text.text-muted Helps reduce request volume (e.g. from crawlers or abusive bots) .form-group - = f.label :throttle_authenticated_web_requests_per_period, 'Max requests per period per user', class: 'label-light' + = f.label :throttle_authenticated_web_requests_per_period, 'Max requests per period per user', class: 'label-bold' = f.number_field :throttle_authenticated_web_requests_per_period, class: 'form-control' .form-group - = f.label :throttle_authenticated_web_period_in_seconds, 'Rate limit period in seconds', class: 'label-light' + = f.label :throttle_authenticated_web_period_in_seconds, 'Rate limit period in seconds', class: 'label-bold' = f.number_field :throttle_authenticated_web_period_in_seconds, class: 'form-control' = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/admin/application_settings/_koding.html.haml b/app/views/admin/application_settings/_koding.html.haml index ae60f68f5fe..798a061feb6 100644 --- a/app/views/admin/application_settings/_koding.html.haml +++ b/app/views/admin/application_settings/_koding.html.haml @@ -10,7 +10,7 @@ .form-text.text-muted Koding integration has been deprecated since GitLab 10.0. If you disable your Koding integration, you will not be able to enable it again. .form-group - = f.label :koding_url, 'Koding URL', class: 'label-light' + = f.label :koding_url, 'Koding URL', class: 'label-bold' = f.text_field :koding_url, class: 'form-control', placeholder: 'http://gitlab.your-koding-instance.com:8090' .form-text.text-muted Koding has integration enabled out of the box for the diff --git a/app/views/admin/application_settings/_logging.html.haml b/app/views/admin/application_settings/_logging.html.haml index a6e549cd1f0..f13df885997 100644 --- a/app/views/admin/application_settings/_logging.html.haml +++ b/app/views/admin/application_settings/_logging.html.haml @@ -13,7 +13,7 @@ %a{ href: 'https://getsentry.com', target: '_blank', rel: 'noopener noreferrer' } https://getsentry.com .form-group - = f.label :sentry_dsn, 'Sentry DSN', class: 'label-light' + = f.label :sentry_dsn, 'Sentry DSN', class: 'label-bold' = f.text_field :sentry_dsn, class: 'form-control' .form-group @@ -26,7 +26,7 @@ %a{ href: 'https://sentry.io/for/javascript/', target: '_blank', rel: 'noopener noreferrer' } https://sentry.io/for/javascript/ .form-group - = f.label :clientside_sentry_dsn, 'Clientside Sentry DSN', class: 'label-light' + = f.label :clientside_sentry_dsn, 'Clientside Sentry DSN', class: 'label-bold' = f.text_field :clientside_sentry_dsn, class: 'form-control' = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/admin/application_settings/_pages.html.haml b/app/views/admin/application_settings/_pages.html.haml index f168ec62ffd..cf4b4c000b5 100644 --- a/app/views/admin/application_settings/_pages.html.haml +++ b/app/views/admin/application_settings/_pages.html.haml @@ -3,7 +3,7 @@ %fieldset .form-group - = f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'label-light' + = f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'label-bold' = f.number_field :max_pages_size, class: 'form-control' .form-text.text-muted 0 for unlimited .form-group diff --git a/app/views/admin/application_settings/_performance_bar.html.haml b/app/views/admin/application_settings/_performance_bar.html.haml index ddbfcc6b77b..c4e14ffb8af 100644 --- a/app/views/admin/application_settings/_performance_bar.html.haml +++ b/app/views/admin/application_settings/_performance_bar.html.haml @@ -8,7 +8,7 @@ = f.label :performance_bar_enabled, class: 'form-check-label' do Enable the Performance Bar .form-group - = f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'label-light' + = f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'label-bold' = f.text_field :performance_bar_allowed_group_path, class: 'form-control', placeholder: 'my-org/my-group', value: @application_setting.performance_bar_allowed_group&.full_path = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/admin/application_settings/_plantuml.html.haml b/app/views/admin/application_settings/_plantuml.html.haml index 259f18b3b96..07ffcc9e73a 100644 --- a/app/views/admin/application_settings/_plantuml.html.haml +++ b/app/views/admin/application_settings/_plantuml.html.haml @@ -8,7 +8,7 @@ = f.label :plantuml_enabled, class: 'form-check-label' do Enable PlantUML .form-group - = f.label :plantuml_url, 'PlantUML URL', class: 'label-light' + = f.label :plantuml_url, 'PlantUML URL', class: 'label-bold' = f.text_field :plantuml_url, class: 'form-control', placeholder: 'http://gitlab.your-plantuml-instance.com:8080' .form-text.text-muted Allow rendering of diff --git a/app/views/admin/application_settings/_realtime.html.haml b/app/views/admin/application_settings/_realtime.html.haml index 120cf4909b2..4d224d7dd51 100644 --- a/app/views/admin/application_settings/_realtime.html.haml +++ b/app/views/admin/application_settings/_realtime.html.haml @@ -3,7 +3,7 @@ %fieldset .form-group - = f.label :polling_interval_multiplier, 'Polling interval multiplier', class: 'label-light' + = f.label :polling_interval_multiplier, 'Polling interval multiplier', class: 'label-bold' = f.text_field :polling_interval_multiplier, class: 'form-control' .form-text.text-muted Change this value to influence how frequently the GitLab UI polls for updates. diff --git a/app/views/admin/application_settings/_registry.html.haml b/app/views/admin/application_settings/_registry.html.haml index beac70482e5..c511b1ee08a 100644 --- a/app/views/admin/application_settings/_registry.html.haml +++ b/app/views/admin/application_settings/_registry.html.haml @@ -3,7 +3,7 @@ %fieldset .form-group - = f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'label-light' + = f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'label-bold' = f.number_field :container_registry_token_expire_delay, class: 'form-control' = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/admin/application_settings/_repository_check.html.haml b/app/views/admin/application_settings/_repository_check.html.haml index 57facc380eb..cacbcf65845 100644 --- a/app/views/admin/application_settings/_repository_check.html.haml +++ b/app/views/admin/application_settings/_repository_check.html.haml @@ -38,17 +38,17 @@ Creating pack file bitmaps makes housekeeping take a little longer but bitmaps should accelerate 'git clone' performance. .form-group - = f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-light' + = f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-bold' = f.number_field :housekeeping_incremental_repack_period, class: 'form-control' .form-text.text-muted Number of Git pushes after which an incremental 'git repack' is run. .form-group - = f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-light' + = f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-bold' = f.number_field :housekeeping_full_repack_period, class: 'form-control' .form-text.text-muted Number of Git pushes after which a full 'git repack' is run. .form-group - = f.label :housekeeping_gc_period, 'Git GC period', class: 'label-light' + = f.label :housekeeping_gc_period, 'Git GC period', class: 'label-bold' = f.number_field :housekeeping_gc_period, class: 'form-control' .form-text.text-muted Number of Git pushes after which 'git gc' is run. diff --git a/app/views/admin/application_settings/_repository_mirrors_form.html.haml b/app/views/admin/application_settings/_repository_mirrors_form.html.haml index beeb5169361..dbc1e9e3a71 100644 --- a/app/views/admin/application_settings/_repository_mirrors_form.html.haml +++ b/app/views/admin/application_settings/_repository_mirrors_form.html.haml @@ -3,7 +3,7 @@ %fieldset .form-group - = f.label :mirror_available, 'Enable mirror configuration', class: 'label-light' + = f.label :mirror_available, 'Enable mirror configuration', class: 'label-bold' .form-check = f.check_box :mirror_available, class: 'form-check-input' = f.label :mirror_available, class: 'form-check-label' do diff --git a/app/views/admin/application_settings/_repository_storage.html.haml b/app/views/admin/application_settings/_repository_storage.html.haml index 5a303666353..685de99917e 100644 --- a/app/views/admin/application_settings/_repository_storage.html.haml +++ b/app/views/admin/application_settings/_repository_storage.html.haml @@ -13,7 +13,7 @@ repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance. %em (EXPERIMENTAL) .form-group - = f.label :repository_storages, 'Storage paths for new projects', class: 'label-light' + = f.label :repository_storages, 'Storage paths for new projects', class: 'label-bold' = f.select :repository_storages, repository_storages_options_for_select(@application_setting.repository_storages), {include_hidden: false}, multiple: true, class: 'form-control' .form-text.text-muted @@ -23,27 +23,27 @@ .sub-section %h4 Circuit breaker .form-group - = f.label :circuitbreaker_check_interval, _('Check interval'), class: 'label-light' + = f.label :circuitbreaker_check_interval, _('Check interval'), class: 'label-bold' = f.number_field :circuitbreaker_check_interval, class: 'form-control' .form-text.text-muted = circuitbreaker_check_interval_help_text .form-group - = f.label :circuitbreaker_access_retries, _('Number of access attempts'), class: 'label-light' + = f.label :circuitbreaker_access_retries, _('Number of access attempts'), class: 'label-bold' = f.number_field :circuitbreaker_access_retries, class: 'form-control' .form-text.text-muted = circuitbreaker_access_retries_help_text .form-group - = f.label :circuitbreaker_storage_timeout, _('Seconds to wait for a storage access attempt'), class: 'label-light' + = f.label :circuitbreaker_storage_timeout, _('Seconds to wait for a storage access attempt'), class: 'label-bold' = f.number_field :circuitbreaker_storage_timeout, class: 'form-control' .form-text.text-muted = circuitbreaker_storage_timeout_help_text .form-group - = f.label :circuitbreaker_failure_count_threshold, _('Maximum git storage failures'), class: 'label-light' + = f.label :circuitbreaker_failure_count_threshold, _('Maximum git storage failures'), class: 'label-bold' = f.number_field :circuitbreaker_failure_count_threshold, class: 'form-control' .form-text.text-muted = circuitbreaker_failure_count_help_text .form-group - = f.label :circuitbreaker_failure_reset_time, _('Seconds before reseting failure information'), class: 'label-light' + = f.label :circuitbreaker_failure_reset_time, _('Seconds before reseting failure information'), class: 'label-bold' = f.number_field :circuitbreaker_failure_reset_time, class: 'form-control' .form-text.text-muted = circuitbreaker_failure_reset_time_help_text diff --git a/app/views/admin/application_settings/_signin.html.haml b/app/views/admin/application_settings/_signin.html.haml index 69d1a43c511..344ed2339fb 100644 --- a/app/views/admin/application_settings/_signin.html.haml +++ b/app/views/admin/application_settings/_signin.html.haml @@ -21,31 +21,31 @@ must be used to authenticate. - if omniauth_enabled? && button_based_providers.any? .form-group - = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'label-light' + = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'label-bold' = hidden_field_tag 'application_setting[enabled_oauth_sign_in_sources][]' .btn-group{ data: { toggle: 'buttons' } } - oauth_providers_checkboxes.each do |source| = source .form-group - = f.label :two_factor_authentication, 'Two-factor authentication', class: 'label-light' + = f.label :two_factor_authentication, 'Two-factor authentication', class: 'label-bold' .form-check = f.check_box :require_two_factor_authentication, class: 'form-check-input' = f.label :require_two_factor_authentication, class: 'form-check-label' do Require all users to setup Two-factor authentication .form-group - = f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'label-light' + = f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'label-bold' = f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0' .form-text.text-muted Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication .form-group - = f.label :home_page_url, 'Home page URL', class: 'label-light' + = f.label :home_page_url, 'Home page URL', class: 'label-bold' = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block' %span.form-text.text-muted#home_help_block We will redirect non-logged in users to this page .form-group - = f.label :after_sign_out_path, class: 'label-light' + = f.label :after_sign_out_path, class: 'label-bold' = f.text_field :after_sign_out_path, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'after_sign_out_path_help_block' %span.form-text.text-muted#after_sign_out_path_help_block We will redirect users to this page after they sign out .form-group - = f.label :sign_in_text, class: 'label-light' + = f.label :sign_in_text, class: 'label-bold' = f.text_area :sign_in_text, class: 'form-control', rows: 4 .form-text.text-muted Markdown enabled diff --git a/app/views/admin/application_settings/_signup.html.haml b/app/views/admin/application_settings/_signup.html.haml index b9ba9128cc9..2b5ff5a38e3 100644 --- a/app/views/admin/application_settings/_signup.html.haml +++ b/app/views/admin/application_settings/_signup.html.haml @@ -13,11 +13,11 @@ = f.label :send_user_confirmation_email, class: 'form-check-label' do Send confirmation email on sign-up .form-group - = f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'label-light' + = f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'label-bold' = f.text_area :domain_whitelist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8 .form-text.text-muted ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com .form-group - = f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'label-light' + = f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'label-bold' .form-check = f.check_box :domain_blacklist_enabled, class: 'form-check-input' = f.label :domain_blacklist_enabled, class: 'form-check-label' do @@ -34,16 +34,16 @@ .option-title Enter blacklist manually .form-group.blacklist-file - = f.label :domain_blacklist_file, 'Blacklist file', class: 'label-light' + = f.label :domain_blacklist_file, 'Blacklist file', class: 'label-bold' = f.file_field :domain_blacklist_file, class: 'form-control', accept: '.txt,.conf' .form-text.text-muted Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries. .form-group.blacklist-raw - = f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'label-light' + = f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'label-bold' = f.text_area :domain_blacklist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8 .form-text.text-muted Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com .form-group - = f.label :after_sign_up_text, class: 'label-light' + = f.label :after_sign_up_text, class: 'label-bold' = f.text_area :after_sign_up_text, class: 'form-control', rows: 4 .form-text.text-muted Markdown enabled diff --git a/app/views/admin/application_settings/_spam.html.haml b/app/views/admin/application_settings/_spam.html.haml index 8f0dce962a9..691b56e8a8e 100644 --- a/app/views/admin/application_settings/_spam.html.haml +++ b/app/views/admin/application_settings/_spam.html.haml @@ -10,14 +10,14 @@ %span.form-text.text-muted#recaptcha_help_block Helps prevent bots from creating accounts .form-group - = f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'label-light' + = f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'label-bold' = f.text_field :recaptcha_site_key, class: 'form-control' .form-text.text-muted Generate site and private keys at %a{ href: 'http://www.google.com/recaptcha', target: 'blank' } http://www.google.com/recaptcha .form-group - = f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'label-light' + = f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'label-bold' = f.text_field :recaptcha_private_key, class: 'form-control' .form-group @@ -28,7 +28,7 @@ %span.form-text.text-muted#akismet_help_block Helps prevent bots from creating issues .form-group - = f.label :akismet_api_key, 'Akismet API Key', class: 'label-light' + = f.label :akismet_api_key, 'Akismet API Key', class: 'label-bold' = f.text_field :akismet_api_key, class: 'form-control' .form-text.text-muted Generate API key at @@ -43,13 +43,13 @@ Helps prevent malicious users hide their activity .form-group - = f.label :unique_ips_limit_per_user, 'IPs per user', class: 'label-light' + = f.label :unique_ips_limit_per_user, 'IPs per user', class: 'label-bold' = f.number_field :unique_ips_limit_per_user, class: 'form-control' .form-text.text-muted Maximum number of unique IPs per user .form-group - = f.label :unique_ips_limit_time_window, 'IP expiration time', class: 'label-light' + = f.label :unique_ips_limit_time_window, 'IP expiration time', class: 'label-bold' = f.number_field :unique_ips_limit_time_window, class: 'form-control' .form-text.text-muted How many seconds an IP will be counted towards the limit diff --git a/app/views/admin/application_settings/_terminal.html.haml b/app/views/admin/application_settings/_terminal.html.haml index 543628ff0ee..dc88a9885c8 100644 --- a/app/views/admin/application_settings/_terminal.html.haml +++ b/app/views/admin/application_settings/_terminal.html.haml @@ -3,7 +3,7 @@ %fieldset .form-group - = f.label :terminal_max_session_time, 'Max session time', class: 'label-light' + = f.label :terminal_max_session_time, 'Max session time', class: 'label-bold' = f.number_field :terminal_max_session_time, class: 'form-control' .form-text.text-muted Maximum time for web terminal websocket connection (in seconds). diff --git a/app/views/admin/application_settings/_visibility_and_access.html.haml b/app/views/admin/application_settings/_visibility_and_access.html.haml index 4cc3e6a7d03..cec65f70e9d 100644 --- a/app/views/admin/application_settings/_visibility_and_access.html.haml +++ b/app/views/admin/application_settings/_visibility_and_access.html.haml @@ -3,19 +3,19 @@ %fieldset .form-group - = f.label :default_branch_protection, class: 'label-light' + = f.label :default_branch_protection, class: 'label-bold' = f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control' .form-group.visibility-level-setting - = f.label :default_project_visibility, class: 'label-light' + = f.label :default_project_visibility, class: 'label-bold' = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new) .form-group.visibility-level-setting - = f.label :default_snippet_visibility, class: 'label-light' + = f.label :default_snippet_visibility, class: 'label-bold' = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new) .form-group.visibility-level-setting - = f.label :default_group_visibility, class: 'label-light' + = f.label :default_group_visibility, class: 'label-bold' = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new) .form-group - = f.label :restricted_visibility_levels, class: 'label-light' + = f.label :restricted_visibility_levels, class: 'label-bold' - checkbox_name = 'application_setting[restricted_visibility_levels][]' = hidden_field_tag(checkbox_name) - restricted_level_checkboxes('restricted-visibility-help', checkbox_name, class: 'form-check-input').each do |level| @@ -25,7 +25,7 @@ Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users. .form-group - = f.label :import_sources, class: 'label-light' + = f.label :import_sources, class: 'label-bold' = hidden_field_tag 'application_setting[import_sources][]' - import_sources_checkboxes('import-sources-help', class: 'form-check-input').each do |source| .form-check= source @@ -44,7 +44,7 @@ Project export enabled .form-group - %label.label-light Enabled Git access protocols + %label.label-bold Enabled Git access protocols = select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control') %span.form-text.text-muted#clone-protocol-help Allow only the selected protocols to be used for Git access. @@ -52,7 +52,7 @@ - ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type| - field_name = :"#{type}_key_restriction" .form-group - = f.label field_name, "#{type.upcase} SSH keys", class: 'label-light' + = f.label field_name, "#{type.upcase} SSH keys", class: 'label-bold' = f.select field_name, key_restriction_options_for_select(type), {}, class: 'form-control' = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/admin/hooks/_form.html.haml b/app/views/admin/hooks/_form.html.haml index 3abde755f0f..072f80b56b9 100644 --- a/app/views/admin/hooks/_form.html.haml +++ b/app/views/admin/hooks/_form.html.haml @@ -1,15 +1,15 @@ = form_errors(hook) .form-group - = form.label :url, 'URL', class: 'label-light' + = form.label :url, 'URL', class: 'label-bold' = form.text_field :url, class: 'form-control' .form-group - = form.label :token, 'Secret Token', class: 'label-light' + = form.label :token, 'Secret Token', class: 'label-bold' = form.text_field :token, class: 'form-control' %p.form-text.text-muted Use this token to validate received payloads .form-group - = form.label :url, 'Trigger', class: 'label-light' + = form.label :url, 'Trigger', class: 'label-bold' %ul.list-unstyled %li .form-text.text-muted @@ -45,7 +45,7 @@ %p.light This URL will be triggered when a merge request is created/updated/merged .form-group - = form.label :enable_ssl_verification, 'SSL verification', class: 'label-light checkbox' + = form.label :enable_ssl_verification, 'SSL verification', class: 'label-bold checkbox' .form-check = form.check_box :enable_ssl_verification, class: 'form-check-input' = form.label :enable_ssl_verification, class: 'form-check-label' do diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml index 1ddd0df54cd..0bc057a8864 100644 --- a/app/views/doorkeeper/applications/_form.html.haml +++ b/app/views/doorkeeper/applications/_form.html.haml @@ -2,11 +2,11 @@ = form_errors(application) .form-group - = f.label :name, class: 'label-light' + = f.label :name, class: 'label-bold' = f.text_field :name, class: 'form-control', required: true .form-group - = f.label :redirect_uri, class: 'label-light' + = f.label :redirect_uri, class: 'label-bold' = f.text_area :redirect_uri, class: 'form-control', required: true %span.form-text.text-muted @@ -16,7 +16,7 @@ = _('Use <code>%{native_redirect_uri}</code> for local tests').html_safe % { native_redirect_uri: Doorkeeper.configuration.native_redirect_uri } .form-group - = f.label :scopes, class: 'label-light' + = f.label :scopes, class: 'label-bold' = render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: application, scopes: @scopes .prepend-top-default diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml index 64786d24266..ab8263533be 100644 --- a/app/views/groups/settings/_general.html.haml +++ b/app/views/groups/settings/_general.html.haml @@ -4,17 +4,17 @@ %fieldset .row .form-group.col-md-9 - = f.label :name, class: 'label-light' do + = f.label :name, class: 'label-bold' do Group name = f.text_field :name, class: 'form-control' .form-group.col-md-3 - = f.label :id, class: 'label-light' do + = f.label :id, class: 'label-bold' do Group ID = f.text_field :id, class: 'form-control', readonly: true .form-group - = f.label :description, class: 'label-light' do + = f.label :description, class: 'label-bold' do Group description %span.light (optional) = f.text_area :description, class: 'form-control', rows: 3, maxlength: 250 diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml index a258fc64b1e..4225ee19217 100644 --- a/app/views/import/gitlab_projects/new.html.haml +++ b/app/views/import/gitlab_projects/new.html.haml @@ -9,7 +9,7 @@ = form_tag import_gitlab_project_path, class: 'new_project', multipart: true do .row .form-group.col-12.col-sm-6 - = label_tag :namespace_id, 'Project path', class: 'label-light' + = label_tag :namespace_id, 'Project path', class: 'label-bold' .form-group .input-group - if current_user.can_select_namespace? @@ -24,7 +24,7 @@ #{user_url(current_user.username)}/ = hidden_field_tag :namespace_id, value: current_user.namespace_id .form-group.col-12.col-sm-6.project-path - = label_tag :path, _('Project name'), class: 'label-light' + = label_tag :path, _('Project name'), class: 'label-bold' = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control", tabindex: 2, autofocus: true, required: true .row @@ -33,7 +33,7 @@ .row .form-group.col-sm-12 = hidden_field_tag :namespace_id, @namespace.id - = label_tag :file, _('GitLab project export'), class: 'label-light' + = label_tag :file, _('GitLab project export'), class: 'label-bold' .form-group = file_field_tag :file, class: '' .row diff --git a/app/views/import/manifest/_form.html.haml b/app/views/import/manifest/_form.html.haml index 763beb5958f..78c7fadb019 100644 --- a/app/views/import/manifest/_form.html.haml +++ b/app/views/import/manifest/_form.html.haml @@ -1,6 +1,6 @@ = form_tag upload_import_manifest_path, multipart: true do .form-group - = label_tag :group_id, nil, class: 'label-light' do + = label_tag :group_id, nil, class: 'label-bold' do = _('Group') .input-group .input-group-prepend.has-tooltip{ title: root_url } @@ -11,7 +11,7 @@ = _('Choose the top-level group for your repository imports.') .form-group - = label_tag :manifest, class: 'label-light' do + = label_tag :manifest, class: 'label-bold' do = _('Manifest') = file_field_tag :manifest, class: 'form-control-file', required: true .form-text.text-muted diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 9c95b6281ba..2220b4eee96 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -29,7 +29,7 @@ %p Activate signin with one of the following services .col-lg-8 - %label.label-light + %label.label-bold Connected Accounts %p Click on icon to activate signin with one of the following services - button_based_providers.each do |provider| diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml index a5db9dbe7f8..04a19ab14dd 100644 --- a/app/views/profiles/emails/index.html.haml +++ b/app/views/profiles/emails/index.html.haml @@ -12,7 +12,7 @@ Add email address = form_for 'email', url: profile_emails_path do |f| .form-group - = f.label :email, class: 'label-light' + = f.label :email, class: 'label-bold' = f.text_field :email, class: 'form-control' .prepend-top-default = f.submit 'Add email address', class: 'btn btn-create' diff --git a/app/views/profiles/gpg_keys/_form.html.haml b/app/views/profiles/gpg_keys/_form.html.haml index 3fcf563d970..aa9b0aad034 100644 --- a/app/views/profiles/gpg_keys/_form.html.haml +++ b/app/views/profiles/gpg_keys/_form.html.haml @@ -3,7 +3,7 @@ = form_errors(@gpg_key) .form-group - = f.label :key, class: 'label-light' + = f.label :key, class: 'label-bold' = f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: "Don't paste the private part of the GPG key. Paste the public part which begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'." .prepend-top-default diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml index 43a2d53b84d..5207921d6fe 100644 --- a/app/views/profiles/keys/_form.html.haml +++ b/app/views/profiles/keys/_form.html.haml @@ -3,11 +3,11 @@ = form_errors(@key) .form-group - = f.label :key, class: 'label-light' + = f.label :key, class: 'label-bold' %p= _("Paste your public SSH key, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'. Don't use your private SSH key.") = f.text_area :key, class: "form-control js-add-ssh-key-validation-input", rows: 8, required: true, placeholder: s_('Profiles|Typically starts with "ssh-rsa …"') .form-group - = f.label :title, class: 'label-light' + = f.label :title, class: 'label-bold' = f.text_field :title, class: "form-control input-lg", required: true, placeholder: s_('Profiles|e.g. My MacBook key') %p.form-text.text-muted= _('Name your individual key via a title') diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index 71ea625e5d5..712eb2a4573 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -23,10 +23,10 @@ = form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f| .form-group - = f.label :notification_email, class: "label-light" + = f.label :notification_email, class: "label-bold" = f.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2" - = label_tag :global_notification_level, "Global notification level", class: "label-light" + = label_tag :global_notification_level, "Global notification level", class: "label-bold" %br .clearfix .form-group.float-left.global-notification-setting diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index 8a51a30191a..9c8cc9c059b 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -18,15 +18,15 @@ - unless @user.password_automatically_set? .form-group - = f.label :current_password, class: 'label-light' + = f.label :current_password, class: 'label-bold' = f.password_field :current_password, required: true, class: 'form-control' %p.form-text.text-muted You must provide your current password in order to change it. .form-group - = f.label :password, 'New password', class: 'label-light' + = f.label :password, 'New password', class: 'label-bold' = f.password_field :password, required: true, class: 'form-control' .form-group - = f.label :password_confirmation, class: 'label-light' + = f.label :password_confirmation, class: 'label-bold' = f.password_field :password_confirmation, required: true, class: 'form-control' .prepend-top-default.append-bottom-default = f.submit 'Save password', class: "btn btn-create append-right-10" diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml index d111113c646..c10d4ea1a4d 100644 --- a/app/views/profiles/personal_access_tokens/index.html.haml +++ b/app/views/profiles/personal_access_tokens/index.html.haml @@ -40,7 +40,7 @@ %p It cannot be used to access any other data. .col-lg-8.feed-token-reset - = label_tag :feed_token, 'Feed token', class: "label-light" + = label_tag :feed_token, 'Feed token', class: "label-bold" = text_field_tag :feed_token, current_user.feed_token, class: 'form-control', readonly: true, onclick: 'this.select()' %p.form-text.text-muted Keep this token secret. Anyone who gets ahold of it can read activity and issue RSS feeds or your calendar feed as if they were you. @@ -59,7 +59,7 @@ %p It cannot be used to access any other data. .col-lg-8.incoming-email-token-reset - = label_tag :incoming_email_token, 'Incoming email token', class: "label-light" + = label_tag :incoming_email_token, 'Incoming email token', class: "label-bold" = text_field_tag :incoming_email_token, current_user.incoming_email_token, class: 'form-control', readonly: true, onclick: 'this.select()' %p.form-text.text-muted Keep this token secret. Anyone who gets ahold of it can create issues as if they were you. diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 8f1078bd41d..fd6dd74e1c5 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -42,17 +42,17 @@ = link_to 'Learn more', help_page_path('user/profile/preferences', anchor: 'behavior'), target: '_blank' .col-lg-8 .form-group - = f.label :layout, class: 'label-light' do + = f.label :layout, class: 'label-bold' do Layout width = f.select :layout, layout_choices, {}, class: 'form-control' .form-text.text-muted Choose between fixed (max. 1200px) and fluid (100%) application layout. .form-group - = f.label :dashboard, class: 'label-light' do + = f.label :dashboard, class: 'label-bold' do Default dashboard = f.select :dashboard, dashboard_choices, {}, class: 'form-control' .form-group - = f.label :project_view, class: 'label-light' do + = f.label :project_view, class: 'label-bold' do Project overview content = f.select :project_view, project_view_choices, {}, class: 'form-control' .form-text.text-muted diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 6950e2e332d..cd10b8758f6 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -49,7 +49,7 @@ .alert.alert-danger = @error .form-group - = label_tag :pin_code, nil, class: "label-light" + = label_tag :pin_code, nil, class: "label-bold" = text_field_tag :pin_code, nil, class: "form-control", required: true .prepend-top-default = submit_tag 'Register with two-factor app', class: 'btn btn-success' diff --git a/app/views/projects/_merge_request_merge_method_settings.html.haml b/app/views/projects/_merge_request_merge_method_settings.html.haml index 3bb220ac6d0..540e996e4d8 100644 --- a/app/views/projects/_merge_request_merge_method_settings.html.haml +++ b/app/views/projects/_merge_request_merge_method_settings.html.haml @@ -2,7 +2,7 @@ - project = local_assigns.fetch(:project) .form-group - = label_tag :merge_method_merge, class: 'label-light' do + = label_tag :merge_method_merge, class: 'label-bold' do Merge method .form-check = form.radio_button :merge_method, :merge, class: "js-merge-method-radio form-check-input" diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml index f4994f5459b..c78baa5dfe4 100644 --- a/app/views/projects/_new_project_fields.html.haml +++ b/app/views/projects/_new_project_fields.html.haml @@ -4,7 +4,7 @@ .row{ id: project_name_id } = f.hidden_field :ci_cd_only, value: ci_cd_only .form-group.project-path.col-sm-6 - = f.label :namespace_id, class: 'label-light' do + = f.label :namespace_id, class: 'label-bold' do %span Project path .input-group @@ -20,7 +20,7 @@ #{user_url(current_user.username)}/ = f.hidden_field :namespace_id, value: current_user.namespace_id .form-group.project-path.col-sm-6 - = f.label :path, class: 'label-light' do + = f.label :path, class: 'label-bold' do %span Project name = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true @@ -30,12 +30,12 @@ = link_to "Create a group", new_group_path .form-group - = f.label :description, class: 'label-light' do + = f.label :description, class: 'label-bold' do Project description %span (optional) = f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250 -= f.label :visibility_level, class: 'label-light' do += f.label :visibility_level, class: 'label-bold' do Visibility Level = link_to icon('question-circle'), help_page_path("public_access/public_access"), aria: { label: 'Documentation for Visibility Level' }, target: '_blank', rel: 'noopener noreferrer' = render 'shared/visibility_level', f: f, visibility_level: visibility_level.to_i, can_change_visibility_level: true, form_model: @project, with_label: false diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml index d08807b5135..9d8627c9eb1 100644 --- a/app/views/projects/_project_templates.html.haml +++ b/app/views/projects/_project_templates.html.haml @@ -12,7 +12,7 @@ .project-fields-form .row .form-group.col-sm-12 - %label.label-light + %label.label-bold Template .input-group.template-input-group .input-group-prepend diff --git a/app/views/projects/artifacts/file.html.haml b/app/views/projects/artifacts/file.html.haml index aac7a1870df..f7174d6b2c6 100644 --- a/app/views/projects/artifacts/file.html.haml +++ b/app/views/projects/artifacts/file.html.haml @@ -27,6 +27,6 @@ .btn-group{ role: "group" }< = copy_blob_source_button(blob) - = open_raw_blob_button(blob) + = download_blob_button(blob) = render 'projects/blob/content', blob: blob diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml index 0a0b3ce1d6f..84ccd816d80 100644 --- a/app/views/projects/blob/_header.html.haml +++ b/app/views/projects/blob/_header.html.haml @@ -8,8 +8,8 @@ .btn-group{ role: "group" }< = copy_blob_source_button(blob) unless blame = open_raw_blob_button(blob) + = download_blob_button(blob) = view_on_environment_button(@commit.sha, @path, @environment) if @environment - .btn-group{ role: "group" }< = render_if_exists 'projects/blob/header_file_locks_link' = edit_blob_button diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml index 0a2e320556d..9133de6559d 100644 --- a/app/views/projects/clusters/gcp/_form.html.haml +++ b/app/views/projects/clusters/gcp/_form.html.haml @@ -15,15 +15,15 @@ = form_for @gcp_cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: create_gcp_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field| = form_errors(@gcp_cluster) .form-group - = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light' + = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold' = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name') .form-group - = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-light' + = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold' = field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope') = field.fields_for :provider_gcp, @gcp_cluster.provider_gcp do |provider_gcp_field| .form-group - = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'), class: 'label-light' + = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'), class: 'label-bold' .js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } } = provider_gcp_field.hidden_field :gcp_project_id .dropdown @@ -34,7 +34,7 @@ %span.form-text.text-muted .form-group - = provider_gcp_field.label :zone, s_('ClusterIntegration|Zone'), class: 'label-light' + = provider_gcp_field.label :zone, s_('ClusterIntegration|Zone'), class: 'label-bold' .js-gcp-zone-dropdown-entry-point = provider_gcp_field.hidden_field :zone .dropdown @@ -46,11 +46,11 @@ = s_('ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: zones_link_url }, help_link_end: help_link_end } .form-group - = provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes'), class: 'label-light' + = provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes'), class: 'label-bold' = provider_gcp_field.text_field :num_nodes, class: 'form-control', placeholder: '3' .form-group - = provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type'), class: 'label-light' + = provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type'), class: 'label-bold' .js-gcp-machine-type-dropdown-entry-point = provider_gcp_field.hidden_field :machine_type .dropdown diff --git a/app/views/projects/clusters/user/_form.html.haml b/app/views/projects/clusters/user/_form.html.haml index 3006bb5073e..e8ef0008802 100644 --- a/app/views/projects/clusters/user/_form.html.haml +++ b/app/views/projects/clusters/user/_form.html.haml @@ -1,28 +1,28 @@ = form_for @user_cluster, url: create_user_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field| = form_errors(@user_cluster) .form-group - = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light' + = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold' = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name') - if has_multiple_clusters?(@project) .form-group - = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-light' + = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold' = field.text_field :environment_scope, class: 'form-control', placeholder: s_('ClusterIntegration|Environment scope') = field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field| .form-group - = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light' + = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold' = platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL') .form-group - = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light' + = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-bold' = platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)') .form-group - = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light' + = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-bold' = platform_kubernetes_field.text_field :token, class: 'form-control', placeholder: s_('ClusterIntegration|Service token'), autocomplete: 'off' .form-group - = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light' + = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold' = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace') .form-group diff --git a/app/views/projects/clusters/user/_show.html.haml b/app/views/projects/clusters/user/_show.html.haml index 4d117f435dc..20a07d6695e 100644 --- a/app/views/projects/clusters/user/_show.html.haml +++ b/app/views/projects/clusters/user/_show.html.haml @@ -1,20 +1,20 @@ = form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field| = form_errors(@cluster) .form-group - = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light' + = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold' = field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name') = field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field| .form-group - = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light' + = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold' = platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL') .form-group - = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light' + = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-bold' = platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)') .form-group - = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light' + = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-bold' .input-group = platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token', type: 'password', placeholder: s_('ClusterIntegration|Token'), autocomplete: 'off' %span.input-group-append.clipboard-addon @@ -23,7 +23,7 @@ = s_('ClusterIntegration|Show') .form-group - = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light' + = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold' = platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace') .form-group diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml index 14a7e84394a..afd70ef5774 100644 --- a/app/views/projects/commit/_change.html.haml +++ b/app/views/projects/commit/_change.html.haml @@ -23,7 +23,7 @@ %p= description = form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "js-#{type}-form js-requires-input" do .form-group.branch - = label_tag 'start_branch', branch_label, class: 'label-light' + = label_tag 'start_branch', branch_label, class: 'label-bold' = hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch' = dropdown_tag(@project.default_branch, options: { title: s_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: s_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: project_branches_path(@project), submit_form_on_click: false } }) diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml index 5ad8091a02b..f8ab0c1ec54 100644 --- a/app/views/projects/deploy_keys/_form.html.haml +++ b/app/views/projects/deploy_keys/_form.html.haml @@ -1,10 +1,10 @@ = form_for [@project.namespace.becomes(Namespace), @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input container" } do |f| = form_errors(@deploy_keys.new_key) .form-group.row - = f.label :title, class: "label-light" + = f.label :title, class: "label-bold" = f.text_field :title, class: 'form-control', required: true .form-group.row - = f.label :key, class: "label-light" + = f.label :key, class: "label-bold" = f.text_area :key, class: "form-control", rows: 5, required: true .form-group.row %p.light.append-bottom-0 diff --git a/app/views/projects/deploy_tokens/_form.html.haml b/app/views/projects/deploy_tokens/_form.html.haml index 329b9e7e562..578a9e2f74d 100644 --- a/app/views/projects/deploy_tokens/_form.html.haml +++ b/app/views/projects/deploy_tokens/_form.html.haml @@ -5,24 +5,24 @@ = form_errors(token) .form-group - = f.label :name, class: 'label-light' + = f.label :name, class: 'label-bold' = f.text_field :name, class: 'form-control', required: true .form-group - = f.label :expires_at, class: 'label-light' + = f.label :expires_at, class: 'label-bold' = f.text_field :expires_at, class: 'datepicker form-control', value: f.object.expires_at .form-group - = f.label :scopes, class: 'label-light' + = f.label :scopes, class: 'label-bold' %fieldset.form-group.form-check = f.check_box :read_repository, class: 'form-check-input' - = label_tag ("deploy_token_read_repository"), 'read_repository', class: 'label-light form-check-label' + = label_tag ("deploy_token_read_repository"), 'read_repository', class: 'label-bold form-check-label' .text-secondary= s_('DeployTokens|Allows read-only access to the repository') - if container_registry_enabled?(project) %fieldset.form-group.form-check = f.check_box :read_registry, class: 'form-check-input' - = label_tag ("deploy_token_read_registry"), 'read_registry', class: 'label-light form-check-label' + = label_tag ("deploy_token_read_registry"), 'read_registry', class: 'label-bold form-check-label' .text-secondary= s_('DeployTokens|Allows read-only access to the registry images') .prepend-top-default diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index c2d900cbcf7..0ff88b82ae6 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -18,17 +18,17 @@ %fieldset .row .form-group.col-md-9 - = f.label :name, class: 'label-light', for: 'project_name_edit' do + = f.label :name, class: 'label-bold', for: 'project_name_edit' do Project name = f.text_field :name, class: "form-control", id: "project_name_edit" .form-group.col-md-3 - = f.label :id, class: 'label-light' do + = f.label :id, class: 'label-bold' do Project ID = f.text_field :id, class: 'form-control', readonly: true .form-group - = f.label :description, class: 'label-light' do + = f.label :description, class: 'label-bold' do Project description %span.light (optional) = f.text_area :description, class: "form-control", rows: 3, maxlength: 250 @@ -37,13 +37,13 @@ - unless @project.empty_repo? .form-group - = f.label :default_branch, "Default Branch", class: 'label-light' + = f.label :default_branch, "Default Branch", class: 'label-bold' = f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'}) = render_if_exists 'shared/repository_size_limit_setting', form: f, type: :project .form-group - = f.label :tag_list, "Tags", class: 'label-light' + = f.label :tag_list, "Tags", class: 'label-bold' = f.text_field :tag_list, value: @project.tag_list.sort.join(', '), maxlength: 2000, class: "form-control" %p.form-text.text-muted Separate tags with commas. %fieldset.features @@ -144,12 +144,12 @@ = render 'projects/errors' = form_for([@project.namespace.becomes(Namespace), @project]) do |f| .form-group.project_name_holder - = f.label :name, class: 'label-light' do + = f.label :name, class: 'label-bold' do Project name .form-group = f.text_field :name, class: "form-control" .form-group - = f.label :path, class: 'label-light' do + = f.label :path, class: 'label-bold' do %span Path .form-group .input-group @@ -169,7 +169,7 @@ Transfer project = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'js-project-transfer-form' } ) do |f| .form-group - = label_tag :new_namespace_id, nil, class: 'label-light' do + = label_tag :new_namespace_id, nil, class: 'label-bold' do %span Select a new namespace .form-group = select_tag :new_namespace_id, namespaces_options(nil), include_blank: true, class: 'select2' diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml index 1605f3a3351..0586dbdf0e2 100644 --- a/app/views/projects/environments/_form.html.haml +++ b/app/views/projects/environments/_form.html.haml @@ -11,10 +11,10 @@ = form_errors(@environment) .form-group - = f.label :name, 'Name', class: 'label-light' + = f.label :name, 'Name', class: 'label-bold' = f.text_field :name, required: true, class: 'form-control' .form-group - = f.label :external_url, 'External URL', class: 'label-light' + = f.label :external_url, 'External URL', class: 'label-bold' = f.url_field :external_url, class: 'form-control' .form-actions diff --git a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml index 4d84ee2488b..8181267184a 100644 --- a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml +++ b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml @@ -9,7 +9,7 @@ .resolve-info = translation.html_safe .col-md-8 - %label.label-light{ "for" => "commit-message" } + %label.label-bold{ "for" => "commit-message" } #{ _('Commit message') } .commit-message-container .max-width-marker diff --git a/app/views/projects/mirrors/_push.html.haml b/app/views/projects/mirrors/_push.html.haml index 2b2871a81e5..08375e09816 100644 --- a/app/views/projects/mirrors/_push.html.haml +++ b/app/views/projects/mirrors/_push.html.haml @@ -32,11 +32,11 @@ .form-group = rm_form.check_box :enabled, class: "float-left" .prepend-left-20 - = rm_form.label :enabled, "Remote mirror repository", class: "label-light append-bottom-0" + = rm_form.label :enabled, "Remote mirror repository", class: "label-bold append-bottom-0" %p.light.append-bottom-0 Automatically update the remote mirror's branches, tags, and commits from this repository every time someone pushes to it. .form-group.has-feedback - = rm_form.label :url, "Git repository URL", class: "label-light" + = rm_form.label :url, "Git repository URL", class: "label-bold" = rm_form.text_field :url, class: "form-control", placeholder: 'https://username:password@gitlab.company.com/group/project.git' = render "projects/mirrors/instructions" @@ -44,7 +44,7 @@ .form-group = rm_form.check_box :only_protected_branches, class: 'float-left' .prepend-left-20 - = rm_form.label :only_protected_branches, class: 'label-light' + = rm_form.label :only_protected_branches, class: 'label-bold' = link_to icon('question-circle'), help_page_path('user/project/protected_branches') = f.submit 'Save changes', class: 'btn btn-create', name: 'update_remote_mirror' diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml index 1cdf981fcb4..9a981d53ab6 100644 --- a/app/views/projects/pipeline_schedules/_form.html.haml +++ b/app/views/projects/pipeline_schedules/_form.html.haml @@ -2,25 +2,25 @@ = form_errors(@schedule) .form-group.row .col-md-9 - = f.label :description, _('Description'), class: 'label-light' + = f.label :description, _('Description'), class: 'label-bold' = f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: s_('PipelineSchedules|Provide a short description for this pipeline') .form-group.row .col-md-9 - = f.label :cron, _('Interval Pattern'), class: 'label-light' + = f.label :cron, _('Interval Pattern'), class: 'label-bold' #interval-pattern-input{ data: { initial_interval: @schedule.cron } } .form-group.row .col-md-9 - = f.label :cron_timezone, _('Cron Timezone'), class: 'label-light' + = f.label :cron_timezone, _('Cron Timezone'), class: 'label-bold' = dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } ) = f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true .form-group.row .col-md-9 - = f.label :ref, _('Target Branch'), class: 'label-light' + = f.label :ref, _('Target Branch'), class: 'label-bold' = dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } ) = f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true .form-group.row.js-ci-variable-list-section .col-md-9 - %label.label-light + %label.label-bold #{ s_('PipelineSchedules|Variables') } %ul.ci-variable-list - @schedule.variables.each do |variable| @@ -34,7 +34,7 @@ = n_('Reveal value', 'Reveal values', @schedule.variables.size) .form-group.row .col-md-9 - = f.label :active, s_('PipelineSchedules|Activated'), class: 'label-light' + = f.label :active, s_('PipelineSchedules|Activated'), class: 'label-bold' %div = f.check_box :active, required: false, value: @schedule.active? = _('Active') diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml index 5064db8d5cd..6272687be1c 100644 --- a/app/views/projects/project_members/_new_project_member.html.haml +++ b/app/views/projects/project_members/_new_project_member.html.haml @@ -2,10 +2,10 @@ .col-sm-12 = form_for @project_member, as: :project_member, url: project_project_members_path(@project), html: { class: 'users-project-form' } do |f| .form-group - = label_tag :user_ids, "Select members to invite", class: "label-light" + = label_tag :user_ids, "Select members to invite", class: "label-bold" = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite") .form-group - = label_tag :access_level, "Choose a role permission", class: "label-light" + = label_tag :access_level, "Choose a role permission", class: "label-bold" .select-wrapper = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select select-control" = icon('chevron-down') @@ -14,7 +14,7 @@ about role permissions .form-group .clearable-input - = label_tag :expires_at, 'Access expiration date', class: 'label-light' + = label_tag :expires_at, 'Access expiration date', class: 'label-bold' = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date' %i.clear-icon.js-clear-input = f.submit "Add to project", class: "btn btn-create" diff --git a/app/views/projects/project_members/_new_shared_group.html.haml b/app/views/projects/project_members/_new_shared_group.html.haml index 684219735e2..d7227c32833 100644 --- a/app/views/projects/project_members/_new_shared_group.html.haml +++ b/app/views/projects/project_members/_new_shared_group.html.haml @@ -2,10 +2,10 @@ .col-sm-12 = form_tag project_group_links_path(@project), class: 'js-requires-input', method: :post do .form-group - = label_tag :link_group_id, "Select a group to share with", class: "label-light" + = label_tag :link_group_id, "Select a group to share with", class: "label-bold" = groups_select_tag(:link_group_id, data: { skip_groups: @skip_groups }, class: "input-clamp", required: true) .form-group - = label_tag :link_group_access, "Max access level", class: "label-light" + = label_tag :link_group_access, "Max access level", class: "label-bold" .select-wrapper = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control" = icon('chevron-down') @@ -13,7 +13,7 @@ = link_to "Read more", help_page_path("user/permissions"), class: "vlink" about role permissions .form-group - = label_tag :expires_at, 'Access expiration date', class: 'label-light' + = label_tag :expires_at, 'Access expiration date', class: 'label-bold' .clearable-input = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date-groups', placeholder: 'Expiration date', id: 'expires_at_groups' %i.clear-icon.js-clear-input diff --git a/app/views/projects/protected_tags/_protected_tag.html.haml b/app/views/projects/protected_tags/_protected_tag.html.haml index da1f97c8d6a..e0912bf39c0 100644 --- a/app/views/projects/protected_tags/_protected_tag.html.haml +++ b/app/views/projects/protected_tags/_protected_tag.html.haml @@ -1,2 +1,4 @@ = render layout: 'projects/protected_tags/shared/protected_tag', locals: { protected_tag: protected_tag } do - = render partial: 'projects/protected_tags/update_protected_tag', locals: { protected_tag: protected_tag } + %td + = render 'projects/protected_tags/protected_tag_create_access_levels', protected_tag: protected_tag, create_access_level: protected_tag.create_access_levels.for_role.first + = render_if_exists 'projects/protected_tags/protected_tag_extra_create_access_levels', protected_tag: protected_tag diff --git a/app/views/projects/protected_tags/_protected_tag_create_access_levels.haml b/app/views/projects/protected_tags/_protected_tag_create_access_levels.haml new file mode 100644 index 00000000000..1d4e9565156 --- /dev/null +++ b/app/views/projects/protected_tags/_protected_tag_create_access_levels.haml @@ -0,0 +1,8 @@ +- protected_tag = local_assigns.fetch(:protected_tag) +- create_access_level = local_assigns.fetch(:create_access_level) +- dropdown_label = create_access_level&.humanize || 'Select' + += hidden_field_tag "allowed_to_create_#{protected_tag.id}", create_access_level&.access_level += dropdown_tag(dropdown_label, + options: { toggle_class: 'js-allowed-to-create', dropdown_class: 'dropdown-menu-selectable capitalize-header js-allowed-to-create-container', + data: { field_name: "allowed_to_create_#{protected_tag.id}", access_level_id: create_access_level&.id }}) diff --git a/app/views/projects/protected_tags/_update_protected_tag.haml b/app/views/projects/protected_tags/_update_protected_tag.haml deleted file mode 100644 index cc80bd04dd0..00000000000 --- a/app/views/projects/protected_tags/_update_protected_tag.haml +++ /dev/null @@ -1,5 +0,0 @@ -%td - = hidden_field_tag "allowed_to_create_#{protected_tag.id}", protected_tag.create_access_levels.first.access_level - = dropdown_tag( (protected_tag.create_access_levels.first.humanize || 'Select') , - options: { toggle_class: 'js-allowed-to-create', dropdown_class: 'dropdown-menu-selectable capitalize-header js-allowed-to-create-container', - data: { field_name: "allowed_to_create_#{protected_tag.id}", access_level_id: protected_tag.create_access_levels.first.id }}) diff --git a/app/views/projects/services/prometheus/_metrics.html.haml b/app/views/projects/services/prometheus/_metrics.html.haml new file mode 100644 index 00000000000..98d64fafe86 --- /dev/null +++ b/app/views/projects/services/prometheus/_metrics.html.haml @@ -0,0 +1,30 @@ +- project = local_assigns.fetch(:project) + +.card.js-panel-monitored-metrics{ data: { active_metrics: active_common_project_prometheus_metrics_path(project, :json), metrics_help_path: help_page_path('user/project/integrations/prometheus_library/metrics') } } + .card-header + %h3.card-title + = s_('PrometheusService|Common metrics') + %span.badge.badge-pill.js-monitored-count 0 + .card-body + .loading-metrics.js-loading-metrics + %p.prepend-top-10.prepend-left-10 + = icon('spinner spin', class: 'metrics-load-spinner') + = s_('PrometheusService|Finding and configuring metrics...') + .empty-metrics.hidden.js-empty-metrics + %p.text-tertiary.prepend-top-10.prepend-left-10 + = s_('PrometheusService|Waiting for your first deployment to an environment to find common metrics') + %ul.list-unstyled.metrics-list.hidden.js-metrics-list + +.card.hidden.js-panel-missing-env-vars + .card-header + %h3.card-title + = icon('caret-right lg fw', class: 'panel-toggle js-panel-toggle', 'aria-label' => 'Toggle panel') + = s_('PrometheusService|Missing environment variable') + %span.badge.badge-pill.js-env-var-count 0 + .card-body.hidden + .flash-container + .flash-notice + .flash-text + = s_("PrometheusService|To set up automatic monitoring, add the environment variable %{variable} to exporter's queries." % { variable: "<code>$CI_ENVIRONMENT_SLUG</code>" }).html_safe + = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus', anchor: 'metrics-and-labels') + %ul.list-unstyled.metrics-list.js-missing-var-metrics-list diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml index bda597cc02b..9741b783db3 100644 --- a/app/views/projects/services/prometheus/_show.html.haml +++ b/app/views/projects/services/prometheus/_show.html.haml @@ -3,35 +3,8 @@ %h4.prepend-top-0 = s_('PrometheusService|Metrics') %p - = s_('PrometheusService|Metrics are automatically configured and monitored based on a library of metrics from popular exporters.') - = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus') + = s_('PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters.') + = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus_library/metrics'), target: '_blank', rel: "noopener noreferrer" .col-lg-9 - .card.js-panel-monitored-metrics{ data: { active_metrics: active_common_project_prometheus_metrics_path(@project, :json), metrics_help_path: help_page_path('user/project/integrations/prometheus_library/metrics') } } - .card-header - %h3.card-title - = s_('PrometheusService|Common metrics') - %span.badge.badge-pill.js-monitored-count 0 - .card-body - .loading-metrics.js-loading-metrics - %p.prepend-top-10.prepend-left-10 - = icon('spinner spin', class: 'metrics-load-spinner') - = s_('PrometheusService|Finding and configuring metrics...') - .empty-metrics.hidden.js-empty-metrics - %p.text-tertiary.prepend-top-10.prepend-left-10 - = s_('PrometheusService|Waiting for your first deployment to an environment to find common metrics') - %ul.list-unstyled.metrics-list.hidden.js-metrics-list - - .card.hidden.js-panel-missing-env-vars - .card-header - %h3.card-title - = icon('caret-right lg fw', class: 'panel-toggle js-panel-toggle', 'aria-label' => 'Toggle panel') - = s_('PrometheusService|Missing environment variable') - %span.badge.badge-pill.js-env-var-count 0 - .card-body.hidden - .flash-container - .flash-notice - .flash-text - = s_("PrometheusService|To set up automatic monitoring, add the environment variable %{variable} to exporter's queries." % { variable: "<code>$CI_ENVIRONMENT_SLUG</code>" }).html_safe - = link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus', anchor: 'metrics-and-labels') - %ul.list-unstyled.metrics-list.js-missing-var-metrics-list + = render_if_exists 'projects/services/prometheus/metrics', project: @project diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml index fb113aa7639..64751e5616a 100644 --- a/app/views/projects/settings/ci_cd/_form.html.haml +++ b/app/views/projects/settings/ci_cd/_form.html.haml @@ -4,7 +4,7 @@ = form_errors(@project) %fieldset.builds-feature .form-group.append-bottom-default.js-secret-runner-token - = f.label :runners_token, _("Runner token"), class: 'label-light' + = f.label :runners_token, _("Runner token"), class: 'label-bold' .form-control.js-secret-value-placeholder = '*' * 20 = f.text_field :runners_token, class: "form-control hide js-secret-value", placeholder: 'xEeFCaDAB89' @@ -36,7 +36,7 @@ %hr .form-group - = f.label :build_timeout_human_readable, _('Timeout'), class: 'label-light' + = f.label :build_timeout_human_readable, _('Timeout'), class: 'label-bold' = f.text_field :build_timeout_human_readable, class: 'form-control' %p.form-text.text-muted = _("Per job. If a job passes this threshold, it will be marked as failed") @@ -44,7 +44,7 @@ %hr .form-group - = f.label :ci_config_path, _('Custom CI config path'), class: 'label-light' + = f.label :ci_config_path, _('Custom CI config path'), class: 'label-bold' = f.text_field :ci_config_path, class: 'form-control', placeholder: '.gitlab-ci.yml' %p.form-text.text-muted = _("The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>") @@ -83,7 +83,7 @@ %hr .form-group - = f.label :build_coverage_regex, _("Test coverage parsing"), class: 'label-light' + = f.label :build_coverage_regex, _("Test coverage parsing"), class: 'label-bold' .input-group %span.input-group-prepend .input-group-text / diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml index 96ecac815c0..dab95ba09f2 100644 --- a/app/views/projects/tags/index.html.haml +++ b/app/views/projects/tags/index.html.haml @@ -26,6 +26,8 @@ = link_to new_project_tag_path(@project), class: 'btn btn-create new-tag-btn' do = s_('TagsPage|New tag') + = render_if_exists 'projects/commits/mirror_status' + .tags - if @tags.any? %ul.flex-list.content-list diff --git a/app/views/projects/triggers/_form.html.haml b/app/views/projects/triggers/_form.html.haml index 3539aea3580..1a5fc56f429 100644 --- a/app/views/projects/triggers/_form.html.haml +++ b/app/views/projects/triggers/_form.html.haml @@ -3,9 +3,9 @@ - if @trigger.token .form-group - %label.label-light Token + %label.label-bold Token %p.form-control-plaintext= @trigger.token .form-group - = f.label :key, "Description", class: "label-light" + = f.label :key, "Description", class: "label-bold" = f.text_field :description, class: "form-control", required: true, title: 'Trigger description is required.', placeholder: "Trigger description" = f.submit btn_text, class: "btn btn-save" diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml index 356e12cf9f8..7b593ca4f76 100644 --- a/app/views/shared/_import_form.html.haml +++ b/app/views/shared/_import_form.html.haml @@ -1,7 +1,7 @@ - ci_cd_only = local_assigns.fetch(:ci_cd_only, false) .form-group.import-url-data - = f.label :import_url, class: 'label-light' do + = f.label :import_url, class: 'label-bold' do %span = _('Git repository URL') diff --git a/app/views/shared/_personal_access_tokens_form.html.haml b/app/views/shared/_personal_access_tokens_form.html.haml index 28407b543b9..58d310fac16 100644 --- a/app/views/shared/_personal_access_tokens_form.html.haml +++ b/app/views/shared/_personal_access_tokens_form.html.haml @@ -11,18 +11,18 @@ .row .form-group.col-md-6 - = f.label :name, class: 'label-light' + = f.label :name, class: 'label-bold' = f.text_field :name, class: "form-control", required: true .row .form-group.col-md-6 - = f.label :expires_at, class: 'label-light' + = f.label :expires_at, class: 'label-bold' .input-icon-wrapper = f.text_field :expires_at, class: "datepicker form-control", placeholder: 'YYYY-MM-DD' = icon('calendar', { class: 'input-icon-right' }) .form-group - = f.label :scopes, class: 'label-light' + = f.label :scopes, class: 'label-bold' = render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: token, scopes: scopes .prepend-top-default diff --git a/app/views/shared/tokens/_scopes_form.html.haml b/app/views/shared/tokens/_scopes_form.html.haml index dcb3fca23f2..af9db5f59a8 100644 --- a/app/views/shared/tokens/_scopes_form.html.haml +++ b/app/views/shared/tokens/_scopes_form.html.haml @@ -5,5 +5,5 @@ - scopes.each do |scope| %fieldset.form-group.form-check = check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}", class: 'form-check-input' - = label_tag ("#{prefix}_scopes_#{scope}"), scope, class: 'label-light form-check-label' + = label_tag ("#{prefix}_scopes_#{scope}"), scope, class: 'label-bold form-check-label' .text-secondary= t scope, scope: [:doorkeeper, :scope_desc] diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml index 660769fa50d..07ebb8680d2 100644 --- a/app/views/shared/web_hooks/_form.html.haml +++ b/app/views/shared/web_hooks/_form.html.haml @@ -1,15 +1,15 @@ = form_errors(hook) .form-group - = form.label :url, 'URL', class: 'label-light' + = form.label :url, 'URL', class: 'label-bold' = form.text_field :url, class: 'form-control', placeholder: 'http://example.com/trigger-ci.json' .form-group - = form.label :token, 'Secret Token', class: 'label-light' + = form.label :token, 'Secret Token', class: 'label-bold' = form.text_field :token, class: 'form-control', placeholder: '' %p.form-text.text-muted Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header. .form-group - = form.label :url, 'Trigger', class: 'label-light' + = form.label :url, 'Trigger', class: 'label-bold' %ul.list-unstyled.prepend-left-20 %li = form.check_box :push_events, class: 'form-check-input' @@ -72,7 +72,7 @@ %p.light.ml-1 This URL will be triggered when a wiki page is created/updated .form-group - = form.label :enable_ssl_verification, 'SSL verification', class: 'label-light checkbox' + = form.label :enable_ssl_verification, 'SSL verification', class: 'label-bold checkbox' .form-check = form.check_box :enable_ssl_verification, class: 'form-check-input' = form.label :enable_ssl_verification, class: 'form-check-label ml-1' do diff --git a/changelogs/unreleased/23705-add-single-file-download-in-repo.yml b/changelogs/unreleased/23705-add-single-file-download-in-repo.yml new file mode 100644 index 00000000000..f156bfb1101 --- /dev/null +++ b/changelogs/unreleased/23705-add-single-file-download-in-repo.yml @@ -0,0 +1,5 @@ +--- +title: Add download button for single file (including raw files) in repository +merge_request: 20480 +author: Kia Mei Somabes +type: added diff --git a/changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml b/changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml new file mode 100644 index 00000000000..b18f7aec546 --- /dev/null +++ b/changelogs/unreleased/45443-unable-to-save-user-profile-update-with-safari.yml @@ -0,0 +1,5 @@ +--- +title: Resolve "Unable to save user profile update with Safari" +merge_request: 20676 +author: +type: fixed diff --git a/changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml b/changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml new file mode 100644 index 00000000000..ec4b843b863 --- /dev/null +++ b/changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml @@ -0,0 +1,5 @@ +--- +title: Fix navigation to First and Next discussion on MR Changes tab +merge_request: 20434 +author: +type: fixed diff --git a/changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml b/changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml new file mode 100644 index 00000000000..80b069c9251 --- /dev/null +++ b/changelogs/unreleased/fix-gb-fix-deserializing-ci-yaml-variables.yml @@ -0,0 +1,5 @@ +--- +title: Fix accessing imported pipeline builds +merge_request: 20713 +author: +type: fixed diff --git a/changelogs/unreleased/fj-49014-wiki-search-error.yml b/changelogs/unreleased/fj-49014-wiki-search-error.yml new file mode 100644 index 00000000000..a76805cb7f9 --- /dev/null +++ b/changelogs/unreleased/fj-49014-wiki-search-error.yml @@ -0,0 +1,5 @@ +--- +title: Fixed bug with invalid repository reference using the wiki search +merge_request: 20722 +author: +type: fixed diff --git a/changelogs/unreleased/jupyter-image.yml b/changelogs/unreleased/jupyter-image.yml new file mode 100644 index 00000000000..8aeefd603d8 --- /dev/null +++ b/changelogs/unreleased/jupyter-image.yml @@ -0,0 +1,5 @@ +--- +title: Rubix, scikit-learn, tensorflow & other useful libraries pre-installed with JupyterHub +merge_request: 20714 +author: Amit Rathi +type: changed diff --git a/config/boot.rb b/config/boot.rb index 84f390f3228..655c54ddb84 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -9,3 +9,8 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../#{gemfile}", __dir__) # Set up gems listed in the Gemfile. require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) +begin + require 'bootsnap/setup' +rescue LoadError + # bootsnap is optional dependency, so if we don't have it it's fine +end diff --git a/config/initializers/bootstrap_form.rb b/config/initializers/bootstrap_form.rb index 11171b38a85..bbc1d83a63f 100644 --- a/config/initializers/bootstrap_form.rb +++ b/config/initializers/bootstrap_form.rb @@ -1,6 +1,6 @@ module BootstrapFormBuilderCustomization def label_class - "label-light" + "label-bold" end end diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb index dc16d5c5169..1eb6a8fa5df 100644 --- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb +++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb @@ -1,9 +1,19 @@ -# See http://doc.gitlab.com/ce/development/migration_style_guide.html -# for more information on how to write migrations for GitLab. - class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers + class Repository + attr_reader :storage + + def initialize(storage, relative_path) + @storage = storage + @relative_path = relative_path + end + + def gitaly_repository + Gitaly::Repository.new(storage_name: @storage, relative_path: @relative_path) + end + end + class Project < ActiveRecord::Base def self.find_including_path(id) select("projects.*, CONCAT(namespaces.path, '/', projects.path) AS path_with_namespace") @@ -11,19 +21,12 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration .find_by(id: id) end - def repository_storage_path - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - Gitlab.config.repositories.storages[repository_storage].legacy_disk_path - end - end - - def repository_path - # TODO: review if the change from Legacy storage needs to reflect here as well. - File.join(repository_storage_path, read_attribute(:path_with_namespace) + '.git') + def commit(rev) + Gitlab::GitalyClient::CommitService.new(repository).find_commit(rev) end def repository - @repository ||= Rugged::Repository.new(repository_path) + @repository ||= Repository.new(repository_storage, read_attribute(:path_with_namespace) + '.git') end end @@ -42,22 +45,19 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration next unless project - begin - commit = project.repository.lookup(payload['args'][2]) - rescue Rugged::OdbError - next - end + commit = project.commit(payload['args'][2]) + next unless commit hash = { - id: commit.oid, - message: encode(commit.message), - parent_ids: commit.parent_ids, - authored_date: commit.author[:time], - author_name: encode(commit.author[:name]), - author_email: encode(commit.author[:email]), - committed_date: commit.committer[:time], - committer_email: encode(commit.committer[:email]), - committer_name: encode(commit.committer[:name]) + id: commit.id, + message: encode(commit.body), + parent_ids: commit.parent_ids.to_a, + authored_date: Time.at(commit.author.date.seconds).utc, + author_name: encode(commit.author.name), + author_email: encode(commit.author.email), + committed_date: Time.at(commit.committer.date.seconds).utc, + committer_email: encode(commit.committer.email), + committer_name: encode(commit.committer.name) } payload['args'][2] = hash diff --git a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb index 133435523e1..db10426b483 100644 --- a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb +++ b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb @@ -1,11 +1,7 @@ -# See http://doc.gitlab.com/ce/development/migration_style_guide.html -# for more information on how to write migrations for GitLab. - class RemoveDotGitFromUsernames < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers include Gitlab::ShellAdapter - # Set this constant to true if this migration requires downtime. DOWNTIME = false def up @@ -64,16 +60,14 @@ class RemoveDotGitFromUsernames < ActiveRecord::Migration # we rename suffix instead of removing it path = path.sub(/\.git\z/, '_git') - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - check_routes(path.dup, 0, path) - end + check_routes(path.dup, 0, path) end def check_routes(base, counter, path) route_exists = route_exists?(path) - Gitlab.config.repositories.storages.each do |shard, storage| - if route_exists || path_exists?(shard, storage.legacy_disk_path) + Gitlab.config.repositories.storages.each do |shard, _storage| + if route_exists || path_exists?(shard, path) counter += 1 path = "#{base}#{counter}" diff --git a/doc/development/README.md b/doc/development/README.md index 5d6fed5bc72..fed3903c771 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -40,6 +40,7 @@ description: 'Learn how to contribute to GitLab.' - [View sent emails or preview mailers](emails.md) - [Shell commands](shell_commands.md) in the GitLab codebase - [`Gemfile` guidelines](gemfile.md) +- [Pry debugging](pry_debugging.md) - [Sidekiq debugging](sidekiq_debugging.md) - [Gotchas](gotchas.md) to avoid - [Avoid modules with instance variables](module_with_instance_variables.md) if possible diff --git a/doc/development/pry_debugging.md b/doc/development/pry_debugging.md new file mode 100644 index 00000000000..de5e1323e6a --- /dev/null +++ b/doc/development/pry_debugging.md @@ -0,0 +1,130 @@ +# Pry debugging + +## Invoking pry debugging + +To invoke the debugger, place `binding.pry` somewhere in your +code. When the Ruby interpreter hits that code, execution will stop, +and you can type in commands to debug the state of the program + +## `byebug` vs `binding.pry` + +`byebug` has a very similar interface as `gdb`, but `byebug` does not +use the powerful Pry REPL. + +`binding.pry` uses Pry, but lacks some of the `byebug` +features. GitLab uses the [`pry-byebug`](https://github.com/deivid-rodriguez/pry-byebug) +gem. This gem brings some capabilities `byebug` to `binding.pry`, so +using that, will give you the most debugging powers. + +## `byebug` + +Check out [the docs](https://github.com/deivid-rodriguez/byebug) for the full list of commands. + +You can start the Pry REPL with the `pry` command. + +## `pry` + +There are **a lot** of features present in `pry`, too much to cover in +this document, so for the full documentation head over to the [Pry wiki](https://github.com/pry/pry/wiki). + +Below are a few features definitely worth checking out, also run +`help` in a pry session to see what else you can do. + +### State navigation + +With the [state navigation](https://github.com/pry/pry/wiki/State-navigation) +you can move around in the code to discover methods and such: + +```ruby +# Change context +[1] pry(main)> cd Pry +[2] pry(Pry):1> + +# Print methods +[2] pry(Pry):1> ls -m + +# Find a method +[3] pry(Pry):1> find-method to_yaml +``` + +### Source browsing + +You [look at the source code](https://github.com/pry/pry/wiki/Source-browsing) +from your `pry` session: + +```ruby +[1] pry(main)> $ Array#first +# The above is equivalent to +[2] pry(main)> cd Array +[3] pry(Array):1> show-source first +``` + +`$` is an alias for `show-source`. + +### Documentation browsing + +Similar to source browsing, is [Documentation browsing](https://github.com/pry/pry/wiki/Documentation-browsing). + +```ruby +[1] pry(main)> show-doc Array#first +``` + +`?` is an alias for `show-doc`. + +### Command history + +With <kdb>Ctrl+R</kbd> you can search your [command history](https://github.com/pry/pry/wiki/History). + +## Stepping + +To step through the code, you can use the following commands: + +- `break`: Manage breakpoints. +- `step`: Step execution into the next line or method. Takes an + optional numeric argument to step multiple times. +- `next`: Step over to the next line within the same frame. Also takes + an optional numeric argument to step multiple lines. +- `finish`: Execute until current stack frame returns. +- `continue`: Continue program execution and end the Pry session. + +## Callstack navigation + +You also can move around in the callstack with these commands: + +- `backtrace`: Shows the current stack. You can use the numbers on the + left side with the frame command to navigate the stack. +- `up`: Moves the stack frame up. Takes an optional numeric argument + to move multiple frames. +- `down`: Moves the stack frame down. Takes an optional numeric + argument to move multiple frames. +- `frame <n>`: Moves to a specific frame. Called without arguments + will show the current frame. + +## Short commands + +When you use `binding.pry` instead of `byebug`, the short commands +like `s`, `n`, `f`, and `c` do not work. To reinstall them, add this +to `~/.pryrc`: + +```ruby +if defined?(PryByebug) + Pry.commands.alias_command 's', 'step' + Pry.commands.alias_command 'n', 'next' + Pry.commands.alias_command 'f', 'finish' + Pry.commands.alias_command 'c', 'continue' +end +``` + +## Repeat last command + +You can repeat the last command by just hitting the <kbd>Enter</kbd> +key (e.g., with `step` or`next`), if you place the following snippet +in your `~/.pryrc`: + +```ruby +Pry::Commands.command /^$/, "repeat last command" do + _pry_.run_command Pry.history.to_a.last +end +``` + +`byebug` supports this out-of-the-box. diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md index 1a926a660f1..acbfa1850b4 100644 --- a/doc/development/testing_guide/best_practices.md +++ b/doc/development/testing_guide/best_practices.md @@ -101,7 +101,7 @@ CHROME_HEADLESS=0 bundle exec rspec some_spec.rb The test will go by quickly, but this will give you an idea of what's happening. -You can also add `byebug` or `binding.pry` to pause execution and step through +You can also add `byebug` or `binding.pry` to pause execution and [step through](../pry_debugging.md#stepping) the test. #### Screenshots diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index 44e9f6c5516..0b38cde811d 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -194,12 +194,12 @@ sudo apt-get install pgloader 1. Switch database from MySQL to PostgreSQL - ``` bash - cd /home/git/gitlab - sudo -u git mv config/database.yml config/database.yml.bak - sudo -u git cp config/database.yml.postgresql config/database.yml - sudo -u git -H chmod o-rwx config/database.yml - ``` + ``` bash + cd /home/git/gitlab + sudo -u git mv config/database.yml config/database.yml.bak + sudo -u git cp config/database.yml.postgresql config/database.yml + sudo -u git -H chmod o-rwx config/database.yml + ``` 1. Run the following commands to prepare the schema: diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md index cc1d65e4e6c..7c552103412 100644 --- a/doc/user/project/clusters/index.md +++ b/doc/user/project/clusters/index.md @@ -167,7 +167,7 @@ twice, which can lead to confusion during deployments. | [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) | 10.2+ | Ingress can provide load balancing, SSL termination, and name-based virtual hosting. It acts as a web proxy for your applications and is useful if you want to use [Auto DevOps] or deploy your own web apps. | | [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | | [GitLab Runner](https://docs.gitlab.com/runner/) | 10.6+ | GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/), the open-source continuous integration service included with GitLab that coordinates the jobs. When installing the GitLab Runner via the applications, it will run in **privileged mode** by default. Make sure you read the [security implications](#security-implications) before doing so. | -| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | +| [JupyterHub](http://jupyter.org/) | 11.0+ | [JupyterHub](https://jupyterhub.readthedocs.io/en/stable/) is a multi-user service for managing notebooks across a team. [Jupyter Notebooks](https://jupyter-notebook.readthedocs.io/en/latest/) provide a web-based interactive programming environment used for data analysis, visualization, and machine learning. We use [this](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) custom Jupyter image that installs additional useful packages on top of the base Jupyter. **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | ## Getting the external IP address diff --git a/doc/user/project/merge_requests/img/merge_request.png b/doc/user/project/merge_requests/img/merge_request.png Binary files differindex f9ca6348953..61b61122b11 100644 --- a/doc/user/project/merge_requests/img/merge_request.png +++ b/doc/user/project/merge_requests/img/merge_request.png diff --git a/doc/user/project/milestones/img/milestones_new_group_milestone.png b/doc/user/project/milestones/img/milestones_new_group_milestone.png Binary files differindex 8780394d72e..b6defab101d 100644 --- a/doc/user/project/milestones/img/milestones_new_group_milestone.png +++ b/doc/user/project/milestones/img/milestones_new_group_milestone.png diff --git a/doc/user/project/milestones/img/milestones_new_project_milestone.png b/doc/user/project/milestones/img/milestones_new_project_milestone.png Binary files differindex ba058428dfa..9aaff7dfef1 100644 --- a/doc/user/project/milestones/img/milestones_new_project_milestone.png +++ b/doc/user/project/milestones/img/milestones_new_project_milestone.png diff --git a/doc/user/project/milestones/img/milestones_promote_milestone.png b/doc/user/project/milestones/img/milestones_promote_milestone.png Binary files differindex 99bee1240d4..5e7f94c316f 100644 --- a/doc/user/project/milestones/img/milestones_promote_milestone.png +++ b/doc/user/project/milestones/img/milestones_promote_milestone.png diff --git a/lib/gitlab/ci/variables/collection/item.rb b/lib/gitlab/ci/variables/collection/item.rb index 222aa06b800..7da6d09d440 100644 --- a/lib/gitlab/ci/variables/collection/item.rb +++ b/lib/gitlab/ci/variables/collection/item.rb @@ -34,7 +34,7 @@ module Gitlab def self.fabricate(resource) case resource when Hash - self.new(resource) + self.new(resource.symbolize_keys) when ::HasVariable self.new(resource.to_runner_variable) when self diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index f23ae1519ef..5b955753a92 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -1,4 +1,3 @@ -# Gitlab::Git::Repository is a wrapper around native Rugged::Repository object require 'tempfile' require 'forwardable' require "rubygems/package" diff --git a/lib/gitlab/json_logger.rb b/lib/gitlab/json_logger.rb new file mode 100644 index 00000000000..28e258196ca --- /dev/null +++ b/lib/gitlab/json_logger.rb @@ -0,0 +1,22 @@ +module Gitlab + class JsonLogger < ::Gitlab::Logger + def self.file_name_noext + raise NotImplementedError + end + + def format_message(severity, timestamp, progname, message) + data = {} + data[:severity] = severity + data[:time] = timestamp.utc.iso8601(3) + + case message + when String + data[:message] = message + when Hash + data.merge!(message) + end + + data.to_json + "\n" + end + end +end diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 38bdc61d8ab..62f9e538c04 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -5,7 +5,7 @@ module Gitlab def initialize(current_user, project, query, repository_ref = nil, per_page: 20) @current_user = current_user @project = project - @repository_ref = repository_ref.presence || project.default_branch + @repository_ref = repository_ref.presence @query = query @per_page = per_page end @@ -95,7 +95,7 @@ module Gitlab def blobs return [] unless Ability.allowed?(@current_user, :download_code, @project) - @blobs ||= Gitlab::FileFinder.new(project, repository_ref).find(query) + @blobs ||= Gitlab::FileFinder.new(project, repository_project_ref).find(query) end def wiki_blobs @@ -103,11 +103,8 @@ module Gitlab @wiki_blobs ||= begin if project.wiki_enabled? && query.present? - project_wiki = ProjectWiki.new(project) - - unless project_wiki.empty? - ref = repository_ref || project.wiki.default_branch - Gitlab::WikiFileFinder.new(project, ref).find(query) + unless project.wiki.empty? + Gitlab::WikiFileFinder.new(project, repository_wiki_ref).find(query) else [] end @@ -150,5 +147,13 @@ module Gitlab def project_ids_relation project end + + def repository_project_ref + @repository_project_ref ||= repository_ref || project.default_branch + end + + def repository_wiki_ref + @repository_wiki_ref ||= repository_ref || project.wiki.default_branch + end end end diff --git a/lib/gitlab/serializer/ci/variables.rb b/lib/gitlab/serializer/ci/variables.rb index c059c454eac..292c8de6229 100644 --- a/lib/gitlab/serializer/ci/variables.rb +++ b/lib/gitlab/serializer/ci/variables.rb @@ -13,8 +13,9 @@ module Gitlab object = YAML.safe_load(string, [Symbol]) object.map do |variable| - variable[:key] = variable[:key].to_s - variable + variable.symbolize_keys.tap do |variable| + variable[:key] = variable[:key].to_s + end end end diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake index 52ae1330d7f..5e07b12ee1c 100644 --- a/lib/tasks/gitlab/cleanup.rake +++ b/lib/tasks/gitlab/cleanup.rake @@ -104,28 +104,5 @@ namespace :gitlab do puts "To block these users run this command with BLOCK=true".color(:yellow) end end - - # This is a rake task which removes faulty refs. These refs where only - # created in the 8.13.RC cycle, and fixed in the stable builds which were - # released. So likely this should only be run once on gitlab.com - # Faulty refs are moved so they are kept around, else some features break. - desc 'GitLab | Cleanup | Remove faulty deployment refs' - task move_faulty_deployment_refs: :gitlab_environment do - projects = Project.where(id: Deployment.select(:project_id).distinct) - - projects.find_each do |project| - rugged = project.repository.rugged - - max_iid = project.deployments.maximum(:iid) - - rugged.references.each('refs/environments/**/*') do |ref| - id = ref.name.split('/').last.to_i - next unless id > max_iid - - project.deployments.find(id).create_ref - project.repository.delete_refs(ref) - end - end - end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 19973f4f321..75b88a2cb2f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4114,6 +4114,9 @@ msgstr "" msgid "PrometheusService|Common metrics" msgstr "" +msgid "PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters." +msgstr "" + msgid "PrometheusService|Finding and configuring metrics..." msgstr "" @@ -4129,9 +4132,6 @@ msgstr "" msgid "PrometheusService|Metrics" msgstr "" -msgid "PrometheusService|Metrics are automatically configured and monitored based on a library of metrics from popular exporters." -msgstr "" - msgid "PrometheusService|Missing environment variable" msgstr "" diff --git a/package.json b/package.json index e1801d4d435..256ebc1fb6e 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "emoji-unicode-version": "^0.2.1", "exports-loader": "^0.7.0", "file-loader": "^1.1.11", + "formdata-polyfill": "^3.0.11", "fuzzaldrin-plus": "^0.5.0", "glob": "^7.1.2", "imports-loader": "^0.8.0", diff --git a/spec/features/merge_request/user_posts_diff_notes_spec.rb b/spec/features/merge_request/user_posts_diff_notes_spec.rb index 02d19db3828..77261f9375c 100644 --- a/spec/features/merge_request/user_posts_diff_notes_spec.rb +++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb @@ -185,11 +185,11 @@ describe 'Merge request > User posts diff notes', :js do end describe 'posting a note' do - xit 'adds as discussion' do + it 'adds as discussion' do expect(page).to have_css('.js-temp-notes-holder', count: 2) should_allow_commenting(find('[id="6eb14e00385d2fb284765eb1cd8d420d33d63fc9_22_22"]'), asset_form_reset: false) - expect(page).to have_css('.notes_holder .note', count: 1) + expect(page).to have_css('.notes_holder .note.note-discussion', count: 1) expect(page).to have_css('.js-temp-notes-holder', count: 1) expect(page).to have_button('Reply...') end diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb index bf4d5396df9..2d268ecab58 100644 --- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb +++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb @@ -342,8 +342,9 @@ describe 'Merge request > User resolves diff notes and discussions', :js do end end - it 'shows jump to next discussion button' do - expect(page.all('.discussion-reply-holder', count: 2)).to all(have_selector('.discussion-next-btn')) + it 'shows jump to next discussion button, apart from the last one' do + expect(page).to have_selector('.discussion-reply-holder', count: 2) + expect(page).to have_selector('.discussion-reply-holder .discussion-next-btn', count: 1) end it 'displays next discussion even if hidden' do diff --git a/spec/javascripts/ide/components/new_dropdown/modal_spec.js b/spec/javascripts/ide/components/new_dropdown/modal_spec.js index 6dcc5880677..70651535e87 100644 --- a/spec/javascripts/ide/components/new_dropdown/modal_spec.js +++ b/spec/javascripts/ide/components/new_dropdown/modal_spec.js @@ -38,7 +38,7 @@ describe('new file modal component', () => { }); it(`sets form label as ${type}`, () => { - expect(vm.$el.querySelector('.label-light').textContent.trim()).toBe('Name'); + expect(vm.$el.querySelector('.label-bold').textContent.trim()).toBe('Name'); }); describe('createEntryInStore', () => { diff --git a/spec/javascripts/notes/components/discussion_counter_spec.js b/spec/javascripts/notes/components/discussion_counter_spec.js index a3869cc6498..d09bc5037ef 100644 --- a/spec/javascripts/notes/components/discussion_counter_spec.js +++ b/spec/javascripts/notes/components/discussion_counter_spec.js @@ -46,7 +46,7 @@ describe('DiscussionCounter component', () => { discussions, }); setFixtures(` - <div data-discussion-id="${firstDiscussionId}"></div> + <div class="discussion" data-discussion-id="${firstDiscussionId}"></div> `); vm.jumpToFirstUnresolvedDiscussion(); diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js index f3f50aed232..2a01bd85520 100644 --- a/spec/javascripts/notes/components/noteable_discussion_spec.js +++ b/spec/javascripts/notes/components/noteable_discussion_spec.js @@ -14,6 +14,7 @@ describe('noteable_discussion component', () => { preloadFixtures(discussionWithTwoUnresolvedNotes); beforeEach(() => { + window.mrTabs = {}; store = createStore(); store.dispatch('setNoteableData', noteableDataMock); store.dispatch('setNotesData', notesDataMock); @@ -106,33 +107,29 @@ describe('noteable_discussion component', () => { describe('methods', () => { describe('jumpToNextDiscussion', () => { - it('expands next unresolved discussion', () => { - spyOn(vm, 'expandDiscussion').and.stub(); - const discussions = [ - discussionMock, - { - ...discussionMock, - id: discussionMock.id + 1, - notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: true }], - }, - { - ...discussionMock, - id: discussionMock.id + 2, - notes: [{ ...discussionMock.notes[0], resolvable: true, resolved: false }], - }, - ]; - const nextDiscussionId = discussionMock.id + 2; - store.replaceState({ - ...store.state, - discussions, - }); - setFixtures(` - <div data-discussion-id="${nextDiscussionId}"></div> - `); + it('expands next unresolved discussion', done => { + const discussion2 = getJSONFixture(discussionWithTwoUnresolvedNotes)[0]; + discussion2.resolved = false; + discussion2.id = 'next'; // prepare this for being identified as next one (to be jumped to) + vm.$store.dispatch('setInitialNotes', [discussionMock, discussion2]); + window.mrTabs.currentAction = 'show'; + + Vue.nextTick() + .then(() => { + spyOn(vm, 'expandDiscussion').and.stub(); + + const nextDiscussionId = discussion2.id; - vm.jumpToNextDiscussion(); + setFixtures(` + <div class="discussion" data-discussion-id="${nextDiscussionId}"></div> + `); - expect(vm.expandDiscussion).toHaveBeenCalledWith({ discussionId: nextDiscussionId }); + vm.jumpToNextDiscussion(); + + expect(vm.expandDiscussion).toHaveBeenCalledWith({ discussionId: nextDiscussionId }); + }) + .then(done) + .catch(done.fail); }); }); }); diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js index be2a8ba67fe..67f6a9629d9 100644 --- a/spec/javascripts/notes/mock_data.js +++ b/spec/javascripts/notes/mock_data.js @@ -1168,3 +1168,87 @@ export const collapsedSystemNotes = [ diff_discussion: false, }, ]; + +export const discussion1 = { + id: 'abc1', + resolvable: true, + resolved: false, + diff_file: { + file_path: 'about.md', + }, + position: { + formatter: { + new_line: 50, + old_line: null, + }, + }, + notes: [ + { + created_at: '2018-07-04T16:25:41.749Z', + }, + ], +}; + +export const resolvedDiscussion1 = { + id: 'abc1', + resolvable: true, + resolved: true, + diff_file: { + file_path: 'about.md', + }, + position: { + formatter: { + new_line: 50, + old_line: null, + }, + }, + notes: [ + { + created_at: '2018-07-04T16:25:41.749Z', + }, + ], +}; + +export const discussion2 = { + id: 'abc2', + resolvable: true, + resolved: false, + diff_file: { + file_path: 'README.md', + }, + position: { + formatter: { + new_line: null, + old_line: 20, + }, + }, + notes: [ + { + created_at: '2018-07-04T12:05:41.749Z', + }, + ], +}; + +export const discussion3 = { + id: 'abc3', + resolvable: true, + resolved: false, + diff_file: { + file_path: 'README.md', + }, + position: { + formatter: { + new_line: 21, + old_line: null, + }, + }, + notes: [ + { + created_at: '2018-07-05T17:25:41.749Z', + }, + ], +}; + +export const unresolvableDiscussion = { + resolvable: false, +}; diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js index 41599e00122..7f8ede51508 100644 --- a/spec/javascripts/notes/stores/getters_spec.js +++ b/spec/javascripts/notes/stores/getters_spec.js @@ -5,6 +5,11 @@ import { noteableDataMock, individualNote, collapseNotesMock, + discussion1, + discussion2, + discussion3, + resolvedDiscussion1, + unresolvableDiscussion, } from '../mock_data'; const discussionWithTwoUnresolvedNotes = 'merge_requests/resolved_diff_discussion.json'; @@ -109,4 +114,154 @@ describe('Getters Notes Store', () => { expect(getters.isNotesFetched(state)).toBeFalsy(); }); }); + + describe('allResolvableDiscussions', () => { + it('should return only resolvable discussions in same order', () => { + const localGetters = { + allDiscussions: [ + discussion3, + unresolvableDiscussion, + discussion1, + unresolvableDiscussion, + discussion2, + ], + }; + + expect(getters.allResolvableDiscussions(state, localGetters)).toEqual([ + discussion3, + discussion1, + discussion2, + ]); + }); + + it('should return empty array if there are no resolvable discussions', () => { + const localGetters = { + allDiscussions: [unresolvableDiscussion, unresolvableDiscussion], + }; + + expect(getters.allResolvableDiscussions(state, localGetters)).toEqual([]); + }); + }); + + describe('unresolvedDiscussionsIdsByDiff', () => { + it('should return all discussions IDs in diff order', () => { + const localGetters = { + allResolvableDiscussions: [discussion3, discussion1, discussion2], + }; + + expect(getters.unresolvedDiscussionsIdsByDiff(state, localGetters)).toEqual([ + 'abc1', + 'abc2', + 'abc3', + ]); + }); + + it('should return empty array if all discussions have been resolved', () => { + const localGetters = { + allResolvableDiscussions: [resolvedDiscussion1], + }; + + expect(getters.unresolvedDiscussionsIdsByDiff(state, localGetters)).toEqual([]); + }); + }); + + describe('unresolvedDiscussionsIdsByDate', () => { + it('should return all discussions in date ascending order', () => { + const localGetters = { + allResolvableDiscussions: [discussion3, discussion1, discussion2], + }; + + expect(getters.unresolvedDiscussionsIdsByDate(state, localGetters)).toEqual([ + 'abc2', + 'abc1', + 'abc3', + ]); + }); + + it('should return empty array if all discussions have been resolved', () => { + const localGetters = { + allResolvableDiscussions: [resolvedDiscussion1], + }; + + expect(getters.unresolvedDiscussionsIdsByDate(state, localGetters)).toEqual([]); + }); + }); + + describe('unresolvedDiscussionsIdsOrdered', () => { + const localGetters = { + unresolvedDiscussionsIdsByDate: ['123', '456'], + unresolvedDiscussionsIdsByDiff: ['abc', 'def'], + }; + + it('should return IDs ordered by diff when diffOrder param is true', () => { + expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(true)).toEqual([ + 'abc', + 'def', + ]); + }); + + it('should return IDs ordered by date when diffOrder param is not true', () => { + expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(false)).toEqual([ + '123', + '456', + ]); + expect(getters.unresolvedDiscussionsIdsOrdered(state, localGetters)(undefined)).toEqual([ + '123', + '456', + ]); + }); + }); + + describe('isLastUnresolvedDiscussion', () => { + const localGetters = { + unresolvedDiscussionsIdsOrdered: () => ['123', '456', '789'], + }; + + it('should return true if the discussion id provided is the last', () => { + expect(getters.isLastUnresolvedDiscussion(state, localGetters)('789')).toBe(true); + }); + + it('should return false if the discussion id provided is not the last', () => { + expect(getters.isLastUnresolvedDiscussion(state, localGetters)('123')).toBe(false); + expect(getters.isLastUnresolvedDiscussion(state, localGetters)('456')).toBe(false); + }); + }); + + describe('nextUnresolvedDiscussionId', () => { + const localGetters = { + unresolvedDiscussionsIdsOrdered: () => ['123', '456', '789'], + }; + + it('should return the ID of the discussion after the ID provided', () => { + expect(getters.nextUnresolvedDiscussionId(state, localGetters)('123')).toBe('456'); + expect(getters.nextUnresolvedDiscussionId(state, localGetters)('456')).toBe('789'); + expect(getters.nextUnresolvedDiscussionId(state, localGetters)('789')).toBe(undefined); + }); + }); + + describe('firstUnresolvedDiscussionId', () => { + const localGetters = { + unresolvedDiscussionsIdsByDate: ['123', '456'], + unresolvedDiscussionsIdsByDiff: ['abc', 'def'], + }; + + it('should return the first discussion id by diff when diffOrder param is true', () => { + expect(getters.firstUnresolvedDiscussionId(state, localGetters)(true)).toBe('abc'); + }); + + it('should return the first discussion id by date when diffOrder param is not true', () => { + expect(getters.firstUnresolvedDiscussionId(state, localGetters)(false)).toBe('123'); + expect(getters.firstUnresolvedDiscussionId(state, localGetters)(undefined)).toBe('123'); + }); + + it('should be falsy if all discussions are resolved', () => { + const localGettersFalsy = { + unresolvedDiscussionsIdsByDiff: [], + unresolvedDiscussionsIdsByDate: [], + }; + + expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(true)).toBeFalsy(); + expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(false)).toBeFalsy(); + }); + }); }); diff --git a/spec/lib/gitlab/ci/variables/collection/item_spec.rb b/spec/lib/gitlab/ci/variables/collection/item_spec.rb index adb3ff4321f..46874662edd 100644 --- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb +++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb @@ -75,6 +75,14 @@ describe Gitlab::Ci::Variables::Collection::Item do expect(resource).to eq variable end + it 'supports using a hash with stringified values' do + variable = { 'key' => 'VARIABLE', 'value' => 'my value' } + + resource = described_class.fabricate(variable) + + expect(resource).to eq(key: 'VARIABLE', value: 'my value') + end + it 'supports using an active record resource' do variable = create(:ci_variable, key: 'CI_VAR', value: '123') resource = described_class.fabricate(variable) diff --git a/spec/lib/gitlab/json_logger_spec.rb b/spec/lib/gitlab/json_logger_spec.rb new file mode 100644 index 00000000000..0a62785f880 --- /dev/null +++ b/spec/lib/gitlab/json_logger_spec.rb @@ -0,0 +1,29 @@ +# coding: utf-8 +require 'spec_helper' + +describe Gitlab::JsonLogger do + subject { described_class.new('/dev/null') } + + let(:now) { Time.now } + + describe '#format_message' do + it 'formats strings' do + output = subject.format_message('INFO', now, 'test', 'Hello world') + data = JSON.parse(output) + + expect(data['severity']).to eq('INFO') + expect(data['time']).to eq(now.utc.iso8601(3)) + expect(data['message']).to eq('Hello world') + end + + it 'formats hashes' do + output = subject.format_message('INFO', now, 'test', { hello: 1 }) + data = JSON.parse(output) + + expect(data['severity']).to eq('INFO') + expect(data['time']).to eq(now.utc.iso8601(3)) + expect(data['hello']).to eq(1) + expect(data['message']).to be_nil + end + end +end diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index 767a3092c73..4a0dc3686ec 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -64,6 +64,49 @@ describe Gitlab::ProjectSearchResults do end end + shared_examples 'blob search repository ref' do |entity_type| + let(:query) { 'files' } + let(:file_finder) { double } + let(:project_branch) { 'project_branch' } + + subject(:results) { described_class.new(user, project, query, repository_ref).objects(blob_type) } + + before do + allow(entity).to receive(:default_branch).and_return(project_branch) + allow(file_finder).to receive(:find).and_return([]) + end + + context 'when repository_ref exists' do + let(:repository_ref) { 'ref_branch' } + + it 'uses it' do + expect(Gitlab::FileFinder).to receive(:new).with(project, repository_ref).and_return(file_finder) + + results + end + end + + context 'when repository_ref is not present' do + let(:repository_ref) { nil } + + it "uses #{entity_type} repository default reference" do + expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder) + + results + end + end + + context 'when repository_ref is blank' do + let(:repository_ref) { '' } + + it "uses #{entity_type} repository default reference" do + expect(Gitlab::FileFinder).to receive(:new).with(project, project_branch).and_return(file_finder) + + results + end + end + end + describe 'blob search' do let(:project) { create(:project, :public, :repository) } @@ -75,6 +118,11 @@ describe Gitlab::ProjectSearchResults do let(:expected_file_by_content) { 'CHANGELOG' } end + it_behaves_like 'blob search repository ref', 'project' do + let(:blob_type) { 'blobs' } + let(:entity) { project } + end + describe 'parsing results' do let(:results) { project.repository.search_files_by_content('feature', 'master') } let(:search_result) { results.first } @@ -212,6 +260,11 @@ describe Gitlab::ProjectSearchResults do let(:expected_file_by_name) { 'Files/Title.md' } let(:expected_file_by_content) { 'CHANGELOG.md' } end + + it_behaves_like 'blob search repository ref', 'wiki' do + let(:blob_type) { 'wiki_blobs' } + let(:entity) { project.wiki } + end end it 'does not list issues on private projects' do diff --git a/spec/lib/gitlab/serializer/ci/variables_spec.rb b/spec/lib/gitlab/serializer/ci/variables_spec.rb index c4b7fda5dbb..1d1fd5b0763 100644 --- a/spec/lib/gitlab/serializer/ci/variables_spec.rb +++ b/spec/lib/gitlab/serializer/ci/variables_spec.rb @@ -1,4 +1,4 @@ -require 'spec_helper' +require 'fast_spec_helper' describe Gitlab::Serializer::Ci::Variables do subject do @@ -6,11 +6,11 @@ describe Gitlab::Serializer::Ci::Variables do end let(:object) do - [{ key: :key, value: 'value', public: true }, + [{ 'key' => :key, 'value' => 'value', 'public' => true }, { key: 'wee', value: 1, public: false }] end - it 'converts keys into strings' do + it 'converts keys into strings and symbolizes hash' do is_expected.to eq([ { key: 'key', value: 'value', public: true }, { key: 'wee', value: 1, public: false } diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb index a30e6c23ac9..6219a67c900 100644 --- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb +++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb @@ -4,14 +4,11 @@ require 'spec_helper' require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_worker_jobs.rb') describe MigrateProcessCommitWorkerJobs do - let(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs - let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs - let(:rugged) do - Gitlab::GitalyClient::StorageSettings.allow_disk_access do - project.repository.rugged - end + set(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + set(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs + let(:commit) do + Gitlab::Git::Commit.last(project.repository.raw) end - let(:commit) { rugged.rev_parse(project.commit.id) } describe 'Project' do describe 'find_including_path' do @@ -29,32 +26,13 @@ describe MigrateProcessCommitWorkerJobs do end end - describe '#repository_storage_path' do - it 'returns the storage path for the repository' do - migration_project = described_class::Project - .find_including_path(project.id) - - expect(File.directory?(migration_project.repository_storage_path)) - .to eq(true) - end - end - - describe '#repository_path' do - it 'returns the path to the repository' do - migration_project = described_class::Project - .find_including_path(project.id) - - expect(File.directory?(migration_project.repository_path)).to eq(true) - end - end - describe '#repository' do - it 'returns a Rugged::Repository' do + it 'returns a mock implemention of ::Repository' do migration_project = described_class::Project .find_including_path(project.id) - expect(migration_project.repository) - .to be_an_instance_of(Rugged::Repository) + expect(migration_project.repository).to respond_to(:storage) + expect(migration_project.repository).to respond_to(:gitaly_repository) end end end @@ -72,7 +50,7 @@ describe MigrateProcessCommitWorkerJobs do before do Sidekiq.redis do |redis| - job = JSON.dump(args: [project.id, user.id, commit.oid]) + job = JSON.dump(args: [project.id, user.id, commit.id]) redis.lpush('queue:process_commit', job) end end @@ -88,9 +66,10 @@ describe MigrateProcessCommitWorkerJobs do end it 'skips jobs using commits that no longer exist' do - allow_any_instance_of(Rugged::Repository).to receive(:lookup) - .with(commit.oid) - .and_raise(Rugged::OdbError) + allow_any_instance_of(Gitlab::GitalyClient::CommitService) + .to receive(:find_commit) + .with(commit.id) + .and_return(nil) migration.up @@ -104,11 +83,7 @@ describe MigrateProcessCommitWorkerJobs do end it 'encodes data to UTF-8' do - allow_any_instance_of(Rugged::Repository).to receive(:lookup) - .with(commit.oid) - .and_return(commit) - - allow(commit).to receive(:message) + allow(commit).to receive(:body) .and_return('김치'.force_encoding('BINARY')) migration.up @@ -140,7 +115,7 @@ describe MigrateProcessCommitWorkerJobs do end it 'includes the commit ID' do - expect(commit_hash['id']).to eq(commit.oid) + expect(commit_hash['id']).to eq(commit.id) end it 'includes the commit message' do @@ -152,27 +127,27 @@ describe MigrateProcessCommitWorkerJobs do end it 'includes the author date' do - expect(commit_hash['authored_date']).to eq(commit.author[:time].to_s) + expect(commit_hash['authored_date']).to eq(commit.authored_date.to_s) end it 'includes the author name' do - expect(commit_hash['author_name']).to eq(commit.author[:name]) + expect(commit_hash['author_name']).to eq(commit.author_name) end it 'includes the author Email' do - expect(commit_hash['author_email']).to eq(commit.author[:email]) + expect(commit_hash['author_email']).to eq(commit.author_email) end it 'includes the commit date' do - expect(commit_hash['committed_date']).to eq(commit.committer[:time].to_s) + expect(commit_hash['committed_date']).to eq(commit.committed_date.to_s) end it 'includes the committer name' do - expect(commit_hash['committer_name']).to eq(commit.committer[:name]) + expect(commit_hash['committer_name']).to eq(commit.committer_name) end it 'includes the committer Email' do - expect(commit_hash['committer_email']).to eq(commit.committer[:email]) + expect(commit_hash['committer_email']).to eq(commit.committer_email) end end end @@ -186,7 +161,7 @@ describe MigrateProcessCommitWorkerJobs do before do Sidekiq.redis do |redis| - job = JSON.dump(args: [project.id, user.id, commit.oid]) + job = JSON.dump(args: [project.id, user.id, commit.id]) redis.lpush('queue:process_commit', job) migration.up @@ -215,7 +190,7 @@ describe MigrateProcessCommitWorkerJobs do end it 'includes the commit SHA' do - expect(job['args'][2]).to eq(commit.oid) + expect(job['args'][2]).to eq(commit.id) end end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index ee923374480..67199eb6d26 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -2269,6 +2269,34 @@ describe Ci::Build do end end + describe '#yaml_variables' do + before do + build.update_attribute(:yaml_variables, variables) + end + + context 'when serialized valu is a symbolized hash' do + let(:variables) do + [{ key: :VARIABLE, value: 'my value 1' }] + end + + it 'keeps symbolizes keys and stringifies variables names' do + expect(build.yaml_variables) + .to eq [{ key: 'VARIABLE', value: 'my value 1' }] + end + end + + context 'when serialized value is a hash with string keys' do + let(:variables) do + [{ 'key' => :VARIABLE, 'value' => 'my value 2' }] + end + + it 'symblizes variables hash' do + expect(build.yaml_variables) + .to eq [{ key: 'VARIABLE', value: 'my value 2' }] + end + end + end + describe 'state transition: any => [:pending]' do let(:build) { create(:ci_build, :created) } diff --git a/vendor/jupyter/values.yaml b/vendor/jupyter/values.yaml index 90817de0f1b..4ea5b44c59c 100644 --- a/vendor/jupyter/values.yaml +++ b/vendor/jupyter/values.yaml @@ -4,6 +4,7 @@ rbac: hub: extraEnv: JUPYTER_ENABLE_LAB: 1 + SINGLEUSER_IMAGE: 'registry.gitlab.com/gitlab-org/jupyterhub-user-image:latest' extraConfig: | c.KubeSpawner.cmd = ['jupyter-labhub'] diff --git a/yarn.lock b/yarn.lock index 67f9aa98c74..85fdb150d34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3211,6 +3211,10 @@ form-data@~2.3.0, form-data@~2.3.1: combined-stream "1.0.6" mime-types "^2.1.12" +formdata-polyfill@^3.0.11: + version "3.0.11" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-3.0.11.tgz#c82b4b4bea3356c0a6752219e54ce1edb2a7fb5b" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" |