Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e860bae967
commit
1e254d9f5a
|
|
@ -16,6 +16,7 @@ export const getMergeRequestsForBranch = (
|
|||
.getProjectMergeRequests(`${projectId}`, {
|
||||
source_branch: branchId,
|
||||
source_project_id: state.projects[projectId].id,
|
||||
state: 'opened',
|
||||
order_by: 'created_at',
|
||||
per_page: 1,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -353,7 +353,10 @@ export default {
|
|||
</gl-deprecated-button>
|
||||
</div>
|
||||
|
||||
<div v-if="externalDashboardUrl.length" class="mb-2 mr-2 d-flex d-sm-block">
|
||||
<div
|
||||
v-if="externalDashboardUrl && externalDashboardUrl.length"
|
||||
class="mb-2 mr-2 d-flex d-sm-block"
|
||||
>
|
||||
<gl-deprecated-button
|
||||
class="flex-grow-1 js-external-dashboard-link"
|
||||
variant="primary"
|
||||
|
|
|
|||
|
|
@ -208,6 +208,14 @@ export const annotationsSymbolIcon = 'path://m5 229 5 8h-10z';
|
|||
*/
|
||||
export const DEFAULT_DASHBOARD_PATH = 'config/prometheus/common_metrics.yml';
|
||||
|
||||
/**
|
||||
* GitLab provide metrics dashboards that are available to a user once
|
||||
* the Prometheus managed app has been installed, without any extra setup
|
||||
* required. These "out of the box" dashboards are defined under the
|
||||
* `config/prometheus` path.
|
||||
*/
|
||||
export const OUT_OF_THE_BOX_DASHBOARDS_PATH_PREFIX = 'config/prometheus/';
|
||||
|
||||
export const OPERATORS = {
|
||||
greaterThan: '>',
|
||||
equalTo: '==',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import Vue from 'vue';
|
||||
import { GlToast } from '@gitlab/ui';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { getParameterValues } from '~/lib/utils/url_utility';
|
||||
import { createStore } from './stores';
|
||||
import createRouter from './router';
|
||||
import { stateAndPropsFromDataset } from './utils';
|
||||
|
||||
Vue.use(GlToast);
|
||||
|
||||
|
|
@ -12,37 +12,10 @@ export default (props = {}) => {
|
|||
|
||||
if (el && el.dataset) {
|
||||
const [currentDashboard] = getParameterValues('dashboard');
|
||||
const { metricsDashboardBasePath, ...dataset } = el.dataset;
|
||||
|
||||
const {
|
||||
deploymentsEndpoint,
|
||||
dashboardEndpoint,
|
||||
dashboardsEndpoint,
|
||||
projectPath,
|
||||
logsPath,
|
||||
currentEnvironmentName,
|
||||
dashboardTimezone,
|
||||
metricsDashboardBasePath,
|
||||
customDashboardBasePath,
|
||||
...dataProps
|
||||
} = el.dataset;
|
||||
|
||||
const store = createStore({
|
||||
currentDashboard,
|
||||
deploymentsEndpoint,
|
||||
dashboardEndpoint,
|
||||
dashboardsEndpoint,
|
||||
dashboardTimezone,
|
||||
projectPath,
|
||||
logsPath,
|
||||
currentEnvironmentName,
|
||||
customDashboardBasePath,
|
||||
});
|
||||
|
||||
// HTML attributes are always strings, parse other types.
|
||||
dataProps.hasMetrics = parseBoolean(dataProps.hasMetrics);
|
||||
dataProps.customMetricsAvailable = parseBoolean(dataProps.customMetricsAvailable);
|
||||
dataProps.prometheusAlertsAvailable = parseBoolean(dataProps.prometheusAlertsAvailable);
|
||||
|
||||
const { initState, dataProps } = stateAndPropsFromDataset({ currentDashboard, ...dataset });
|
||||
const store = createStore(initState);
|
||||
const router = createRouter(metricsDashboardBasePath);
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { mergeURLVariables, parseTemplatingVariables } from './variable_mapping'
|
|||
import { DATETIME_RANGE_TYPES } from '~/lib/utils/constants';
|
||||
import { timeRangeToParams, getRangeType } from '~/lib/utils/datetime_range';
|
||||
import { isSafeURL, mergeUrlParams } from '~/lib/utils/url_utility';
|
||||
import { NOT_IN_DB_PREFIX, linkTypes, DEFAULT_DASHBOARD_PATH } from '../constants';
|
||||
import { NOT_IN_DB_PREFIX, linkTypes, OUT_OF_THE_BOX_DASHBOARDS_PATH_PREFIX } from '../constants';
|
||||
|
||||
export const gqClient = createGqClient(
|
||||
{},
|
||||
|
|
@ -479,7 +479,7 @@ export const normalizeCustomDashboardPath = (dashboard, dashboardPrefix = '') =>
|
|||
dashboardPath = '';
|
||||
} else if (
|
||||
currDashboard.startsWith(dashboardPrefix) ||
|
||||
currDashboard.startsWith(DEFAULT_DASHBOARD_PATH)
|
||||
currDashboard.startsWith(OUT_OF_THE_BOX_DASHBOARDS_PATH_PREFIX)
|
||||
) {
|
||||
dashboardPath = currDashboard;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
removeParams,
|
||||
updateHistory,
|
||||
} from '~/lib/utils/url_utility';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import {
|
||||
timeRangeParamNames,
|
||||
timeRangeFromParams,
|
||||
|
|
@ -12,6 +13,46 @@ import {
|
|||
} from '~/lib/utils/datetime_range';
|
||||
import { VARIABLE_PREFIX } from './constants';
|
||||
|
||||
/**
|
||||
* Extracts the initial state and props from HTML dataset
|
||||
* and places them in separate objects to setup bundle.
|
||||
* @param {*} dataset
|
||||
*/
|
||||
export const stateAndPropsFromDataset = (dataset = {}) => {
|
||||
const {
|
||||
currentDashboard,
|
||||
deploymentsEndpoint,
|
||||
dashboardEndpoint,
|
||||
dashboardsEndpoint,
|
||||
dashboardTimezone,
|
||||
projectPath,
|
||||
logsPath,
|
||||
currentEnvironmentName,
|
||||
customDashboardBasePath,
|
||||
...dataProps
|
||||
} = dataset;
|
||||
|
||||
// HTML attributes are always strings, parse other types.
|
||||
dataProps.hasMetrics = parseBoolean(dataProps.hasMetrics);
|
||||
dataProps.customMetricsAvailable = parseBoolean(dataProps.customMetricsAvailable);
|
||||
dataProps.prometheusAlertsAvailable = parseBoolean(dataProps.prometheusAlertsAvailable);
|
||||
|
||||
return {
|
||||
initState: {
|
||||
currentDashboard,
|
||||
deploymentsEndpoint,
|
||||
dashboardEndpoint,
|
||||
dashboardsEndpoint,
|
||||
dashboardTimezone,
|
||||
projectPath,
|
||||
logsPath,
|
||||
currentEnvironmentName,
|
||||
customDashboardBasePath,
|
||||
},
|
||||
dataProps,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* List of non time range url parameters
|
||||
* This will be removed once we add support for free text variables
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@ export default {
|
|||
required: false,
|
||||
default: false,
|
||||
},
|
||||
requirementsAvailable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
visibilityHelpPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
|
@ -131,6 +136,7 @@ export default {
|
|||
snippetsAccessLevel: featureAccessLevel.EVERYONE,
|
||||
pagesAccessLevel: featureAccessLevel.EVERYONE,
|
||||
metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
|
||||
requirementsAccessLevel: featureAccessLevel.EVERYONE,
|
||||
containerRegistryEnabled: true,
|
||||
lfsEnabled: true,
|
||||
requestAccessEnabled: true,
|
||||
|
|
@ -233,6 +239,10 @@ export default {
|
|||
featureAccessLevel.PROJECT_MEMBERS,
|
||||
this.metricsDashboardAccessLevel,
|
||||
);
|
||||
this.requirementsAccessLevel = Math.min(
|
||||
featureAccessLevel.PROJECT_MEMBERS,
|
||||
this.requirementsAccessLevel,
|
||||
);
|
||||
if (this.pagesAccessLevel === featureAccessLevel.EVERYONE) {
|
||||
// When from Internal->Private narrow access for only members
|
||||
this.pagesAccessLevel = featureAccessLevel.PROJECT_MEMBERS;
|
||||
|
|
@ -256,6 +266,9 @@ export default {
|
|||
this.pagesAccessLevel = featureAccessLevel.EVERYONE;
|
||||
if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
|
||||
this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE;
|
||||
if (this.requirementsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
|
||||
this.requirementsAccessLevel = featureAccessLevel.EVERYONE;
|
||||
|
||||
this.highlightChanges();
|
||||
}
|
||||
},
|
||||
|
|
@ -470,6 +483,18 @@ export default {
|
|||
/>
|
||||
</project-setting-row>
|
||||
</div>
|
||||
<project-setting-row
|
||||
v-if="requirementsAvailable"
|
||||
ref="requirements-settings"
|
||||
:label="s__('ProjectSettings|Requirements')"
|
||||
:help-text="s__('ProjectSettings|Requirements management system for this project')"
|
||||
>
|
||||
<project-feature-setting
|
||||
v-model="requirementsAccessLevel"
|
||||
:options="featureAccessLevelOptions"
|
||||
name="project[project_feature_attributes][requirements_access_level]"
|
||||
/>
|
||||
</project-setting-row>
|
||||
<project-setting-row
|
||||
ref="wiki-settings"
|
||||
:label="s__('ProjectSettings|Wiki')"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
packagesEnabled: false,
|
||||
requirementsEnabled: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
|
|
|||
|
|
@ -356,6 +356,20 @@ class ProjectsController < Projects::ApplicationController
|
|||
.merge(import_url_params)
|
||||
end
|
||||
|
||||
def project_feature_attributes
|
||||
%i[
|
||||
builds_access_level
|
||||
issues_access_level
|
||||
forking_access_level
|
||||
merge_requests_access_level
|
||||
repository_access_level
|
||||
snippets_access_level
|
||||
wiki_access_level
|
||||
pages_access_level
|
||||
metrics_dashboard_access_level
|
||||
]
|
||||
end
|
||||
|
||||
def project_params_attributes
|
||||
[
|
||||
:allow_merge_on_skipped_pipeline,
|
||||
|
|
@ -391,22 +405,10 @@ class ProjectsController < Projects::ApplicationController
|
|||
:initialize_with_readme,
|
||||
:autoclose_referenced_issues,
|
||||
:suggestion_commit_message,
|
||||
|
||||
project_feature_attributes: %i[
|
||||
builds_access_level
|
||||
issues_access_level
|
||||
forking_access_level
|
||||
merge_requests_access_level
|
||||
repository_access_level
|
||||
snippets_access_level
|
||||
wiki_access_level
|
||||
pages_access_level
|
||||
metrics_dashboard_access_level
|
||||
],
|
||||
project_setting_attributes: %i[
|
||||
show_default_award_emojis
|
||||
]
|
||||
]
|
||||
] + [project_feature_attributes: project_feature_attributes]
|
||||
end
|
||||
|
||||
def project_params_create_attributes
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ module Types
|
|||
description: 'Indicates if Large File Storage (LFS) is enabled for namespace'
|
||||
field :request_access_enabled, GraphQL::BOOLEAN_TYPE, null: true,
|
||||
description: 'Indicates if users can request access to namespace'
|
||||
field :storage_size_limit, GraphQL::INT_TYPE, null: true,
|
||||
field :storage_size_limit, GraphQL::FLOAT_TYPE, null: true,
|
||||
description: 'Total storage limit of the root namespace in bytes',
|
||||
resolve: -> (obj, _args, _ctx) { Namespace::RootStorageSize.new(obj).limit }
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@ module StorageHelper
|
|||
counter_repositories: storage_counter(statistics.repository_size),
|
||||
counter_wikis: storage_counter(statistics.wiki_size),
|
||||
counter_build_artifacts: storage_counter(statistics.build_artifacts_size),
|
||||
counter_lfs_objects: storage_counter(statistics.lfs_objects_size)
|
||||
counter_lfs_objects: storage_counter(statistics.lfs_objects_size),
|
||||
counter_snippets: storage_counter(statistics.snippets_size)
|
||||
}
|
||||
|
||||
_("Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects}") % counters
|
||||
_("Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}") % counters
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -566,7 +566,7 @@ module Ci
|
|||
next unless project.repository_exists?
|
||||
|
||||
project.repository.list_commits_by_ref_name(refs).then do |commits|
|
||||
loader.call(ref, commits[ref])
|
||||
commits.each { |key, commit| loader.call(key, commits[key]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ module Featurable
|
|||
|
||||
class_methods do
|
||||
def set_available_features(available_features = [])
|
||||
@available_features = available_features
|
||||
@available_features ||= []
|
||||
@available_features += available_features
|
||||
|
||||
class_eval do
|
||||
available_features.each do |feature|
|
||||
|
|
|
|||
|
|
@ -88,3 +88,5 @@ module ProjectFeaturesCompatibility
|
|||
project_feature.__send__(:write_attribute, field, value) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
end
|
||||
|
||||
ProjectsHelper.prepend_if_ee('EE::ProjectFeaturesCompatibility')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomEmoji < ApplicationRecord
|
||||
belongs_to :namespace, inverse_of: :custom_emoji
|
||||
|
||||
validate :valid_emoji_name
|
||||
|
||||
validates :namespace, presence: true
|
||||
validates :name,
|
||||
uniqueness: { scope: [:namespace_id, :name] },
|
||||
presence: true,
|
||||
length: { maximum: 36 },
|
||||
format: { with: /\A\w+\z/ }
|
||||
|
||||
private
|
||||
|
||||
def valid_emoji_name
|
||||
if Gitlab::Emoji.emoji_exists?(name)
|
||||
errors.add(:name, _('%{name} is already being used for another emoji') % { name: self.name })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -32,6 +32,7 @@ class Namespace < ApplicationRecord
|
|||
|
||||
belongs_to :parent, class_name: "Namespace"
|
||||
has_many :children, class_name: "Namespace", foreign_key: :parent_id
|
||||
has_many :custom_emoji, inverse_of: :namespace
|
||||
has_one :chat_team, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
|
||||
has_one :root_storage_statistics, class_name: 'Namespace::RootStorageStatistics'
|
||||
has_one :aggregation_schedule, class_name: 'Namespace::AggregationSchedule'
|
||||
|
|
|
|||
|
|
@ -79,8 +79,6 @@ module Projects
|
|||
full_path
|
||||
)
|
||||
|
||||
new_repository.create_repository
|
||||
|
||||
new_repository.replicate(raw_repository)
|
||||
new_checksum = new_repository.checksum
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ module ResourceAccessTokens
|
|||
return unless feature_enabled?
|
||||
return error("User does not have permission to create #{resource_type} Access Token") unless has_permission_to_create?
|
||||
|
||||
# We skip authorization by default, since the user creating the bot is not an admin
|
||||
# and project/group bot users are not created via sign-up
|
||||
user = create_user
|
||||
|
||||
return error(user.errors.full_messages.to_sentence) unless user.persisted?
|
||||
|
|
@ -49,6 +47,11 @@ module ResourceAccessTokens
|
|||
end
|
||||
|
||||
def create_user
|
||||
# Even project maintainers can create project access tokens, which in turn
|
||||
# creates a bot user, and so it becomes necessary to have `skip_authorization: true`
|
||||
# since someone like a project maintainer does not inherently have the ability
|
||||
# to create a new user in the system.
|
||||
|
||||
Users::CreateService.new(current_user, default_user_params).execute(skip_authorization: true)
|
||||
end
|
||||
|
||||
|
|
@ -57,7 +60,8 @@ module ResourceAccessTokens
|
|||
name: params[:name] || "#{resource.name.to_s.humanize} bot",
|
||||
email: generate_email,
|
||||
username: generate_username,
|
||||
user_type: "#{resource_type}_bot".to_sym
|
||||
user_type: "#{resource_type}_bot".to_sym,
|
||||
skip_confirmation: true # Bot users should always have their emails confirmed.
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Only show open Merge Requests in Web IDE
|
||||
merge_request: 35514
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Project bot users should always have their emails confirmed by default
|
||||
merge_request: 35498
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add requirements visibility/access project settings
|
||||
merge_request: 34420
|
||||
author: Lee Tickett
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add custom emoji model and database table
|
||||
merge_request: 24229
|
||||
author: Rajendra Kadam
|
||||
type: added
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use FLOAT_TYPE for storage limit
|
||||
merge_request: 35559
|
||||
author:
|
||||
type: fixed
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Use snapshot transfers for repository shard moves when possible
|
||||
merge_request: 34113
|
||||
author:
|
||||
type: performance
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#
|
||||
ActiveSupport::Inflector.inflections do |inflect|
|
||||
inflect.uncountable %w(
|
||||
custom_emoji
|
||||
award_emoji
|
||||
container_repository_registry
|
||||
design_registry
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
class CreateCustomEmojis < ActiveRecord::Migration[5.2]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
unless table_exists?(:custom_emoji)
|
||||
create_table :custom_emoji do |t|
|
||||
t.references :namespace, index: false, null: false, foreign_key: { on_delete: :cascade }
|
||||
t.datetime_with_timezone :created_at, null: false
|
||||
t.datetime_with_timezone :updated_at, null: false
|
||||
t.text :name, null: false
|
||||
t.text :file, null: false
|
||||
end
|
||||
end
|
||||
|
||||
unless index_exists?(:custom_emoji, [:namespace_id, :name], unique: true)
|
||||
add_index :custom_emoji, [:namespace_id, :name], unique: true
|
||||
end
|
||||
|
||||
add_text_limit(:custom_emoji, :name, 36)
|
||||
add_text_limit(:custom_emoji, :file, 255)
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table :custom_emoji
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddRequirementsAccessLevelToProjectFeatures < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
add_column :project_features, :requirements_access_level, :integer, default: 20, null: false
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_column :project_features, :requirements_access_level, :integer
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -10818,6 +10818,26 @@ CREATE SEQUENCE public.conversational_development_index_metrics_id_seq
|
|||
|
||||
ALTER SEQUENCE public.conversational_development_index_metrics_id_seq OWNED BY public.conversational_development_index_metrics.id;
|
||||
|
||||
CREATE TABLE public.custom_emoji (
|
||||
id bigint NOT NULL,
|
||||
namespace_id bigint NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
name text NOT NULL,
|
||||
file text NOT NULL,
|
||||
CONSTRAINT check_8c586dd507 CHECK ((char_length(name) <= 36)),
|
||||
CONSTRAINT check_dd5d60f1fb CHECK ((char_length(file) <= 255))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.custom_emoji_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE public.custom_emoji_id_seq OWNED BY public.custom_emoji.id;
|
||||
|
||||
CREATE TABLE public.dependency_proxy_blobs (
|
||||
id integer NOT NULL,
|
||||
group_id integer NOT NULL,
|
||||
|
|
@ -13956,7 +13976,8 @@ CREATE TABLE public.project_features (
|
|||
repository_access_level integer DEFAULT 20 NOT NULL,
|
||||
pages_access_level integer NOT NULL,
|
||||
forking_access_level integer,
|
||||
metrics_dashboard_access_level integer
|
||||
metrics_dashboard_access_level integer,
|
||||
requirements_access_level integer DEFAULT 20 NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.project_features_id_seq
|
||||
|
|
@ -16393,6 +16414,8 @@ ALTER TABLE ONLY public.container_repositories ALTER COLUMN id SET DEFAULT nextv
|
|||
|
||||
ALTER TABLE ONLY public.conversational_development_index_metrics ALTER COLUMN id SET DEFAULT nextval('public.conversational_development_index_metrics_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.custom_emoji ALTER COLUMN id SET DEFAULT nextval('public.custom_emoji_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.dependency_proxy_blobs ALTER COLUMN id SET DEFAULT nextval('public.dependency_proxy_blobs_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY public.dependency_proxy_group_settings ALTER COLUMN id SET DEFAULT nextval('public.dependency_proxy_group_settings_id_seq'::regclass);
|
||||
|
|
@ -17348,6 +17371,9 @@ ALTER TABLE ONLY public.container_repositories
|
|||
ALTER TABLE ONLY public.conversational_development_index_metrics
|
||||
ADD CONSTRAINT conversational_development_index_metrics_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY public.custom_emoji
|
||||
ADD CONSTRAINT custom_emoji_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY public.dependency_proxy_blobs
|
||||
ADD CONSTRAINT dependency_proxy_blobs_pkey PRIMARY KEY (id);
|
||||
|
||||
|
|
@ -18814,6 +18840,8 @@ CREATE UNIQUE INDEX index_container_repositories_on_project_id_and_name ON publi
|
|||
|
||||
CREATE INDEX index_container_repository_on_name_trigram ON public.container_repositories USING gin (name public.gin_trgm_ops);
|
||||
|
||||
CREATE UNIQUE INDEX index_custom_emoji_on_namespace_id_and_name ON public.custom_emoji USING btree (namespace_id, name);
|
||||
|
||||
CREATE UNIQUE INDEX index_daily_build_group_report_results_unique_columns ON public.ci_daily_build_group_report_results USING btree (project_id, ref_path, date, group_name);
|
||||
|
||||
CREATE UNIQUE INDEX index_daily_report_results_unique_columns ON public.ci_daily_report_results USING btree (project_id, ref_path, param_type, date, title);
|
||||
|
|
@ -21778,6 +21806,9 @@ ALTER TABLE ONLY public.project_custom_attributes
|
|||
ALTER TABLE ONLY public.slack_integrations
|
||||
ADD CONSTRAINT fk_rails_73db19721a FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY public.custom_emoji
|
||||
ADD CONSTRAINT fk_rails_745925b412 FOREIGN KEY (namespace_id) REFERENCES public.namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY public.merge_request_context_commit_diff_files
|
||||
ADD CONSTRAINT fk_rails_74a00a1787 FOREIGN KEY (merge_request_context_commit_id) REFERENCES public.merge_request_context_commits(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
@ -23073,6 +23104,7 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200227164113
|
||||
20200227165129
|
||||
20200228160542
|
||||
20200229171700
|
||||
20200302142052
|
||||
20200302152516
|
||||
20200303055348
|
||||
|
|
@ -23427,6 +23459,7 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200615121217
|
||||
20200615123055
|
||||
20200615193524
|
||||
20200615203153
|
||||
20200615232735
|
||||
20200615234047
|
||||
20200616145031
|
||||
|
|
|
|||
|
|
@ -19,11 +19,20 @@ for steps to test filesystem performance.
|
|||
|
||||
## Known kernel version incompatibilities
|
||||
|
||||
The following kernel versions should NOT be used with GitLab:
|
||||
RedHat Enterprise Linux (RHEL) and CentOS v7.7 and v7.8 ship with kernel
|
||||
version `3.10.0-1127`, which [contains a
|
||||
bug](https://bugzilla.redhat.com/show_bug.cgi?id=1783554) that causes
|
||||
[uploads to fail to copy over NFS](https://gitlab.com/gitlab-org/gitlab/-/issues/218999). The
|
||||
following GitLab versions include a fix to work properly with that
|
||||
kernel version:
|
||||
|
||||
|Distribution |Kernel Versions|Affected GitLab versions|Details|
|
||||
|-------------|---------------|------------------------|-------|
|
||||
|RHEL/CentOS|`3.10.0-1127*`|All prior to GitLab 13.1|[Uploads fail to copy over NFS](https://gitlab.com/gitlab-org/gitlab/-/issues/218999)|
|
||||
1. [12.10.12](https://about.gitlab.com/releases/2020/06/25/gitlab-12-10-12-released/)
|
||||
1. [13.0.7](https://about.gitlab.com/releases/2020/06/25/gitlab-13-0-7-released/)
|
||||
1. [13.1.1](https://about.gitlab.com/releases/2020/06/24/gitlab-13-1-1-released/)
|
||||
1. 13.2 and up
|
||||
|
||||
If you are using that kernel version, be sure to upgrade GitLab to avoid
|
||||
errors.
|
||||
|
||||
## NFS Server features
|
||||
|
||||
|
|
|
|||
|
|
@ -5236,7 +5236,7 @@ type Group {
|
|||
"""
|
||||
Total storage limit of the root namespace in bytes
|
||||
"""
|
||||
storageSizeLimit: Int
|
||||
storageSizeLimit: Float
|
||||
|
||||
"""
|
||||
The permission level required to create subgroups within the group
|
||||
|
|
@ -8081,7 +8081,7 @@ type Namespace {
|
|||
"""
|
||||
Total storage limit of the root namespace in bytes
|
||||
"""
|
||||
storageSizeLimit: Int
|
||||
storageSizeLimit: Float
|
||||
|
||||
"""
|
||||
Visibility of the namespace
|
||||
|
|
|
|||
|
|
@ -14407,7 +14407,7 @@
|
|||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"name": "Float",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
|
|
@ -24094,7 +24094,7 @@
|
|||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"name": "Float",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
|
|
|
|||
|
|
@ -804,7 +804,7 @@ Autogenerated return type of EpicTreeReorder
|
|||
| `requireTwoFactorAuthentication` | Boolean | Indicates if all users in this group are required to set up two-factor authentication |
|
||||
| `rootStorageStatistics` | RootStorageStatistics | Aggregated storage statistics of the namespace. Only available for root namespaces |
|
||||
| `shareWithGroupLock` | Boolean | Indicates if sharing a project with another group within this group is prevented |
|
||||
| `storageSizeLimit` | Int | Total storage limit of the root namespace in bytes |
|
||||
| `storageSizeLimit` | Float | Total storage limit of the root namespace in bytes |
|
||||
| `subgroupCreationLevel` | String | The permission level required to create subgroups within the group |
|
||||
| `twoFactorGracePeriod` | Int | Time before two-factor authentication is enforced |
|
||||
| `userPermissions` | GroupPermissions! | Permissions for the current user on the resource |
|
||||
|
|
@ -1239,7 +1239,7 @@ Contains statistics about a milestone
|
|||
| `path` | String! | Path of the namespace |
|
||||
| `requestAccessEnabled` | Boolean | Indicates if users can request access to namespace |
|
||||
| `rootStorageStatistics` | RootStorageStatistics | Aggregated storage statistics of the namespace. Only available for root namespaces |
|
||||
| `storageSizeLimit` | Int | Total storage limit of the root namespace in bytes |
|
||||
| `storageSizeLimit` | Float | Total storage limit of the root namespace in bytes |
|
||||
| `visibility` | String | Visibility of the namespace |
|
||||
|
||||
## Note
|
||||
|
|
|
|||
|
|
@ -1048,6 +1048,7 @@ POST /projects
|
|||
| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||
| `pages_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
|
||||
| `requirements_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
|
||||
| `emails_disabled` | boolean | no | Disable email notifications |
|
||||
| `show_default_award_emojis` | boolean | no | Show default award emojis |
|
||||
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
|
||||
|
|
@ -1119,6 +1120,7 @@ POST /projects/user/:user_id
|
|||
| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||
| `pages_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
|
||||
| `requirements_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
|
||||
| `emails_disabled` | boolean | no | Disable email notifications |
|
||||
| `show_default_award_emojis` | boolean | no | Show default award emojis |
|
||||
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
|
||||
|
|
@ -1189,6 +1191,7 @@ PUT /projects/:id
|
|||
| `wiki_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||
| `snippets_access_level` | string | no | One of `disabled`, `private` or `enabled` |
|
||||
| `pages_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
|
||||
| `requirements_access_level` | string | no | One of `disabled`, `private`, `enabled` or `public` |
|
||||
| `emails_disabled` | boolean | no | Disable email notifications |
|
||||
| `show_default_award_emojis` | boolean | no | Show default award emojis |
|
||||
| `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ You can add a command to your `.gitlab-ci.yml` file to
|
|||
| `CI_PAGES_URL` | 11.8 | all | URL to GitLab Pages-built pages. Always belongs to a subdomain of `CI_PAGES_DOMAIN`. |
|
||||
| `CI_PIPELINE_ID` | 8.10 | all | The unique ID of the current pipeline that GitLab CI/CD uses internally |
|
||||
| `CI_PIPELINE_IID` | 11.0 | all | The unique ID of the current pipeline scoped to project |
|
||||
| `CI_PIPELINE_SOURCE` | 10.0 | all | Indicates how the pipeline was triggered. Possible options are: `push`, `web`, `trigger`, `schedule`, `api`, `pipeline`, `parent_pipeline`, `external`, `chat`, `merge_request_event`, and `external_pull_request_event`. For pipelines created before GitLab 9.5, this will show as `unknown` |
|
||||
| `CI_PIPELINE_SOURCE` | 10.0 | all | Indicates how the pipeline was triggered. Possible options are: `push`, `web`, `trigger`, `schedule`, `api`, `external`, `pipeline` (renamed to `cross_project_pipeline` since 13.0), `chat`, `webide`, `merge_request_event`, `external_pull_request_event`, and `parent_pipeline`. For pipelines created before GitLab 9.5, this will show as `unknown` |
|
||||
| `CI_PIPELINE_TRIGGERED` | all | all | The flag to indicate that job was [triggered](../triggers/README.md) |
|
||||
| `CI_PIPELINE_URL` | 11.1 | 0.5 | Pipeline details URL |
|
||||
| `CI_PROJECT_DIR` | all | all | The full path where the repository is cloned and where the job is run. If the GitLab Runner `builds_dir` parameter is set, this variable is set relative to the value of `builds_dir`. For more information, see [Advanced configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) for GitLab Runner. |
|
||||
|
|
|
|||
|
|
@ -813,6 +813,40 @@ file which is used by the `spec/fast_spec_helper.rb` file. See
|
|||
[Fast unit tests](#fast-unit-tests) for more details about the
|
||||
`spec/fast_spec_helper.rb` file.
|
||||
|
||||
### Test environment logging
|
||||
|
||||
Services for the test environment are automatically configured and started when
|
||||
tests are run, including Gitaly, Workhorse, Elasticsearch, and Capybara. When run in CI, or
|
||||
if the service needs to be installed, the test environment will log information
|
||||
about set-up time, producing log messages like the following:
|
||||
|
||||
```plaintext
|
||||
==> Setting up Gitaly...
|
||||
Gitaly set up in 31.459649 seconds...
|
||||
|
||||
==> Setting up GitLab Workhorse...
|
||||
GitLab Workhorse set up in 29.695619 seconds...
|
||||
fatal: update refs/heads/diff-files-symlink-to-image: invalid <newvalue>: 8cfca84
|
||||
From https://gitlab.com/gitlab-org/gitlab-test
|
||||
* [new branch] diff-files-image-to-symlink -> origin/diff-files-image-to-symlink
|
||||
* [new branch] diff-files-symlink-to-image -> origin/diff-files-symlink-to-image
|
||||
* [new branch] diff-files-symlink-to-text -> origin/diff-files-symlink-to-text
|
||||
* [new branch] diff-files-text-to-symlink -> origin/diff-files-text-to-symlink
|
||||
b80faa8..40232f7 snippet/multiple-files -> origin/snippet/multiple-files
|
||||
* [new branch] testing/branch-with-#-hash -> origin/testing/branch-with-#-hash
|
||||
|
||||
==> Setting up GitLab Elasticsearch Indexer...
|
||||
GitLab Elasticsearch Indexer set up in 26.514623 seconds...
|
||||
```
|
||||
|
||||
This information is omitted when running locally and when no action needs
|
||||
to be performed. If you would always like to see these messages, set the
|
||||
following environment variable:
|
||||
|
||||
```shell
|
||||
GITLAB_TESTING_LOG_LEVEL=debug
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[Return to Testing documentation](index.md)
|
||||
|
|
|
|||
|
|
@ -271,6 +271,22 @@ you defined.
|
|||
| `week` | 4 |
|
||||
| `month` | 12 |
|
||||
|
||||
#### `query.period_field`
|
||||
|
||||
Define the timestamp field used to group "issuables".
|
||||
|
||||
Supported values are:
|
||||
|
||||
- `created_at` (default): Group data using the `created_at` field.
|
||||
- `closed_at`: Group data using the `closed_at` field (for issues only).
|
||||
- `merged_at`: Group data using the `merged_at` field (for merge requests only).
|
||||
|
||||
The `period_field` is automatically set to:
|
||||
|
||||
- `closed_at` if `query.issuable_state` is `closed`
|
||||
- `merged_at` if `query.issuable_state` is `merged`
|
||||
- `created_at` otherwise
|
||||
|
||||
### `projects`
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10904) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.4.
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ Use the switches to enable or disable the following features:
|
|||
| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md) |
|
||||
| **Pages** | ✓ | Allows you to [publish static websites](../pages/) |
|
||||
| **Metrics Dashboard** | ✓ | Control access to [metrics dashboard](../integrations/prometheus.md)
|
||||
| **Requirements** | ✓ | Control access to [Requirements Management](../requirements/index.md)
|
||||
|
||||
Some features depend on others:
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ now search for code within other teams that can help your own project.
|
|||
GitLab leverages the search capabilities of [Elasticsearch](https://www.elastic.co/elasticsearch/) and enables it when
|
||||
searching in:
|
||||
|
||||
- GitLab application
|
||||
- Projects
|
||||
- Repositories
|
||||
- Commits
|
||||
|
|
|
|||
|
|
@ -34,26 +34,13 @@ class Feature
|
|||
def persisted_names
|
||||
return [] unless Gitlab::Database.exists?
|
||||
|
||||
if Gitlab::Utils.to_boolean(ENV['FF_LEGACY_PERSISTED_NAMES'])
|
||||
# To be removed:
|
||||
# This uses a legacy persisted names that are know to work (always)
|
||||
Gitlab::SafeRequestStore[:flipper_persisted_names] ||=
|
||||
begin
|
||||
# We saw on GitLab.com, this database request was called 2300
|
||||
# times/s. Let's cache it for a minute to avoid that load.
|
||||
Gitlab::ProcessMemoryCache.cache_backend.fetch('flipper:persisted_names', expires_in: 1.minute) do
|
||||
FlipperFeature.feature_names
|
||||
end.to_set
|
||||
end
|
||||
else
|
||||
# This loads names of all stored feature flags
|
||||
# and returns a stable Set in the following order:
|
||||
# - Memoized: using Gitlab::SafeRequestStore or @flipper
|
||||
# - L1: using Process cache
|
||||
# - L2: using Redis cache
|
||||
# - DB: using a single SQL query
|
||||
flipper.adapter.features
|
||||
end
|
||||
# This loads names of all stored feature flags
|
||||
# and returns a stable Set in the following order:
|
||||
# - Memoized: using Gitlab::SafeRequestStore or @flipper
|
||||
# - L1: using Process cache
|
||||
# - L2: using Redis cache
|
||||
# - DB: using a single SQL query
|
||||
flipper.adapter.features
|
||||
end
|
||||
|
||||
def persisted_name?(feature_name)
|
||||
|
|
|
|||
|
|
@ -44,6 +44,10 @@ module Gitlab
|
|||
"<img class='emoji' title=':#{name}:' alt=':#{name}:' src='#{src}' height='20' width='20' align='absmiddle' />"
|
||||
end
|
||||
|
||||
def emoji_exists?(name)
|
||||
emojis.has_key?(name)
|
||||
end
|
||||
|
||||
# CSS sprite fallback takes precedence over image fallback
|
||||
def gl_emoji_tag(name, options = {})
|
||||
emoji_name = emojis_aliases[name] || name
|
||||
|
|
|
|||
|
|
@ -396,13 +396,13 @@ module Gitlab
|
|||
|
||||
def list_commits_by_ref_name(refs)
|
||||
request = Gitaly::ListCommitsByRefNameRequest
|
||||
.new(repository: @gitaly_repo, ref_names: refs)
|
||||
.new(repository: @gitaly_repo, ref_names: refs.map { |ref| encode_binary(ref) })
|
||||
|
||||
response = GitalyClient.call(@repository.storage, :commit_service, :list_commits_by_ref_name, request, timeout: GitalyClient.medium_timeout)
|
||||
|
||||
commit_refs = response.flat_map do |message|
|
||||
message.commit_refs.map do |commit_ref|
|
||||
[commit_ref.ref_name, Gitlab::Git::Commit.new(@repository, commit_ref.commit)]
|
||||
[encode_utf8(commit_ref.ref_name), Gitlab::Git::Commit.new(@repository, commit_ref.commit)]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ module Quality
|
|||
assignee_ids: Array(team.pluck(:id).sample(3)),
|
||||
labels: labels.join(',')
|
||||
}
|
||||
params[:closed_at] = params[:created_at] + rand(35).days if params[:state] == 'closed'
|
||||
issue = ::Issues::CreateService.new(project, team.sample, params).execute
|
||||
|
||||
if issue.persisted?
|
||||
|
|
|
|||
|
|
@ -476,6 +476,9 @@ msgstr ""
|
|||
msgid "%{name} found %{resultsString}"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{name} is already being used for another emoji"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{name} is scheduled for %{action}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -17782,6 +17785,12 @@ msgstr ""
|
|||
msgid "ProjectSettings|Repository"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Requirements"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Requirements management system for this project"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectSettings|Share code pastes with others out of Git repository"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -19222,7 +19231,7 @@ msgstr ""
|
|||
msgid "Repository sync capacity"
|
||||
msgstr ""
|
||||
|
||||
msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects}"
|
||||
msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets}"
|
||||
msgstr ""
|
||||
|
||||
msgid "RepositorySettingsAccessLevel|Select"
|
||||
|
|
@ -24728,6 +24737,9 @@ msgstr ""
|
|||
msgid "UsageQuota|Repository"
|
||||
msgstr ""
|
||||
|
||||
msgid "UsageQuota|Snippets"
|
||||
msgstr ""
|
||||
|
||||
msgid "UsageQuota|Storage"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :custom_emoji, class: 'CustomEmoji' do
|
||||
sequence(:name) { |n| "custom_emoji#{n}" }
|
||||
namespace
|
||||
file { fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')) }
|
||||
end
|
||||
end
|
||||
|
|
@ -181,6 +181,7 @@ FactoryBot.define do
|
|||
|
||||
transient do
|
||||
create_templates { nil }
|
||||
create_branch { nil }
|
||||
end
|
||||
|
||||
after :create do |project, evaluator|
|
||||
|
|
@ -206,6 +207,16 @@ FactoryBot.define do
|
|||
message: 'test 2',
|
||||
branch_name: 'master')
|
||||
end
|
||||
|
||||
if evaluator.create_branch
|
||||
project.repository.create_file(
|
||||
project.creator,
|
||||
'README.md',
|
||||
"README on branch #{evaluator.create_branch}",
|
||||
message: 'Add README.md',
|
||||
branch_name: evaluator.create_branch)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ RSpec.describe "Admin > Admin sees project statistics" do
|
|||
let(:project) { create(:project, :repository) }
|
||||
|
||||
it "shows project statistics" do
|
||||
expect(page).to have_content("Storage: 0 Bytes (Repository: 0 Bytes / Wikis: 0 Bytes / Build Artifacts: 0 Bytes / LFS: 0 Bytes)")
|
||||
expect(page).to have_content("Storage: 0 Bytes (Repository: 0 Bytes / Wikis: 0 Bytes / Build Artifacts: 0 Bytes / LFS: 0 Bytes / Snippets: 0 Bytes)")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ RSpec.describe MetricsDashboard, '(JavaScript fixtures)', type: :controller do
|
|||
include JavaScriptFixturesHelpers
|
||||
include MetricsDashboardHelpers
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { project_with_dashboard('.gitlab/dashboards/test.yml') }
|
||||
let(:environment) { create(:environment, project: project) }
|
||||
let(:params) { { environment: environment } }
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:namespace) { create(:namespace, name: 'monitoring' )}
|
||||
let_it_be(:project) { project_with_dashboard_namespace('.gitlab/dashboards/test.yml', namespace: namespace) }
|
||||
let_it_be(:environment) { create(:environment, id: 1, project: project) }
|
||||
let_it_be(:params) { { environment: environment } }
|
||||
|
||||
before(:all) do
|
||||
clean_frontend_fixtures('metrics_dashboard/')
|
||||
|
|
@ -24,6 +25,7 @@ RSpec.describe MetricsDashboard, '(JavaScript fixtures)', type: :controller do
|
|||
project.add_maintainer(user)
|
||||
|
||||
allow(controller).to receive(:project).and_return(project)
|
||||
allow(controller).to receive(:environment).and_return(environment)
|
||||
allow(controller)
|
||||
.to receive(:metrics_dashboard_params)
|
||||
.and_return(params)
|
||||
|
|
@ -35,7 +37,9 @@ RSpec.describe MetricsDashboard, '(JavaScript fixtures)', type: :controller do
|
|||
|
||||
it 'metrics_dashboard/environment_metrics_dashboard.json' do
|
||||
routes.draw { get "metrics_dashboard" => "anonymous#metrics_dashboard" }
|
||||
|
||||
response = get :metrics_dashboard, format: :json
|
||||
|
||||
expect(response).to be_successful
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ describe('IDE store merge request actions', () => {
|
|||
expect(service.getProjectMergeRequests).toHaveBeenCalledWith(TEST_PROJECT, {
|
||||
source_branch: 'bar',
|
||||
source_project_id: TEST_PROJECT_ID,
|
||||
state: 'opened',
|
||||
order_by: 'created_at',
|
||||
per_page: 1,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ exports[`Dashboard template matches the default snapshot 1`] = `
|
|||
<div
|
||||
class="prometheus-graphs"
|
||||
data-qa-selector="prometheus_graphs"
|
||||
environmentstate="available"
|
||||
metricsdashboardbasepath="/monitoring/monitor-project/-/environments/1/metrics"
|
||||
metricsendpoint="/monitoring/monitor-project/-/environments/1/additional_metrics.json"
|
||||
prometheusstatus=""
|
||||
>
|
||||
<div
|
||||
class="prometheus-graphs-header d-sm-flex flex-sm-wrap pt-2 pr-1 pb-0 pl-2 border-bottom bg-gray-light"
|
||||
|
|
@ -135,15 +139,15 @@ exports[`Dashboard template matches the default snapshot 1`] = `
|
|||
<!---->
|
||||
|
||||
<empty-state-stub
|
||||
clusterspath="/path/to/clusters"
|
||||
documentationpath="/path/to/docs"
|
||||
emptygettingstartedsvgpath="/path/to/getting-started.svg"
|
||||
emptyloadingsvgpath="/path/to/loading.svg"
|
||||
emptynodatasmallsvgpath="/path/to/no-data-small.svg"
|
||||
emptynodatasvgpath="/path/to/no-data.svg"
|
||||
emptyunabletoconnectsvgpath="/path/to/unable-to-connect.svg"
|
||||
clusterspath="/monitoring/monitor-project/-/clusters"
|
||||
documentationpath="/help/administration/monitoring/prometheus/index.md"
|
||||
emptygettingstartedsvgpath="/images/illustrations/monitoring/getting_started.svg"
|
||||
emptyloadingsvgpath="/images/illustrations/monitoring/loading.svg"
|
||||
emptynodatasmallsvgpath="/images/illustrations/chart-empty-state-small.svg"
|
||||
emptynodatasvgpath="/images/illustrations/monitoring/no_data.svg"
|
||||
emptyunabletoconnectsvgpath="/images/illustrations/monitoring/unable_to_connect.svg"
|
||||
selectedstate="gettingStarted"
|
||||
settingspath="/path/to/settings"
|
||||
settingspath="/monitoring/monitor-project/-/services/prometheus/edit"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import {
|
|||
singleStatMetricsResult,
|
||||
graphDataPrometheusQueryRangeMultiTrack,
|
||||
barMockData,
|
||||
propsData,
|
||||
} from '../mock_data';
|
||||
import { dashboardProps, graphData, graphDataEmpty } from '../fixture_data';
|
||||
|
||||
import { panelTypes } from '~/monitoring/constants';
|
||||
|
||||
|
|
@ -32,7 +32,6 @@ import MonitorColumnChart from '~/monitoring/components/charts/column.vue';
|
|||
import MonitorBarChart from '~/monitoring/components/charts/bar.vue';
|
||||
import MonitorStackedColumnChart from '~/monitoring/components/charts/stacked_column.vue';
|
||||
|
||||
import { graphData, graphDataEmpty } from '../fixture_data';
|
||||
import { createStore, monitoringDashboard } from '~/monitoring/stores';
|
||||
import { createStore as createEmbedGroupStore } from '~/monitoring/stores/embed_group';
|
||||
|
||||
|
|
@ -63,7 +62,7 @@ describe('Dashboard Panel', () => {
|
|||
wrapper = shallowMount(DashboardPanel, {
|
||||
propsData: {
|
||||
graphData,
|
||||
settingsPath: propsData.settingsPath,
|
||||
settingsPath: dashboardProps.settingsPath,
|
||||
...props,
|
||||
},
|
||||
store,
|
||||
|
|
@ -316,7 +315,7 @@ describe('Dashboard Panel', () => {
|
|||
|
||||
return wrapper.vm.$nextTick(() => {
|
||||
expect(findEditCustomMetricLink().text()).toBe('Edit metrics');
|
||||
expect(findEditCustomMetricLink().attributes('href')).toBe(propsData.settingsPath);
|
||||
expect(findEditCustomMetricLink().attributes('href')).toBe(dashboardProps.settingsPath);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -433,7 +432,7 @@ describe('Dashboard Panel', () => {
|
|||
wrapper = shallowMount(DashboardPanel, {
|
||||
propsData: {
|
||||
clipboardText: exampleText,
|
||||
settingsPath: propsData.settingsPath,
|
||||
settingsPath: dashboardProps.settingsPath,
|
||||
graphData: {
|
||||
y_label: 'metric',
|
||||
...graphData,
|
||||
|
|
@ -483,7 +482,7 @@ describe('Dashboard Panel', () => {
|
|||
wrapper = shallowMount(DashboardPanel, {
|
||||
propsData: {
|
||||
graphData,
|
||||
settingsPath: propsData.settingsPath,
|
||||
settingsPath: dashboardProps.settingsPath,
|
||||
namespace: mockNamespace,
|
||||
},
|
||||
store,
|
||||
|
|
|
|||
|
|
@ -29,8 +29,12 @@ import {
|
|||
setupStoreWithVariable,
|
||||
setupStoreWithLinks,
|
||||
} from '../store_utils';
|
||||
import { environmentData, dashboardGitResponse, propsData } from '../mock_data';
|
||||
import { metricsDashboardViewModel, metricsDashboardPanelCount } from '../fixture_data';
|
||||
import { environmentData, dashboardGitResponse } from '../mock_data';
|
||||
import {
|
||||
metricsDashboardViewModel,
|
||||
metricsDashboardPanelCount,
|
||||
dashboardProps,
|
||||
} from '../fixture_data';
|
||||
import createFlash from '~/flash';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
|
@ -50,7 +54,7 @@ describe('Dashboard', () => {
|
|||
|
||||
const createShallowWrapper = (props = {}, options = {}) => {
|
||||
wrapper = shallowMount(Dashboard, {
|
||||
propsData: { ...propsData, ...props },
|
||||
propsData: { ...dashboardProps, ...props },
|
||||
store,
|
||||
stubs: {
|
||||
DashboardHeader,
|
||||
|
|
@ -61,7 +65,7 @@ describe('Dashboard', () => {
|
|||
|
||||
const createMountedWrapper = (props = {}, options = {}) => {
|
||||
wrapper = mount(Dashboard, {
|
||||
propsData: { ...propsData, ...props },
|
||||
propsData: { ...dashboardProps, ...props },
|
||||
store,
|
||||
stubs: {
|
||||
'graph-group': true,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import Dashboard from '~/monitoring/components/dashboard.vue';
|
|||
import DashboardHeader from '~/monitoring/components/dashboard_header.vue';
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import { setupAllDashboards } from '../store_utils';
|
||||
import { propsData } from '../mock_data';
|
||||
import { dashboardProps } from '../fixture_data';
|
||||
|
||||
jest.mock('~/lib/utils/url_utility');
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ describe('Dashboard template', () => {
|
|||
|
||||
it('matches the default snapshot', () => {
|
||||
wrapper = shallowMount(Dashboard, {
|
||||
propsData: { ...propsData },
|
||||
propsData: { ...dashboardProps },
|
||||
store,
|
||||
stubs: {
|
||||
DashboardHeader,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import {
|
|||
updateHistory,
|
||||
} from '~/lib/utils/url_utility';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { mockProjectDir, propsData } from '../mock_data';
|
||||
import { mockProjectDir } from '../mock_data';
|
||||
import { dashboardProps } from '../fixture_data';
|
||||
|
||||
import Dashboard from '~/monitoring/components/dashboard.vue';
|
||||
import DashboardHeader from '~/monitoring/components/dashboard_header.vue';
|
||||
|
|
@ -26,7 +27,7 @@ describe('dashboard invalid url parameters', () => {
|
|||
|
||||
const createMountedWrapper = (props = { hasMetrics: true }, options = {}) => {
|
||||
wrapper = mount(Dashboard, {
|
||||
propsData: { ...propsData, ...props },
|
||||
propsData: { ...dashboardProps, ...props },
|
||||
store,
|
||||
stubs: { 'graph-group': true, 'dashboard-panel': true, 'dashboard-header': DashboardHeader },
|
||||
...options,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { stateAndPropsFromDataset } from '~/monitoring/utils';
|
||||
import { mapToDashboardViewModel } from '~/monitoring/stores/utils';
|
||||
import { metricStates } from '~/monitoring/constants';
|
||||
import { convertObjectProps } from '~/lib/utils/common_utils';
|
||||
import { convertToCamelCase } from '~/lib/utils/text_utility';
|
||||
|
||||
import { metricsResult } from './mock_data';
|
||||
|
||||
|
|
@ -8,6 +11,19 @@ export const metricsDashboardResponse = getJSONFixture(
|
|||
'metrics_dashboard/environment_metrics_dashboard.json',
|
||||
);
|
||||
export const metricsDashboardPayload = metricsDashboardResponse.dashboard;
|
||||
|
||||
const datasetState = stateAndPropsFromDataset(
|
||||
// It's preferable to have props in snake_case, this will be addressed at:
|
||||
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33574
|
||||
convertObjectProps(
|
||||
// Some props use kebab-case, convert to snake_case first
|
||||
key => convertToCamelCase(key.replace(/-/g, '_')),
|
||||
metricsDashboardResponse.metrics_data,
|
||||
),
|
||||
);
|
||||
|
||||
export const dashboardProps = datasetState.dataProps;
|
||||
|
||||
export const metricsDashboardViewModel = mapToDashboardViewModel(metricsDashboardPayload);
|
||||
|
||||
export const metricsDashboardPanelCount = 22;
|
||||
|
|
|
|||
|
|
@ -877,6 +877,8 @@ describe('normalizeCustomDashboardPath', () => {
|
|||
${['.gitlab/dashboards/links.yml', '.gitlab/dashboards']} | ${'.gitlab/dashboards/links.yml'}
|
||||
${['.gitlab/dashboards/dir1/links.yml', '.gitlab/dashboards']} | ${'.gitlab/dashboards/dir1/links.yml'}
|
||||
${['.gitlab/dashboards/dir1/dir2/links.yml', '.gitlab/dashboards']} | ${'.gitlab/dashboards/dir1/dir2/links.yml'}
|
||||
${['config/prometheus/pod_metrics.yml', '.gitlab/dashboards']} | ${'config/prometheus/pod_metrics.yml'}
|
||||
${['config/prometheus/pod_metrics.yml']} | ${'config/prometheus/pod_metrics.yml'}
|
||||
`(`normalizeCustomDashboardPath returns $expected for $input`, ({ input, expected }) => {
|
||||
expect(normalizeCustomDashboardPath(...input)).toEqual(expected);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,10 +30,11 @@ RSpec.describe StorageHelper do
|
|||
repository_size: 10.kilobytes,
|
||||
wiki_size: 10.bytes,
|
||||
lfs_objects_size: 20.gigabytes,
|
||||
build_artifacts_size: 30.megabytes))
|
||||
build_artifacts_size: 30.megabytes,
|
||||
snippets_size: 40.megabytes))
|
||||
end
|
||||
|
||||
let(:message) { 'Repository: 10 KB / Wikis: 10 Bytes / Build Artifacts: 30 MB / LFS: 20 GB' }
|
||||
let(:message) { 'Repository: 10 KB / Wikis: 10 Bytes / Build Artifacts: 30 MB / LFS: 20 GB / Snippets: 40 MB' }
|
||||
|
||||
it 'works on ProjectStatistics' do
|
||||
expect(helper.storage_counters_details(project.statistics)).to eq(message)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import Dashboard from '~/monitoring/components/dashboard.vue';
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { mockApiEndpoint, propsData } from '../mock_data';
|
||||
import { metricsDashboardPayload } from '../fixture_data';
|
||||
import { mockApiEndpoint } from '../mock_data';
|
||||
import { metricsDashboardPayload, dashboardProps } from '../fixture_data';
|
||||
import { setupStoreWithData } from '../store_utils';
|
||||
|
||||
const localVue = createLocalVue();
|
||||
|
|
@ -56,7 +56,7 @@ describe('Dashboard', () => {
|
|||
component = new DashboardComponent({
|
||||
el: document.querySelector('.prometheus-graphs'),
|
||||
propsData: {
|
||||
...propsData,
|
||||
...dashboardProps,
|
||||
hasMetrics: true,
|
||||
showPanels: true,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,66 +21,29 @@ RSpec.describe Feature, stub_feature_flags: false do
|
|||
end
|
||||
|
||||
describe '.persisted_names' do
|
||||
context 'when FF_LEGACY_PERSISTED_NAMES=false' do
|
||||
before do
|
||||
stub_env('FF_LEGACY_PERSISTED_NAMES', 'false')
|
||||
end
|
||||
it 'returns the names of the persisted features' do
|
||||
Feature.enable('foo')
|
||||
|
||||
it 'returns the names of the persisted features' do
|
||||
Feature.enable('foo')
|
||||
|
||||
expect(described_class.persisted_names).to contain_exactly('foo')
|
||||
end
|
||||
|
||||
it 'returns an empty Array when no features are presisted' do
|
||||
expect(described_class.persisted_names).to be_empty
|
||||
end
|
||||
|
||||
it 'caches the feature names when request store is active',
|
||||
:request_store, :use_clean_rails_memory_store_caching do
|
||||
Feature.enable('foo')
|
||||
|
||||
expect(Gitlab::ProcessMemoryCache.cache_backend)
|
||||
.to receive(:fetch)
|
||||
.once
|
||||
.with('flipper/v1/features', expires_in: 1.minute)
|
||||
.and_call_original
|
||||
|
||||
2.times do
|
||||
expect(described_class.persisted_names).to contain_exactly('foo')
|
||||
end
|
||||
end
|
||||
expect(described_class.persisted_names).to contain_exactly('foo')
|
||||
end
|
||||
|
||||
context 'when FF_LEGACY_PERSISTED_NAMES=true' do
|
||||
before do
|
||||
stub_env('FF_LEGACY_PERSISTED_NAMES', 'true')
|
||||
end
|
||||
it 'returns an empty Array when no features are presisted' do
|
||||
expect(described_class.persisted_names).to be_empty
|
||||
end
|
||||
|
||||
it 'returns the names of the persisted features' do
|
||||
Feature.enable('foo')
|
||||
it 'caches the feature names when request store is active',
|
||||
:request_store, :use_clean_rails_memory_store_caching do
|
||||
Feature.enable('foo')
|
||||
|
||||
expect(Gitlab::ProcessMemoryCache.cache_backend)
|
||||
.to receive(:fetch)
|
||||
.once
|
||||
.with('flipper/v1/features', expires_in: 1.minute)
|
||||
.and_call_original
|
||||
|
||||
2.times do
|
||||
expect(described_class.persisted_names).to contain_exactly('foo')
|
||||
end
|
||||
|
||||
it 'returns an empty Array when no features are presisted' do
|
||||
expect(described_class.persisted_names).to be_empty
|
||||
end
|
||||
|
||||
it 'caches the feature names when request store is active',
|
||||
:request_store, :use_clean_rails_memory_store_caching do
|
||||
Feature.enable('foo')
|
||||
|
||||
expect(Gitlab::ProcessMemoryCache.cache_backend)
|
||||
.to receive(:fetch)
|
||||
.once
|
||||
.with('flipper:persisted_names', expires_in: 1.minute)
|
||||
.and_call_original
|
||||
|
||||
2.times do
|
||||
expect(described_class.persisted_names).to contain_exactly('foo')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'fetches all flags once in a single query', :request_store do
|
||||
|
|
|
|||
|
|
@ -95,6 +95,20 @@ RSpec.describe Gitlab::Emoji do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.emoji_exists?' do
|
||||
it 'returns true if the name exists' do
|
||||
emoji_exists = described_class.emoji_exists?('100')
|
||||
|
||||
expect(emoji_exists).to be_truthy
|
||||
end
|
||||
|
||||
it 'returns false if the name does not exist' do
|
||||
emoji_exists = described_class.emoji_exists?('random')
|
||||
|
||||
expect(emoji_exists).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
describe '.gl_emoji_tag' do
|
||||
it 'returns gl emoji tag if emoji is found' do
|
||||
gl_tag = described_class.gl_emoji_tag('small_airplane')
|
||||
|
|
|
|||
|
|
@ -2187,34 +2187,47 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
|
|||
'gitaly_address' => Gitlab.config.repositories.storages.default.gitaly_address,
|
||||
'path' => TestEnv::SECOND_STORAGE_PATH
|
||||
})
|
||||
new_repository.create_repository
|
||||
end
|
||||
|
||||
after do
|
||||
new_repository.remove
|
||||
end
|
||||
|
||||
it 'mirrors the source repository' do
|
||||
subject
|
||||
|
||||
expect(refs(new_repository_path)).to eq(refs(repository_path))
|
||||
end
|
||||
|
||||
context 'with keep-around refs' do
|
||||
let(:sha) { SeedRepo::Commit::ID }
|
||||
let(:keep_around_ref) { "refs/keep-around/#{sha}" }
|
||||
let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" }
|
||||
|
||||
before do
|
||||
repository.write_ref(keep_around_ref, sha)
|
||||
repository.write_ref(tmp_ref, sha)
|
||||
end
|
||||
|
||||
it 'includes the temporary and keep-around refs' do
|
||||
context 'destination does not exist' do
|
||||
it 'mirrors the source repository' do
|
||||
subject
|
||||
|
||||
expect(refs(new_repository_path)).to include(keep_around_ref)
|
||||
expect(refs(new_repository_path)).to include(tmp_ref)
|
||||
expect(refs(new_repository_path)).to eq(refs(repository_path))
|
||||
end
|
||||
end
|
||||
|
||||
context 'destination exists' do
|
||||
before do
|
||||
new_repository.create_repository
|
||||
end
|
||||
|
||||
it 'mirrors the source repository' do
|
||||
subject
|
||||
|
||||
expect(refs(new_repository_path)).to eq(refs(repository_path))
|
||||
end
|
||||
|
||||
context 'with keep-around refs' do
|
||||
let(:sha) { SeedRepo::Commit::ID }
|
||||
let(:keep_around_ref) { "refs/keep-around/#{sha}" }
|
||||
let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" }
|
||||
|
||||
before do
|
||||
repository.write_ref(keep_around_ref, sha)
|
||||
repository.write_ref(tmp_ref, sha)
|
||||
end
|
||||
|
||||
it 'includes the temporary and keep-around refs' do
|
||||
subject
|
||||
|
||||
expect(refs(new_repository_path)).to include(keep_around_ref)
|
||||
expect(refs(new_repository_path)).to include(tmp_ref)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -387,12 +387,16 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
|
|||
end
|
||||
|
||||
describe '#list_commits_by_ref_name' do
|
||||
it 'lists latest commits grouped by a ref name' do
|
||||
response = client.list_commits_by_ref_name(%w[master feature v1.0.0 nonexistent])
|
||||
let(:project) { create(:project, :repository, create_branch: 'ü/unicode/multi-byte') }
|
||||
|
||||
it 'lists latest commits grouped by a ref name' do
|
||||
response = client.list_commits_by_ref_name(%w[master feature v1.0.0 nonexistent ü/unicode/multi-byte])
|
||||
|
||||
expect(response.keys.count).to eq 4
|
||||
expect(response.fetch('master').id).to eq 'b83d6e391c22777fca1ed3012fce84f633d7fed0'
|
||||
expect(response.fetch('feature').id).to eq '0b4bc9a49b562e85de7cc9e834518ea6828729b9'
|
||||
expect(response.fetch('v1.0.0').id).to eq '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
|
||||
expect(response.fetch('ü/unicode/multi-byte')).to be_present
|
||||
expect(response).not_to have_key 'nonexistent'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -533,6 +533,7 @@ Project:
|
|||
- merge_requests_enabled
|
||||
- wiki_enabled
|
||||
- snippets_enabled
|
||||
- requirements_enabled
|
||||
- visibility_level
|
||||
- archived
|
||||
- created_at
|
||||
|
|
@ -600,6 +601,7 @@ ProjectFeature:
|
|||
- repository_access_level
|
||||
- pages_access_level
|
||||
- metrics_dashboard_access_level
|
||||
- requirements_access_level
|
||||
- created_at
|
||||
- updated_at
|
||||
ProtectedBranch::MergeAccessLevel:
|
||||
|
|
|
|||
|
|
@ -1489,8 +1489,31 @@ RSpec.describe Ci::Pipeline, :mailer do
|
|||
end
|
||||
|
||||
describe '#lazy_ref_commit' do
|
||||
let(:another) do
|
||||
create(:ci_pipeline,
|
||||
project: project,
|
||||
ref: 'feature',
|
||||
sha: project.commit('feature').sha)
|
||||
end
|
||||
|
||||
let(:unicode) do
|
||||
create(:ci_pipeline,
|
||||
project: project,
|
||||
ref: 'ü/unicode/multi-byte')
|
||||
end
|
||||
|
||||
it 'returns the latest commit for a ref lazily' do
|
||||
expect(pipeline.lazy_ref_commit.id).to eq project.commit(pipeline.ref).id
|
||||
expect(project.repository)
|
||||
.to receive(:list_commits_by_ref_name).once
|
||||
.and_call_original
|
||||
|
||||
pipeline.lazy_ref_commit
|
||||
another.lazy_ref_commit
|
||||
unicode.lazy_ref_commit
|
||||
|
||||
expect(pipeline.lazy_ref_commit.id).to eq pipeline.sha
|
||||
expect(another.lazy_ref_commit.id).to eq another.sha
|
||||
expect(unicode.lazy_ref_commit).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe CustomEmoji do
|
||||
describe 'Associations' do
|
||||
it { is_expected.to belong_to(:namespace) }
|
||||
it { is_expected.to have_db_column(:file) }
|
||||
it { is_expected.to validate_length_of(:name).is_at_most(36) }
|
||||
it { is_expected.to validate_presence_of(:name) }
|
||||
end
|
||||
|
||||
describe 'exclusion of duplicated emoji' do
|
||||
let(:emoji_name) { Gitlab::Emoji.emojis_names.sample }
|
||||
|
||||
it 'disallows emoji names of built-in emoji' do
|
||||
new_emoji = build(:custom_emoji, name: emoji_name)
|
||||
|
||||
expect(new_emoji).not_to be_valid
|
||||
expect(new_emoji.errors.messages).to eq(name: ["#{emoji_name} is already being used for another emoji"])
|
||||
end
|
||||
|
||||
it 'disallows duplicate custom emoji names within namespace' do
|
||||
old_emoji = create(:custom_emoji)
|
||||
new_emoji = build(:custom_emoji, name: old_emoji.name, namespace: old_emoji.namespace)
|
||||
|
||||
expect(new_emoji).not_to be_valid
|
||||
expect(new_emoji.errors.messages).to eq(name: ["has already been taken"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -17,6 +17,7 @@ RSpec.describe Namespace do
|
|||
it { is_expected.to have_many :children }
|
||||
it { is_expected.to have_one :root_storage_statistics }
|
||||
it { is_expected.to have_one :aggregation_schedule }
|
||||
it { is_expected.to have_many :custom_emoji }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ RSpec.describe ProjectPolicy do
|
|||
let(:additional_guest_permissions) { [] }
|
||||
let(:additional_reporter_permissions) { [] }
|
||||
let(:additional_maintainer_permissions) { [] }
|
||||
let(:additional_owner_permissions) { [] }
|
||||
|
||||
let(:guest_permissions) { base_guest_permissions + additional_guest_permissions }
|
||||
let(:reporter_permissions) { base_reporter_permissions + additional_reporter_permissions }
|
||||
|
|
|
|||
|
|
@ -37,8 +37,6 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
|
|||
project.repository.path_to_repo
|
||||
end
|
||||
|
||||
expect(project_repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
expect(project_repository_double).to receive(:replicate)
|
||||
.with(project.repository.raw)
|
||||
expect(project_repository_double).to receive(:checksum)
|
||||
|
|
@ -72,8 +70,6 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
|
|||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
|
||||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
|
||||
|
||||
expect(project_repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
expect(project_repository_double).to receive(:replicate)
|
||||
.with(project.repository.raw)
|
||||
.and_raise(Gitlab::Git::CommandError)
|
||||
|
|
@ -92,8 +88,6 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
|
|||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
|
||||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
|
||||
|
||||
expect(project_repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
expect(project_repository_double).to receive(:replicate)
|
||||
.with(project.repository.raw)
|
||||
expect(project_repository_double).to receive(:checksum)
|
||||
|
|
@ -115,8 +109,6 @@ RSpec.describe Projects::UpdateRepositoryStorageService do
|
|||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
|
||||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
|
||||
|
||||
expect(project_repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
expect(project_repository_double).to receive(:replicate)
|
||||
.with(project.repository.raw)
|
||||
expect(project_repository_double).to receive(:checksum)
|
||||
|
|
|
|||
|
|
@ -45,6 +45,27 @@ RSpec.describe ResourceAccessTokens::CreateService do
|
|||
expect(access_token.user.reload.user_type).to eq("#{resource_type}_bot")
|
||||
end
|
||||
|
||||
context 'email confirmation status' do
|
||||
shared_examples_for 'creates a user that has their email confirmed' do
|
||||
it 'creates a user that has their email confirmed' do
|
||||
response = subject
|
||||
access_token = response.payload[:access_token]
|
||||
|
||||
expect(access_token.user.reload.confirmed?).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when created by an admin' do
|
||||
it_behaves_like 'creates a user that has their email confirmed' do
|
||||
let(:user) { create(:admin) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when created by a non-admin' do
|
||||
it_behaves_like 'creates a user that has their email confirmed'
|
||||
end
|
||||
end
|
||||
|
||||
context 'bot name' do
|
||||
context 'when no value is passed' do
|
||||
it 'uses default value' do
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@ module MetricsDashboardHelpers
|
|||
create(:project, :custom_repo, files: { dashboard_path => dashboard_yml })
|
||||
end
|
||||
|
||||
def project_with_dashboard_namespace(dashboard_path, dashboard_yml = nil)
|
||||
dashboard_yml ||= fixture_file('lib/gitlab/metrics/dashboard/sample_dashboard.yml')
|
||||
|
||||
create(:project, :custom_repo, namespace: namespace, path: 'monitor-project', files: { dashboard_path => dashboard_yml })
|
||||
end
|
||||
|
||||
def delete_project_dashboard(project, user, dashboard_path)
|
||||
project.repository.delete_file(
|
||||
user,
|
||||
|
|
|
|||
|
|
@ -25,15 +25,11 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
|
|||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
|
||||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
|
||||
|
||||
allow(project_repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
allow(project_repository_double).to receive(:replicate)
|
||||
.with(project.repository.raw)
|
||||
allow(project_repository_double).to receive(:checksum)
|
||||
.and_return(project_repository_checksum)
|
||||
|
||||
allow(repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
allow(repository_double).to receive(:replicate)
|
||||
.with(repository.raw)
|
||||
allow(repository_double).to receive(:checksum)
|
||||
|
|
@ -104,15 +100,11 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
|
|||
it 'unmarks the repository as read-only without updating the repository storage' do
|
||||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
|
||||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
|
||||
allow(project_repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
allow(project_repository_double).to receive(:replicate)
|
||||
.with(project.repository.raw)
|
||||
allow(project_repository_double).to receive(:checksum)
|
||||
.and_return(project_repository_checksum)
|
||||
|
||||
allow(repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
allow(repository_double).to receive(:replicate)
|
||||
.with(repository.raw)
|
||||
.and_raise(Gitlab::Git::CommandError)
|
||||
|
|
@ -131,15 +123,11 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
|
|||
it 'unmarks the repository as read-only without updating the repository storage' do
|
||||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original
|
||||
allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid)
|
||||
allow(project_repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
allow(project_repository_double).to receive(:replicate)
|
||||
.with(project.repository.raw)
|
||||
allow(project_repository_double).to receive(:checksum)
|
||||
.and_return(project_repository_checksum)
|
||||
|
||||
allow(repository_double).to receive(:create_repository)
|
||||
.and_return(true)
|
||||
allow(repository_double).to receive(:replicate)
|
||||
.with(repository.raw)
|
||||
allow(repository_double).to receive(:checksum)
|
||||
|
|
|
|||
Loading…
Reference in New Issue