Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0f50c47cd7
commit
1760f824bb
26 changed files with 476 additions and 176 deletions
|
@ -1 +1 @@
|
|||
a67a6fdd96ba690d57c919f9a042dceebab2832e
|
||||
ac32a7f07a26a15fb59ea897ee7150f98910903d
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
import { GlPopover, GlButton, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { GlPopover, GlButton, GlTooltipDirective, GlTabs, GlTab } from '@gitlab/ui';
|
||||
import $ from 'jquery';
|
||||
import { keysFor, BOLD_TEXT, ITALIC_TEXT, LINK_TEXT } from '~/behaviors/shortcuts/keybindings';
|
||||
import { getSelectedFragment } from '~/lib/utils/common_utils';
|
||||
import { s__ } from '~/locale';
|
||||
import { s__, __ } from '~/locale';
|
||||
import { CopyAsGFM } from '../../../behaviors/markdown/copy_as_gfm';
|
||||
import ToolbarButton from './toolbar_button.vue';
|
||||
|
||||
|
@ -12,6 +12,8 @@ export default {
|
|||
ToolbarButton,
|
||||
GlPopover,
|
||||
GlButton,
|
||||
GlTabs,
|
||||
GlTab,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
|
@ -144,136 +146,143 @@ export default {
|
|||
italic: keysFor(ITALIC_TEXT),
|
||||
link: keysFor(LINK_TEXT),
|
||||
},
|
||||
i18n: {
|
||||
writeTabTitle: __('Write'),
|
||||
previewTabTitle: __('Preview'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="md-header">
|
||||
<ul class="nav-links clearfix">
|
||||
<li :class="{ active: !previewMarkdown }" class="md-header-tab">
|
||||
<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"
|
||||
type="button"
|
||||
@click="previewMarkdownTab($event)"
|
||||
>
|
||||
{{ __('Preview') }}
|
||||
</button>
|
||||
</li>
|
||||
<li :class="{ active: !previewMarkdown }" class="md-header-toolbar">
|
||||
<toolbar-button
|
||||
tag="**"
|
||||
:button-title="
|
||||
sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), { modifierKey })
|
||||
"
|
||||
:shortcuts="$options.shortcuts.bold"
|
||||
icon="bold"
|
||||
/>
|
||||
<toolbar-button
|
||||
tag="_"
|
||||
:button-title="
|
||||
sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), { modifierKey })
|
||||
"
|
||||
:shortcuts="$options.shortcuts.italic"
|
||||
icon="italic"
|
||||
/>
|
||||
<toolbar-button
|
||||
:prepend="true"
|
||||
:tag="tag"
|
||||
:button-title="__('Insert a quote')"
|
||||
icon="quote"
|
||||
@click="handleQuote"
|
||||
/>
|
||||
<template v-if="canSuggest">
|
||||
<gl-tabs content-class="gl-display-none">
|
||||
<gl-tab
|
||||
title-link-class="gl-pt-3 gl-px-3 js-md-write-button"
|
||||
:title="$options.i18n.writeTabTitle"
|
||||
:active="!previewMarkdown"
|
||||
data-testid="write-tab"
|
||||
@click="writeMarkdownTab($event)"
|
||||
/>
|
||||
<gl-tab
|
||||
title-link-class="gl-pt-3 gl-px-3 js-md-preview-button"
|
||||
:title="$options.i18n.previewTabTitle"
|
||||
:active="previewMarkdown"
|
||||
data-testid="preview-tab"
|
||||
@click="previewMarkdownTab($event)"
|
||||
/>
|
||||
|
||||
<template v-if="!previewMarkdown" #tabs-end>
|
||||
<div class="md-header-toolbar gl-ml-auto gl-pb-3 gl-justify-content-center">
|
||||
<toolbar-button
|
||||
ref="suggestButton"
|
||||
:tag="mdSuggestion"
|
||||
:prepend="true"
|
||||
:button-title="__('Insert suggestion')"
|
||||
:cursor-offset="4"
|
||||
:tag-content="lineContent"
|
||||
icon="doc-code"
|
||||
data-qa-selector="suggestion_button"
|
||||
class="js-suggestion-btn"
|
||||
@click="handleSuggestDismissed"
|
||||
tag="**"
|
||||
:button-title="
|
||||
sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), { modifierKey })
|
||||
"
|
||||
:shortcuts="$options.shortcuts.bold"
|
||||
icon="bold"
|
||||
/>
|
||||
<gl-popover
|
||||
v-if="suggestPopoverVisible"
|
||||
:target="$refs.suggestButton.$el"
|
||||
:css-classes="['diff-suggest-popover']"
|
||||
placement="bottom"
|
||||
:show="suggestPopoverVisible"
|
||||
>
|
||||
<strong>{{ __('New! Suggest changes directly') }}</strong>
|
||||
<p class="mb-2">
|
||||
{{
|
||||
__(
|
||||
'Suggest code changes which can be immediately applied in one click. Try it out!',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<gl-button
|
||||
variant="info"
|
||||
category="primary"
|
||||
size="small"
|
||||
<toolbar-button
|
||||
tag="_"
|
||||
:button-title="
|
||||
sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), { modifierKey })
|
||||
"
|
||||
:shortcuts="$options.shortcuts.italic"
|
||||
icon="italic"
|
||||
/>
|
||||
<toolbar-button
|
||||
:prepend="true"
|
||||
:tag="tag"
|
||||
:button-title="__('Insert a quote')"
|
||||
icon="quote"
|
||||
@click="handleQuote"
|
||||
/>
|
||||
<template v-if="canSuggest">
|
||||
<toolbar-button
|
||||
ref="suggestButton"
|
||||
:tag="mdSuggestion"
|
||||
:prepend="true"
|
||||
:button-title="__('Insert suggestion')"
|
||||
:cursor-offset="4"
|
||||
:tag-content="lineContent"
|
||||
icon="doc-code"
|
||||
data-qa-selector="suggestion_button"
|
||||
class="js-suggestion-btn"
|
||||
@click="handleSuggestDismissed"
|
||||
/>
|
||||
<gl-popover
|
||||
v-if="suggestPopoverVisible"
|
||||
:target="$refs.suggestButton.$el"
|
||||
:css-classes="['diff-suggest-popover']"
|
||||
placement="bottom"
|
||||
:show="suggestPopoverVisible"
|
||||
>
|
||||
{{ __('Got it') }}
|
||||
</gl-button>
|
||||
</gl-popover>
|
||||
</template>
|
||||
<toolbar-button tag="`" tag-block="```" :button-title="__('Insert code')" icon="code" />
|
||||
<toolbar-button
|
||||
tag="[{text}](url)"
|
||||
tag-select="url"
|
||||
:button-title="
|
||||
sprintf(s__('MarkdownEditor|Add a link (%{modifierKey}K)'), { modifierKey })
|
||||
"
|
||||
:shortcuts="$options.shortcuts.link"
|
||||
icon="link"
|
||||
/>
|
||||
<toolbar-button
|
||||
:prepend="true"
|
||||
tag="- "
|
||||
:button-title="__('Add a bullet list')"
|
||||
icon="list-bulleted"
|
||||
/>
|
||||
<toolbar-button
|
||||
:prepend="true"
|
||||
tag="1. "
|
||||
:button-title="__('Add a numbered list')"
|
||||
icon="list-numbered"
|
||||
/>
|
||||
<toolbar-button
|
||||
:prepend="true"
|
||||
tag="- [ ] "
|
||||
:button-title="__('Add a task list')"
|
||||
icon="list-task"
|
||||
/>
|
||||
<toolbar-button
|
||||
:tag="mdCollapsibleSection"
|
||||
:prepend="true"
|
||||
tag-select="Click to expand"
|
||||
:button-title="__('Add a collapsible section')"
|
||||
icon="details-block"
|
||||
/>
|
||||
<toolbar-button
|
||||
:tag="mdTable"
|
||||
:prepend="true"
|
||||
:button-title="__('Add a table')"
|
||||
icon="table"
|
||||
/>
|
||||
<toolbar-button
|
||||
class="js-zen-enter"
|
||||
:prepend="true"
|
||||
:button-title="__('Go full screen')"
|
||||
icon="maximize"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<strong>{{ __('New! Suggest changes directly') }}</strong>
|
||||
<p class="mb-2">
|
||||
{{
|
||||
__(
|
||||
'Suggest code changes which can be immediately applied in one click. Try it out!',
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<gl-button
|
||||
variant="info"
|
||||
category="primary"
|
||||
size="small"
|
||||
@click="handleSuggestDismissed"
|
||||
>
|
||||
{{ __('Got it') }}
|
||||
</gl-button>
|
||||
</gl-popover>
|
||||
</template>
|
||||
<toolbar-button tag="`" tag-block="```" :button-title="__('Insert code')" icon="code" />
|
||||
<toolbar-button
|
||||
tag="[{text}](url)"
|
||||
tag-select="url"
|
||||
:button-title="
|
||||
sprintf(s__('MarkdownEditor|Add a link (%{modifierKey}K)'), { modifierKey })
|
||||
"
|
||||
:shortcuts="$options.shortcuts.link"
|
||||
icon="link"
|
||||
/>
|
||||
<toolbar-button
|
||||
:prepend="true"
|
||||
tag="- "
|
||||
:button-title="__('Add a bullet list')"
|
||||
icon="list-bulleted"
|
||||
/>
|
||||
<toolbar-button
|
||||
:prepend="true"
|
||||
tag="1. "
|
||||
:button-title="__('Add a numbered list')"
|
||||
icon="list-numbered"
|
||||
/>
|
||||
<toolbar-button
|
||||
:prepend="true"
|
||||
tag="- [ ] "
|
||||
:button-title="__('Add a task list')"
|
||||
icon="list-task"
|
||||
/>
|
||||
<toolbar-button
|
||||
:tag="mdCollapsibleSection"
|
||||
:prepend="true"
|
||||
tag-select="Click to expand"
|
||||
:button-title="__('Add a collapsible section')"
|
||||
icon="details-block"
|
||||
/>
|
||||
<toolbar-button
|
||||
:tag="mdTable"
|
||||
:prepend="true"
|
||||
:button-title="__('Add a table')"
|
||||
icon="table"
|
||||
/>
|
||||
<toolbar-button
|
||||
class="js-zen-enter"
|
||||
:prepend="true"
|
||||
:button-title="__('Go full screen')"
|
||||
icon="maximize"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</gl-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -67,6 +67,27 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gl-tabs-nav {
|
||||
@include media-breakpoint-down(xs) {
|
||||
.nav-item {
|
||||
flex: 1;
|
||||
border-bottom: 1px solid $border-color;
|
||||
}
|
||||
|
||||
.gl-tab-nav-item {
|
||||
padding-top: $gl-padding-4;
|
||||
padding-bottom: $gl-padding-8;
|
||||
}
|
||||
|
||||
.md-header-toolbar {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-top: $gl-padding-8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.md-header-tab {
|
||||
|
|
|
@ -23,4 +23,42 @@ module StorageHelper
|
|||
|
||||
_("Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / Pipeline Artifacts: %{counter_pipeline_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}") % counters
|
||||
end
|
||||
|
||||
def storage_enforcement_banner_info(namespace)
|
||||
return if namespace.paid?
|
||||
return unless namespace.storage_enforcement_date && namespace.storage_enforcement_date >= Date.today
|
||||
return if user_dismissed_storage_enforcement_banner?(namespace)
|
||||
|
||||
{
|
||||
text: html_escape_once(s_("UsageQuota|From %{storage_enforcement_date} storage limits will apply to this namespace. " \
|
||||
"View and manage your usage in %{strong_start}Group Settings > Usage quotas%{strong_end}.")).html_safe %
|
||||
{ storage_enforcement_date: namespace.storage_enforcement_date, strong_start: "<strong>".html_safe, strong_end: "</strong>".html_safe },
|
||||
variant: 'warning',
|
||||
callouts_path: group_callouts_path,
|
||||
callouts_feature_name: storage_enforcement_banner_user_callouts_feature_name(namespace),
|
||||
learn_more_link: link_to(_('Learn more.'), help_page_path('/'), rel: 'noopener noreferrer', target: '_blank') # TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def storage_enforcement_banner_user_callouts_feature_name(namespace)
|
||||
"storage_enforcement_banner_#{storage_enforcement_banner_threshold(namespace)}_enforcement_threshold"
|
||||
end
|
||||
|
||||
def storage_enforcement_banner_threshold(namespace)
|
||||
days_to_enforcement_date = (namespace.storage_enforcement_date - Date.today)
|
||||
|
||||
return :first if days_to_enforcement_date > 30
|
||||
return :second if days_to_enforcement_date > 15 && days_to_enforcement_date <= 30
|
||||
return :third if days_to_enforcement_date > 7 && days_to_enforcement_date <= 15
|
||||
return :fourth if days_to_enforcement_date > 0 && days_to_enforcement_date <= 7
|
||||
end
|
||||
|
||||
def user_dismissed_storage_enforcement_banner?(namespace)
|
||||
return false unless current_user
|
||||
|
||||
current_user.dismissed_callout_for_group?(feature_name: storage_enforcement_banner_user_callouts_feature_name(namespace),
|
||||
group: namespace)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -79,7 +79,7 @@ class ContainerRepository < ApplicationRecord
|
|||
)
|
||||
end
|
||||
|
||||
state_machine :migration_state, initial: :default do
|
||||
state_machine :migration_state, initial: :default, use_transactions: false do
|
||||
state :pre_importing do
|
||||
validates :migration_pre_import_started_at, presence: true
|
||||
validates :migration_pre_import_done_at, presence: false
|
||||
|
|
|
@ -513,6 +513,12 @@ class Namespace < ApplicationRecord
|
|||
Feature.enabled?(:create_project_namespace_on_project_create, self, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def storage_enforcement_date
|
||||
# should return something like Date.new(2022, 02, 03)
|
||||
# TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expire_child_caches
|
||||
|
|
|
@ -10,7 +10,11 @@ module Users
|
|||
|
||||
enum feature_name: {
|
||||
invite_members_banner: 1,
|
||||
approaching_seat_count_threshold: 2 # EE-only
|
||||
approaching_seat_count_threshold: 2, # EE-only
|
||||
storage_enforcement_banner_first_enforcement_threshold: 43,
|
||||
storage_enforcement_banner_second_enforcement_threshold: 44,
|
||||
storage_enforcement_banner_third_enforcement_threshold: 45,
|
||||
storage_enforcement_banner_fourth_enforcement_threshold: 46
|
||||
}
|
||||
|
||||
validates :group, presence: true
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
- add_page_specific_style 'page_bundles/ci_status'
|
||||
|
||||
- runner_name = "##{@runner.id} (#{@runner.short_sha})"
|
||||
- if Feature.enabled?(:runner_read_only_admin_view)
|
||||
- if Feature.enabled?(:runner_read_only_admin_view, default_enabled: :yaml)
|
||||
- breadcrumb_title _('Edit')
|
||||
- page_title _('Edit'), runner_name
|
||||
- add_to_breadcrumbs _('Runners'), admin_runners_path
|
||||
|
|
|
@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/350164
|
|||
milestone: '14.7'
|
||||
type: development
|
||||
group: group::runner
|
||||
default_enabled: false
|
||||
default_enabled: true
|
||||
|
|
|
@ -295,6 +295,36 @@ To use an external Prometheus server:
|
|||
|
||||
1. Reload the Prometheus server.
|
||||
|
||||
### Configure the storage retention size
|
||||
|
||||
Prometheus has several custom flags to configure local storage:
|
||||
|
||||
- `storage.tsdb.retention.time`: when to remove old data. Defaults to `15d`. Overrides
|
||||
`storage.tsdb.retention` if this flag is set to anything other than the default.
|
||||
- `storage.tsdb.retention.size`: [EXPERIMENTAL] the maximum number of bytes of storage blocks to
|
||||
retain. The oldest data is removed first. Defaults to `0` (disabled). This flag is experimental
|
||||
and may change in future releases. Units supported: `B`, `KB`, `MB`, `GB`, `TB`, `PB`, `EB`. For
|
||||
example, `512MB`.
|
||||
|
||||
To configure the storage retention size:
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
prometheus['flags'] = {
|
||||
'storage.tsdb.path' => "/var/opt/gitlab/prometheus/data",
|
||||
'storage.tsdb.retention.time' => "7d",
|
||||
'storage.tsdb.retention.size' => "2GB",
|
||||
'config.file' => "/var/opt/gitlab/prometheus/prometheus.yml"
|
||||
}
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
## Viewing performance metrics
|
||||
|
||||
You can visit `http://localhost:9090` for the dashboard that Prometheus offers by default.
|
||||
|
@ -402,3 +432,35 @@ To disable the monitoring of Kubernetes:
|
|||
|
||||
1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to
|
||||
take effect.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
## `/var/opt/gitlab/prometheus` consumes too much disk space
|
||||
|
||||
If you are **not** using Prometheus monitoring:
|
||||
|
||||
1. [Disable Prometheus](index.md#configuring-prometheus).
|
||||
1. Delete the data under `/var/opt/gitlab/prometheus`.
|
||||
|
||||
If you are using Prometheus monitoring:
|
||||
|
||||
1. Stop Prometheus (deleting data while it's running can cause data corruption):
|
||||
|
||||
```shell
|
||||
gitlab-ctl stop prometheus
|
||||
```
|
||||
|
||||
1. Delete the data under `/var/opt/gitlab/prometheus/data`.
|
||||
1. Start the service again:
|
||||
|
||||
```shell
|
||||
gitlab-ctl start prometheus
|
||||
```
|
||||
|
||||
1. Verify the service is up and running:
|
||||
|
||||
```shell
|
||||
gitlab-ctl status prometheus
|
||||
```
|
||||
|
||||
1. Optional. [Configure the storage retention size](index.md#configure-the-storage-retention-size).
|
||||
|
|
|
@ -372,7 +372,8 @@ DELETE /projects/:id/registry/repositories/:repository_id/tags
|
|||
| `keep_n` | integer | no | The amount of latest tags of given name to keep. |
|
||||
| `older_than` | string | no | Tags to delete that are older than the given time, written in human readable form `1h`, `1d`, `1month`. |
|
||||
|
||||
This API call performs the following operations:
|
||||
This API returns [HTTP response status code 202](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202)
|
||||
if successful, and performs the following operations:
|
||||
|
||||
- It orders all tags by creation date. The creation date is the time of the
|
||||
manifest creation, not the time of tag push.
|
||||
|
|
|
@ -1143,7 +1143,7 @@ Input type: `ConfigureSecretDetectionInput`
|
|||
|
||||
### `Mutation.corpusCreate`
|
||||
|
||||
Available only when feature flag `corpus_management` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
|
||||
Available only when feature flag `corpus_management` is enabled. This flag is enabled by default.
|
||||
|
||||
Input type: `CorpusCreateInput`
|
||||
|
||||
|
@ -13467,7 +13467,7 @@ Represents vulnerability finding of a security report on the pipeline.
|
|||
| <a id="projectcontainerexpirationpolicy"></a>`containerExpirationPolicy` | [`ContainerExpirationPolicy`](#containerexpirationpolicy) | Container expiration policy of the project. |
|
||||
| <a id="projectcontainerregistryenabled"></a>`containerRegistryEnabled` | [`Boolean`](#boolean) | Indicates if Container Registry is enabled for the current user. |
|
||||
| <a id="projectcontainerrepositoriescount"></a>`containerRepositoriesCount` | [`Int!`](#int) | Number of container repositories in the project. |
|
||||
| <a id="projectcorpuses"></a>`corpuses` | [`CoverageFuzzingCorpusConnection`](#coveragefuzzingcorpusconnection) | Find corpuses of the project. Available only when feature flag `corpus_management` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
|
||||
| <a id="projectcorpuses"></a>`corpuses` | [`CoverageFuzzingCorpusConnection`](#coveragefuzzingcorpusconnection) | Find corpuses of the project. Available only when feature flag `corpus_management` is enabled. This flag is enabled by default. (see [Connections](#connections)) |
|
||||
| <a id="projectcreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of the project creation. |
|
||||
| <a id="projectdastscannerprofiles"></a>`dastScannerProfiles` | [`DastScannerProfileConnection`](#dastscannerprofileconnection) | DAST scanner profiles associated with the project. (see [Connections](#connections)) |
|
||||
| <a id="projectdastsiteprofiles"></a>`dastSiteProfiles` | [`DastSiteProfileConnection`](#dastsiteprofileconnection) | DAST Site Profiles associated with the project. (see [Connections](#connections)) |
|
||||
|
|
|
@ -6,7 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Install the GitLab Agent **(FREE)**
|
||||
|
||||
> [Moved](https://gitlab.com/groups/gitlab-org/-/epics/6290) from GitLab Premium to GitLab Free in 14.5.
|
||||
> - [Moved](https://gitlab.com/groups/gitlab-org/-/epics/6290) from GitLab Premium to GitLab Free in 14.5.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/merge_requests/594) multi-arch images in GitLab 14.8. The first multi-arch release is `v14.8.1`. It supports AMD64 and ARM64 architectures.
|
||||
|
||||
To connect a cluster to GitLab, you need to install the GitLab Agent
|
||||
onto your cluster.
|
||||
|
|
|
@ -239,6 +239,17 @@ SPDY protocol.
|
|||
We [plan to add support for these features](https://gitlab.com/gitlab-org/gitlab/-/issues/346248)
|
||||
in a future version of GitLab.
|
||||
|
||||
### `kubectl` requires TLS
|
||||
|
||||
`kubectl` would never send credentials over an unencrypted connection. Self-managed users should ensure that their
|
||||
GitLab instance is configured with TLS for the CI/CD tunnel feature to work. Trying to use it without TLS
|
||||
would produce errors:
|
||||
|
||||
```shell
|
||||
$ kubectl get pods
|
||||
error: You must be logged in to the server (the server has asked for the client to provide credentials)
|
||||
```
|
||||
|
||||
## Use impersonation to restrict project and group access **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345014) in GitLab 14.5.
|
||||
|
|
|
@ -39153,6 +39153,9 @@ msgstr ""
|
|||
msgid "UsageQuota|File attachments and smaller design graphics."
|
||||
msgstr ""
|
||||
|
||||
msgid "UsageQuota|From %{storage_enforcement_date} storage limits will apply to this namespace. View and manage your usage in %{strong_start}Group Settings > Usage quotas%{strong_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "UsageQuota|Git repository."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
"@gitlab/at.js": "1.5.7",
|
||||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "2.5.0",
|
||||
"@gitlab/ui": "36.1.0",
|
||||
"@gitlab/ui": "36.6.0",
|
||||
"@gitlab/visual-review-tools": "1.6.1",
|
||||
"@rails/actioncable": "6.1.4-6",
|
||||
"@rails/ujs": "6.1.4-6",
|
||||
|
|
|
@ -55,7 +55,7 @@ module QA
|
|||
# wait for the "Save comment" button to disappear
|
||||
saved = has_no_element?(:save_comment_button)
|
||||
|
||||
raise ExpectationNotMet, %q(There was a problem while adding the annotation) unless saved
|
||||
raise RSpec::Expectations::ExpectationNotMetError, %q(There was a problem while adding the annotation) unless saved
|
||||
end
|
||||
|
||||
def add_design(design_file_path)
|
||||
|
|
|
@ -133,7 +133,7 @@ RSpec.describe 'Merge request > User posts notes', :js do
|
|||
describe 'when previewing a note' do
|
||||
it 'shows the toolbar buttons when editing a note' do
|
||||
page.within('.js-main-target-form') do
|
||||
expect(page).to have_css('.md-header-toolbar.active')
|
||||
expect(page).to have_css('.md-header-toolbar')
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -141,7 +141,7 @@ RSpec.describe 'Merge request > User posts notes', :js do
|
|||
wait_for_requests
|
||||
find('.js-md-preview-button').click
|
||||
page.within('.js-main-target-form') do
|
||||
expect(page).not_to have_css('.md-header-toolbar.active')
|
||||
expect(page).not_to have_css('.md-header-toolbar')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,8 +38,8 @@ exports[`packages_list_app renders 1`] = `
|
|||
class="gl-font-size-h-display gl-line-height-36 h4"
|
||||
>
|
||||
|
||||
There are no packages yet
|
||||
|
||||
There are no packages yet
|
||||
|
||||
</h1>
|
||||
|
||||
<p
|
||||
|
|
|
@ -38,8 +38,8 @@ exports[`PackagesListApp renders 1`] = `
|
|||
class="gl-font-size-h-display gl-line-height-36 h4"
|
||||
>
|
||||
|
||||
There are no packages yet
|
||||
|
||||
There are no packages yet
|
||||
|
||||
</h1>
|
||||
|
||||
<p
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import AxiosMockAdapter from 'axios-mock-adapter';
|
||||
import $ from 'jquery';
|
||||
import { TEST_HOST, FIXTURES_PATH } from 'spec/test_constants';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
||||
const markdownPreviewPath = `${TEST_HOST}/preview`;
|
||||
const markdownDocsPath = `${TEST_HOST}/docs`;
|
||||
|
@ -12,8 +12,8 @@ const textareaValue = 'testing\n123';
|
|||
const uploadsPath = 'test/uploads';
|
||||
|
||||
function assertMarkdownTabs(isWrite, writeLink, previewLink, wrapper) {
|
||||
expect(writeLink.element.parentNode.classList.contains('active')).toBe(isWrite);
|
||||
expect(previewLink.element.parentNode.classList.contains('active')).toBe(!isWrite);
|
||||
expect(writeLink.element.children[0].classList.contains('active')).toBe(isWrite);
|
||||
expect(previewLink.element.children[0].classList.contains('active')).toBe(!isWrite);
|
||||
expect(wrapper.find('.md-preview-holder').element.style.display).toBe(isWrite ? 'none' : '');
|
||||
}
|
||||
|
||||
|
@ -29,14 +29,13 @@ describe('Markdown field component', () => {
|
|||
|
||||
afterEach(() => {
|
||||
subject.destroy();
|
||||
subject = null;
|
||||
axiosMock.restore();
|
||||
});
|
||||
|
||||
function createSubject(lines = []) {
|
||||
// We actually mount a wrapper component so that we can force Vue to rerender classes in order to test a regression
|
||||
// caused by mixing Vanilla JS and Vue.
|
||||
subject = mount(
|
||||
subject = mountExtended(
|
||||
{
|
||||
components: {
|
||||
MarkdownField,
|
||||
|
@ -72,8 +71,8 @@ describe('Markdown field component', () => {
|
|||
);
|
||||
}
|
||||
|
||||
const getPreviewLink = () => subject.find('.nav-links .js-preview-link');
|
||||
const getWriteLink = () => subject.find('.nav-links .js-write-link');
|
||||
const getPreviewLink = () => subject.findByTestId('preview-tab');
|
||||
const getWriteLink = () => subject.findByTestId('write-tab');
|
||||
const getMarkdownButton = () => subject.find('.js-md');
|
||||
const getAllMarkdownButtons = () => subject.findAll('.js-md');
|
||||
const getVideo = () => subject.find('video');
|
||||
|
@ -107,15 +106,15 @@ describe('Markdown field component', () => {
|
|||
|
||||
it('sets preview link as active', async () => {
|
||||
previewLink = getPreviewLink();
|
||||
previewLink.trigger('click');
|
||||
previewLink.vm.$emit('click', { target: {} });
|
||||
|
||||
await nextTick();
|
||||
expect(previewLink.element.parentNode.classList.contains('active')).toBeTruthy();
|
||||
expect(previewLink.element.children[0].classList.contains('active')).toBe(true);
|
||||
});
|
||||
|
||||
it('shows preview loading text', async () => {
|
||||
previewLink = getPreviewLink();
|
||||
previewLink.trigger('click');
|
||||
previewLink.vm.$emit('click', { target: {} });
|
||||
|
||||
await nextTick();
|
||||
expect(subject.find('.md-preview-holder').element.textContent.trim()).toContain('Loading…');
|
||||
|
@ -126,7 +125,7 @@ describe('Markdown field component', () => {
|
|||
|
||||
previewLink = getPreviewLink();
|
||||
|
||||
previewLink.trigger('click');
|
||||
previewLink.vm.$emit('click', { target: {} });
|
||||
|
||||
await axios.waitFor(markdownPreviewPath);
|
||||
expect(subject.find('.md-preview-holder').element.innerHTML).toContain(previewHTML);
|
||||
|
@ -135,7 +134,7 @@ describe('Markdown field component', () => {
|
|||
|
||||
it('calls video.pause() on comment input when isSubmitting is changed to true', async () => {
|
||||
previewLink = getPreviewLink();
|
||||
previewLink.trigger('click');
|
||||
previewLink.vm.$emit('click', { target: {} });
|
||||
|
||||
await axios.waitFor(markdownPreviewPath);
|
||||
const video = getVideo();
|
||||
|
@ -151,19 +150,19 @@ describe('Markdown field component', () => {
|
|||
writeLink = getWriteLink();
|
||||
previewLink = getPreviewLink();
|
||||
|
||||
writeLink.trigger('click');
|
||||
writeLink.vm.$emit('click', { target: {} });
|
||||
await nextTick();
|
||||
|
||||
assertMarkdownTabs(true, writeLink, previewLink, subject);
|
||||
writeLink.trigger('click');
|
||||
writeLink.vm.$emit('click', { target: {} });
|
||||
await nextTick();
|
||||
|
||||
assertMarkdownTabs(true, writeLink, previewLink, subject);
|
||||
previewLink.trigger('click');
|
||||
previewLink.vm.$emit('click', { target: {} });
|
||||
await nextTick();
|
||||
|
||||
assertMarkdownTabs(false, writeLink, previewLink, subject);
|
||||
previewLink.trigger('click');
|
||||
previewLink.vm.$emit('click', { target: {} });
|
||||
await nextTick();
|
||||
|
||||
assertMarkdownTabs(false, writeLink, previewLink, subject);
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import $ from 'jquery';
|
||||
import { nextTick } from 'vue';
|
||||
import { GlTabs } from '@gitlab/ui';
|
||||
import HeaderComponent from '~/vue_shared/components/markdown/header.vue';
|
||||
import ToolbarButton from '~/vue_shared/components/markdown/toolbar_button.vue';
|
||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
|
||||
|
||||
describe('Markdown field header component', () => {
|
||||
let wrapper;
|
||||
|
||||
const createWrapper = (props) => {
|
||||
wrapper = shallowMount(HeaderComponent, {
|
||||
wrapper = shallowMountExtended(HeaderComponent, {
|
||||
propsData: {
|
||||
previewMarkdown: false,
|
||||
...props,
|
||||
},
|
||||
stubs: { GlTabs },
|
||||
});
|
||||
};
|
||||
|
||||
const findWriteTab = () => wrapper.findByTestId('write-tab');
|
||||
const findPreviewTab = () => wrapper.findByTestId('preview-tab');
|
||||
const findToolbarButtons = () => wrapper.findAll(ToolbarButton);
|
||||
const findToolbarButtonByProp = (prop, value) =>
|
||||
findToolbarButtons()
|
||||
|
@ -34,7 +38,6 @@ describe('Markdown field header component', () => {
|
|||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
describe('markdown header buttons', () => {
|
||||
|
@ -75,23 +78,26 @@ describe('Markdown field header component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('renders `write` link as active when previewMarkdown is false', () => {
|
||||
expect(wrapper.find('li:nth-child(1)').classes()).toContain('active');
|
||||
it('activates `write` tab when previewMarkdown is false', () => {
|
||||
expect(findWriteTab().attributes('active')).toBe('true');
|
||||
expect(findPreviewTab().attributes('active')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('renders `preview` link as active when previewMarkdown is true', () => {
|
||||
it('activates `preview` tab when previewMarkdown is true', () => {
|
||||
createWrapper({ previewMarkdown: true });
|
||||
|
||||
expect(wrapper.find('li:nth-child(2)').classes()).toContain('active');
|
||||
expect(findWriteTab().attributes('active')).toBeUndefined();
|
||||
expect(findPreviewTab().attributes('active')).toBe('true');
|
||||
});
|
||||
|
||||
it('emits toggle markdown event when clicking preview', async () => {
|
||||
wrapper.find('.js-preview-link').trigger('click');
|
||||
it('emits toggle markdown event when clicking preview tab', async () => {
|
||||
const eventData = { target: {} };
|
||||
findPreviewTab().vm.$emit('click', eventData);
|
||||
|
||||
await nextTick();
|
||||
expect(wrapper.emitted('preview-markdown').length).toEqual(1);
|
||||
|
||||
wrapper.find('.js-write-link').trigger('click');
|
||||
findWriteTab().vm.$emit('click', eventData);
|
||||
|
||||
await nextTick();
|
||||
expect(wrapper.emitted('write-markdown').length).toEqual(1);
|
||||
|
@ -109,12 +115,10 @@ describe('Markdown field header component', () => {
|
|||
});
|
||||
|
||||
it('blurs preview link after click', () => {
|
||||
const link = wrapper.find('li:nth-child(2) button');
|
||||
jest.spyOn(HTMLElement.prototype, 'blur').mockImplementation();
|
||||
const target = { blur: jest.fn() };
|
||||
findPreviewTab().vm.$emit('click', { target });
|
||||
|
||||
link.trigger('click');
|
||||
|
||||
expect(link.element.blur).toHaveBeenCalled();
|
||||
expect(target.blur).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders markdown table template', () => {
|
||||
|
|
|
@ -50,4 +50,87 @@ RSpec.describe StorageHelper do
|
|||
expect(helper.storage_counters_details(namespace_stats)).to eq(message)
|
||||
end
|
||||
end
|
||||
|
||||
describe "storage_enforcement_banner" do
|
||||
let_it_be_with_refind(:current_user) { create(:user) }
|
||||
let_it_be(:free_group) { create(:group) }
|
||||
let_it_be(:paid_group) { create(:group) }
|
||||
|
||||
before do
|
||||
allow(helper).to receive(:current_user) { current_user }
|
||||
allow(Gitlab).to receive(:com?).and_return(true)
|
||||
allow(paid_group).to receive(:paid?).and_return(true)
|
||||
end
|
||||
|
||||
describe "#storage_enforcement_banner_info" do
|
||||
it 'returns nil when namespace is not free' do
|
||||
expect(storage_enforcement_banner_info(paid_group)).to be(nil)
|
||||
end
|
||||
|
||||
it 'returns nil when storage_enforcement_date is not set' do
|
||||
allow(free_group).to receive(:storage_enforcement_date).and_return(nil)
|
||||
|
||||
expect(storage_enforcement_banner_info(free_group)).to be(nil)
|
||||
end
|
||||
|
||||
it 'returns a hash when storage_enforcement_date is set' do
|
||||
storage_enforcement_date = Date.today + 30
|
||||
allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
|
||||
|
||||
expect(storage_enforcement_banner_info(free_group)).to eql({
|
||||
text: "From #{storage_enforcement_date} storage limits will apply to this namespace. View and manage your usage in <strong>Group Settings > Usage quotas</strong>.",
|
||||
variant: 'warning',
|
||||
callouts_feature_name: 'storage_enforcement_banner_second_enforcement_threshold',
|
||||
callouts_path: '/-/users/group_callouts',
|
||||
learn_more_link: '<a rel="noopener noreferrer" target="_blank" href="/help//">Learn more.</a>'
|
||||
})
|
||||
end
|
||||
|
||||
context 'when storage_enforcement_date is set and dismissed callout exists' do
|
||||
before do
|
||||
create(:group_callout,
|
||||
user: current_user,
|
||||
group_id: free_group.id,
|
||||
feature_name: 'storage_enforcement_banner_second_enforcement_threshold')
|
||||
storage_enforcement_date = Date.today + 30
|
||||
allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
|
||||
end
|
||||
|
||||
it { expect(storage_enforcement_banner_info(free_group)).to be(nil) }
|
||||
end
|
||||
|
||||
context 'callouts_feature_name' do
|
||||
let(:days_from_now) { 45 }
|
||||
|
||||
subject do
|
||||
storage_enforcement_date = Date.today + days_from_now
|
||||
allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
|
||||
|
||||
storage_enforcement_banner_info(free_group)[:callouts_feature_name]
|
||||
end
|
||||
|
||||
it 'returns first callouts_feature_name' do
|
||||
is_expected.to eq('storage_enforcement_banner_first_enforcement_threshold')
|
||||
end
|
||||
|
||||
context 'returns second callouts_feature_name' do
|
||||
let(:days_from_now) { 20 }
|
||||
|
||||
it { is_expected.to eq('storage_enforcement_banner_second_enforcement_threshold') }
|
||||
end
|
||||
|
||||
context 'returns third callouts_feature_name' do
|
||||
let(:days_from_now) { 13 }
|
||||
|
||||
it { is_expected.to eq('storage_enforcement_banner_third_enforcement_threshold') }
|
||||
end
|
||||
|
||||
context 'returns fourth callouts_feature_name' do
|
||||
let(:days_from_now) { 3 }
|
||||
|
||||
it { is_expected.to eq('storage_enforcement_banner_fourth_enforcement_threshold') }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -339,6 +339,55 @@ RSpec.describe ContainerRepository, :aggregate_failures do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when triggering registry API requests' do
|
||||
let(:repository_state) { nil }
|
||||
let(:repository) { create(:container_repository, repository_state) }
|
||||
|
||||
shared_examples 'a state machine configured with use_transactions: false' do
|
||||
it 'executes the registry API request outside of a transaction', :delete do
|
||||
expect(repository).to receive(:save).and_call_original do
|
||||
expect(ApplicationRecord.connection.transaction_open?).to be true
|
||||
end
|
||||
|
||||
expect(repository).to receive(:try_import) do
|
||||
expect(ApplicationRecord.connection.transaction_open?).to be false
|
||||
end
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'when responding to a start_pre_import event' do
|
||||
subject { repository.start_pre_import }
|
||||
|
||||
it_behaves_like 'a state machine configured with use_transactions: false'
|
||||
end
|
||||
|
||||
context 'when responding to a retry_pre_import event' do
|
||||
let(:repository_state) { :import_aborted }
|
||||
|
||||
subject { repository.retry_pre_import }
|
||||
|
||||
it_behaves_like 'a state machine configured with use_transactions: false'
|
||||
end
|
||||
|
||||
context 'when responding to a start_import event' do
|
||||
let(:repository_state) { :pre_import_done }
|
||||
|
||||
subject { repository.start_import }
|
||||
|
||||
it_behaves_like 'a state machine configured with use_transactions: false'
|
||||
end
|
||||
|
||||
context 'when responding to a retry_import event' do
|
||||
let(:repository_state) { :import_aborted }
|
||||
|
||||
subject { repository.retry_import }
|
||||
|
||||
it_behaves_like 'a state machine configured with use_transactions: false'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#retry_aborted_migration' do
|
||||
subject { repository.retry_aborted_migration }
|
||||
|
||||
|
|
|
@ -2219,4 +2219,13 @@ RSpec.describe Namespace do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'storage_enforcement_date' do
|
||||
let_it_be(:namespace) { create(:group) }
|
||||
|
||||
# Date TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632
|
||||
it 'returns false' do
|
||||
expect(namespace.storage_enforcement_date).to be(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -986,10 +986,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.5.0.tgz#e0569916fa858462b1801cc90ef8dd9706a12e96"
|
||||
integrity sha512-cH/EBs//wdkH6kG+kDpvRCIl63/A8JgjAhBJ+ZWucPgtNCDD6x6RDMGdQrxSqhYwcCKDoLStfcxmblBkuiSRXQ==
|
||||
|
||||
"@gitlab/ui@36.1.0":
|
||||
version "36.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-36.1.0.tgz#1cd3d74fabd429a5ff5086eb1f6b4db22506e1b3"
|
||||
integrity sha512-hTSG1l12AX+2SuGu+04bTc3lt1xE4FXej7O1UIrGELo197GfnpfnQM76/+JK0+b1w8vHw5MODBlt/c536dgaVg==
|
||||
"@gitlab/ui@36.6.0":
|
||||
version "36.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-36.6.0.tgz#902ec76623de3b46d450fbe2074d00a39a58d61c"
|
||||
integrity sha512-hHuknkt4KTQVPEA8t+Cg29hocqMUv4bYfVH7Hinj3qFaIK32zMKUGQ2P/w5BG8R+cP9PTjw+WxNYc4WpRPpcUw==
|
||||
dependencies:
|
||||
"@babel/standalone" "^7.0.0"
|
||||
bootstrap-vue "2.20.1"
|
||||
|
|
Loading…
Reference in a new issue