Коммит b0107e87 создал по автору GitLab Bot's avatar GitLab Bot
Просмотр файлов

Add latest changes from gitlab-org/gitlab@master

владелец 6aab1870
......@@ -328,6 +328,9 @@ Cop/SidekiqOptionsQueue:
Graphql/AuthorizeTypes:
Enabled: true
Include:
- 'app/graphql/types/**/*'
- 'ee/app/graphql/types/**/*'
Exclude:
- 'spec/**/*.rb'
- 'ee/spec/**/*.rb'
......
......@@ -3,7 +3,7 @@ import LogLine from './line.vue';
import LogLineHeader from './line_header.vue';
export default {
name: 'CollpasibleLogSection',
name: 'CollapsibleLogSection',
components: {
LogLine,
LogLineHeader,
......
<script>
import { mapState, mapActions } from 'vuex';
import CollpasibleLogSection from './collapsible_section.vue';
import CollapsibleLogSection from './collapsible_section.vue';
import LogLine from './line.vue';
export default {
components: {
CollpasibleLogSection,
CollapsibleLogSection,
LogLine,
},
computed: {
......@@ -51,7 +51,7 @@ export default {
<template>
<code class="job-log d-block" data-qa-selector="job_log_content">
<template v-for="(section, index) in trace">
<collpasible-log-section
<collapsible-log-section
v-if="section.isHeader"
:key="`collapsible-${index}`"
:section="section"
......
......@@ -195,7 +195,7 @@ export const receiveTraceError = ({ dispatch }) => {
flash(__('An error occurred while fetching the job log.'));
};
/**
* When the user clicks a collpasible line in the job
* When the user clicks a collapsible line in the job
* log, we commit a mutation to update the state
*
* @param {Object} section
......
......@@ -11,7 +11,7 @@ export const parseLine = (line = {}, lineNumber) => ({
/**
* When a line has `section_header` set to true, we create a new
* structure to allow to nest the lines that belong to the
* collpasible section
* collapsible section
*
* @param Object line
* @param Number lineNumber
......@@ -91,7 +91,7 @@ export const getIncrementalLineNumber = acc => {
* Parses the job log content into a structure usable by the template
*
* For collaspible lines (section_header = true):
* - creates a new array to hold the lines that are collpasible,
* - creates a new array to hold the lines that are collapsible,
* - adds a isClosed property to handle toggle
* - adds a isHeader property to handle template logic
* - adds the section_duration
......
<script>
import { GlTabs, GlTab, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import createFlash from '~/flash';
import ForkGroupsListItem from './fork_groups_list_item.vue';
export default {
components: {
GlTabs,
GlTab,
GlLoadingIcon,
GlSearchBoxByType,
ForkGroupsListItem,
},
props: {
hasReachedProjectLimit: {
type: Boolean,
required: true,
},
endpoint: {
type: String,
required: true,
},
},
data() {
return {
namespaces: null,
filter: '',
};
},
computed: {
filteredNamespaces() {
return this.namespaces.filter(n => n.name.toLowerCase().includes(this.filter.toLowerCase()));
},
},
mounted() {
this.loadGroups();
},
methods: {
loadGroups() {
axios
.get(this.endpoint)
.then(response => {
this.namespaces = response.data.namespaces;
})
.catch(() => createFlash(__('There was a problem fetching groups.')));
},
},
i18n: {
searchPlaceholder: __('Search by name'),
},
};
</script>
<template>
<gl-tabs class="fork-groups">
<gl-tab :title="__('Groups and subgroups')">
<gl-loading-icon v-if="!namespaces" size="md" class="gl-mt-3" />
<template v-else-if="namespaces.length === 0">
<div class="gl-text-center">
<div class="h5">{{ __('No available groups to fork the project.') }}</div>
<p class="gl-mt-5">
{{ __('You must have permission to create a project in a group before forking.') }}
</p>
</div>
</template>
<div v-else-if="filteredNamespaces.length === 0" class="gl-text-center gl-mt-3">
{{ s__('GroupsTree|No groups matched your search') }}
</div>
<ul v-else class="groups-list group-list-tree">
<fork-groups-list-item
v-for="(namespace, index) in filteredNamespaces"
:key="index"
:group="namespace"
:has-reached-project-limit="hasReachedProjectLimit"
/>
</ul>
</gl-tab>
<template #tabs-end>
<gl-search-box-by-type
v-if="namespaces && namespaces.length"
v-model="filter"
:placeholder="$options.i18n.searchPlaceholder"
class="gl-align-self-center gl-ml-auto fork-filtered-search"
/>
</template>
</gl-tabs>
</template>
<script>
import {
GlLink,
GlButton,
GlIcon,
GlAvatar,
GlTooltipDirective,
GlTooltip,
GlBadge,
} from '@gitlab/ui';
import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '~/groups/constants';
import { __ } from '~/locale';
import csrf from '~/lib/utils/csrf';
export default {
components: {
GlIcon,
GlAvatar,
GlBadge,
GlButton,
GlTooltip,
GlLink,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
group: {
type: Object,
required: true,
},
hasReachedProjectLimit: {
type: Boolean,
required: true,
},
},
data() {
return { namespaces: null };
},
computed: {
rowClass() {
return {
'has-description': this.group.description,
'being-removed': this.isGroupPendingRemoval,
};
},
isGroupPendingRemoval() {
return this.group.marked_for_deletion;
},
hasForkedProject() {
return Boolean(this.group.forked_project_path);
},
visibilityIcon() {
return VISIBILITY_TYPE_ICON[this.group.visibility];
},
visibilityTooltip() {
return GROUP_VISIBILITY_TYPE[this.group.visibility];
},
isSelectButtonDisabled() {
return this.hasReachedProjectLimit || !this.group.can_create_project;
},
selectButtonDisabledTooltip() {
return this.hasReachedProjectLimit
? this.$options.i18n.hasReachedProjectLimitMessage
: this.$options.i18n.insufficientPermissionsMessage;
},
},
i18n: {
hasReachedProjectLimitMessage: __('You have reached your project limit'),
insufficientPermissionsMessage: __(
'You must have permission to create a project in a namespace before forking.',
),
},
csrf,
};
</script>
<template>
<li :class="rowClass" class="group-row">
<div class="group-row-contents gl-display-flex gl-align-items-center gl-py-3 gl-pr-5">
<div class="folder-toggle-wrap gl-mr-2 gl-display-flex gl-align-items-center">
<gl-icon name="folder-o" />
</div>
<gl-link
:href="group.relative_path"
class="gl-display-none gl-flex-shrink-0 gl-display-sm-flex gl-mr-3"
>
<gl-avatar :size="32" shape="rect" :entity-name="group.name" :src="group.avatarUrl" />
</gl-link>
<div class="gl-min-w-0 gl-display-flex gl-flex-grow-1 gl-flex-shrink-1 gl-align-items-center">
<div class="gl-min-w-0 gl-flex-grow-1 flex-shrink-1">
<div class="title gl-display-flex gl-align-items-center gl-flex-wrap gl-mr-3">
<gl-link :href="group.relative_path" class="gl-mt-3 gl-mr-3 gl-text-gray-900!">{{
group.full_name
}}</gl-link>
<gl-icon
v-gl-tooltip.hover.bottom
class="gl-mr-0 gl-inline-flex gl-mt-3 text-secondary"
:name="visibilityIcon"
:title="visibilityTooltip"
/>
<gl-badge
v-if="isGroupPendingRemoval"
variant="warning"
class="gl-display-none gl-display-sm-flex gl-mt-3 gl-mr-1"
>{{ __('pending removal') }}</gl-badge
>
<span v-if="group.permission" class="user-access-role gl-mt-3">
{{ group.permission }}
</span>
</div>
<div v-if="group.description" class="description">
<span v-html="group.markdown_description"> </span>
</div>
</div>
<div class="gl-display-flex gl-flex-shrink-0">
<gl-button
v-if="hasForkedProject"
class="gl-h-7 gl-text-decoration-none!"
:href="group.forked_project_path"
>{{ __('Go to fork') }}</gl-button
>
<template v-else>
<div ref="selectButtonWrapper">
<form method="POST" :action="group.fork_path">
<input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
<gl-button
type="submit"
class="gl-h-7 gl-text-decoration-none!"
:data-qa-name="group.full_name"
variant="success"
:disabled="isSelectButtonDisabled"
>{{ __('Select') }}</gl-button
>
</form>
</div>
<gl-tooltip v-if="isSelectButtonDisabled" :target="() => $refs.selectButtonWrapper">
{{ selectButtonDisabledTooltip }}
</gl-tooltip>
</template>
</div>
</div>
</div>
</li>
</template>
......@@ -17,11 +17,8 @@ module Routable
after_validation :set_path_errors
before_validation do
if full_path_changed? || full_name_changed?
prepare_route
end
end
before_validation :prepare_route
before_save :prepare_route # in case validation is skipped
end
class_methods do
......@@ -118,6 +115,8 @@ def build_full_name
end
def prepare_route
return unless full_path_changed? || full_name_changed?
route || build_route(source: self)
route.path = build_full_path
route.name = build_full_name
......
......@@ -14,6 +14,7 @@ class ProjectStatistics < ApplicationRecord
COLUMNS_TO_REFRESH = [:repository_size, :wiki_size, :lfs_objects_size, :commit_count, :snippets_size].freeze
INCREMENTABLE_COLUMNS = { build_artifacts_size: %i[storage_size], packages_size: %i[storage_size] }.freeze
NAMESPACE_RELATABLE_COLUMNS = [:repository_size, :wiki_size, :lfs_objects_size].freeze
FLAGGED_NAMESPACE_RELATABLE_COLUMNS = [*NAMESPACE_RELATABLE_COLUMNS, :snippets_size].freeze
scope :for_project_ids, ->(project_ids) { where(project_id: project_ids) }
......@@ -31,7 +32,7 @@ def refresh!(only: [])
end
end
if only.empty? || only.any? { |column| NAMESPACE_RELATABLE_COLUMNS.include?(column) }
if only.empty? || only.any? { |column| namespace_relatable_columns.include?(column) }
schedule_namespace_aggregation_worker
end
......@@ -110,6 +111,10 @@ def schedule_namespace_aggregation_worker
Namespaces::ScheduleAggregationWorker.perform_async(project.namespace_id)
end
end
def namespace_relatable_columns
Feature.enabled?(:namespace_snippets_size_stat) ? FLAGGED_NAMESPACE_RELATABLE_COLUMNS : NAMESPACE_RELATABLE_COLUMNS
end
end
ProjectStatistics.prepend_if_ee('EE::ProjectStatistics')
......@@ -1527,7 +1527,7 @@
- :name: project_update_repository_storage
:feature_category: :gitaly
:has_external_dependencies:
:urgency: :low
:urgency: :throttled
:resource_boundary: :unknown
:weight: 1
:idempotent: true
......
......@@ -5,6 +5,7 @@ class ProjectUpdateRepositoryStorageWorker
idempotent!
feature_category :gitaly
urgency :throttled
def perform(project_id, new_repository_storage_key, repository_storage_move_id = nil)
repository_storage_move =
......
---
title: Throttle ProjectUpdateRepositoryStorageWorker Jobs
merge_request: 35230
author:
type: other
---
title: Create associated routes when a new bot user is created
merge_request: 35711
author:
type: fixed
......@@ -10,17 +10,30 @@
# we use randomized approach (e.g. `Array#sample`).
return unless source_project
fork_project = Projects::ForkService.new(
source_project,
user,
namespace: user.namespace,
skip_disk_validation: true
).execute
Sidekiq::Worker.skipping_transaction_check do
fork_project = Projects::ForkService.new(
source_project,
user,
namespace: user.namespace,
skip_disk_validation: true
).execute
if fork_project.valid?
print '.'
else
print 'F'
# Seed-Fu runs this entire fixture in a transaction, so the `after_commit`
# hook won't run until after the fixture is loaded. That is too late
# since the Sidekiq::Testing block has already exited. Force clearing
# the `after_commit` queue to ensure the job is run now.
fork_project.send(:_run_after_commit_queue)
fork_project.import_state.send(:_run_after_commit_queue)
# Expire repository cache after import to ensure
# valid_repo? call below returns a correct answer
fork_project.repository.expire_all_method_caches
if fork_project.valid? && fork_project.valid_repo?
print '.'
else
print 'F'
end
end
end
end
......
......@@ -11310,6 +11310,56 @@ type RunDASTScanPayload {
pipelineUrl: String
}
"""
Represents a resource scanned by a security scan
"""
type ScannedResource {
"""
The HTTP request method used to access the URL
"""
requestMethod: String
"""
The URL scanned by the scanner
"""
url: String
}
"""
The connection type for ScannedResource.
"""
type ScannedResourceConnection {
"""
A list of edges.
"""
edges: [ScannedResourceEdge]
"""
A list of nodes.
"""
nodes: [ScannedResource]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type ScannedResourceEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: ScannedResource
}
"""
Represents summary of a security report
"""
......@@ -11349,6 +11399,31 @@ type SecurityReportSummary {
Represents a section of a summary of a security report
"""
type SecurityReportSummarySection {
"""
A list of the first 20 scanned resources
"""
scannedResources(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): ScannedResourceConnection
"""
Total number of scanned resources
"""
......
......@@ -33217,6 +33217,159 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ScannedResource",
"description": "Represents a resource scanned by a security scan",
"fields": [
{
"name": "requestMethod",
"description": "The HTTP request method used to access the URL",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "url",
"description": "The URL scanned by the scanner",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ScannedResourceConnection",
"description": "The connection type for ScannedResource.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "ScannedResourceEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "ScannedResource",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "ScannedResourceEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "ScannedResource",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "SecurityReportSummary",
......@@ -33319,6 +33472,59 @@
"name": "SecurityReportSummarySection",
"description": "Represents a section of a summary of a security report",
"fields": [
{
"name": "scannedResources",
"description": "A list of the first 20 scanned resources",
"args": [
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "ScannedResourceConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "scannedResourcesCount",
"description": "Total number of scanned resources",
......@@ -1637,6 +1637,15 @@ Autogenerated return type of RunDASTScan
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `pipelineUrl` | String | URL of the pipeline that was created. |
## ScannedResource
Represents a resource scanned by a security scan
| Name | Type | Description |
| --- | ---- | ---------- |
| `requestMethod` | String | The HTTP request method used to access the URL |
| `url` | String | The URL scanned by the scanner |
## SecurityReportSummary
Represents summary of a security report
......
---
redirect_to: '../user/project/description_templates.md#setting-a-default-template-for-merge-requests-and-issues--starter'
redirect_to: '../user/project/description_templates.md'
---
This document was moved to [description_templates](../user/project/description_templates.md#setting-a-default-template-for-merge-requests-and-issues--starter).
This document was moved to [description_templates](../user/project/description_templates.md).
......@@ -666,8 +666,9 @@ appear to be associated to any of the services running, since they all appear to
| `clusters_applications_runner` | `usage_activity_by_stage` | `verify` | | | Unique clusters with Runner enabled |
| `projects_reporting_ci_cd_back_to_github: 0` | `usage_activity_by_stage` | `verify` | | | Unique projects with a GitHub pipeline enabled |
| `merge_requests_users` | `usage_activity_by_stage_monthly` | `create` | | | Unique count of users who used a merge request |
| `nodes` | `topology` | `enablement` | | | The list of server nodes on which GitLab components are running |
| `duration_s` | `topology` | `enablement` | | | Time it took to collect topology data |
| `application_requests_per_hour` | `topology` | `enablement` | | | Number of requests to the web application per hour |
| `nodes` | `topology` | `enablement` | | | The list of server nodes on which GitLab components are running |
| `node_memory_total_bytes` | `topology > nodes` | `enablement` | | | The total available memory of this node |
| `node_cpus` | `topology > nodes` | `enablement` | | | The number of CPU cores of this node |
| `node_services` | `topology > nodes` | `enablement` | | | The list of GitLab services running on this node |
......@@ -873,6 +874,8 @@ The following is example content of the Usage Ping payload.
}
},
"topology": {
"duration_s": 0.013836685999194742,
"application_requests_per_hour": 4224,
"nodes": [
{
"node_memory_total_bytes": 33269903360,
......@@ -897,8 +900,7 @@ The following is example content of the Usage Ping payload.
...
},
...
],
"duration_s": 0.013836685999194742
]
}
}
```
......@@ -81,7 +81,7 @@ changes you made after picking the template and return it to its initial status.
![Description templates](img/description_templates.png)
## Setting a default template for merge requests and issues **(STARTER)**
## Setting a default template for merge requests and issues **(STARTER)**
> - This feature was introduced before [description templates](#overview) and is available in [GitLab Starter](https://about.gitlab.com/pricing/). It can be enabled in the project's settings.
> - Templates for issues were [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28) in GitLab EE 8.1.
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать