Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-05-14 15:08:14 +00:00
parent 674e7e2c3d
commit 87f286558d
96 changed files with 1169 additions and 294 deletions

View File

@ -46,8 +46,6 @@
- name: redis:alpine
variables:
POSTGRES_HOST_AUTH_METHOD: trust
cache:
key: "debian-stretch-ruby-2.6.6-pg11-node-12.x"
.use-pg11-ee:
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34"
@ -58,8 +56,6 @@
- name: elasticsearch:6.4.2
variables:
POSTGRES_HOST_AUTH_METHOD: trust
cache:
key: "debian-stretch-ruby-2.6.6-pg11-node-12.x"
# Pin kaniko to v0.16.0 due to https://github.com/GoogleContainerTools/kaniko/issues/1162
.use-kaniko:

View File

@ -1,11 +1,20 @@
.rails:needs:setup-and-assets:
needs: ["setup-test-env", "compile-assets pull-cache"]
.rails-cache:
cache:
key: "ruby-go-cache-v1"
paths:
- vendor/ruby
- vendor/gitaly-ruby
- .go/pkg/mod
policy: pull
.rails-job-base:
extends:
- .default-retry
- .default-cache
- .default-before_script
- .rails-cache
#######################################################
# EE/FOSS: default refs (MRs, master, schedules) jobs #
@ -13,15 +22,25 @@
extends:
- .rails-job-base
stage: prepare
variables:
GITLAB_TEST_EAGER_LOAD: "0"
script:
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
- scripts/gitaly-test-build # Do not use 'bundle exec' here
- run_timed_command "bundle exec ruby -I. -e 'require \"config/environment\"; TestEnv.init'"
- run_timed_command "scripts/gitaly-test-build" # Do not use 'bundle exec' here
artifacts:
expire_in: 7d
paths:
- tmp/tests
- config/secrets.yml
- vendor/gitaly-ruby
- tmp/tests/gitaly
- tmp/tests/gitlab-elasticsearch-indexer
- tmp/tests/gitlab-shell
- tmp/tests/gitlab-test-fork
- tmp/tests/gitlab-test-fork_bare
- tmp/tests/gitlab-test
- tmp/tests/gitlab-workhorse
- tmp/tests/repositories
- tmp/tests/second_storage
when: always
cache:
policy: pull-push
@ -52,8 +71,8 @@ static-analysis:
downtime_check:
extends:
- .rails-job-base
- .rails:needs:setup-and-assets
- .rails:rules:downtime_check
needs: ["setup-test-env"]
stage: test
variables:
SETUP_DB: "false"
@ -176,7 +195,7 @@ gitlab:setup:
# db/fixtures/development/04_project.rb thanks to SIZE=1 below
- git clone https://gitlab.com/gitlab-org/gitlab-test.git
/home/git/repositories/gitlab-org/gitlab-test.git
- scripts/gitaly-test-spawn
- run_timed_command "scripts/gitaly-test-spawn"
- force=yes SIZE=1 FIXTURE_PATH="db/fixtures/development" bundle exec rake gitlab:setup
artifacts:
when: on_failure

View File

@ -46,6 +46,7 @@
"Elasticsearch",
"Facebook",
"GDK",
"Geo",
"Git LFS",
"git-annex",
"Git",

View File

@ -1 +1 @@
12.2.0
13.2.0

View File

@ -1,12 +1,14 @@
<script>
import ActiveToggle from './active_toggle.vue';
import JiraTriggerFields from './jira_trigger_fields.vue';
import TriggerFields from './trigger_fields.vue';
export default {
name: 'IntegrationForm',
components: {
ActiveToggle,
JiraTriggerFields,
TriggerFields,
},
props: {
activeToggleProps: {
@ -21,6 +23,11 @@ export default {
type: Object,
required: true,
},
triggerEvents: {
type: Array,
required: false,
default: () => [],
},
type: {
type: String,
required: true,
@ -38,5 +45,6 @@ export default {
<div>
<active-toggle v-if="showActive" v-bind="activeToggleProps" />
<jira-trigger-fields v-if="isJira" v-bind="triggerFieldsProps" />
<trigger-fields v-else-if="triggerEvents.length" :events="triggerEvents" :type="type" />
</div>
</template>

View File

@ -0,0 +1,73 @@
<script>
import { startCase } from 'lodash';
import { __ } from '~/locale';
import { GlFormGroup, GlFormCheckbox, GlFormInput } from '@gitlab/ui';
const typeWithPlaceholder = {
SLACK: 'slack',
MATTERMOST: 'mattermost',
};
const placeholderForType = {
[typeWithPlaceholder.SLACK]: __('Slack channels (e.g. general, development)'),
[typeWithPlaceholder.MATTERMOST]: __('Channel handle (e.g. town-square)'),
};
export default {
name: 'TriggerFields',
components: {
GlFormGroup,
GlFormCheckbox,
GlFormInput,
},
props: {
events: {
type: Array,
required: false,
default: null,
},
type: {
type: String,
required: true,
},
},
computed: {
placeholder() {
return placeholderForType[this.type];
},
},
methods: {
checkboxName(name) {
return `service[${name}]`;
},
fieldName(name) {
return `service[${name}]`;
},
startCase,
},
};
</script>
<template>
<gl-form-group
class="gl-pt-3"
:label="__('Trigger')"
label-for="trigger-fields"
data-testid="trigger-fields-group"
>
<div id="trigger-fields" class="gl-pt-3">
<gl-form-group v-for="event in events" :key="event.title" :description="event.description">
<input :name="checkboxName(event.name)" type="hidden" value="false" />
<gl-form-checkbox v-model="event.value" :name="checkboxName(event.name)">
{{ startCase(event.title) }}
</gl-form-checkbox>
<gl-form-input
v-if="event.field"
v-model="event.field.value"
:name="fieldName(event.field.name)"
:placeholder="placeholder"
/>
</gl-form-group>
</div>
</gl-form-group>
</template>

View File

@ -15,7 +15,7 @@ export default el => {
return result;
}
const { type, commentDetail, ...booleanAttributes } = el.dataset;
const { type, commentDetail, triggerEvents, ...booleanAttributes } = el.dataset;
const {
showActive,
activated,
@ -40,6 +40,7 @@ export default el => {
initialEnableComments: enableComments,
initialCommentDetail: commentDetail,
},
triggerEvents: JSON.parse(triggerEvents),
},
});
},

View File

@ -226,6 +226,7 @@ export default {
'environmentsLoading',
'expandedPanel',
'promVariables',
'isUpdatingStarredValue',
]),
...mapGetters('monitoringDashboard', ['getMetricStates', 'filteredEnvironments']),
firstDashboard() {
@ -312,6 +313,7 @@ export default {
'filterEnvironments',
'setExpandedPanel',
'clearExpandedPanel',
'toggleStarredValue',
]),
updatePanels(key, panels) {
this.setPanelGroupMetrics({
@ -422,6 +424,8 @@ export default {
},
i18n: {
goBackLabel: s__('Metrics|Go back (Esc)'),
starDashboard: s__('Metrics|Star dashboard'),
unstarDashboard: s__('Metrics|Unstar dashboard'),
},
};
</script>
@ -518,6 +522,32 @@ export default {
<div class="flex-grow-1"></div>
<div class="d-sm-flex">
<div v-if="selectedDashboard" class="mb-2 mr-2 d-flex">
<!--
wrapper for tooltip as button can be `disabled`
https://bootstrap-vue.org/docs/components/tooltip#disabled-elements
-->
<div
v-gl-tooltip
class="flex-grow-1"
:title="
selectedDashboard.starred
? $options.i18n.unstarDashboard
: $options.i18n.starDashboard
"
>
<gl-deprecated-button
ref="toggleStarBtn"
class="w-100"
:disabled="isUpdatingStarredValue"
variant="default"
@click="toggleStarredValue()"
>
<gl-icon :name="selectedDashboard.starred ? 'star' : 'star-o'" />
</gl-deprecated-button>
</div>
</div>
<div v-if="showRearrangePanelsBtn" class="mb-2 mr-2 d-flex">
<gl-deprecated-button
:pressed="isRearrangingPanels"

View File

@ -7,17 +7,28 @@ import TreePage from './pages/tree.vue';
Vue.use(VueRouter);
export default function createRouter(base, baseRef) {
const treePathRoute = {
component: TreePage,
props: route => ({
path: route.params.path?.replace(/^\//, '') || '/',
}),
};
return new VueRouter({
mode: 'history',
base: joinPaths(gon.relative_url_root || '', base),
routes: [
{
path: `(/-)?/tree/${baseRef}/:path*`,
name: 'treePathDecoded',
// Sometimes the ref needs decoding depending on how the backend sends it to us
path: `(/-)?/tree/${decodeURI(baseRef)}/:path*`,
...treePathRoute,
},
{
name: 'treePath',
component: TreePage,
props: route => ({
path: route.params.path?.replace(/^\//, '') || '/',
}),
// Support without decoding as well just in case the ref doesn't need to be decoded
path: `(/-)?/tree/${baseRef}/:path*`,
...treePathRoute,
},
{
path: '/',

View File

@ -1,20 +1,14 @@
<script>
import { GlFormTextarea } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import RichContentEditor from '~/vue_shared/components/rich_content_editor/rich_content_editor.vue';
import PublishToolbar from '../components/publish_toolbar.vue';
import EditHeader from '../components/edit_header.vue';
export default {
components: {
GlFormTextarea,
RichContentEditor,
PublishToolbar,
EditHeader,
},
mixins: [glFeatureFlagsMixin()],
props: {
title: {
type: String,
@ -55,8 +49,7 @@ export default {
<template>
<div class="d-flex flex-grow-1 flex-column">
<edit-header class="py-2" :title="title" />
<rich-content-editor v-if="glFeatures.richContentEditor" v-model="editableContent" />
<gl-form-textarea v-else v-model="editableContent" class="h-100 shadow-none" />
<rich-content-editor v-model="editableContent" class="mb-9" />
<publish-toolbar
class="gl-fixed gl-left-0 gl-bottom-0 gl-w-full"
:return-url="returnUrl"

View File

@ -19,6 +19,7 @@ class GraphqlController < ApplicationController
before_action :authorize_access_api!
before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
before_action :set_user_last_activity
# Since we deactivate authentication from the main ApplicationController and
# defer it to :authorize_access_api!, we need to override the bypass session
@ -47,6 +48,12 @@ class GraphqlController < ApplicationController
private
def set_user_last_activity
return unless current_user
Users::ActivityService.new(current_user).execute
end
def execute_multiplex
GitlabSchema.multiplex(multiplex_queries, context: context)
end

View File

@ -10,10 +10,6 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
before_action :assign_ref_and_path, only: [:show]
before_action :authorize_edit_tree!, only: [:show]
before_action do
push_frontend_feature_flag(:rich_content_editor)
end
def show
@config = Gitlab::StaticSiteEditor::Config.new(@repository, @ref, @path, params[:return_url])
end

View File

@ -17,6 +17,6 @@ class MilestoneNote < SyntheticNote
def note_text(html: false)
format = milestone&.group_milestone? ? :name : :iid
milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project, format: format)}"
event.remove? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project, format: format)}"
end
end

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
class Namespace::RootStorageSize
ALERT_USAGE_THRESHOLD = 0.5
def initialize(root_namespace)
@root_namespace = root_namespace
end
@ -27,12 +25,6 @@ class Namespace::RootStorageSize
@limit ||= Gitlab::CurrentSettings.namespace_storage_size_limit.megabytes
end
def show_alert?
return false if limit == 0
usage_ratio >= ALERT_USAGE_THRESHOLD
end
private
attr_reader :root_namespace

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
class ServiceEventEntity < Grape::Entity
include RequestAwareEntity
expose :title do |event|
event
end
expose :event_field_name, as: :name
expose :value do |event|
service[event_field_name]
end
expose :description do |event|
service.class.event_description(event)
end
expose :field, if: -> (_, _) { event_field } do
expose :name do |event|
event_field[:name]
end
expose :value do |event|
service.public_send(event_field[:name]) # rubocop:disable GitlabSecurity/PublicSend
end
end
private
alias_method :event, :object
def event_field_name
ServicesHelper.service_event_field_name(event)
end
def event_field
@event_field ||= service.event_field(event)
end
def service
request.service
end
end

View File

@ -0,0 +1,5 @@
# frozen_string_literal: true
class ServiceEventSerializer < BaseSerializer
entity ServiceEventEntity
end

View File

@ -4,7 +4,7 @@ module Issuable
class CommonSystemNotesService < ::BaseService
attr_reader :issuable
def execute(issuable, old_labels: [], is_update: true)
def execute(issuable, old_labels: [], old_milestone: nil, is_update: true)
@issuable = issuable
# We disable touch so that created system notes do not update
@ -22,17 +22,13 @@ module Issuable
end
create_due_date_note if issuable.previous_changes.include?('due_date')
create_milestone_note if has_milestone_changes?
create_milestone_note(old_milestone) if issuable.previous_changes.include?('milestone_id')
create_labels_note(old_labels) if old_labels && issuable.labels != old_labels
end
end
private
def has_milestone_changes?
issuable.previous_changes.include?('milestone_id')
end
def handle_time_tracking_note
if issuable.previous_changes.include?('time_estimate')
create_time_estimate_note
@ -98,15 +94,19 @@ module Issuable
SystemNoteService.change_time_spent(issuable, issuable.project, issuable.time_spent_user)
end
def create_milestone_note
def create_milestone_note(old_milestone)
if milestone_changes_tracking_enabled?
# Creates a synthetic note
ResourceEvents::ChangeMilestoneService.new(issuable, current_user).execute
create_milestone_change_event(old_milestone)
else
SystemNoteService.change_milestone(issuable, issuable.project, current_user, issuable.milestone)
end
end
def create_milestone_change_event(old_milestone)
ResourceEvents::ChangeMilestoneService.new(issuable, current_user, old_milestone: old_milestone)
.execute
end
def milestone_changes_tracking_enabled?
::Feature.enabled?(:track_resource_milestone_change_events, issuable.project)
end

View File

@ -241,7 +241,8 @@ class IssuableBaseService < BaseService
end
if issuable_saved
Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: old_associations[:labels])
Issuable::CommonSystemNotesService.new(project, current_user).execute(
issuable, old_labels: old_associations[:labels], old_milestone: old_associations[:milestone])
handle_changes(issuable, old_associations: old_associations)
@ -364,7 +365,8 @@ class IssuableBaseService < BaseService
{
labels: issuable.labels.to_a,
mentioned_users: issuable.mentioned_users(current_user).to_a,
assignees: issuable.assignees.to_a
assignees: issuable.assignees.to_a,
milestone: issuable.try(:milestone)
}
associations[:total_time_spent] = issuable.total_time_spent if issuable.respond_to?(:total_time_spent)
associations[:description] = issuable.description

View File

@ -3,45 +3,70 @@
module Namespaces
class CheckStorageSizeService
include ActiveSupport::NumberHelper
include Gitlab::Allowable
include Gitlab::Utils::StrongMemoize
def initialize(namespace)
def initialize(namespace, user)
@root_namespace = namespace.root_ancestor
@root_storage_size = Namespace::RootStorageSize.new(root_namespace)
@user = user
end
def execute
return ServiceResponse.success unless Feature.enabled?(:namespace_storage_limit, root_namespace)
return ServiceResponse.success unless root_storage_size.show_alert?
return ServiceResponse.success if alert_level == :none
if root_storage_size.above_size_limit?
ServiceResponse.error(message: above_size_limit_message, payload: payload)
else
ServiceResponse.success(message: info_message, payload: payload)
ServiceResponse.success(payload: payload)
end
end
private
attr_reader :root_namespace, :root_storage_size
attr_reader :root_namespace, :root_storage_size, :user
USAGE_THRESHOLDS = {
none: 0.0,
info: 0.5,
warning: 0.75,
alert: 0.95,
error: 1.0
}.freeze
def payload
return {} unless can?(user, :admin_namespace, root_namespace)
{
current_usage_message: current_usage_message,
usage_ratio: root_storage_size.usage_ratio
explanation_message: explanation_message,
usage_message: usage_message,
alert_level: alert_level
}
end
def current_usage_message
params = {
usage_in_percent: number_to_percentage(root_storage_size.usage_ratio * 100, precision: 0),
namespace_name: root_namespace.name,
used_storage: formatted(root_storage_size.current_size),
storage_limit: formatted(root_storage_size.limit)
}
s_("You reached %{usage_in_percent} of %{namespace_name}'s capacity (%{used_storage} of %{storage_limit})" % params)
def explanation_message
root_storage_size.above_size_limit? ? above_size_limit_message : below_size_limit_message
end
def info_message
def usage_message
s_("You reached %{usage_in_percent} of %{namespace_name}'s capacity (%{used_storage} of %{storage_limit})" % current_usage_params)
end
def alert_level
strong_memoize(:alert_level) do
usage_ratio = root_storage_size.usage_ratio
current_level = USAGE_THRESHOLDS.each_key.first
USAGE_THRESHOLDS.each do |level, threshold|
current_level = level if usage_ratio >= threshold
end
current_level
end
end
def below_size_limit_message
s_("If you reach 100%% storage capacity, you will not be able to: %{base_message}" % { base_message: base_message } )
end
@ -53,6 +78,15 @@ module Namespaces
s_("push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines.")
end
def current_usage_params
{
usage_in_percent: number_to_percentage(root_storage_size.usage_ratio * 100, precision: 0),
namespace_name: root_namespace.name,
used_storage: formatted(root_storage_size.current_size),
storage_limit: formatted(root_storage_size.limit)
}
end
def formatted(number)
number_to_human_size(number, delimiter: ',', precision: 2)
end

View File

@ -29,6 +29,8 @@ class PostReceiveService
response.add_alert_message(message)
end
response.add_alert_message(storage_size_limit_alert)
broadcast_message = BroadcastMessage.current_banner_messages&.last&.message
response.add_alert_message(broadcast_message)
@ -74,6 +76,19 @@ class PostReceiveService
::MergeRequests::GetUrlsService.new(project).execute(params[:changes])
end
private
def storage_size_limit_alert
return unless repository&.repo_type&.project?
payload = Namespaces::CheckStorageSizeService.new(project.namespace, user).execute.payload
return unless payload.present?
alert_level = "##### #{payload[:alert_level].to_s.upcase} #####"
[alert_level, payload[:usage_message], payload[:explanation_message]].join("\n")
end
end
PostReceiveService.prepend_if_ee('EE::PostReceiveService')

View File

@ -2,13 +2,14 @@
module ResourceEvents
class ChangeMilestoneService
attr_reader :resource, :user, :event_created_at, :milestone
attr_reader :resource, :user, :event_created_at, :milestone, :old_milestone
def initialize(resource, user, created_at: Time.now)
def initialize(resource, user, created_at: Time.now, old_milestone:)
@resource = resource
@user = user
@event_created_at = created_at
@milestone = resource&.milestone
@old_milestone = old_milestone
end
def execute
@ -26,7 +27,7 @@ module ResourceEvents
{
user_id: user.id,
created_at: event_created_at,
milestone_id: milestone&.id,
milestone_id: action == :add ? milestone&.id : old_milestone&.id,
state: ResourceMilestoneEvent.states[resource.state],
action: ResourceMilestoneEvent.actions[action],
key => resource.id

View File

@ -1,5 +1,5 @@
.row.prepend-top-default.append-bottom-default
.col-lg-3
.col-lg-4
%h4.prepend-top-0
= @service.title
- [true, false].each do |value|
@ -9,7 +9,7 @@
- if @service.respond_to?(:detailed_description)
%p= @service.detailed_description
.col-lg-9
.col-lg-8
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, service: @service
.footer-block.row-content-block

View File

@ -1,6 +1,7 @@
- breadcrumb_title @service.title
- add_to_breadcrumbs _('Integration Settings'), project_settings_integrations_path(@project)
- page_title @service.title, _('Integrations')
- @content_class = 'limit-container-width' unless fluid_layout
= render 'form'
- if @web_hook_logs

View File

@ -1,6 +1,6 @@
- if milestone.expired? and not milestone.closed?
.status-box.status-box-expired.append-bottom-5 Expired
.status-box.status-box-expired.append-bottom-5 = _('Expired')
- if milestone.upcoming?
.status-box.status-box-mr-merged.append-bottom-5 Upcoming
.status-box.status-box-mr-merged.append-bottom-5 = _('Upcoming')
- if milestone.closed?
.status-box.status-box-closed.append-bottom-5 Closed
.status-box.status-box-closed.append-bottom-5 = _('Closed')

View File

@ -1,4 +1,5 @@
= form_errors(@service)
- trigger_events = Feature.enabled?(:integration_form_refactor) ? ServiceEventSerializer.new(service: @service).represent(@service.configurable_events).to_json : []
- if lookup_context.template_exists?('help', "projects/services/#{@service.to_param}", true)
= render "projects/services/#{@service.to_param}/help", subject: @service
@ -9,9 +10,9 @@
.service-settings
.js-vue-integration-settings{ data: { show_active: @service.show_active_box?.to_s, activated: (@service.active || @service.new_record?).to_s, type: @service.to_param, merge_request_events: @service.merge_requests_events.to_s,
commit_events: @service.commit_events.to_s, enable_comments: @service.comment_on_event_enabled.to_s, comment_detail: @service.comment_detail } }
commit_events: @service.commit_events.to_s, enable_comments: @service.comment_on_event_enabled.to_s, comment_detail: @service.comment_detail, trigger_events: trigger_events } }
- if @service.configurable_events.present? && !@service.is_a?(JiraService)
- if @service.configurable_events.present? && !@service.is_a?(JiraService) && Feature.disabled?(:integration_form_refactor)
.form-group.row
%label.col-form-label.col-sm-2= _('Trigger')
@ -33,15 +34,4 @@ commit_events: @service.commit_events.to_s, enable_comments: @service.comment_on
= @service.class.event_description(event)
- @service.global_fields.each do |field|
- type = field[:type]
- if type == 'fieldset'
- fields = field[:fields]
- legend = field[:legend]
%fieldset
%legend= legend
- fields.each do |subfield|
= render 'shared/field', form: form, field: subfield
- else
= render 'shared/field', form: form, field: field
= render 'shared/field', form: form, field: field

View File

@ -0,0 +1,5 @@
---
title: Enable deploy token authentication for the NPM registry
merge_request: 31264
author:
type: added

View File

@ -0,0 +1,6 @@
---
title: Allow users to star/unstar dashboards which will appear at the top of their
dashboards options
merge_request: 31597
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Add WYSIWYG editor to the Static Site Editor
merge_request: 31099
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Externalize i18n strings from ./app/views/shared/_milestone_expired.html.haml
merge_request: 32121
author: Gilang Gumilar
type: changed

View File

@ -0,0 +1,5 @@
---
title: 'Disable Docker-in-Docker for Dependency Scanning by default'
merge_request: 31588
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: 'Disable Docker-in-Docker for SAST by default'
merge_request: 31589
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Add user last_activity logging in GraphQL
merge_request: 23063
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Remove detection of file in Dependency Scanning template
merge_request: 31819
author:
type: fixed

View File

@ -1,6 +1,7 @@
require 'gitlab/testing/request_blocker_middleware'
require 'gitlab/testing/request_inspector_middleware'
require 'gitlab/testing/clear_process_memory_cache_middleware'
require 'gitlab/utils'
Rails.application.configure do
# Make sure the middleware is inserted first in middleware chain
@ -43,7 +44,7 @@ Rails.application.configure do
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
config.eager_load = true
config.eager_load = Gitlab::Utils.to_boolean(ENV['GITLAB_TEST_EAGER_LOAD'], default: true)
config.cache_store = :null_store

View File

@ -13,6 +13,7 @@ swap:
gitlab omnibus: Omnibus GitLab
param: parameter
params: parameters
pg: PostgreSQL
postgres: PostgreSQL
raketask: Rake task
raketasks: Rake tasks

View File

@ -172,11 +172,12 @@ do this manually.
The `gitlab-ctl promote-to-primary-node` command cannot be used in conjunction with
an external PostgreSQL database, as it can only perform changes on a **secondary**
node with GitLab and the database on the same machine. As a result, a manual process is
required. For example, PostgreSQL databases hosted on Amazon RDS:
required:
1. Promote the replica database associated with the **secondary** site. This will
set the database to read-write:
- Amazon RDS - [Promoting a Read Replica to Be a Standalone DB Instance](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html#USER_ReadRepl.Promote)
- Azure Database for PostgreSQL - [Stop replication](https://docs.microsoft.com/en-us/azure/postgresql/howto-read-replicas-portal#stop-replication)
1. Edit `/etc/gitlab/gitlab.rb` on every node in the **secondary** site to
reflect its new status as **primary** by removing any lines that enabled the

View File

@ -262,7 +262,7 @@ You can login to the **secondary** node with the same credentials you used for t
**secondary** Geo node and if Geo is enabled.
The initial replication, or 'backfill', will probably still be in progress. You
can monitor the synchronization process on each geo node from the **primary**
can monitor the synchronization process on each Geo node from the **primary**
node's **Geo Nodes** dashboard in your browser.
![Geo dashboard](img/geo_node_dashboard.png)

View File

@ -38,7 +38,14 @@ Given you have a primary node set up on AWS EC2 that uses RDS.
You can now just create a read-only replica in a different region and the
replication process will be managed by AWS. Make sure you've set Network ACL, Subnet, and
Security Group according to your needs, so the secondary application node can access the database.
Skip to the [Configure secondary application node](#configure-secondary-application-nodes-to-use-the-external-read-replica) section below.
The following instructions detail how to create a read-only replica for common
cloud providers:
- Amazon RDS - [Creating a Read Replica](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_ReadRepl.html#USER_ReadRepl.Create)
- Azure Database for PostgreSQL - [Create and manage read replicas in Azure Database for PostgreSQL](https://docs.microsoft.com/en-us/azure/postgresql/howto-read-replicas-portal)
Once your read-only replica is set up, you can skip to [configure you secondary application node](#configure-secondary-application-nodes-to-use-the-external-read-replica).
#### Manually configure the primary database for replication
@ -133,6 +140,10 @@ To configure the connection to the external read-replica database and enable Log
gitlab_rails['db_username'] = 'gitlab'
gitlab_rails['db_host'] = '<database_read_replica_host>'
# Disable the bundled Omnibus PostgreSQL, since we are
# using an external PostgreSQL
postgresql['enable'] = false
```
1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure)

View File

@ -1,85 +1,101 @@
# Geo validation tests
The Geo team performs manual testing and validation on common deployment configurations to ensure that Geo works when upgrading between minor GitLab versions and major PostgreSQL database versions. This section contains a journal of recent validation tests and links to the relevant issues.
The Geo team performs manual testing and validation on common deployment configurations to ensure
that Geo works when upgrading between minor GitLab versions and major PostgreSQL database versions.
## GitLab Upgrades
This section contains a journal of recent validation tests and links to the relevant issues.
## GitLab upgrades
The following are GitLab upgrade validation tests we performed.
### February 2020
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/201837)
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/201837):
- Description: Tested upgrading from GitLab 12.7.5 to the latest 12.8 package in a high availability configuration.
- Outcome: Partial success because we did not run the looping pipeline during the demo to monitor downtime.
- Description: Tested upgrading from GitLab 12.7.5 to the latest GitLab 12.8 package in a high
availability configuration.
- Outcome: Partial success because we did not run the looping pipeline during the demo to monitor
downtime.
### January 2020
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/200085)
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/200085):
- Description: Tested upgrading from GitLab 12.6.x to the latest 12.7 package in a high availability configuration.
- Description: Tested upgrading from GitLab 12.6.x to the latest GitLab 12.7 package in a high
availability configuration.
- Outcome: Upgrade test was successful.
- Follow up issues:
- [Investigate Geo End to End Test Failures](https://gitlab.com/gitlab-org/gitlab/issues/201823)
- [Add More Logging to Geo End to End Tests](https://gitlab.com/gitlab-org/gitlab/issues/201830)
- [Excess Service Restarts During Zero-Downtime Upgrade](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5047)
- [Investigate Geo end-to-end test failures](https://gitlab.com/gitlab-org/gitlab/issues/201823).
- [Add more logging to Geo end-to-end tests](https://gitlab.com/gitlab-org/gitlab/issues/201830).
- [Excess service restarts during zero-downtime upgrade](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5047).
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/199836)
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/199836):
- Description: Tested upgrading from GitLab 12.5.7 to 12.6.6 in a high availability configuration
- Description: Tested upgrading from GitLab 12.5.7 to GitLab 12.6.6 in a high availability
configuration.
- Outcome: Upgrade test was successful.
- Follow up issue:
[Update documentation for zero-downtime upgrades to ensure deploy node it not in use](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5046).
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/37044):
- Description: Tested upgrading from GitLab 12.4.x to the latest GitLab 12.5 package in a high
availability configuration.
- Outcome: Upgrade test was successful.
- Follow up issues:
- [Update docs for zero-downtime upgrades to ensure deploy node it not in-use](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5046)
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/37044)
- Description: Tested upgrading from GitLab 12.4.x to the latest 12.5 package in a high availability configuration.
- Outcome: Upgrade test was successful.
- Follow up issues:
- [Investigate why http push spec failed on primary node](https://gitlab.com/gitlab-org/gitlab/issues/199825)
- [Investigate if docs should be modified to include refresh foreign tables task](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5041)
- [Investigate why HTTP push spec failed on primary node](https://gitlab.com/gitlab-org/gitlab/issues/199825).
- [Investigate if documentation should be modified to include refresh foreign tables task](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5041).
### October 2019
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/35262)
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/35262):
- Description: Tested uprgading from GitLab 12.3.5 to 12.4.1 in a high availability configuration.
- Description: Tested upgrading from GitLab 12.3.5 to GitLab 12.4.1 in a high availability configuration.
- Outcome: Upgrade test was successful.
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/32437)
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/32437):
- Description: Tested upgrading from GitLab 12.2.8 to 12.3.5
- Description: Tested upgrading from GitLab 12.2.8 to GitLab 12.3.5.
- Outcome: Upgrade test was successful.
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/32435)
[Upgrade Geo HA installation](https://gitlab.com/gitlab-org/gitlab/-/issues/32435):
- Description: Tested upgrading from GitLab 12.1.9 to 12.2.8
- Description: Tested upgrading from GitLab 12.1.9 to GitLab 12.2.8.
- Outcome: Partial success due to possible misconfiguration issues.
## PostgreSQL Upgrades
## PostgreSQL upgrades
The following are PostgreSQL upgrade validation tests we performed.
### April 2020
[PostgreSQL 11 upgrade procedure for GEO installations](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4975)
[PostgreSQL 11 upgrade procedure for Geo installations](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4975):
- Description: Prior to making PostgreSQL 11 the default version of PG in GitLab 12.10, we tested upgrading to PG 11 in Geo deployments on GitLab 12.9.
- Outcome: Partially successful. Issues were discovered in HA configurations with a separate tracking database and concerns were raised about allowing automatic upgrades when Geo enabled.
- Description: Prior to making PostgreSQL 11 the default version of PostgreSQL in GitLab 12.10, we
tested upgrading to PostgreSQL 11 in Geo deployments on GitLab 12.9.
- Outcome: Partially successful. Issues were discovered in HA configurations with a separate
tracking database and concerns were raised about allowing automatic upgrades when Geo enabled.
- Follow up issues:
- [replicate-geo-database incorrectly tries to backup repos](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5241)
- [pg-upgrade fails to upgrade a standalone Geo tracking DB](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5242)
- [revert-pg-upgrade fails to downgrade a Geo secondarys standalone tracking DBs PG data](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5243)
- [Timeout error on Geo secondary read-replica near the end of `gitlab-ctl pg-upgrade`](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5235)
- [`replicate-geo-database` incorrectly tries to back up repositories](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5241).
- [`pg-upgrade` fails to upgrade a standalone Geo tracking database](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5242).
- [`revert-pg-upgrade` fails to downgrade the PostgreSQL data of a Geo secondarys standalone tracking database](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5243).
- [Timeout error on Geo secondary read-replica near the end of `gitlab-ctl pg-upgrade`](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5235).
[Verify GEO installation with PostgreSQL 11](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4971)
[Verify Geo installation with PostgreSQL 11](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4971):
- Description: Prior to making PostgreSQL 11 the default version of PG in GitLab 12.10, we tested fresh installations of GitLab 12.9 with Geo, installed with PG 11.
- Description: Prior to making PostgreSQL 11 the default version of PostgreSQL in GitLab 12.10, we
tested fresh installations of GitLab 12.9 with Geo installed with PostgreSQL 11.
- Outcome: Installation test was successful.
### September 2019
[Test and validate PostgreSQL 10.0 upgrade for Geo](https://gitlab.com/gitlab-org/gitlab/issues/12092)
[Test and validate PostgreSQL 10.0 upgrade for Geo](https://gitlab.com/gitlab-org/gitlab/issues/12092):
- Description: With the 12.0 release, GitLab required an upgrade to PostgreSQL 10.0. We tested various upgrade scenarios from GitLab 11.11.5 through to 12.1.8.
- Description: With the 12.0 release, GitLab required an upgrade to PostgreSQL 10.0. We tested
various upgrade scenarios from GitLab 11.11.5 through to GitLab 12.1.8.
- Outcome: Multiple issues were found when upgrading and addressed in follow-up issues.
- Follow up issues:
- [`gitlab-ctl` reconfigure fails on Redis node in HA Geo setup](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4706)
- [HA with Geo upgrade from 12.0.9 to 12.1.9 does not upgrade PostgreSQL](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4705)
- [refresh foreign tables fails on app server in HA setup after upgrade to 12.1.9](https://gitlab.com/gitlab-org/gitlab/-/issues/32119)
- [`gitlab-ctl` reconfigure fails on Redis node in HA Geo setup](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4706).
- [HA with Geo upgrade from 12.0.9 to 12.1.9 does not upgrade PostgreSQL](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4705).
- [Refresh foreign tables fails on app server in HA setup after upgrade to 12.1.9](https://gitlab.com/gitlab-org/gitlab/-/issues/32119).

View File

@ -73,7 +73,7 @@ from [owasp.org](https://owasp.org/).
- Nothing Geo-specific. Any user where `admin: true` is set in the database is
considered an admin with super-user privileges.
- See also: [more granular access control](https://gitlab.com/gitlab-org/gitlab-foss/issues/32730)
(not geo-specific)
(not Geo-specific).
- Much of Geos integration (database replication, for instance) must be
configured with the application, typically by system administrators.

View File

@ -860,6 +860,8 @@ which Geo expects to have access to. It usually means, either:
- An unsupported replication method was used (for example, logical replication).
- The instructions to setup a [Geo database replication](database.md) were not followed correctly.
- Your database connection details are incorrect, that is you have specified the wrong
user in your `/etc/gitlab/gitlab.rb` file.
A common source of confusion with **secondary** nodes is that it requires two separate
PostgreSQL instances:

View File

@ -125,7 +125,7 @@ otherwise the networks will become a single point of failure.
#### Architecture
![PG HA Architecture](img/pg_ha_architecture.png)
![PostgreSQL HA Architecture](img/pg_ha_architecture.png)
Database nodes run two services with PostgreSQL:

View File

@ -33,7 +33,7 @@ This section is for links to information elsewhere in the GitLab documentation.
- [More about external PostgreSQL](../external_database.md)
- [Running GEO with external PostgreSQL](../geo/replication/external_database.md)
- [Running Geo with external PostgreSQL](../geo/replication/external_database.md)
- [Upgrades when running PostgreSQL configured for HA.](https://docs.gitlab.com/omnibus/settings/database.html#upgrading-a-gitlab-ha-cluster)
@ -71,7 +71,7 @@ This section is for links to information elsewhere in the GitLab documentation.
HINT: Free one or increase max_replication_slots.
```
- GEO [replication errors](../geo/replication/troubleshooting.md#fixing-replication-errors) including:
- Geo [replication errors](../geo/replication/troubleshooting.md#fixing-replication-errors) including:
```plaintext
ERROR: replication slots can only be used if max_replication_slots > 0
@ -83,11 +83,11 @@ This section is for links to information elsewhere in the GitLab documentation.
PANIC: could not write to file pg_xlog/xlogtemp.123: No space left on device
```
- [Checking GEO configuration](../geo/replication/troubleshooting.md#checking-configuration) including
- [Checking Geo configuration](../geo/replication/troubleshooting.md#checking-configuration) including
- reconfiguring hosts/ports
- checking and fixing user/password mappings
- [Common GEO errors](../geo/replication/troubleshooting.md#fixing-common-errors)
- [Common Geo errors](../geo/replication/troubleshooting.md#fixing-common-errors)
## Support topics

View File

@ -1409,6 +1409,7 @@ The activities that update the timestamp are:
- User logging in into GitLab
- User visiting pages related to Dashboards, Projects, Issues, and Merge Requests ([introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/54947) in GitLab 11.8)
- User using the API
- User using the GraphQL API
By default, it shows the activity for all users in the last 6 months, but this can be
amended by using the `from` parameter.

View File

@ -64,7 +64,7 @@ It picks reviewers and maintainers from the list at the
page, with these behaviors:
1. It will not pick people whose [GitLab status](../user/profile/index.md#current-status)
contains the string 'OOO'.
contains the string 'OOO', or the emoji is `:palm_tree:` or `:beach:`.
1. [Trainee maintainers](https://about.gitlab.com/handbook/engineering/workflow/code-review/#trainee-maintainer)
are three times as likely to be picked as other reviewers.
1. It always picks the same reviewers and maintainers for the same

View File

@ -153,7 +153,7 @@ request, be sure to start the `dont-interrupt-me` job before pushing.
### Current versions testing
| Where? | PG version |
| Where? | PostgreSQL version |
| ------ | ------ |
| MRs | 11 |
| `master` (non-scheduled pipelines) | 11 |
@ -163,7 +163,7 @@ request, be sure to start the `dont-interrupt-me` job before pushing.
We follow the [PostgreSQL versions shipped with Omnibus GitLab](https://docs.gitlab.com/omnibus/package-information/postgresql_versions.html):
| PG version | 12.10 (April 2020) | 13.0 (May 2020) | 13.1 (June 2020) | 13.2 (July 2020) | 13.3 (August 2020) | 13.4, 13.5 | 13.6 (November 2020) | 14.0 (May 2021?) |
| PostgreSQL version | 12.10 (April 2020) | 13.0 (May 2020) | 13.1 (June 2020) | 13.2 (July 2020) | 13.3 (August 2020) | 13.4, 13.5 | 13.6 (November 2020) | 14.0 (May 2021?) |
| ------ | ------------------ | --------------- | ---------------- | ---------------- | ------------------ | ------------ | -------------------- | ---------------- |
| PG9.6 | MRs/`master`/`2-hour`/`nightly` | - | - | - | - | - | - | - |
| PG10 | `nightly` | - | - | - | - | - | - | - |

View File

@ -1,5 +1,10 @@
# Jenkins CI service **(STARTER)**
NOTE: **Note:**
This documentation focuses only on how to **configure** a Jenkins *integration* with
GitLab. Learn how to **migrate** from Jenkins to GitLab CI/CD in our
[Migrating from Jenkins](../ci/jenkins/index.md) documentation.
From GitLab, you can trigger a Jenkins build when you push code to a repository, or when a merge
request is created. In return, Jenkins shows the pipeline status on merge requests widgets and
on the GitLab project's home page.

View File

@ -26,3 +26,4 @@ How do we measure the activity of users? GitLab considers a user active if:
- The user has Git activity (whether push or pull).
- The user visits pages related to Dashboards, Projects, Issues, and Merge Requests ([introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/54947) in GitLab 11.8).
- The user uses the API
- The user uses the GraphQL API

View File

@ -100,14 +100,15 @@ configure GitLab as a remote registry.
If a project is private or you want to upload an NPM package to GitLab,
credentials will need to be provided for authentication. [Personal access tokens](../../profile/personal_access_tokens.md)
and [deploy tokens](../../project/deploy_tokens/index.md)
are preferred, but support is available for [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow).
CAUTION: **2FA is only supported with personal access tokens:**
If you have 2FA enabled, you need to use a [personal access token](../../profile/personal_access_tokens.md) with OAuth headers with the scope set to `api`. Standard OAuth tokens won't be able to authenticate to the GitLab NPM Registry.
CAUTION: **Two-factor authentication (2FA) is only supported with personal access tokens:**
If you have 2FA enabled, you need to use a [personal access token](../../profile/personal_access_tokens.md) with OAuth headers with the scope set to `api` or a [deploy token](../../project/deploy_tokens/index.md) with `read_package_registry` or `write_package_registry` scopes. Standard OAuth tokens won't be able to authenticate to the GitLab NPM Registry.
### Authenticating with a personal access token
### Authenticating with a personal access token or deploy token
To authenticate with a [personal access token](../../profile/personal_access_tokens.md),
To authenticate with a [personal access token](../../profile/personal_access_tokens.md) or [deploy token](../../project/deploy_tokens/index.md),
set your NPM configuration:
```shell
@ -125,7 +126,7 @@ npm config set '//gitlab.com/api/v4/projects/<your_project_id>/packages/npm/:_au
```
Replace `<your_project_id>` with your project ID which can be found on the home page
of your project and `<your_token>` with your personal access token.
of your project and `<your_token>` with your personal access token or deploy token.
If you have a self-managed GitLab installation, replace `gitlab.com` with your
domain name.
@ -160,7 +161,7 @@ Then, you could run `npm publish` either locally or via GitLab CI/CD:
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/9104) in GitLab Premium 12.5.
If youre using NPM with GitLab CI/CD, a CI job token can be used instead of a personal access token.
If youre using NPM with GitLab CI/CD, a CI job token can be used instead of a personal access token or deploy token.
The token will inherit the permissions of the user that generates the pipeline.
Add a corresponding section to your `.npmrc` file:
@ -286,7 +287,7 @@ page.
## Publishing a package with CI/CD
To work with NPM commands within [GitLab CI/CD](./../../../ci/README.md), you can use
`CI_JOB_TOKEN` in place of the personal access token in your commands.
`CI_JOB_TOKEN` in place of the personal access token or deploy token in your commands.
A simple example `.gitlab-ci.yml` file for publishing NPM packages:
@ -323,7 +324,7 @@ info Visit https://classic.yarnpkg.com/en/docs/cli/install for documentation abo
```
In this case, try adding this to your `.npmrc` file (and replace `<your_token>`
with your personal access token):
with your personal access token or deploy token):
```text
//gitlab.com/api/v4/projects/:_authToken=<your_token>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -5,7 +5,8 @@ description: "The static site editor enables users to edit content on static web
# Static Site Editor
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28758) in GitLab 12.10.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28758) in GitLab 12.10.
> - WYSIWYG editor [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214559) in GitLab 13.0.
Static Site Editor enables users to edit content on static websites without
prior knowledge of the underlying templating language, site architecture, or
@ -44,7 +45,7 @@ When clicking it, GitLab will open up an editor window from which the content
can be directly edited. When you're ready, you can submit your changes in a
click of a button:
![Static Site Editor](img/static_site_editor_v12_10.png)
![Static Site Editor](img/wysiwyg_editor_v13_0.png)
When an editor submits their changes, in the background, GitLab automatically
creates a new branch, commits their changes, and opens a merge request. The
@ -79,7 +80,8 @@ company and a new feature has been added to the company product.
1. You are assigned the task of updating the documentation.
1. You visit a page and see content that needs to be edited.
1. Click the **Edit this page** button on the production site.
1. The file is opened in the Static Site Editor.
1. The file is opened in the Static Site Editor in **WYSIWYG** mode. If you wish to edit the raw Markdown
instead, you can toggle the **Markdown** mode in the bottom-right corner.
1. You edit the file right there and click **Submit changes**.
1. A new merge request is automatically created and you assign it to your colleague for review.

View File

@ -107,9 +107,12 @@ module Gitlab
def deploy_token_from_request
return unless route_authentication_setting[:deploy_token_allowed]
token = current_request.env[DEPLOY_TOKEN_HEADER].presence
token = current_request.env[DEPLOY_TOKEN_HEADER].presence || parsed_oauth_token
DeployToken.active.find_by_token(token)
deploy_token = DeployToken.active.find_by_token(token)
@current_authenticated_deploy_token = deploy_token # rubocop:disable Gitlab/ModuleWithInstanceVariables
deploy_token
end
def find_runner_from_token
@ -122,6 +125,9 @@ module Gitlab
end
def validate_access_token!(scopes: [])
# return early if we've already authenticated via a deploy token
return if @current_authenticated_deploy_token.present? # rubocop:disable Gitlab/ModuleWithInstanceVariables
return unless access_token
case AccessTokenValidationService.new(access_token, request: request).validate(scopes: scopes)

View File

@ -14,7 +14,7 @@ variables:
DS_DEFAULT_ANALYZERS: "bundler-audit, retire.js, gemnasium, gemnasium-maven, gemnasium-python"
DS_MAJOR_VERSION: 2
DS_DISABLE_DIND: "false"
DS_DISABLE_DIND: "true"
dependency_scanning:
stage: test
@ -106,7 +106,6 @@ gemnasium-dependency_scanning:
$DS_DEFAULT_ANALYZERS =~ /gemnasium([^-]|$)/
exists:
- 'Gemfile.lock'
- 'Pipfile.lock'
- 'composer.lock'
- 'gems.locked'
- 'go.sum'

View File

@ -14,7 +14,7 @@ variables:
SAST_DEFAULT_ANALYZERS: "bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, tslint, secrets, sobelow, pmd-apex, kubesec"
SAST_ANALYZER_IMAGE_TAG: 2
SAST_DISABLE_DIND: "false"
SAST_DISABLE_DIND: "true"
SCAN_KUBERNETES_MANIFESTS: "false"
sast:

View File

@ -1,12 +1,19 @@
# frozen_string_literal: true
require 'cgi'
require 'set'
module Gitlab
module Danger
class Teammate
attr_reader :name, :username, :role, :projects
AT_CAPACITY_EMOJI = Set.new(%w[red_circle]).freeze
OOO_EMOJI = Set.new(%w[
palm_tree
beach beach_umbrella beach_with_umbrella
]).freeze
def initialize(options = {})
@username = options['username']
@name = options['name'] || @username
@ -37,10 +44,14 @@ module Gitlab
end
def status
api_endpoint = "https://gitlab.com/api/v4/users/#{CGI.escape(username)}/status"
@status ||= Gitlab::Danger::RequestHelper.http_get_json(api_endpoint)
rescue Gitlab::Danger::RequestHelper::HTTPError, JSON::ParserError
nil # better no status than a crashing Danger
return @status if defined?(@status)
@status ||=
begin
Gitlab::Danger::RequestHelper.http_get_json(status_api_endpoint)
rescue Gitlab::Danger::RequestHelper::HTTPError, JSON::ParserError
nil # better no status than a crashing Danger
end
end
# @return [Boolean]
@ -50,14 +61,22 @@ module Gitlab
private
def status_api_endpoint
"https://gitlab.com/api/v4/users/#{CGI.escape(username)}/status"
end
def status_emoji
status&.dig("emoji")
end
# @return [Boolean]
def out_of_office?
status&.dig("message")&.match?(/OOO/i) || false
status&.dig("message")&.match?(/OOO/i) || OOO_EMOJI.include?(status_emoji)
end
# @return [Boolean]
def has_capacity?
status&.dig("emoji") != 'red_circle'
!AT_CAPACITY_EMOJI.include?(status_emoji)
end
def has_capability?(project, category, kind, labels)

View File

@ -157,8 +157,8 @@ module Gitlab
Rails.env.test? ? Rails.root.join('tmp/tests') : Gitlab.config.gitlab.user_home
end
def checkout_or_clone_version(version:, repo:, target_dir:)
clone_repo(repo, target_dir) unless Dir.exist?(target_dir)
def checkout_or_clone_version(version:, repo:, target_dir:, clone_opts: [])
clone_repo(repo, target_dir, clone_opts: clone_opts) unless Dir.exist?(target_dir)
checkout_version(get_version(version), target_dir)
end
@ -171,8 +171,8 @@ module Gitlab
"v#{component_version}"
end
def clone_repo(repo, target_dir)
run_command!(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{target_dir}])
def clone_repo(repo, target_dir, clone_opts: [])
run_command!(%W[#{Gitlab.config.git.bin_path} clone] + clone_opts + %W[-- #{repo} #{target_dir}])
end
def checkout_version(version, target_dir)

View File

@ -75,12 +75,12 @@ module Gitlab
str.gsub(/\r?\n/, '')
end
def to_boolean(value)
def to_boolean(value, default: nil)
return value if [true, false].include?(value)
return true if value =~ /^(true|t|yes|y|1|on)$/i
return false if value =~ /^(false|f|no|n|0|off)$/i
nil
default
end
def boolean_to_yes_no(bool)

View File

@ -12,7 +12,7 @@ module SystemCheck
def show_error
try_fixing_it(
"Please migrate all projects to hashed storage#{' on the primary' if Gitlab.ee? && Gitlab::Geo.secondary?}",
"as legacy storage is deprecated in 13.0 and support will be removed in 13.4."
"as legacy storage is deprecated in 13.0 and support will be removed in 14.0."
)
for_more_information('doc/administration/repository_storage_types.md')

View File

@ -13,7 +13,7 @@ Usage: rake "gitlab:gitaly:install[/installation/dir,/storage/path]")
version = Gitlab::GitalyClient.expected_server_version
checkout_or_clone_version(version: version, repo: args.repo, target_dir: args.dir)
checkout_or_clone_version(version: version, repo: args.repo, target_dir: args.dir, clone_opts: %w[--depth 1])
command = []
_, status = Gitlab::Popen.popen(%w[which gmake])

View File

@ -12,7 +12,7 @@ namespace :gitlab do
gitlab_url += '/' unless gitlab_url.end_with?('/')
target_dir = Gitlab.config.gitlab_shell.path
checkout_or_clone_version(version: default_version, repo: args.repo, target_dir: target_dir)
checkout_or_clone_version(version: default_version, repo: args.repo, target_dir: target_dir, clone_opts: %w[--depth 1])
# Make sure we're on the right tag
Dir.chdir(target_dir) do

View File

@ -12,7 +12,7 @@ namespace :gitlab do
version = Gitlab::Workhorse.version
checkout_or_clone_version(version: version, repo: args.repo, target_dir: args.dir)
checkout_or_clone_version(version: version, repo: args.repo, target_dir: args.dir, clone_opts: %w[--depth 1])
_, status = Gitlab::Popen.popen(%w[which gmake])
command = status.zero? ? 'gmake' : 'make'

View File

@ -3797,6 +3797,9 @@ msgstr ""
msgid "Changing group path can have unintended side effects."
msgstr ""
msgid "Channel handle (e.g. town-square)"
msgstr ""
msgid "Charts"
msgstr ""
@ -9719,15 +9722,15 @@ msgstr ""
msgid "Geo"
msgstr ""
msgid "Geo Designs"
msgstr ""
msgid "Geo Nodes"
msgstr ""
msgid "Geo Nodes|Cannot remove a primary node if there is a secondary node"
msgstr ""
msgid "Geo Replication"
msgstr ""
msgid "Geo Settings"
msgstr ""
@ -10019,6 +10022,9 @@ msgstr ""
msgid "Geo|Reverify all"
msgstr ""
msgid "Geo|Review replication status, and resynchronize and reverify items with the primary node."
msgstr ""
msgid "Geo|Status"
msgstr ""
@ -13474,6 +13480,9 @@ msgstr ""
msgid "Metrics|Refresh dashboard"
msgstr ""
msgid "Metrics|Star dashboard"
msgstr ""
msgid "Metrics|There was an error creating the dashboard."
msgstr ""
@ -13510,6 +13519,9 @@ msgstr ""
msgid "Metrics|Unit label"
msgstr ""
msgid "Metrics|Unstar dashboard"
msgstr ""
msgid "Metrics|Used as a title for the chart"
msgstr ""
@ -17705,6 +17717,9 @@ msgstr ""
msgid "Replaces the clone URL root."
msgstr ""
msgid "Replication"
msgstr ""
msgid "Reply by email"
msgstr ""

View File

@ -20,7 +20,7 @@ module GitalyTest
'HOME' => File.expand_path('tmp/tests'),
'GEM_PATH' => Gem.path.join(':'),
'BUNDLE_APP_CONFIG' => File.join(File.dirname(gemfile), '.bundle/config'),
'BUNDLE_FLAGS' => "--jobs=4 --retry=3",
'BUNDLE_FLAGS' => "--jobs=4 --retry=3 --quiet",
'BUNDLE_INSTALL_FLAGS' => nil,
'BUNDLE_GEMFILE' => gemfile,
'RUBYOPT' => nil,

View File

@ -6,12 +6,17 @@ export BUNDLE_INSTALL_FLAGS="--without=production --jobs=$(nproc) --path=vendor
if [ "$USE_BUNDLE_INSTALL" != "false" ]; then
bundle --version
bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check
run_timed_command "bundle install --clean ${BUNDLE_INSTALL_FLAGS}"
run_timed_command "bundle check"
# When we test multiple versions of PG in the same pipeline, we have a single `setup-test-env`
# job but the `pg` gem needs to be rebuilt since it includes extensions (https://guides.rubygems.org/gems-with-extensions).
# Uncomment the following line if multiple versions of PG are tested in the same pipeline.
# run_timed_command "bundle pristine pg"
fi
# Only install knapsack after bundle install! Otherwise oddly some native
# gems could not be found under some circumstance. No idea why, hours wasted.
retry gem install knapsack --no-document
run_timed_command "gem install knapsack --no-document"
cp config/gitlab.yml.example config/gitlab.yml
sed -i 's/bin_path: \/usr\/bin\/git/bin_path: \/usr\/local\/bin\/git/' config/gitlab.yml

View File

@ -18,11 +18,8 @@ function setup_db_user_only() {
}
function setup_db() {
setup_db_user_only
bundle exec rake db:drop db:create db:structure:load db:migrate
bundle exec rake gitlab:db:setup_ee
run_timed_command "setup_db_user_only"
run_timed_command "bundle exec rake db:drop db:create db:structure:load db:migrate gitlab:db:setup_ee"
}
function install_api_client_dependencies_with_apk() {
@ -38,6 +35,24 @@ function install_gitlab_gem() {
gem install gitlab --no-document --version 4.13.0
}
function run_timed_command() {
local cmd="${1}"
local start=$(date +%s)
echosuccess "\$ ${cmd}"
eval "${cmd}"
local ret=$?
local end=$(date +%s)
local runtime=$((end-start))
if [[ $ret -eq 0 ]]; then
echosuccess "==> '${cmd}' succeeded in ${runtime} seconds."
return 0
else
echoerr "==> '${cmd}' failed (${ret}) in ${runtime} seconds."
return $ret
fi
}
function echoerr() {
local header="${2}"
@ -58,6 +73,16 @@ function echoinfo() {
fi
}
function echosuccess() {
local header="${2}"
if [ -n "${header}" ]; then
printf "\n\033[0;32m** %s **\n\033[0m" "${1}" >&2;
else
printf "\033[0;32m%s\n\033[0m" "${1}" >&2;
fi
}
function get_job_id() {
local job_name="${1}"
local query_string="${2:+&${2}}"

View File

@ -32,7 +32,7 @@ describe GraphqlController do
describe 'POST #execute' do
context 'when user is logged in' do
let(:user) { create(:user) }
let(:user) { create(:user, last_activity_on: Date.yesterday) }
before do
sign_in(user)
@ -56,6 +56,19 @@ describe GraphqlController do
expect(response).to have_gitlab_http_status(:forbidden)
expect(response).to render_template('errors/access_denied')
end
it 'updates the users last_activity_on field' do
expect { post :execute }.to change { user.reload.last_activity_on }
end
end
context 'when user uses an API token' do
let(:user) { create(:user, last_activity_on: Date.yesterday) }
let(:token) { create(:personal_access_token, user: user, scopes: [:api]) }
it 'updates the users last_activity_on field' do
expect { post :execute, params: { access_token: token.token } }.to change { user.reload.last_activity_on }
end
end
context 'when user is not logged in' do

View File

@ -158,6 +158,13 @@ FactoryBot.define do
token { 'test_token' }
end
factory :slack_service do
project
active { true }
webhook { 'https://slack.service.url' }
type { 'SlackService' }
end
# this is for testing storing values inside properties, which is deprecated and will be removed in
# https://gitlab.com/gitlab-org/gitlab/issues/29404
trait :without_properties_callback do

View File

@ -212,12 +212,12 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc
expect(current_settings.hide_third_party_offers).to be true
end
it 'Change Slack Notifications Service template settings' do
it 'Change Slack Notifications Service template settings', :js do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
fill_in 'Webhook', with: 'http://localhost'
fill_in 'Username', with: 'test_user'
fill_in 'service_push_channel', with: '#test_channel'
fill_in 'service[push_channel]', with: '#test_channel'
page.check('Notify only broken pipelines')
page.select 'All branches', from: 'Branches to be notified'
@ -231,10 +231,10 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc
expect(page.all('input[type=checkbox]')).to all(be_checked)
expect(find_field('Webhook').value).to eq 'http://localhost'
expect(find_field('Username').value).to eq 'test_user'
expect(find('#service_push_channel').value).to eq '#test_channel'
expect(find('[name="service[push_channel]"]').value).to eq '#test_channel'
end
it 'defaults Deployment events to false for chat notification template settings' do
it 'defaults Deployment events to false for chat notification template settings', :js do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
@ -500,13 +500,13 @@ describe 'Admin updates settings', :clean_gitlab_redis_shared_state, :do_not_moc
def check_all_events
page.check('Push')
page.check('Issue')
page.check('Confidential issue')
page.check('Merge request')
page.check('Confidential Issue')
page.check('Merge Request')
page.check('Note')
page.check('Confidential note')
page.check('Tag push')
page.check('Confidential Note')
page.check('Tag Push')
page.check('Pipeline')
page.check('Wiki page')
page.check('Wiki Page')
page.check('Deployment')
end

View File

@ -209,6 +209,33 @@ describe "User browses files" do
end
end
context "when browsing a `Ääh-test-utf-8` branch", :js do
before do
project.repository.create_branch('Ääh-test-utf-8', project.repository.root_ref)
visit(project_tree_path(project, "Ääh-test-utf-8"))
end
it "shows files from a repository" do
expect(page).to have_content("VERSION")
.and have_content(".gitignore")
.and have_content("LICENSE")
click_link("files")
page.within('.repo-breadcrumb') do
expect(page).to have_link('files')
end
click_link("html")
page.within('.repo-breadcrumb') do
expect(page).to have_link('html')
end
expect(page).to have_link('500.html')
end
end
context "when browsing a `test-#` branch", :js do
before do
project.repository.create_branch('test-#', project.repository.root_ref)

View File

@ -18,7 +18,10 @@ describe('ActiveToggle', () => {
};
afterEach(() => {
if (wrapper) wrapper.destroy();
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const findGlToggle = () => wrapper.find(GlToggle);

View File

@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import IntegrationForm from '~/integrations/edit/components/integration_form.vue';
import ActiveToggle from '~/integrations/edit/components/active_toggle.vue';
import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue';
import TriggerFields from '~/integrations/edit/components/trigger_fields.vue';
describe('IntegrationForm', () => {
let wrapper;
@ -38,6 +39,7 @@ describe('IntegrationForm', () => {
const findActiveToggle = () => wrapper.find(ActiveToggle);
const findJiraTriggerFields = () => wrapper.find(JiraTriggerFields);
const findTriggerFields = () => wrapper.find(TriggerFields);
describe('template', () => {
describe('showActive is true', () => {
@ -77,5 +79,21 @@ describe('IntegrationForm', () => {
expect(findJiraTriggerFields().exists()).toBe(true);
});
});
describe('triggerEvents is present', () => {
it('renders TriggerFields', () => {
const events = [{ title: 'push' }];
const type = 'slack';
createComponent({
triggerEvents: events,
type,
});
expect(findTriggerFields().exists()).toBe(true);
expect(findTriggerFields().props('events')).toBe(events);
expect(findTriggerFields().props('type')).toBe(type);
});
});
});
});

View File

@ -18,7 +18,10 @@ describe('JiraTriggerFields', () => {
};
afterEach(() => {
if (wrapper) wrapper.destroy();
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const findCommentSettings = () => wrapper.find('[data-testid="comment-settings"]');

View File

@ -0,0 +1,136 @@
import { mount } from '@vue/test-utils';
import TriggerFields from '~/integrations/edit/components/trigger_fields.vue';
import { GlFormGroup, GlFormCheckbox, GlFormInput } from '@gitlab/ui';
describe('TriggerFields', () => {
let wrapper;
const defaultProps = {
type: 'slack',
};
const createComponent = props => {
wrapper = mount(TriggerFields, {
propsData: { ...defaultProps, ...props },
});
};
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const findAllGlFormCheckboxes = () => wrapper.findAll(GlFormCheckbox);
const findAllGlFormInputs = () => wrapper.findAll(GlFormInput);
describe('template', () => {
it('renders a label with text "Trigger"', () => {
createComponent();
const triggerLabel = wrapper.find('[data-testid="trigger-fields-group"]').find('label');
expect(triggerLabel.exists()).toBe(true);
expect(triggerLabel.text()).toBe('Trigger');
});
describe('events without field property', () => {
const events = [
{
title: 'push',
name: 'push_event',
description: 'Event on push',
value: true,
},
{
title: 'merge_request',
name: 'merge_requests_event',
description: 'Event on merge_request',
value: false,
},
];
beforeEach(() => {
createComponent({
events,
});
});
it('does not render GlFormInput for each event', () => {
expect(findAllGlFormInputs().exists()).toBe(false);
});
it('renders GlFormInput with description for each event', () => {
const groups = wrapper.find('#trigger-fields').findAll(GlFormGroup);
expect(groups).toHaveLength(2);
groups.wrappers.forEach((group, index) => {
expect(group.find('small').text()).toBe(events[index].description);
});
});
it('renders GlFormCheckbox for each event', () => {
const checkboxes = findAllGlFormCheckboxes();
const expectedResults = [
{ labelText: 'Push', inputName: 'service[push_event]' },
{ labelText: 'Merge Request', inputName: 'service[merge_requests_event]' },
];
expect(checkboxes).toHaveLength(2);
checkboxes.wrappers.forEach((checkbox, index) => {
expect(checkbox.find('label').text()).toBe(expectedResults[index].labelText);
expect(checkbox.find('input').attributes('name')).toBe(expectedResults[index].inputName);
expect(checkbox.vm.$attrs.checked).toBe(events[index].value);
});
});
});
describe('events with field property', () => {
const events = [
{
field: {
name: 'push_channel',
value: '',
},
},
{
field: {
name: 'merge_request_channel',
value: 'gitlab-development',
},
},
];
beforeEach(() => {
createComponent({
events,
});
});
it('renders GlFormCheckbox for each event', () => {
expect(findAllGlFormCheckboxes()).toHaveLength(2);
});
it('renders GlFormInput for each event', () => {
const fields = findAllGlFormInputs();
const expectedResults = [
{
name: 'service[push_channel]',
placeholder: 'Slack channels (e.g. general, development)',
},
{
name: 'service[merge_request_channel]',
placeholder: 'Slack channels (e.g. general, development)',
},
];
expect(fields).toHaveLength(2);
fields.wrappers.forEach((field, index) => {
expect(field.attributes()).toMatchObject(expectedResults[index]);
expect(field.vm.$attrs.value).toBe(events[index].field.value);
});
});
});
});
});

View File

@ -100,6 +100,26 @@ exports[`Dashboard template matches the default snapshot 1`] = `
<div
class="d-sm-flex"
>
<div
class="mb-2 mr-2 d-flex"
>
<div
class="flex-grow-1"
title="Star dashboard"
>
<gl-deprecated-button-stub
class="w-100"
size="md"
variant="default"
>
<gl-icon-stub
name="star-o"
size="16"
/>
</gl-deprecated-button-stub>
</div>
</div>
<!---->
<!---->

View File

@ -1,7 +1,7 @@
import { shallowMount, mount } from '@vue/test-utils';
import Tracking from '~/tracking';
import { ESC_KEY, ESC_KEY_IE11 } from '~/lib/utils/keys';
import { GlModal, GlDropdownItem, GlDeprecatedButton } from '@gitlab/ui';
import { GlModal, GlDropdownItem, GlDeprecatedButton, GlIcon } from '@gitlab/ui';
import { objectToQuery } from '~/lib/utils/url_utility';
import VueDraggable from 'vuedraggable';
import MockAdapter from 'axios-mock-adapter';
@ -353,6 +353,83 @@ describe('Dashboard', () => {
});
});
describe('star dashboards', () => {
const findToggleStar = () => wrapper.find({ ref: 'toggleStarBtn' });
const findToggleStarIcon = () => findToggleStar().find(GlIcon);
beforeEach(() => {
createShallowWrapper();
});
it('toggle star button is shown', () => {
expect(findToggleStar().exists()).toBe(true);
expect(findToggleStar().props('disabled')).toBe(false);
});
it('toggle star button is disabled when starring is taking place', () => {
store.commit(`monitoringDashboard/${types.REQUEST_DASHBOARD_STARRING}`);
return wrapper.vm.$nextTick(() => {
expect(findToggleStar().exists()).toBe(true);
expect(findToggleStar().props('disabled')).toBe(true);
});
});
describe('when the dashboard list is loaded', () => {
// Tooltip element should wrap directly
const getToggleTooltip = () => findToggleStar().element.parentElement.getAttribute('title');
beforeEach(() => {
wrapper.vm.$store.commit(
`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
dashboardGitResponse,
);
jest.spyOn(store, 'dispatch');
});
it('dispatches a toggle star action', () => {
findToggleStar().vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(store.dispatch).toHaveBeenCalledWith(
'monitoringDashboard/toggleStarredValue',
undefined,
);
});
});
describe('when dashboard is not starred', () => {
beforeEach(() => {
wrapper.setProps({ currentDashboard: dashboardGitResponse[0].path });
return wrapper.vm.$nextTick();
});
it('toggle star button shows "Star dashboard"', () => {
expect(getToggleTooltip()).toBe('Star dashboard');
});
it('toggle star button shows an unstarred state', () => {
expect(findToggleStarIcon().attributes('name')).toBe('star-o');
});
});
describe('when dashboard is starred', () => {
beforeEach(() => {
wrapper.setProps({ currentDashboard: dashboardGitResponse[1].path });
return wrapper.vm.$nextTick();
});
it('toggle star button shows "Star dashboard"', () => {
expect(getToggleTooltip()).toBe('Unstar dashboard');
});
it('toggle star button shows a starred state', () => {
expect(findToggleStarIcon().attributes('name')).toBe('star');
});
});
});
});
it('hides the environments dropdown list when there is no environments', () => {
createMountedWrapper({ hasMetrics: true });

View File

@ -15,9 +15,6 @@ describe('~/static_site_editor/components/edit_area.vue', () => {
const buildWrapper = (propsData = {}) => {
wrapper = shallowMount(EditArea, {
provide: {
glFeatures: { richContentEditor: true },
},
propsData: {
title,
content,

View File

@ -220,6 +220,24 @@ describe Gitlab::Auth::AuthFinders do
it { is_expected.to be_nil }
end
end
context 'with oauth headers' do
before do
set_header('HTTP_AUTHORIZATION', "Bearer #{deploy_token.token}")
end
it { is_expected.to eq deploy_token }
it_behaves_like 'an unauthenticated route'
context 'with invalid token' do
before do
set_header('HTTP_AUTHORIZATION', "Bearer invalid_token")
end
it { is_expected.to be_nil }
end
end
end
describe '#find_user_from_access_token' do

View File

@ -163,6 +163,13 @@ describe Gitlab::Danger::Teammate do
{ message: 'OOO: massage' } | false
{ message: 'love it SOOO much' } | false
{ emoji: 'red_circle' } | false
{ emoji: 'palm_tree' } | false
{ emoji: 'beach' } | false
{ emoji: 'beach_umbrella' } | false
{ emoji: 'beach_with_umbrella' } | false
{ emoji: nil } | true
{ emoji: '' } | true
{ emoji: 'dancer' } | true
end
with_them do
@ -175,9 +182,9 @@ describe Gitlab::Danger::Teammate do
end
it 'returns true if request fails' do
expect(Gitlab::Danger::RequestHelper).to receive(:http_get_json)
.twice
.and_raise(Gitlab::Danger::RequestHelper::HTTPError.new)
expect(Gitlab::Danger::RequestHelper)
.to receive(:http_get_json)
.and_raise(Gitlab::Danger::RequestHelper::HTTPError.new)
expect(subject.available?).to be true
end

View File

@ -130,7 +130,7 @@ describe Gitlab::Utils do
expect(to_boolean(false)).to be(false)
end
it 'converts a valid string to a boolean' do
it 'converts a valid value to a boolean' do
expect(to_boolean(true)).to be(true)
expect(to_boolean('true')).to be(true)
expect(to_boolean('YeS')).to be(true)
@ -146,12 +146,35 @@ describe Gitlab::Utils do
expect(to_boolean('oFF')).to be(false)
end
it 'converts an invalid string to nil' do
it 'converts an invalid value to nil' do
expect(to_boolean('fals')).to be_nil
expect(to_boolean('yeah')).to be_nil
expect(to_boolean('')).to be_nil
expect(to_boolean(nil)).to be_nil
end
it 'accepts a default value, and does not return it when a valid value is given' do
expect(to_boolean(true, default: false)).to be(true)
expect(to_boolean('true', default: false)).to be(true)
expect(to_boolean('YeS', default: false)).to be(true)
expect(to_boolean('t', default: false)).to be(true)
expect(to_boolean('1', default: 'any value')).to be(true)
expect(to_boolean('ON', default: 42)).to be(true)
expect(to_boolean('FaLse', default: true)).to be(false)
expect(to_boolean('F', default: true)).to be(false)
expect(to_boolean('NO', default: true)).to be(false)
expect(to_boolean('n', default: true)).to be(false)
expect(to_boolean('0', default: 'any value')).to be(false)
expect(to_boolean('oFF', default: 42)).to be(false)
end
it 'accepts a default value, and returns it when an invalid value is given' do
expect(to_boolean('fals', default: true)).to eq(true)
expect(to_boolean('yeah', default: false)).to eq(false)
expect(to_boolean('', default: 'any value')).to eq('any value')
expect(to_boolean(nil, default: 42)).to eq(42)
end
end
describe '.boolean_to_yes_no' do

View File

@ -14,5 +14,15 @@ describe MilestoneNote do
it_behaves_like 'a system note', exclude_project: true do
let(:action) { 'milestone' }
end
context 'with a remove milestone event' do
let(:milestone) { create(:milestone) }
let(:event) { create(:resource_milestone_event, action: :remove, issue: noteable, milestone: milestone) }
it 'creates the expected note' do
expect(subject.note_html).to include('removed milestone')
expect(subject.note_html).not_to include('changed milestone to')
end
end
end
end

View File

@ -64,22 +64,4 @@ RSpec.describe Namespace::RootStorageSize, type: :model do
it { is_expected.to eq(limit.megabytes) }
end
describe '#show_alert?' do
subject { model.show_alert? }
it { is_expected.to eq(true) }
context 'when limit is 0' do
let(:limit) { 0 }
it { is_expected.to eq(false) }
end
context 'when is below threshold' do
let(:current_size) { 49.megabytes }
it { is_expected.to eq(false) }
end
end
end

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'spec_helper'
describe ServiceEventEntity do
let(:request) { double('request') }
subject { described_class.new(event, request: request, service: service).as_json }
before do
allow(request).to receive(:service).and_return(service)
end
describe '#as_json' do
context 'service without fields' do
let(:service) { create(:emails_on_push_service, push_events: true) }
let(:event) { 'push' }
it 'exposes correct attributes' do
expect(subject[:description]).to eq('Event will be triggered by a push to the repository')
expect(subject[:name]).to eq('push_events')
expect(subject[:title]).to eq('push')
expect(subject[:value]).to be(true)
end
end
context 'service with fields' do
let(:service) { create(:slack_service, note_events: false, note_channel: 'note-channel') }
let(:event) { 'note' }
it 'exposes correct attributes' do
expect(subject[:description]).to eq('Event will be triggered when someone adds a comment')
expect(subject[:name]).to eq('note_events')
expect(subject[:title]).to eq('note')
expect(subject[:value]).to eq(false)
expect(subject[:field][:name]).to eq('note_channel')
expect(subject[:field][:value]).to eq('note-channel')
end
end
end
end

View File

@ -34,7 +34,7 @@ describe Ci::CreatePipelineService do
it 'creates a pipeline using the content passed in as param' do
expect(subject).to be_persisted
expect(subject.builds.map(&:name)).to eq %w[rspec custom]
expect(subject.builds.pluck(:name)).to match_array %w[rspec custom]
expect(subject.config_source).to eq 'bridge_source'
end
@ -59,7 +59,7 @@ describe Ci::CreatePipelineService do
it 'created a pipeline using the content passed in as param and download the artifact' do
expect(subject).to be_persisted
expect(subject.builds.pluck(:name)).to eq %w[rspec time custom]
expect(subject.builds.pluck(:name)).to match_array %w[rspec time custom]
expect(subject.config_source).to eq 'bridge_source'
end
end

View File

@ -92,6 +92,7 @@ describe MergeRequests::UpdateService, :mailer do
labels: [],
mentioned_users: [user2],
assignees: [user3],
milestone: nil,
total_time_spent: 0,
description: "FYI #{user2.to_reference}"
}

View File

@ -3,92 +3,157 @@
require 'spec_helper'
describe Namespaces::CheckStorageSizeService, '#execute' do
let_it_be(:root_group) { create(:group) }
let(:nested_group) { create(:group, parent: root_group) }
let(:service) { described_class.new(nested_group) }
let(:namespace) { build_stubbed(:namespace) }
let(:user) { build(:user, namespace: namespace) }
let(:service) { described_class.new(namespace, user) }
let(:current_size) { 150.megabytes }
let(:limit) { 100 }
let(:limit) { 100.megabytes }
subject { service.execute }
subject(:response) { service.execute }
before do
stub_application_setting(namespace_storage_size_limit: limit)
allow(namespace).to receive(:root_ancestor).and_return(namespace)
create(:namespace_root_storage_statistics, namespace: root_group, storage_size: current_size)
root_storage_size = instance_double("RootStorageSize",
current_size: current_size,
limit: limit,
usage_ratio: limit == 0 ? 0 : current_size.to_f / limit.to_f,
above_size_limit?: current_size > limit
)
expect(Namespace::RootStorageSize).to receive(:new).and_return(root_storage_size)
end
context 'feature flag' do
it 'is successful when disabled' do
stub_feature_flags(namespace_storage_limit: false)
expect(subject).to be_success
expect(response).to be_success
end
it 'errors when enabled' do
stub_feature_flags(namespace_storage_limit: true)
expect(subject).to be_error
expect(response).to be_error
end
it 'is successful when feature flag is activated for another group' do
stub_feature_flags(namespace_storage_limit: create(:group))
it 'is successful when feature flag is activated for another namespace' do
stub_feature_flags(namespace_storage_limit: build(:namespace))
expect(subject).to be_success
expect(response).to be_success
end
it 'errors when feature flag is activated for the current group' do
stub_feature_flags(namespace_storage_limit: root_group)
it 'errors when feature flag is activated for the current namespace' do
stub_feature_flags(namespace_storage_limit: namespace )
expect(subject).to be_error
expect(response).to be_error
expect(response.message).to be_present
end
end
context 'when limit is set to 0' do
let(:limit) { 0 }
it { is_expected.to be_success }
it 'does not respond with a payload' do
result = subject
expect(result.message).to be_nil
expect(result.payload).to be_empty
it 'is successful and has no payload' do
expect(response).to be_success
expect(response.payload).to be_empty
end
end
context 'when current size is below threshold to show an alert' do
context 'when current size is below threshold' do
let(:current_size) { 10.megabytes }
it { is_expected.to be_success }
end
context 'when current size exceeds limit' do
it 'returns an error with a payload' do
result = subject
current_usage_message = result.payload[:current_usage_message]
expect(result).to be_error
expect(result.message).to include("#{root_group.name} is now read-only.")
expect(current_usage_message).to include("150%")
expect(current_usage_message).to include(root_group.name)
expect(current_usage_message).to include("150 MB of 100 MB")
expect(result.payload[:usage_ratio]).to eq(1.5)
it 'is successful and has no payload' do
expect(response).to be_success
expect(response.payload).to be_empty
end
end
context 'when current size is below limit but should show an alert' do
let(:current_size) { 50.megabytes }
context 'when not admin of the namespace' do
let(:other_namespace) { build_stubbed(:namespace) }
it 'returns success with a payload' do
result = subject
current_usage_message = result.payload[:current_usage_message]
subject(:response) { described_class.new(other_namespace, user).execute }
expect(result).to be_success
expect(result.message).to be_present
expect(current_usage_message).to include("50%")
expect(current_usage_message).to include(root_group.name)
expect(current_usage_message).to include("50 MB of 100 MB")
expect(result.payload[:usage_ratio]).to eq(0.5)
before do
allow(other_namespace).to receive(:root_ancestor).and_return(other_namespace)
end
it 'errors and has no payload' do
expect(response).to be_error
expect(response.payload).to be_empty
end
end
context 'when providing the child namespace' do
let(:namespace) { build_stubbed(:group) }
let(:child_namespace) { build_stubbed(:group, parent: namespace) }
subject(:response) { described_class.new(child_namespace, user).execute }
before do
allow(child_namespace).to receive(:root_ancestor).and_return(namespace)
namespace.add_owner(user)
end
it 'uses the root namespace' do
expect(response).to be_error
end
end
describe 'payload alert_level' do
subject { service.execute.payload[:alert_level] }
context 'when above info threshold' do
let(:current_size) { 50.megabytes }
it { is_expected.to eq(:info) }
end
context 'when above warning threshold' do
let(:current_size) { 75.megabytes }
it { is_expected.to eq(:warning) }
end
context 'when above alert threshold' do
let(:current_size) { 95.megabytes }
it { is_expected.to eq(:alert) }
end
context 'when above error threshold' do
let(:current_size) { 100.megabytes }
it { is_expected.to eq(:error) }
end
end
describe 'payload explanation_message' do
subject(:response) { service.execute.payload[:explanation_message] }
context 'when above limit' do
let(:current_size) { 110.megabytes }
it 'returns message with read-only warning' do
expect(response).to include("#{namespace.name} is now read-only")
end
end
context 'when below limit' do
let(:current_size) { 60.megabytes }
it { is_expected.to include('If you reach 100% storage capacity') }
end
end
describe 'payload usage_message' do
let(:current_size) { 60.megabytes }
subject(:response) { service.execute.payload[:usage_message] }
it 'returns current usage information' do
expect(response).to include("60 MB of 100 MB")
expect(response).to include("60%")
end
end
end

View File

@ -166,6 +166,41 @@ describe PostReceiveService do
expect(subject).to include(build_alert_message(message))
end
end
context 'storage size limit alerts' do
let(:check_storage_size_response) { ServiceResponse.success }
before do
expect_next_instance_of(Namespaces::CheckStorageSizeService, project.namespace, user) do |check_storage_size_service|
expect(check_storage_size_service).to receive(:execute).and_return(check_storage_size_response)
end
end
context 'when there is no payload' do
it 'adds no alert' do
expect(subject.size).to eq(1)
end
end
context 'when there is payload' do
let(:check_storage_size_response) do
ServiceResponse.success(
payload: {
alert_level: :info,
usage_message: "Usage",
explanation_message: "Explanation"
}
)
end
it 'adds an alert' do
response = subject
expect(response.size).to eq(2)
expect(response).to include(build_alert_message("##### INFO #####\nUsage\nExplanation"))
end
end
end
end
context 'with PersonalSnippet' do

View File

@ -3,11 +3,9 @@
require 'spec_helper'
describe ResourceEvents::ChangeMilestoneService do
it_behaves_like 'a milestone events creator' do
let(:resource) { create(:issue) }
end
it_behaves_like 'a milestone events creator' do
let(:resource) { create(:merge_request) }
[:issue, :merge_request].each do |issuable|
it_behaves_like 'a milestone events creator' do
let(:resource) { create(issuable) }
end
end
end

View File

@ -8,10 +8,12 @@ ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true'
ENV["RSPEC_ALLOW_INVALID_URLS"] = 'true'
require File.expand_path('../config/environment', __dir__)
require 'rspec/mocks'
require 'rspec/rails'
require 'shoulda/matchers'
require 'rspec/retry'
require 'rspec-parameterized'
require 'shoulda/matchers'
require 'test_prof/recipes/rspec/let_it_be'
rspec_profiling_is_configured =

View File

@ -1,7 +1,5 @@
# frozen_string_literal: true
require 'rspec/mocks'
module TestEnv
extend ActiveSupport::Concern
extend self
@ -284,29 +282,33 @@ module TestEnv
end
def setup_factory_repo
setup_repo(factory_repo_path, factory_repo_path_bare, factory_repo_name,
BRANCH_SHA)
setup_repo(factory_repo_path, factory_repo_path_bare, factory_repo_name, BRANCH_SHA)
end
# This repo has a submodule commit that is not present in the main test
# repository.
def setup_forked_repo
setup_repo(forked_repo_path, forked_repo_path_bare, forked_repo_name,
FORKED_BRANCH_SHA)
setup_repo(forked_repo_path, forked_repo_path_bare, forked_repo_name, FORKED_BRANCH_SHA)
end
def setup_repo(repo_path, repo_path_bare, repo_name, refs)
clone_url = "https://gitlab.com/gitlab-org/#{repo_name}.git"
unless File.directory?(repo_path)
system(*%W(#{Gitlab.config.git.bin_path} clone -q #{clone_url} #{repo_path}))
puts "\n==> Setting up #{repo_name} repository in #{repo_path}..."
start = Time.now
system(*%W(#{Gitlab.config.git.bin_path} clone --quiet -- #{clone_url} #{repo_path}))
puts " #{repo_path} set up in #{Time.now - start} seconds...\n"
end
set_repo_refs(repo_path, refs)
unless File.directory?(repo_path_bare)
puts "\n==> Setting up #{repo_name} bare repository in #{repo_path_bare}..."
start = Time.now
# We must copy bare repositories because we will push to them.
system(git_env, *%W(#{Gitlab.config.git.bin_path} clone -q --bare #{repo_path} #{repo_path_bare}))
system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --quiet --bare -- #{repo_path} #{repo_path_bare}))
puts " #{repo_path_bare} set up in #{Time.now - start} seconds...\n"
end
end

View File

@ -5,6 +5,7 @@ shared_context 'project service activation' do
let(:user) { create(:user) }
before do
stub_feature_flags(integration_form_refactor: false)
project.add_maintainer(user)
sign_in(user)
end

View File

@ -4,7 +4,7 @@ shared_examples 'a milestone events creator' do
let_it_be(:user) { create(:user) }
let(:created_at_time) { Time.utc(2019, 12, 30) }
let(:service) { described_class.new(resource, user, created_at: created_at_time) }
let(:service) { described_class.new(resource, user, created_at: created_at_time, old_milestone: nil) }
context 'when milestone is present' do
let_it_be(:milestone) { create(:milestone) }
@ -25,10 +25,13 @@ shared_examples 'a milestone events creator' do
resource.milestone = nil
end
let(:old_milestone) { create(:milestone, project: resource.project) }
let(:service) { described_class.new(resource, user, created_at: created_at_time, old_milestone: old_milestone) }
it 'creates the expected event records' do
expect { service.execute }.to change { ResourceMilestoneEvent.count }.by(1)
expect_event_record(ResourceMilestoneEvent.last, action: 'remove', milestone: nil, state: 'opened')
expect_event_record(ResourceMilestoneEvent.last, action: 'remove', milestone: old_milestone, state: 'opened')
end
end

View File

@ -46,7 +46,7 @@ describe 'gitlab:gitaly namespace rake task' do
it 'calls checkout_or_clone_version with the right arguments' do
expect(main_object)
.to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
.to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path, clone_opts: %w[--depth 1])
subject
end

View File

@ -28,7 +28,7 @@ describe Gitlab::TaskHelpers do
context "target_dir doesn't exist" do
it 'clones the repo' do
expect(subject).to receive(:clone_repo).with(repo, clone_path)
expect(subject).to receive(:clone_repo).with(repo, clone_path, clone_opts: [])
subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path)
end
@ -45,6 +45,12 @@ describe Gitlab::TaskHelpers do
subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path)
end
end
it 'accepts clone_opts' do
expect(subject).to receive(:clone_repo).with(repo, clone_path, clone_opts: %w[--depth 1])
subject.checkout_or_clone_version(version: version, repo: repo, target_dir: clone_path, clone_opts: %w[--depth 1])
end
end
describe '#clone_repo' do
@ -54,6 +60,13 @@ describe Gitlab::TaskHelpers do
subject.clone_repo(repo, clone_path)
end
it 'accepts clone_opts' do
expect(subject)
.to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} clone --depth 1 -- #{repo} #{clone_path}])
subject.clone_repo(repo, clone_path, clone_opts: %w[--depth 1])
end
end
describe '#checkout_version' do

View File

@ -36,7 +36,7 @@ describe 'gitlab:workhorse namespace rake task' do
it 'calls checkout_or_clone_version with the right arguments' do
expect(main_object)
.to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
.to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path, clone_opts: %w[--depth 1])
run_rake_task('gitlab:workhorse:install', clone_path)
end

View File

@ -7,6 +7,8 @@ describe 'projects/services/_form' do
let(:user) { create(:admin) }
before do
stub_feature_flags(integration_form_refactor: false)
assign(:project, project)
allow(controller).to receive(:current_user).and_return(user)