Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-06-22 15:09:27 +00:00
parent 6046a605fd
commit e829ca213b
180 changed files with 1387 additions and 414 deletions

View File

@ -164,7 +164,7 @@ export default {
<template>
<form
:class="{ 'was-validated': wasValidated }"
class="prepend-top-default append-bottom-default needs-validation"
class="prepend-top-default gl-mb-3 needs-validation"
novalidate
@submit.prevent.stop="onSubmit"
>

View File

@ -96,7 +96,7 @@ export default {
<preview-item :draft="draft" :is-last="isLast(index)" />
</li>
</ul>
<gl-loading-icon v-else size="lg" class="prepend-top-default append-bottom-default" />
<gl-loading-icon v-else size="lg" class="prepend-top-default gl-mb-3" />
</div>
<div class="dropdown-footer">
<publish-button

View File

@ -62,9 +62,7 @@ export default {
</script>
<template>
<div
class="js-notebook-viewer-mounted container-fluid md prepend-top-default append-bottom-default"
>
<div class="js-notebook-viewer-mounted container-fluid md prepend-top-default gl-mb-3">
<div v-if="loading && !error" class="text-center loading">
<gl-loading-icon class="mt-5" size="lg" />
</div>

View File

@ -34,7 +34,7 @@ export default {
</script>
<template>
<div class="js-pdf-viewer container-fluid md prepend-top-default append-bottom-default">
<div class="js-pdf-viewer container-fluid md prepend-top-default gl-mb-3">
<div v-if="loading && !error" class="text-center loading">
<gl-loading-icon class="mt-5" size="lg" />
</div>

View File

@ -56,7 +56,7 @@ export default class SketchLoader {
error() {
const errorMsg = document.createElement('p');
errorMsg.className = 'prepend-top-default append-bottom-default text-center';
errorMsg.className = 'prepend-top-default gl-mb-3 text-center';
errorMsg.textContent = __(`
Cannot show preview. For previews on sketch files, they must have the file format
introduced by Sketch version 43 and above.

View File

@ -115,7 +115,7 @@ export default {
</script>
<template>
<div class="append-bottom-default deploy-keys">
<div class="gl-mb-3 deploy-keys">
<gl-loading-icon
v-if="isLoading && !hasKeys"
:label="s__('DeployKeys|Loading deploy keys')"

View File

@ -398,7 +398,7 @@ export default {
class="files d-flex"
>
<div
v-show="showTreeList"
v-if="showTreeList"
:style="{ width: `${treeWidth}px` }"
class="diff-tree-list js-diff-tree-list mr-3"
>

View File

@ -84,7 +84,7 @@ export default {
v-for="(suggestion, index) in issues"
:key="suggestion.id"
:class="{
'append-bottom-default': index !== issues.length - 1,
'gl-mb-3': index !== issues.length - 1,
}"
>
<suggestion :suggestion="suggestion" />

View File

@ -63,7 +63,7 @@ export default {
</script>
<template>
<div class="prepend-top-default append-bottom-default clearfix">
<div class="prepend-top-default gl-mb-3 clearfix">
<button
:class="{ disabled: formState.updateLoading || !isSubmitEnabled }"
:disabled="formState.updateLoading || !isSubmitEnabled"

View File

@ -274,7 +274,7 @@ export default {
};
</script>
<template>
<div class="prepend-top-default append-bottom-default js-environment-container">
<div class="prepend-top-default gl-mb-3 js-environment-container">
<div class="environment-information">
<ci-icon :status="iconStatus" />
<p class="inline gl-mb-0" v-html="environment"></p>

View File

@ -108,7 +108,7 @@ export default {
/>
</ci-header>
<gl-loading-icon v-if="isLoading" size="lg" class="prepend-top-default append-bottom-default" />
<gl-loading-icon v-if="isLoading" size="lg" class="prepend-top-default gl-mb-3" />
<gl-modal
:modal-id="$options.DELETE_MODAL_ID"

View File

@ -42,7 +42,7 @@ export default {
</div>
</div>
<div v-if="hasSuites" class="test-reports-table append-bottom-default js-test-cases-table">
<div v-if="hasSuites" class="test-reports-table gl-mb-3 js-test-cases-table">
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold fgray">
<div role="rowheader" class="table-section section-20">
{{ __('Class') }}

View File

@ -46,7 +46,7 @@ export default {
</div>
</div>
<div v-if="hasSuites" class="test-reports-table append-bottom-default js-test-suites-table">
<div v-if="hasSuites" class="test-reports-table gl-mb-3 js-test-suites-table">
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold">
<div role="rowheader" class="table-section section-25 pl-3">
{{ __('Suite') }}

View File

@ -71,7 +71,7 @@ export default {
<template>
<section id="serverless-function-details">
<h3 class="serverless-function-name">{{ name }}</h3>
<div class="append-bottom-default serverless-function-description">
<div class="gl-mb-3 serverless-function-description">
<div v-for="(line, index) in description.split('\n')" :key="index">{{ line }}</div>
</div>
<url :uri="funcUrl" />

View File

@ -75,11 +75,7 @@ export default {
<template>
<section id="serverless-functions" class="flex-grow">
<gl-loading-icon
v-if="checkingInstalled"
size="lg"
class="prepend-top-default append-bottom-default"
/>
<gl-loading-icon v-if="checkingInstalled" size="lg" class="prepend-top-default gl-mb-3" />
<div v-else-if="isInstalled">
<div v-if="hasFunctionData">
@ -98,7 +94,7 @@ export default {
<gl-loading-icon
v-if="isLoading"
size="lg"
class="prepend-top-default append-bottom-default js-functions-loader"
class="prepend-top-default gl-mb-3 js-functions-loader"
/>
</div>
<div v-else class="empty-state js-empty-state">

View File

@ -77,7 +77,7 @@ export default {
};
</script>
<template>
<div class="d-flex mr-source-target append-bottom-default">
<div class="d-flex mr-source-target gl-mb-3">
<mr-widget-icon name="git-merge" />
<div class="git-merge-container d-flex">
<div class="normal">

View File

@ -34,7 +34,7 @@ export default {
};
</script>
<template>
<div :id="$options.popoverContainer" class="d-flex mr-pipeline-suggest append-bottom-default">
<div :id="$options.popoverContainer" class="d-flex mr-pipeline-suggest gl-mb-3">
<mr-widget-icon :name="$options.iconName" />
<div :id="$options.popoverTarget">
<gl-sprintf

View File

@ -29,7 +29,7 @@ export default {
<textarea
:id="inputId"
:value="value"
class="form-control js-gfm-input append-bottom-default commit-message-edit"
class="form-control js-gfm-input gl-mb-3 commit-message-edit"
dir="auto"
required="required"
rows="7"

View File

@ -231,7 +231,7 @@ export default {
<template>
<div
ref="gl-form"
:class="{ 'prepend-top-default append-bottom-default': addSpacingClasses }"
:class="{ 'prepend-top-default gl-mb-3': addSpacingClasses }"
class="js-vue-markdown-field md-area position-relative"
>
<markdown-header

View File

@ -89,14 +89,13 @@ export default {
<div class="md-header">
<ul class="nav-links clearfix">
<li :class="{ active: !previewMarkdown }" class="md-header-tab">
<button class="js-write-link" tabindex="-1" type="button" @click="writeMarkdownTab($event)">
<button class="js-write-link" type="button" @click="writeMarkdownTab($event)">
{{ __('Write') }}
</button>
</li>
<li :class="{ active: previewMarkdown }" class="md-header-tab">
<button
class="js-preview-link js-md-preview-button"
tabindex="-1"
type="button"
@click="previewMarkdownTab($event)"
>

View File

@ -64,7 +64,6 @@ export default {
:aria-label="buttonTitle"
type="button"
class="toolbar-btn js-md"
tabindex="-1"
data-container="body"
@click="() => $emit('click')"
>

View File

@ -421,7 +421,6 @@ img.emoji {
.append-bottom-10 { margin-bottom: 10px; }
.append-bottom-15 { margin-bottom: 15px; }
.append-bottom-20 { margin-bottom: 20px; }
.append-bottom-default { margin-bottom: $gl-padding; }
.prepend-bottom-32 { margin-bottom: 32px; }
.ml-10 { margin-left: 4.5rem; }
.inline { display: inline-block; }

View File

@ -10,7 +10,7 @@ module KnownSignIn
private
def verify_known_sign_in
return unless current_user
return unless Gitlab::CurrentSettings.notify_on_unknown_sign_in? && current_user
notify_user unless known_device? || known_remote_ip?

View File

@ -3,11 +3,14 @@
class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper
include PaginatedCollection
include Analytics::UniqueVisitsHelper
before_action :authorize_read_project!, only: :index
before_action :authorize_read_group!, only: :index
before_action :find_todos, only: [:index, :destroy_all]
track_unique_visits :index, target_id: 'u_analytics_todos'
def index
@sort = params[:sort]
@todos = @todos.page(params[:page])

View File

@ -1,8 +1,12 @@
# frozen_string_literal: true
class InstanceStatistics::CohortsController < InstanceStatistics::ApplicationController
include Analytics::UniqueVisitsHelper
before_action :authenticate_usage_ping_enabled_or_admin!
track_unique_visits :index, target_id: 'i_analytics_cohorts'
def index
if Gitlab::CurrentSettings.usage_ping_enabled
cohorts_results = Rails.cache.fetch('cohorts', expires_in: 1.day) do

View File

@ -1,6 +1,10 @@
# frozen_string_literal: true
class InstanceStatistics::DevOpsScoreController < InstanceStatistics::ApplicationController
include Analytics::UniqueVisitsHelper
track_unique_visits :index, target_id: 'i_analytics_dev_ops_score'
# rubocop: disable CodeReuse/ActiveRecord
def index
@metric = DevOpsScore::Metric.order(:created_at).last&.present

View File

@ -4,10 +4,13 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TextHelper
include CycleAnalyticsParams
include Analytics::UniqueVisitsHelper
before_action :whitelist_query_limiting, only: [:show]
before_action :authorize_read_cycle_analytics!
track_unique_visits :show, target_id: 'p_analytics_valuestream'
def show
@cycle_analytics = ::CycleAnalytics::ProjectLevel.new(@project, options: options(cycle_analytics_project_params))

View File

@ -2,12 +2,15 @@
class Projects::GraphsController < Projects::ApplicationController
include ExtractsPath
include Analytics::UniqueVisitsHelper
# Authorize
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_read_repository_graphs!
track_unique_visits :charts, target_id: 'p_analytics_repo'
def show
respond_to do |format|
format.html

View File

@ -2,6 +2,7 @@
class Projects::PipelinesController < Projects::ApplicationController
include ::Gitlab::Utils::StrongMemoize
include Analytics::UniqueVisitsHelper
before_action :whitelist_query_limiting, only: [:create, :retry]
before_action :pipeline, except: [:index, :new, :create, :charts]
@ -20,6 +21,8 @@ class Projects::PipelinesController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
track_unique_visits :charts, target_id: 'p_analytics_pipelines'
wrap_parameters Ci::Pipeline
POLLING_INTERVAL = 10_000

View File

@ -71,7 +71,7 @@ module Ci
# rubocop: disable CodeReuse/ActiveRecord
def by_status(items)
return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
return items unless Ci::HasStatus::AVAILABLE_STATUSES.include?(params[:status])
items.where(status: params[:status])
end

View File

@ -21,7 +21,7 @@ module Ci
# rubocop: disable CodeReuse/ActiveRecord
def by_status(items)
return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
return items unless Ci::HasStatus::AVAILABLE_STATUSES.include?(params[:status])
items.where(status: params[:status])
end

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
module Analytics
module UniqueVisitsHelper
extend ActiveSupport::Concern
def visitor_id
return cookies[:visitor_id] if cookies[:visitor_id].present?
return unless current_user
uuid = SecureRandom.uuid
cookies[:visitor_id] = { value: uuid, expires: 24.months }
uuid
end
def track_visit(target_id)
return unless Feature.enabled?(:track_unique_visits)
return unless Gitlab::CurrentSettings.usage_ping_enabled?
return unless visitor_id
Gitlab::Analytics::UniqueVisits.new.track_visit(visitor_id, target_id)
end
class_methods do
def track_unique_visits(controller_actions, target_id:)
after_action only: controller_actions, if: -> { request.format.html? && request.headers['DNT'] != '1' } do
track_visit(target_id)
end
end
end
end
end

View File

@ -244,6 +244,7 @@ module ApplicationSettingsHelper
:metrics_method_call_threshold,
:minimum_password_length,
:mirror_available,
:notify_on_unknown_sign_in,
:pages_domain_verification_enabled,
:password_authentication_enabled_for_web,
:password_authentication_enabled_for_git,

View File

@ -244,7 +244,6 @@ module MarkupHelper
content_tag :button,
type: 'button',
class: 'toolbar-btn js-md has-tooltip',
tabindex: -1,
data: data,
title: options[:title],
aria: { label: options[:title] } do

View File

@ -88,6 +88,7 @@ module ApplicationSettingImplementation
max_attachment_size: Settings.gitlab['max_attachment_size'],
max_import_size: 50,
mirror_available: true,
notify_on_unknown_sign_in: true,
outbound_local_requests_whitelist: [],
password_authentication_enabled_for_git: true,
password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'],

View File

@ -16,6 +16,7 @@ class AuditEvent < ApplicationRecord
scope :by_entity_type, -> (entity_type) { where(entity_type: entity_type) }
scope :by_entity_id, -> (entity_id) { where(entity_id: entity_id) }
scope :by_author_id, -> (author_id) { where(author_id: author_id) }
after_initialize :initialize_details

View File

@ -3,7 +3,7 @@
module Ci
class Pipeline < ApplicationRecord
extend Gitlab::Ci::Model
include HasStatus
include Ci::HasStatus
include Importable
include AfterCommitQueue
include Presentable
@ -640,7 +640,7 @@ module Ci
when 'manual' then block
when 'scheduled' then delay
else
raise HasStatus::UnknownStatusError,
raise Ci::HasStatus::UnknownStatusError,
"Unknown status `#{new_status}`"
end
end

View File

@ -4,10 +4,10 @@ module Ci
class Stage < ApplicationRecord
extend Gitlab::Ci::Model
include Importable
include HasStatus
include Ci::HasStatus
include Gitlab::OptimisticLocking
enum status: HasStatus::STATUSES_ENUM
enum status: Ci::HasStatus::STATUSES_ENUM
belongs_to :project
belongs_to :pipeline
@ -98,7 +98,7 @@ module Ci
when 'scheduled' then delay
when 'skipped', nil then skip
else
raise HasStatus::UnknownStatusError,
raise Ci::HasStatus::UnknownStatusError,
"Unknown status `#{new_status}`"
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class CommitStatus < ApplicationRecord
include HasStatus
include Ci::HasStatus
include Importable
include AfterCommitQueue
include Presentable

View File

@ -0,0 +1,168 @@
# frozen_string_literal: true
module Ci
module HasStatus
extend ActiveSupport::Concern
DEFAULT_STATUS = 'created'
BLOCKED_STATUS = %w[manual scheduled].freeze
AVAILABLE_STATUSES = %w[created waiting_for_resource preparing pending running success failed canceled skipped manual scheduled].freeze
STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
ACTIVE_STATUSES = %w[waiting_for_resource preparing pending running].freeze
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
ORDERED_STATUSES = %w[failed preparing pending running waiting_for_resource manual scheduled canceled success skipped created].freeze
PASSED_WITH_WARNINGS_STATUSES = %w[failed canceled].to_set.freeze
EXCLUDE_IGNORED_STATUSES = %w[manual failed canceled].to_set.freeze
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
failed: 4, canceled: 5, skipped: 6, manual: 7,
scheduled: 8, preparing: 9, waiting_for_resource: 10 }.freeze
UnknownStatusError = Class.new(StandardError)
class_methods do
def legacy_status_sql
scope_relevant = respond_to?(:exclude_ignored) ? exclude_ignored : all
scope_warnings = respond_to?(:failed_but_allowed) ? failed_but_allowed : none
builds = scope_relevant.select('count(*)').to_sql
created = scope_relevant.created.select('count(*)').to_sql
success = scope_relevant.success.select('count(*)').to_sql
manual = scope_relevant.manual.select('count(*)').to_sql
scheduled = scope_relevant.scheduled.select('count(*)').to_sql
preparing = scope_relevant.preparing.select('count(*)').to_sql
waiting_for_resource = scope_relevant.waiting_for_resource.select('count(*)').to_sql
pending = scope_relevant.pending.select('count(*)').to_sql
running = scope_relevant.running.select('count(*)').to_sql
skipped = scope_relevant.skipped.select('count(*)').to_sql
canceled = scope_relevant.canceled.select('count(*)').to_sql
warnings = scope_warnings.select('count(*) > 0').to_sql.presence || 'false'
Arel.sql(
"(CASE
WHEN (#{builds})=(#{skipped}) AND (#{warnings}) THEN 'success'
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success}) THEN 'success'
WHEN (#{builds})=(#{created}) THEN 'created'
WHEN (#{builds})=(#{preparing}) THEN 'preparing'
WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success'
WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled'
WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
WHEN (#{running})+(#{pending})>0 THEN 'running'
WHEN (#{waiting_for_resource})>0 THEN 'waiting_for_resource'
WHEN (#{manual})>0 THEN 'manual'
WHEN (#{scheduled})>0 THEN 'scheduled'
WHEN (#{preparing})>0 THEN 'preparing'
WHEN (#{created})>0 THEN 'running'
ELSE 'failed'
END)"
)
end
def legacy_status
all.pluck(legacy_status_sql).first
end
# This method should not be used.
# This method performs expensive calculation of status:
# 1. By plucking all related objects,
# 2. Or executes expensive SQL query
def slow_composite_status(project:)
if ::Gitlab::Ci::Features.composite_status?(project)
Gitlab::Ci::Status::Composite
.new(all, with_allow_failure: columns_hash.key?('allow_failure'))
.status
else
legacy_status
end
end
def started_at
all.minimum(:started_at)
end
def finished_at
all.maximum(:finished_at)
end
def all_state_names
state_machines.values.flat_map(&:states).flat_map { |s| s.map(&:name) }
end
def completed_statuses
COMPLETED_STATUSES.map(&:to_sym)
end
end
included do
validates :status, inclusion: { in: AVAILABLE_STATUSES }
state_machine :status, initial: :created do
state :created, value: 'created'
state :waiting_for_resource, value: 'waiting_for_resource'
state :preparing, value: 'preparing'
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
state :success, value: 'success'
state :canceled, value: 'canceled'
state :skipped, value: 'skipped'
state :manual, value: 'manual'
state :scheduled, value: 'scheduled'
end
scope :created, -> { with_status(:created) }
scope :waiting_for_resource, -> { with_status(:waiting_for_resource) }
scope :preparing, -> { with_status(:preparing) }
scope :relevant, -> { without_status(:created) }
scope :running, -> { with_status(:running) }
scope :pending, -> { with_status(:pending) }
scope :success, -> { with_status(:success) }
scope :failed, -> { with_status(:failed) }
scope :canceled, -> { with_status(:canceled) }
scope :skipped, -> { with_status(:skipped) }
scope :manual, -> { with_status(:manual) }
scope :scheduled, -> { with_status(:scheduled) }
scope :alive, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running) }
scope :alive_or_scheduled, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running, :scheduled) }
scope :created_or_pending, -> { with_status(:created, :pending) }
scope :running_or_pending, -> { with_status(:running, :pending) }
scope :finished, -> { with_status(:success, :failed, :canceled) }
scope :failed_or_canceled, -> { with_status(:failed, :canceled) }
scope :incomplete, -> { without_statuses(completed_statuses) }
scope :cancelable, -> do
where(status: [:running, :waiting_for_resource, :preparing, :pending, :created, :scheduled])
end
scope :without_statuses, -> (names) do
with_status(all_state_names - names.to_a)
end
end
def started?
STARTED_STATUSES.include?(status) && started_at
end
def active?
ACTIVE_STATUSES.include?(status)
end
def complete?
COMPLETED_STATUSES.include?(status)
end
def blocked?
BLOCKED_STATUS.include?(status)
end
private
def calculate_duration
if started_at && finished_at
finished_at - started_at
elsif started_at
Time.current - started_at
end
end
end
end

View File

@ -1,166 +0,0 @@
# frozen_string_literal: true
module HasStatus
extend ActiveSupport::Concern
DEFAULT_STATUS = 'created'
BLOCKED_STATUS = %w[manual scheduled].freeze
AVAILABLE_STATUSES = %w[created waiting_for_resource preparing pending running success failed canceled skipped manual scheduled].freeze
STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
ACTIVE_STATUSES = %w[waiting_for_resource preparing pending running].freeze
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
ORDERED_STATUSES = %w[failed preparing pending running waiting_for_resource manual scheduled canceled success skipped created].freeze
PASSED_WITH_WARNINGS_STATUSES = %w[failed canceled].to_set.freeze
EXCLUDE_IGNORED_STATUSES = %w[manual failed canceled].to_set.freeze
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
failed: 4, canceled: 5, skipped: 6, manual: 7,
scheduled: 8, preparing: 9, waiting_for_resource: 10 }.freeze
UnknownStatusError = Class.new(StandardError)
class_methods do
def legacy_status_sql
scope_relevant = respond_to?(:exclude_ignored) ? exclude_ignored : all
scope_warnings = respond_to?(:failed_but_allowed) ? failed_but_allowed : none
builds = scope_relevant.select('count(*)').to_sql
created = scope_relevant.created.select('count(*)').to_sql
success = scope_relevant.success.select('count(*)').to_sql
manual = scope_relevant.manual.select('count(*)').to_sql
scheduled = scope_relevant.scheduled.select('count(*)').to_sql
preparing = scope_relevant.preparing.select('count(*)').to_sql
waiting_for_resource = scope_relevant.waiting_for_resource.select('count(*)').to_sql
pending = scope_relevant.pending.select('count(*)').to_sql
running = scope_relevant.running.select('count(*)').to_sql
skipped = scope_relevant.skipped.select('count(*)').to_sql
canceled = scope_relevant.canceled.select('count(*)').to_sql
warnings = scope_warnings.select('count(*) > 0').to_sql.presence || 'false'
Arel.sql(
"(CASE
WHEN (#{builds})=(#{skipped}) AND (#{warnings}) THEN 'success'
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success}) THEN 'success'
WHEN (#{builds})=(#{created}) THEN 'created'
WHEN (#{builds})=(#{preparing}) THEN 'preparing'
WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success'
WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled'
WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
WHEN (#{running})+(#{pending})>0 THEN 'running'
WHEN (#{waiting_for_resource})>0 THEN 'waiting_for_resource'
WHEN (#{manual})>0 THEN 'manual'
WHEN (#{scheduled})>0 THEN 'scheduled'
WHEN (#{preparing})>0 THEN 'preparing'
WHEN (#{created})>0 THEN 'running'
ELSE 'failed'
END)"
)
end
def legacy_status
all.pluck(legacy_status_sql).first
end
# This method should not be used.
# This method performs expensive calculation of status:
# 1. By plucking all related objects,
# 2. Or executes expensive SQL query
def slow_composite_status(project:)
if ::Gitlab::Ci::Features.composite_status?(project)
Gitlab::Ci::Status::Composite
.new(all, with_allow_failure: columns_hash.key?('allow_failure'))
.status
else
legacy_status
end
end
def started_at
all.minimum(:started_at)
end
def finished_at
all.maximum(:finished_at)
end
def all_state_names
state_machines.values.flat_map(&:states).flat_map { |s| s.map(&:name) }
end
def completed_statuses
COMPLETED_STATUSES.map(&:to_sym)
end
end
included do
validates :status, inclusion: { in: AVAILABLE_STATUSES }
state_machine :status, initial: :created do
state :created, value: 'created'
state :waiting_for_resource, value: 'waiting_for_resource'
state :preparing, value: 'preparing'
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
state :success, value: 'success'
state :canceled, value: 'canceled'
state :skipped, value: 'skipped'
state :manual, value: 'manual'
state :scheduled, value: 'scheduled'
end
scope :created, -> { with_status(:created) }
scope :waiting_for_resource, -> { with_status(:waiting_for_resource) }
scope :preparing, -> { with_status(:preparing) }
scope :relevant, -> { without_status(:created) }
scope :running, -> { with_status(:running) }
scope :pending, -> { with_status(:pending) }
scope :success, -> { with_status(:success) }
scope :failed, -> { with_status(:failed) }
scope :canceled, -> { with_status(:canceled) }
scope :skipped, -> { with_status(:skipped) }
scope :manual, -> { with_status(:manual) }
scope :scheduled, -> { with_status(:scheduled) }
scope :alive, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running) }
scope :alive_or_scheduled, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running, :scheduled) }
scope :created_or_pending, -> { with_status(:created, :pending) }
scope :running_or_pending, -> { with_status(:running, :pending) }
scope :finished, -> { with_status(:success, :failed, :canceled) }
scope :failed_or_canceled, -> { with_status(:failed, :canceled) }
scope :incomplete, -> { without_statuses(completed_statuses) }
scope :cancelable, -> do
where(status: [:running, :waiting_for_resource, :preparing, :pending, :created, :scheduled])
end
scope :without_statuses, -> (names) do
with_status(all_state_names - names.to_a)
end
end
def started?
STARTED_STATUSES.include?(status) && started_at
end
def active?
ACTIVE_STATUSES.include?(status)
end
def complete?
COMPLETED_STATUSES.include?(status)
end
def blocked?
BLOCKED_STATUS.include?(status)
end
private
def calculate_duration
if started_at && finished_at
finished_at - started_at
elsif started_at
Time.current - started_at
end
end
end

View File

@ -147,7 +147,7 @@ class Environment < ApplicationRecord
Ci::Build.joins(inner_join_stop_actions)
.with(cte.to_arel)
.where(ci_builds[:commit_id].in(pipeline_ids))
.where(status: HasStatus::BLOCKED_STATUS)
.where(status: Ci::HasStatus::BLOCKED_STATUS)
.preload_project_and_pipeline_project
.preload(:user, :metadata, :deployment)
end

View File

@ -2414,6 +2414,11 @@ class Project < ApplicationRecord
super || build_metrics_setting
end
def service_desk_enabled
false
end
alias_method :service_desk_enabled?, :service_desk_enabled
private
def find_service(services, name)

View File

@ -656,6 +656,15 @@ class User < ApplicationRecord
end
end
def support_bot
email_pattern = "support%s@#{Settings.gitlab.host}"
unique_internal(where(user_type: :support_bot), 'support-bot', email_pattern) do |u|
u.bio = 'The GitLab support bot used for Service Desk'
u.name = 'GitLab Support Bot'
end
end
# Return true if there is only single non-internal user in the deployment,
# ghost user is ignored.
def single_user?

View File

@ -21,6 +21,10 @@ class BasePolicy < DeclarativePolicy::Base
with_options scope: :user, score: 0
condition(:deactivated) { @user&.deactivated? }
desc "User is support bot"
with_options scope: :user, score: 0
condition(:support_bot) { @user&.support_bot? }
desc "User email is unconfirmed or user account is locked"
with_options scope: :user, score: 0
condition(:inactive) do

View File

@ -45,6 +45,10 @@ module PolicyActor
false
end
def support_bot?
false
end
def deactivated?
false
end

View File

@ -123,6 +123,9 @@ class ProjectPolicy < BasePolicy
!@subject.design_management_enabled?
end
with_scope :subject
condition(:service_desk_enabled) { @subject.service_desk_enabled? }
# We aren't checking `:read_issue` or `:read_merge_request` in this case
# because it could be possible for a user to see an issuable-iid
# (`:read_issue_iid` or `:read_merge_request_iid`) but then wouldn't be
@ -578,6 +581,12 @@ class ProjectPolicy < BasePolicy
enable :read_build_report_results
end
rule { support_bot }.enable :guest_access
rule { support_bot & ~service_desk_enabled }.policy do
prevent :create_note
prevent :read_project
end
private
def team_member?
@ -626,6 +635,7 @@ class ProjectPolicy < BasePolicy
def lookup_access_level!
return ::Gitlab::Access::REPORTER if alert_bot?
return ::Gitlab::Access::REPORTER if support_bot? && service_desk_enabled?
# NOTE: max_member_access has its own cache
project.team.max_member_access(@user.id)

View File

@ -59,13 +59,13 @@ class StageEntity < Grape::Entity
end
def latest_statuses
HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
Ci::HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
grouped_statuses.fetch(ordered_status, [])
end
end
def retried_statuses
HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
Ci::HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
grouped_retried_statuses.fetch(ordered_status, [])
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
module AuthorizedProjectUpdate
class PeriodicRecalculateService
BATCH_SIZE = 480
DELAY_INTERVAL = 30.seconds.to_i
def execute
# Using this approach (instead of eg. User.each_batch) keeps the arguments
# the same for AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker
# even if the user list changes, so we can deduplicate these jobs.
(1..User.maximum(:id)).each_slice(BATCH_SIZE).with_index do |batch, index|
delay = DELAY_INTERVAL * index
AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker.perform_in(delay, *batch.minmax)
end
end
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
module AuthorizedProjectUpdate
class RecalculateForUserRangeService
def initialize(start_user_id, end_user_id)
@start_user_id = start_user_id
@end_user_id = end_user_id
end
def execute
User.where(id: start_user_id..end_user_id).select(:id).find_each do |user| # rubocop: disable CodeReuse/ActiveRecord
Users::RefreshAuthorizedProjectsService.new(user).execute
end
end
private
attr_reader :start_user_id, :end_user_id
end
end

View File

@ -77,7 +77,7 @@ module Ci
def update_processable!(processable)
status = processable_status(processable)
return unless HasStatus::COMPLETED_STATUSES.include?(status)
return unless Ci::HasStatus::COMPLETED_STATUSES.include?(status)
# transition status if possible
Gitlab::OptimisticLocking.retry_lock(processable) do |subject|

View File

@ -80,7 +80,7 @@ module Ci
# TODO: This is hack to support
# the same exact behaviour for Atomic and Legacy processing
# that DAG is blocked from executing if dependent is not "complete"
if dag && statuses.any? { |status| HasStatus::COMPLETED_STATUSES.exclude?(status[:status]) }
if dag && statuses.any? { |status| Ci::HasStatus::COMPLETED_STATUSES.exclude?(status[:status]) }
return 'pending'
end

View File

@ -35,7 +35,7 @@ module Ci
def process_stage_for_stage_scheduling(index)
current_status = status_for_prior_stages(index)
return unless HasStatus::COMPLETED_STATUSES.include?(current_status)
return unless Ci::HasStatus::COMPLETED_STATUSES.include?(current_status)
created_stage_scheduled_processables_in_stage(index).find_each.select do |build|
process_build(build, current_status)
@ -73,7 +73,7 @@ module Ci
def process_dag_build_with_needs(build)
current_status = status_for_build_needs(build.needs.map(&:name))
return unless HasStatus::COMPLETED_STATUSES.include?(current_status)
return unless Ci::HasStatus::COMPLETED_STATUSES.include?(current_status)
process_build(build, current_status)
end

View File

@ -93,25 +93,25 @@ module Projects
old_repository_storage = project.repository_storage
new_project_path = moved_path(project.disk_path)
# Notice that the block passed to `run_after_commit` will run with `project`
# Notice that the block passed to `run_after_commit` will run with `repository_storage_move`
# as its context
project.run_after_commit do
repository_storage_move.run_after_commit do
GitlabShellWorker.perform_async(:mv_repository,
old_repository_storage,
disk_path,
project.disk_path,
new_project_path)
if wiki.repository_exists?
if project.wiki.repository_exists?
GitlabShellWorker.perform_async(:mv_repository,
old_repository_storage,
wiki.disk_path,
project.wiki.disk_path,
"#{new_project_path}.wiki")
end
if design_repository.exists?
if project.design_repository.exists?
GitlabShellWorker.perform_async(:mv_repository,
old_repository_storage,
design_repository.disk_path,
project.design_repository.disk_path,
"#{new_project_path}.design")
end
end

View File

@ -85,8 +85,6 @@ module Users
# remove - The IDs of the authorization rows to remove.
# add - Rows to insert in the form `[user id, project id, access level]`
def update_authorizations(remove = [], add = [])
return if remove.empty? && add.empty?
User.transaction do
user.remove_project_authorizations(remove) unless remove.empty?
ProjectAuthorization.insert_authorizations(add) unless add.empty?

View File

@ -100,7 +100,7 @@
.hint
= parsed_with_gfm
.prepend-top-default.append-bottom-default
.prepend-top-default.gl-mb-3
= f.submit 'Update appearance settings', class: 'btn btn-success'
- if @appearance.persisted? || @appearance.updated_at
.mt-4

View File

@ -32,6 +32,15 @@
= 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 set up Two-factor authentication
.form-group
= f.label :unknown_sign_in, _('Email notification for unknown sign-ins'), class: 'label-bold'
.form-check
= f.check_box :notify_on_unknown_sign_in, class: 'form-check-input'
= f.label :notify_on_unknown_sign_in, class: 'form-check-label' do
= _('Notify users by email when sign-in location is not recognized')
= link_to icon('question-circle'),
'https://docs.gitlab.com/ee/user/profile/unknown_sign_in_notification.html',
target: '_blank'
.form-group
= 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'

View File

@ -1,7 +1,7 @@
- page_title _("Groups")
.top-area
.prepend-top-default.append-bottom-default
.prepend-top-default.gl-mb-3
= form_tag admin_groups_path, method: :get, class: 'js-search-form' do |f|
= hidden_field_tag :sort, @sort
.search-holder

View File

@ -1,4 +1,4 @@
.row.prepend-top-default.append-bottom-default
.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0
Recent Deliveries

View File

@ -5,7 +5,7 @@
.col-lg-3
= render 'shared/web_hooks/title_and_docs', hook: @hook
.col-lg-9.append-bottom-default
.col-lg-9.gl-mb-3
= form_for @hook, as: :hook, url: admin_hook_path do |f|
= render partial: 'form', locals: { form: f, hook: @hook }
.form-actions

View File

@ -4,7 +4,7 @@
.col-lg-4
= render 'shared/web_hooks/title_and_docs', hook: @hook
.col-lg-8.append-bottom-default
.col-lg-8.gl-mb-3
= form_for @hook, as: :hook, url: admin_hooks_path do |f|
= render partial: 'form', locals: { form: f, hook: @hook }
= f.submit _('Add system hook'), class: 'btn btn-success'

View File

@ -28,4 +28,4 @@
= link_to "Identities", admin_user_identities_path(@user)
= nav_link(controller: :impersonation_tokens) do
= link_to "Impersonation Tokens", admin_user_impersonation_tokens_path(@user)
.append-bottom-default
.gl-mb-3

View File

@ -1,5 +1,5 @@
- link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert', data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } }
.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.gl-mb-3{ role: 'alert', data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } }
%button.close.js-close{ type: "button" } &times;
.gcp-signup-offer--content
.gcp-signup-offer--icon.gl-mr-3

View File

@ -49,7 +49,7 @@
- else
.settings-message.text-center
= _("You don't have any applications")
.oauth-authorized-applications.prepend-top-20.append-bottom-default
.oauth-authorized-applications.prepend-top-20.gl-mb-3
- if user_oauth_applications?
%h5
= _("Authorized applications (%{size})") % { size: @authorized_apps.size + @authorized_anonymous_tokens.size }

View File

@ -5,7 +5,7 @@
%p= s_('Check the %{docs_link_start}documentation%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: '</a>'.html_safe }
.form-group.append-bottom-default
.form-group.gl-mb-3
.form-check
= f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input', data: { qa_selector: 'lfs_checkbox' }
= f.label :lfs_enabled, class: 'form-check-label' do

View File

@ -7,7 +7,7 @@
.form-group
= render 'shared/allow_request_access', form: f
.form-group.append-bottom-default
.form-group.gl-mb-3
.form-check
= f.check_box :share_with_group_lock, disabled: !can_change_share_with_group_lock?(@group), class: 'form-check-input'
= f.label :share_with_group_lock, class: 'form-check-label' do
@ -16,14 +16,14 @@
= s_('GroupSettings|Prevent sharing a project within %{group} with other groups').html_safe % { group: group_link }
%span.js-descr.text-muted= share_with_group_lock_help_text(@group)
.form-group.append-bottom-default
.form-group.gl-mb-3
.form-check
= f.check_box :emails_disabled, checked: @group.emails_disabled?, disabled: !can_disable_group_emails?(@group), class: 'form-check-input'
= f.label :emails_disabled, class: 'form-check-label' do
%span.d-block= s_('GroupSettings|Disable email notifications')
%span.text-muted= s_('GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects.')
.form-group.append-bottom-default
.form-group.gl-mb-3
.form-check
= f.check_box :mentions_disabled, checked: @group.mentions_disabled?, class: 'form-check-input'
= f.label :mentions_disabled, class: 'form-check-label' do

View File

@ -72,4 +72,4 @@
- else
%p
= s_("Profiles|You don't have access to delete this user.")
.append-bottom-default
.gl-mb-3

View File

@ -8,7 +8,7 @@
%p
= _('This is a list of devices that have logged into your account. Revoke any sessions that you do not recognize.')
.col-lg-8
.append-bottom-default
.gl-mb-3
.card.border-0
%ul.list-group.list-group-flush

View File

@ -19,7 +19,7 @@
%hr
%h4.gl-mt-0
= _('Linked emails (%{email_count})') % { email_count: @emails.load.size + 1 }
.account-well.append-bottom-default
.account-well.gl-mb-3
%ul
%li
= _('Your Primary Email will be used for avatar detection.')

View File

@ -17,5 +17,5 @@
%hr
%h5
= _('Your GPG keys (%{count})') % { count:@gpg_keys.count}
.append-bottom-default
.gl-mb-3
= render 'key_table'

View File

@ -20,5 +20,5 @@
%hr
%h5
= _('Your SSH keys (%{count})') % { count:@keys.count }
.append-bottom-default
.gl-mb-3
= render 'key_table'

View File

@ -47,7 +47,7 @@
= _('Projects (%{count})') % { count: @project_notifications.size }
%p.account-well
= _('To specify the notification level per project of a group you belong to, you need to visit project page and change notification level there.')
.append-bottom-default
.gl-mb-3
%ul.bordered-list
- @project_notifications.each do |setting|
= render 'project_settings', setting: setting, project: setting.source

View File

@ -29,7 +29,7 @@
.form-group
= f.label :password_confirmation, _('Password confirmation'), class: 'label-bold'
= f.password_field :password_confirmation, required: true, class: 'form-control', data: { qa_selector: 'confirm_password_field' }
.prepend-top-default.append-bottom-default
.prepend-top-default.gl-mb-3
= f.submit _('Save password'), class: "btn btn-success append-right-10", data: { qa_selector: 'save_password_button' }
- unless @user.password_automatically_set?
= link_to _('I forgot my password'), reset_profile_password_path, method: :put

View File

@ -25,7 +25,7 @@
.md
= brand_profile_image_guidelines
.col-lg-8
.clearfix.avatar-image.append-bottom-default
.clearfix.avatar-image.gl-mb-3
= link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
= image_tag avatar_icon_for_user(@user, 160), alt: '', class: 'avatar s160'
%h5.gl-mt-0= s_("Profiles|Upload new avatar")
@ -118,7 +118,7 @@
= f.check_box :include_private_contributions, label: s_('Profiles|Include private contributions on my profile'), wrapper_class: 'mb-2', inline: true
.help-block
= s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information")
.prepend-top-default.append-bottom-default
.prepend-top-default.gl-mb-3
= f.submit s_("Profiles|Update profile settings"), class: 'btn btn-success'
= link_to _("Cancel"), user_path(current_user), class: 'btn btn-cancel'

View File

@ -18,7 +18,7 @@
= render 'shared/commit_well', commit: commit, ref: ref, project: project
- if is_project_overview
.project-buttons.append-bottom-default{ class: ("js-show-on-project-root" if vue_file_list_enabled?) }
.project-buttons.gl-mb-3{ class: ("js-show-on-project-root" if vue_file_list_enabled?) }
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
- if vue_file_list_enabled?

View File

@ -1,6 +1,6 @@
- if @wiki_home.present?
%div{ class: container_class }
.md.prepend-top-default.append-bottom-default
.md.prepend-top-default.gl-mb-3
= render_wiki_content(@wiki_home)
- else
- can_create_wiki = can?(current_user, :create_wiki, @project)

View File

@ -2,7 +2,7 @@
- file_name = params[:id].split("/").last ||= ""
- is_markdown = Gitlab::MarkupHelper.gitlab_markdown?(file_name)
.file-holder-bottom-radius.file-holder.file.append-bottom-default
.file-holder-bottom-radius.file-holder.file.gl-mb-3
.js-file-title.file-title.align-items-center.clearfix{ data: { current_action: action } }
.editor-ref.block-truncated
= sprite_icon('fork', size: 12)

View File

@ -1,2 +1,2 @@
.text-center.prepend-top-default.append-bottom-default
.text-center.prepend-top-default.gl-mb-3
= icon('spinner spin 2x', 'aria-hidden' => 'true', 'aria-label' => 'Loading content…', class: 'qa-spinner')

View File

@ -1,3 +1,3 @@
.file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } }
.js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
.js-loading-icon.text-center.prepend-top-default.gl-mb-3.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
= icon('spinner spin 2x', 'aria-hidden' => 'true');

View File

@ -1,7 +1,7 @@
.file-content.is-stl-loading
.text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } }
= icon('spinner spin 2x', class: 'prepend-top-default append-bottom-default', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
.text-center.prepend-top-default.append-bottom-default.stl-controls
= icon('spinner spin 2x', class: 'prepend-top-default gl-mb-3', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
.text-center.prepend-top-default.gl-mb-3.stl-controls
.btn-group
%button.btn.btn-default.btn-sm.js-material-changer{ data: { type: 'wireframe' } }
Wireframe

View File

@ -1,4 +1,4 @@
.row.prepend-top-default.append-bottom-default
.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0
= _("Environments")

View File

@ -2,7 +2,7 @@
- can_create_project = current_user.can?(:create_projects, namespace)
- if forked_project = namespace.find_fork_of(@project)
.bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.append-bottom-default.forked
.bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.gl-mb-3.forked
= link_to project_path(forked_project) do
- if /no_((\w*)_)*avatar/.match(avatar)
= group_icon(namespace, class: "avatar rect-avatar s100 identicon mx-auto")
@ -12,7 +12,7 @@
%h5.prepend-top-default
= namespace.human_name
- else
.bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.append-bottom-default{ class: ("disabled" unless can_create_project) }
.bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.gl-mb-3{ class: ("disabled" unless can_create_project) }
= link_to project_forks_path(@project, namespace_key: namespace.id),
method: "POST",
class: ("disabled has-tooltip" unless can_create_project),

View File

@ -1,4 +1,4 @@
.row.gl-mt-7.append-bottom-default
.row.gl-mt-7.gl-mb-3
.col-lg-3
%h4.gl-mt-0
Recent Deliveries

View File

@ -2,7 +2,7 @@
- add_to_breadcrumbs _('Webhook Settings'), namespace_project_hooks_path
- page_title _('Webhook Logs')
.row.prepend-top-default.append-bottom-default
.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0
Request details

View File

@ -6,7 +6,7 @@
.col-lg-3
= render 'shared/web_hooks/title_and_docs', hook: @hook
.col-lg-9.append-bottom-default
.col-lg-9.gl-mb-3
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: project_hook_path(@project, @hook) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }

View File

@ -6,7 +6,7 @@
.col-lg-4
= render 'shared/web_hooks/title_and_docs', hook: @hook
.col-lg-8.append-bottom-default
.col-lg-8.gl-mb-3
= form_for @hook, as: :hook, url: polymorphic_path([@project.namespace.becomes(Namespace), @project, :hooks]) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Add webhook', class: 'btn btn-success'

View File

@ -1,6 +1,6 @@
The subject will be used as the title of the new issue, and the message will be the description.
= link_to 'Quick actions', help_page_path('user/project/quick_actions'), target: '_blank', tabindex: -1
= link_to 'Quick actions', help_page_path('user/project/quick_actions'), target: '_blank'
and styling with
= link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1
= link_to 'Markdown', help_page_path('user/markdown'), target: '_blank'
are supported.

View File

@ -1,4 +1,4 @@
.account-well.prepend-top-default.append-bottom-default
.account-well.prepend-top-default.gl-mb-3
%ul
%li
= _('The repository must be accessible over <code>http://</code>,

View File

@ -1,5 +1,5 @@
- grouped_statuses = @stage.statuses.latest_ordered.group_by(&:status)
- HasStatus::ORDERED_STATUSES.each do |ordered_status|
- Ci::HasStatus::ORDERED_STATUSES.each do |ordered_status|
- grouped_statuses.fetch(ordered_status, []).each do |status|
%li
= render 'ci/status/dropdown_graph_badge', subject: status

View File

@ -1,6 +1,6 @@
- page_title @protected_ref.name, _("Protected Branches")
.row.prepend-top-default.append-bottom-default
.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0.ref-name
= @protected_ref.name

View File

@ -1,6 +1,6 @@
- page_title @protected_ref.name, _("Protected Tags")
.row.prepend-top-default.append-bottom-default
.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0.ref-name
= @protected_ref.name

View File

@ -1,4 +1,4 @@
.row.prepend-top-default.append-bottom-default
.row.prepend-top-default.gl-mb-3
.col-lg-4
%h4.gl-mt-0
= @service.title

View File

@ -1,7 +1,7 @@
- if @project
= render 'projects/services/prometheus/configuration_banner', project: @project, service: @service
%h4.append-bottom-default
%h4.gl-mb-3
= s_('PrometheusService|Manual configuration')
%p
= s_('PrometheusService|Select the Active checkbox to override the Auto Configuration with custom settings. If unchecked, Auto Configuration settings are used.')

View File

@ -3,7 +3,7 @@
%h4.gl-mt-0
= s_('PrometheusService|Metrics')
.row.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring
.row.gl-mb-3.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring
= render 'projects/services/prometheus/metrics', project: @project
= render 'projects/services/prometheus/external_alerts', project: @project

View File

@ -11,7 +11,7 @@
- if @project
= render 'projects/settings/operations/configuration_banner', project: @project, service: service
%b.append-bottom-default
%b.gl-mb-3
= s_('PrometheusService|Manual configuration')
%p
= s_('PrometheusService|Select the Active checkbox to override the Auto Configuration with custom settings. If unchecked, Auto Configuration settings are used.')

View File

@ -59,7 +59,7 @@
%pre.wrap{ data: { qa_selector: 'tag_message_content' } }
= strip_signature(@tag.message)
.append-bottom-default.prepend-top-default
.gl-mb-3.prepend-top-default
- if @release.description.present?
.description.md{ data: { qa_selector: 'tag_release_notes_content' } }
= markdown_field(@release, :description)

View File

@ -1,4 +1,4 @@
.row.prepend-top-default.append-bottom-default.triggers-container
.row.prepend-top-default.gl-mb-3.triggers-container
.col-lg-12
.card
.card-header
@ -21,7 +21,7 @@
%th
= render partial: 'projects/triggers/trigger', collection: @triggers, as: :trigger
- else
%p.settings-message.text-center.append-bottom-default
%p.settings-message.text-center.gl-mb-3
No triggers have been created yet. Add one using the form above.
.card-footer

View File

@ -1,6 +1,6 @@
- page_title _("Trigger")
.row.prepend-top-default.append-bottom-default
.row.prepend-top-default.gl-mb-3
.col-lg-12
%h4.gl-mt-0
Update trigger

View File

@ -1,4 +1,4 @@
.info-well.d-none.d-sm-block.project-last-commit.append-bottom-default
.info-well.d-none.d-sm-block.project-last-commit.gl-mb-3
.well-segment
%ul.blob-commit-info
= render 'projects/commits/commit', commit: commit, ref: ref, project: project

View File

@ -11,10 +11,10 @@
.md-header
%ul.nav.nav-tabs.nav-links.clearfix
%li.md-header-tab.active
%button.js-md-write-button{ tabindex: -1 }
%button.js-md-write-button
= _("Write")
%li.md-header-tab
%button.js-md-preview-button{ tabindex: -1 }
%button.js-md-preview-button
= _("Preview")
%li.md-header-toolbar.active

Some files were not shown because too many files have changed in this diff Show More