Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-11-29 15:18:04 +00:00
parent 06937a48fd
commit 5e081a82c8
49 changed files with 696 additions and 284 deletions

View File

@ -1 +1 @@
3193622a3bea75c0eb55a4a03718890894305b7e
25020130cc7c734d56bc729cba1134f7de89cb7c

View File

@ -124,6 +124,12 @@ export const SYNC_STATUS_BADGES = {
text: s__('Environment|Reconciling'),
popoverText: s__('Deployment|Flux sync reconciling'),
},
suspended: {
variant: 'warning',
icon: 'status-paused',
text: __('Paused'),
popoverText: s__('Deployment|Flux sync is suspended'),
},
stalled: {
variant: 'warning',
icon: 'status-paused',

View File

@ -158,7 +158,10 @@ export default {
return Object.values(this.failedState).some((item) => item);
},
fluxResourceStatus() {
return this.fluxKustomization.conditions || this.fluxHelmRelease.conditions;
const conditions = this.fluxKustomization.conditions || this.fluxHelmRelease.conditions || [];
const spec = this.fluxKustomization.spec || this.fluxHelmRelease.spec || false;
return { conditions, suspend: spec?.suspend };
},
fluxNamespace() {
return (
@ -185,7 +188,7 @@ export default {
return {
name: item.metadata.name,
namespace: item.metadata.namespace,
status: fluxSyncStatus(item.status.conditions).status,
status: fluxSyncStatus({ conditions: item.status.conditions }).status,
labels: item.metadata.labels,
annotations: item.metadata.annotations,
kind: item.kind,

View File

@ -60,9 +60,9 @@ export default {
required: true,
},
fluxResourceStatus: {
type: Array,
type: Object,
required: false,
default: () => [],
default: () => {},
},
fluxNamespace: {
type: String,
@ -144,6 +144,8 @@ export default {
const fluxStatus = fluxSyncStatus(this.fluxResourceStatus);
switch (fluxStatus.status) {
case 'suspended':
return SYNC_STATUS_BADGES.suspended;
case 'failed':
return {
...SYNC_STATUS_BADGES.failed,
@ -171,7 +173,7 @@ export default {
return Boolean(this.fluxConnectionParams.resourceType);
},
fluxResourcePresent() {
return Boolean(this.fluxResourceStatus?.length);
return Boolean(this.fluxResourceStatus?.conditions?.length);
},
fluxBadgeHref() {
return this.fluxResourcePresent ? '#' : null;

View File

@ -72,7 +72,7 @@ export default {
fluxKustomizationStatus() {
if (!this.fluxKustomization.conditions?.length) return '';
return fluxSyncStatus(this.fluxKustomization.conditions).status;
return fluxSyncStatus({ conditions: this.fluxKustomization.conditions }).status;
},
fluxInventory() {
return (

View File

@ -56,7 +56,12 @@ const fluxAnyFailed = (fluxConditions) => {
});
};
export const fluxSyncStatus = (fluxConditions) => {
export const fluxSyncStatus = (fluxResourceStatus) => {
const fluxConditions = fluxResourceStatus.conditions;
if (fluxResourceStatus.suspend) {
return { status: 'suspended' };
}
if (fluxAnyFailed(fluxConditions)) {
return { status: 'failed', message: fluxAnyFailed(fluxConditions).message };
}

View File

@ -42,7 +42,7 @@ export default {
},
computed: {
isK8sOptionSelected() {
return this.selectedTarget === K8S_OPTION;
return this.selectedTarget === K8S_OPTION.value;
},
},
mounted() {

View File

@ -1,22 +1,66 @@
import { s__ } from '~/locale';
export const K8S_OPTION = s__('DeploymentTarget|Kubernetes (GKE, EKS, OpenShift, and so on)');
export const K8S_OPTION = {
value: 'kubernetes',
text: s__('DeploymentTarget|Kubernetes (GKE, EKS, OpenShift, and so on)'),
};
export const DEPLOYMENT_TARGET_SELECTIONS = [
K8S_OPTION,
s__('DeploymentTarget|Managed container runtime (Fargate, Cloud Run, DigitalOcean App)'),
s__('DeploymentTarget|Self-managed container runtime (Podman, Docker Swarm, Docker Compose)'),
s__('DeploymentTarget|Heroku'),
s__('DeploymentTarget|Virtual machine (for example, EC2)'),
s__('DeploymentTarget|Mobile app store'),
s__('DeploymentTarget|Registry (package or container)'),
s__('DeploymentTarget|Infrastructure provider (Terraform, Cloudformation, and so on)'),
s__('DeploymentTarget|Serverless backend (Lambda, Cloud functions)'),
s__('DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)'),
s__('DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)'),
s__('DeploymentTarget|GitLab Pages'),
s__('DeploymentTarget|Other hosting service'),
s__('DeploymentTarget|No deployment planned'),
{
value: 'managed_container_runtime',
text: s__('DeploymentTarget|Managed container runtime (Fargate, Cloud Run, DigitalOcean App)'),
},
{
value: 'self_managed_container_runtime',
text: s__(
'DeploymentTarget|Self-managed container runtime (Podman, Docker Swarm, Docker Compose)',
),
},
{
value: 'heroku',
text: s__('DeploymentTarget|Heroku'),
},
{
value: 'virtual_machine',
text: s__('DeploymentTarget|Virtual machine (for example, EC2)'),
},
{
value: 'mobile_app_store',
text: s__('DeploymentTarget|Mobile app store'),
},
{
value: 'registry',
text: s__('DeploymentTarget|Registry (package or container)'),
},
{
value: 'infrastructure_provider',
text: s__('DeploymentTarget|Infrastructure provider (Terraform, Cloudformation, and so on)'),
},
{
value: 'serverless_backend',
text: s__('DeploymentTarget|Serverless backend (Lambda, Cloud functions)'),
},
{
value: 'edge_computing',
text: s__('DeploymentTarget|Edge Computing (e.g. Cloudflare Workers)'),
},
{
value: 'web_deployment_platform',
text: s__('DeploymentTarget|Web Deployment Platform (Netlify, Vercel, Gatsby)'),
},
{
value: 'gitlab_pages',
text: s__('DeploymentTarget|GitLab Pages'),
},
{
value: 'other_hosting_service',
text: s__('DeploymentTarget|Other hosting service'),
},
{
value: 'no_deployment',
text: s__('DeploymentTarget|No deployment planned'),
},
];
export const NEW_PROJECT_FORM = 'new_project';

View File

@ -0,0 +1,147 @@
<script>
import { GlTooltipDirective, GlIcon, GlLink, GlButton } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import SafeHtml from '~/vue_shared/directives/safe_html';
import defaultAvatarUrl from 'images/no_avatar.png';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
import getRefMixin from '../mixins/get_ref';
export default {
components: {
UserAvatarLink,
TimeagoTooltip,
GlIcon,
GlButton,
GlLink,
UserAvatarImage,
},
directives: {
GlTooltip: GlTooltipDirective,
SafeHtml,
},
mixins: [getRefMixin],
props: {
commit: {
type: Object,
required: true,
},
historyUrl: {
type: String,
required: false,
default: '',
},
},
data() {
return { showDescription: false };
},
computed: {
commitDescription() {
// Strip the newline at the beginning
return this.commit?.descriptionHtml?.replace(/^&#x000A;/, '');
},
avatarLinkAltText() {
return sprintf(__(`%{username}'s avatar`), { username: this.commit.authorName });
},
commitId() {
return this.commit?.sha?.substr(0, 8);
},
},
methods: {
toggleShowDescription() {
this.showDescription = !this.showDescription;
},
},
defaultAvatarUrl,
safeHtmlConfig: {
ADD_TAGS: ['gl-emoji'],
},
i18n: {
toggleCommitDescription: __('Toggle commit description'),
authored: __('authored'),
},
};
</script>
<template>
<div class="well-segment">
<div class="gl-flex gl-items-center gl-justify-between">
<div class="gl-flex gl-items-center gl-gap-3">
<user-avatar-link
v-if="commit.author"
:link-href="commit.author.webPath"
:img-src="commit.author.avatarUrl"
:img-alt="avatarLinkAltText"
:img-size="32"
class="gl-my-2 gl-mr-3"
/>
<user-avatar-image
v-else
class="gl-my-2 gl-mr-3"
:img-src="commit.authorGravatar || $options.defaultAvatarUrl"
:size="32"
/>
<timeago-tooltip :time="commit.authoredDate" tooltip-placement="bottom" />
<gl-link
:href="commit.webPath"
:class="{ 'gl-italic': !commit.message }"
class="commit-row-message item-title gl-line-clamp-1 gl-whitespace-normal !gl-break-all"
>
<gl-icon name="commit" />
{{ commitId }}
</gl-link>
</div>
<div class="gl-flex gl-items-center">
<gl-button
v-gl-tooltip
:class="{ open: showDescription }"
:title="$options.i18n.toggleCommitDescription"
:aria-label="$options.i18n.toggleCommitDescription"
:selected="showDescription"
class="text-expander !gl-ml-0"
icon="ellipsis_h"
data-testid="text-expander"
@click="toggleShowDescription"
/>
<gl-button
category="tertiary"
data-testid="collapsible-commit-history"
:href="historyUrl"
class="gl-ml-2"
>
{{ __('History') }}
</gl-button>
</div>
</div>
<div v-if="showDescription" class="gl-mt-4">
<p
:class="{ 'gl-italic': !commit.message }"
class="commit-row-message gl-line-clamp-1 gl-whitespace-normal !gl-break-all gl-font-bold"
>
{{ commit.titleHtml }}
</p>
<div class="committer gl-basis-full gl-truncate gl-text-sm" data-testid="committer">
<gl-link
v-if="commit.author"
:href="commit.author.webPath"
class="commit-author-link js-user-link"
>
{{ commit.author.name }}</gl-link
>
<template v-else>
{{ commit.authorName }}
</template>
{{ $options.i18n.authored }}
<timeago-tooltip :time="commit.authoredDate" tooltip-placement="bottom" />
</div>
<pre
v-if="commit.descriptionHtml"
v-safe-html:[$options.safeHtmlConfig]="commitDescription"
class="commit-row-description gl-mb-3 gl-whitespace-pre-wrap"
></pre>
</div>
</div>
</template>

View File

@ -12,10 +12,12 @@ import projectPathQuery from '../queries/project_path.query.graphql';
import eventHub from '../event_hub';
import { FORK_UPDATED_EVENT } from '../constants';
import CommitInfo from './commit_info.vue';
import CollapsibleCommitInfo from './collapsible_commit_info.vue';
export default {
components: {
CommitInfo,
CollapsibleCommitInfo,
ClipboardButton,
SignatureBadge,
CiIcon,
@ -114,34 +116,41 @@ export default {
<template>
<gl-loading-icon v-if="isLoading" size="md" color="dark" class="m-auto gl-min-h-8 gl-py-6" />
<commit-info v-else-if="commit" :commit="commit">
<div class="commit-actions gl-flex gl-items-start">
<signature-badge v-if="commit.signature" :signature="commit.signature" />
<div v-if="commit.pipeline" class="gl-ml-5 gl-flex gl-h-7 gl-items-center">
<ci-icon
:status="commit.pipeline.detailedStatus"
:aria-label="statusTitle"
class="js-commit-pipeline"
/>
<div v-else-if="commit">
<commit-info :commit="commit" class="gl-hidden sm:gl-flex">
<div class="commit-actions gl-flex gl-items-start">
<signature-badge v-if="commit.signature" :signature="commit.signature" class="gl-h-7" />
<div v-if="commit.pipeline" class="gl-ml-5 gl-flex gl-h-7 gl-items-center">
<ci-icon
:status="commit.pipeline.detailedStatus"
:aria-label="statusTitle"
class="js-commit-pipeline"
/>
</div>
<gl-button-group class="js-commit-sha-group gl-ml-4 gl-flex gl-items-center">
<gl-button label class="gl-font-monospace" data-testid="last-commit-id-label">{{
showCommitId
}}</gl-button>
<clipboard-button
:text="commit.sha"
:title="__('Copy commit SHA')"
class="input-group-text"
/>
</gl-button-group>
<gl-button
category="tertiary"
data-testid="last-commit-history"
:href="historyUrl"
class="gl-ml-4"
>
{{ __('History') }}
</gl-button>
</div>
<gl-button-group class="js-commit-sha-group gl-ml-4 gl-flex gl-items-center">
<gl-button label class="gl-font-monospace" data-testid="last-commit-id-label">{{
showCommitId
}}</gl-button>
<clipboard-button
:text="commit.sha"
:title="__('Copy commit SHA')"
class="input-group-text"
/>
</gl-button-group>
<gl-button
category="tertiary"
data-testid="last-commit-history"
:href="historyUrl"
class="gl-ml-4"
>
{{ __('History') }}
</gl-button>
</div>
</commit-info>
</commit-info>
<collapsible-commit-info
:commit="commit"
:history-url="historyUrl"
class="gl-block sm:gl-hidden"
/>
</div>
</template>

View File

@ -1,10 +1,8 @@
import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
// eslint-disable-next-line no-restricted-imports
import Vuex from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
import { joinPaths, visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
import PerformancePlugin from '~/performance/vue_performance_plugin';
import createStore from '~/code_navigation/store';
@ -269,35 +267,6 @@ export default function setupVueRepositoryList() {
});
}
const treeHistoryLinkEl = document.getElementById('js-tree-history-link');
if (treeHistoryLinkEl) {
const { historyLink } = treeHistoryLinkEl.dataset;
// eslint-disable-next-line no-new
new Vue({
el: treeHistoryLinkEl,
router,
render(h) {
const url = generateHistoryUrl(
historyLink,
this.$route.params.path,
this.$route.meta.refType || this.$route.query.ref_type,
);
return h(
GlButton,
{
attrs: {
href: url.href,
// Ideally passing this class to `props` should work
// But it doesn't work here. :(
class: 'btn btn-default btn-md gl-button',
},
},
[__('History')],
);
},
});
}
initWebIdeLink({ el: document.getElementById('js-tree-web-ide-link'), router });
// eslint-disable-next-line no-new

View File

@ -221,6 +221,7 @@ class Namespace < ApplicationRecord
scope :without_deleted, -> { joins(:namespace_details).where(namespace_details: { deleted_at: nil }) }
scope :user_namespaces, -> { where(type: Namespaces::UserNamespace.sti_name) }
scope :group_namespaces, -> { where(type: Group.sti_name) }
scope :project_namespaces, -> { where(type: Namespaces::ProjectNamespace.sti_name) }
scope :without_project_namespaces, -> { where(Namespace.arel_table[:type].not_eq(Namespaces::ProjectNamespace.sti_name)) }
scope :sort_by_type, -> { order(arel_table[:type].asc.nulls_first) }
scope :include_route, -> { includes(:route) }

View File

@ -7,7 +7,7 @@ module Ml
class IncrementVersionService
def initialize(version, increment_type = nil)
@version = version
@increment_type = increment_type || :major
@increment_type = increment_type || :patch
@parsed_version = Packages::SemVer.parse(@version.to_s)
raise "Version must be in a valid SemVer format" unless @parsed_version || @version.nil?

View File

@ -10,7 +10,7 @@
#tree-holder.tree-holder.clearfix.js-per-page.gl-mt-5{ data: { blame_per_page: Gitlab::Git::BlamePagination::PAGINATION_PER_PAGE } }
- if params[:common_repository_blob_header_app] == 'true'
#js-repository-blob-header-app{ data: { project_id: @project.id, ref: ref, ref_type: @ref_type.to_s, history_link: project_commits_path(@project, @ref), breadcrumbs: breadcrumb_data_attributes, project_root_path: project_path(@project), project_path: project.full_path, compare_path: compare_path, escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref) } }
#js-repository-blob-header-app{ data: { project_id: @project.id, ref: ref, ref_type: @ref_type.to_s, breadcrumbs: breadcrumb_data_attributes, project_root_path: project_path(@project), project_path: project.full_path, compare_path: compare_path, escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref) } }
- else
.nav-block.gl-flex.gl-flex-col.sm:gl-flex-row.gl-items-stretch
@ -19,10 +19,10 @@
- if project.forked?
#js-fork-info{ data: vue_fork_divergence_data(project, ref) }
.info-well.gl-hidden.sm:gl-flex.project-last-commit.gl-flex-col.gl-mt-5
.info-well.project-last-commit.gl-flex-col.gl-mt-5
#js-last-commit.gl-m-auto{ data: {ref_type: @ref_type.to_s, history_link: project_commits_path(@project, @ref)} }
= gl_loading_icon(size: 'md')
- if project.licensed_feature_available?(:code_owners)
#js-code-owners{ data: { branch: @ref, can_view_branch_rules: can_view_branch_rules?, branch_rules_path: branch_rules_path } }
#js-code-owners.gl-hidden.sm:gl-flex{ data: { branch: @ref, can_view_branch_rules: can_view_branch_rules?, branch_rules_path: branch_rules_path } }
#js-tree-list{ data: vue_file_list_data(project, ref) }

View File

@ -7,7 +7,7 @@
- if (readme = @repository.readme) && readme.rich_viewer
.tree-holder.gl-mt-5
- if params[:common_repository_blob_header_app] == 'true'
#js-repository-blob-header-app{ data: { project_id: @project.id, ref: ref, ref_type: @ref_type.to_s, history_link: project_commits_path(@project, @ref), breadcrumbs: breadcrumb_data_attributes, project_root_path: project_path(@project), project_path: project.full_path, compare_path: compare_path, escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref) } }
#js-repository-blob-header-app{ data: { project_id: @project.id, ref: ref, ref_type: @ref_type.to_s, breadcrumbs: breadcrumb_data_attributes, project_root_path: project_path(@project), project_path: project.full_path, compare_path: compare_path, escaped_ref: ActionDispatch::Journey::Router::Utils.escape_path(ref) } }
- else
.nav-block.mt-0

View File

@ -10,9 +10,6 @@
= render_if_exists 'projects/tree/lock_link'
= render 'projects/buttons/compare', project: @project, ref: @ref, root_ref: @repository&.root_ref
.gl-block.sm:gl-hidden.gl-w-full
#js-tree-history-link{ data: { history_link: project_commits_path(@project, @ref) } }
= render 'projects/find_file_link'
= render 'shared/web_ide_button', blob: nil, css_classes: 'gl-w-full sm:gl-w-auto'

View File

@ -13,11 +13,16 @@ class PruneOldEventsWorker # rubocop:disable Scalability/IdempotentWorker
feature_category :user_profile
DELETE_LIMIT = 10_000
# Contribution calendar shows maximum 12 months of events, we retain 3 years for data integrity.
CUTOFF_DATE = (3.years + 1.day).freeze
def self.pruning_enabled?
Feature.enabled?(:ops_prune_old_events, type: :ops)
end
def perform
if Feature.enabled?(:ops_prune_old_events, type: :ops)
# Contribution calendar shows maximum 12 months of events, we retain 3 years for data integrity.
cutoff_date = (3.years + 1.day).ago
if self.class.pruning_enabled?
cutoff_date = CUTOFF_DATE.ago
Event.unscoped.created_before(cutoff_date).delete_with_limit(DELETE_LIMIT)
else

View File

@ -1,8 +0,0 @@
---
name: handle_structured_gitaly_errors
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128366
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/420865
milestone: '16.3'
type: development
group: group::source code
default_enabled: false

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -75,9 +75,10 @@ the authentication token is stored in the `config.toml`.
### Create an instance runner with a registration token (deprecated)
WARNING:
The ability to pass a runner registration token, and support for certain configuration arguments was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) in GitLab 15.6 and will be removed in GitLab 18.0. Runner authentication tokens
should be used instead. For more information, see [Migrating to the new runner registration workflow](new_creation_workflow.md).
The option to pass a runner registration token and support for certain configuration arguments was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) in GitLab 15.6. They are scheduled for removal
in GitLab 18.0. Use runner authentication tokens instead. For more information, see
[Migrating to the new runner registration workflow](new_creation_workflow.md).
Prerequisites:
@ -172,7 +173,7 @@ To disable instance runners for a project:
Instance runners are automatically disabled for a project:
- If the instance runners setting for the parent group is disabled, and
- If overriding this setting is not permitted at the project level.
- If overriding this setting is not permitted for projects.
### Disable instance runners for a group
@ -274,9 +275,10 @@ The runner authentication token displays in the UI for only a short period of ti
> - Path changed from **Settings > CI/CD > Runners**.
WARNING:
The ability to pass a runner registration token, and support for certain configuration arguments was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) in GitLab 15.6 and will be removed in GitLab 18.0. Authentication tokens
should be used instead. For more information, see [Migrating to the new runner registration workflow](new_creation_workflow.md).
The option to pass a runner registration token and support for certain configuration arguments was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) in GitLab 15.6. They are scheduled for removal
in GitLab 18.0. Use runner authentication tokens instead. For more information, see
[Migrating to the new runner registration workflow](new_creation_workflow.md).
Prerequisites:
@ -476,9 +478,10 @@ The runner authentication token displays in the UI for only a short period of ti
### Create a project runner with a registration token (deprecated)
WARNING:
The ability to pass a runner registration token, and support for certain configuration arguments was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) in GitLab 15.6 and will be removed in GitLab 18.0. Runner authentication tokens
should be used instead. For more information, see [Migrating to the new runner registration workflow](new_creation_workflow.md).
The option to pass a runner registration token and support for certain configuration arguments was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) in GitLab 15.6. They are scheduled for removal
in GitLab 18.0. Use runner authentication tokens instead. For more information, see
[Migrating to the new runner registration workflow](new_creation_workflow.md).
Prerequisites:
@ -587,7 +590,7 @@ A runner can have one of the following statuses.
| Status | Description |
|---------|-------------|
| `online` | The runner has contacted GitLab within the last 2 hours and is available to run jobs. |
| `online` | The runner has contacted GitLab in the last 2 hours and is available to run jobs. |
| `offline` | The runner has not contacted GitLab in more than 2 hours and is not available to run jobs. Check the runner to see if you can bring it online. |
| `stale` | The runner has not contacted GitLab in more than 7 days. If the runner was created more than 7 days ago, but it never contacted the instance, it is also considered **stale**. |
| `never_contacted` | The runner has never contacted GitLab. To make the runner contact GitLab, run `gitlab-runner run`. |
@ -606,8 +609,8 @@ The **Median job queued time** value is calculated by sampling the queue duratio
most recent 100 jobs that were run by Instance runners. Jobs from only the latest 5000
runners are considered.
The median is a value that falls into the 50th percentile: half of the jobs
queued for longer than the median value, and half of the jobs queued for less than the
The median is a value that falls into the 50th percentile. Half of the jobs
queue longer than the median value, and half queue for less time than the
median value.
To view runner statistics:
@ -646,11 +649,10 @@ To determine which runners need to be upgraded:
## Determine the IP address of a runner
It may be useful to know the IP address of a runner so you can troubleshoot
issues with that runner. GitLab stores and displays the IP address by viewing
the source of the HTTP requests it makes to GitLab when polling for jobs. The
IP address is always kept up to date so if the runner IP changes it
automatically updates in GitLab.
To troubleshoot runner issues, you might need to know the runner's IP address.
GitLab stores and displays the IP address by viewing the source of the
HTTP requests when the runner polls for jobs.
GitLab automatically updates the runner's IP address whenever it is updated.
The IP address for instance runners and project runners can be found in
different places.
@ -678,24 +680,26 @@ project.
1. Go to the project's **Settings > CI/CD** and expand the **Runners** section.
1. Select the runner name and find the **IP Address** row.
![Project runner IP address](img/project_runner_ip_address_v10_7.png)
![Project runner IP address](img/project_runner_ip_address_v17_6.png)
## Enable use of runner registration tokens in projects and groups
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148557) in GitLab 16.11
WARNING:
The ability to pass a runner registration token, and support for certain configuration arguments was deprecated in GitLab 15.6 and will be removed in GitLab 18.0. Runner authentication tokens should be used instead. For more information, see [Migrating to the new runner registration workflow](../../ci/runners/new_creation_workflow.md).
The option to pass a runner registration token and support for certain configuration arguments was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/380872) in GitLab 15.6. They are scheduled for removal
in GitLab 18.0. Use runner authentication tokens instead. For more information, see
[Migrating to the new runner registration workflow](new_creation_workflow.md).
In GitLab 17.0, the use of runner registration tokens to create runners will be disabled in all GitLab instances.
Users must use runner authentication tokens instead.
If you have not yet [migrated to the use of runner authentication tokens](../../ci/runners/new_creation_workflow.md),
you can enable runner registration tokens for projects and groups. This setting and support for runner registration tokens will be removed in GitLab 18.0.
In GitLab 17.0, the use of runner registration tokens is disabled in all GitLab instances.
Prerequisites:
- Runner registration tokens must be [enabled](../../administration/settings/continuous_integration.md#allow-runner-registrations-tokens) in the **Admin** area.
To enable the use of runner registration token in project and groups:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > CI/CD**.
1. Expand **Runners**.

View File

@ -156,8 +156,8 @@ DETAILS:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96561) in GitLab 15.4 [with a flag](../../administration/feature_flags.md) named `dora_configuration`. Disabled by default. This feature is an [experiment](../../policy/development_stages_support.md).
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, an administrator can [enable the feature flag](../../administration/feature_flags.md) named `dora_configuration`.
On GitLab.com and GitLab Dedicated, this feature is not available.
The availability of this feature is controlled by a feature flag.
For more information, see the history.
This feature is an [experiment](../../policy/development_stages_support.md).
To join the list of users testing this feature, [here is a suggested test flow](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96561#steps-to-check-on-localhost).

View File

@ -194,8 +194,12 @@ To disable 2FA:
### Enable the extension marketplace for the Web IDE and workspaces
DETAILS:
**Status:** Beta
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/161819) as a [beta](../../policy/development_stages_support.md#beta) in GitLab 17.0 [with flags](../../administration/feature_flags.md) named `web_ide_oauth` and `web_ide_extensions_marketplace`. Disabled by default.
> - Feature flag `web_ide_oauth` [enabled on GitLab.com, self-managed, and GitLab Dedicated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/163181) and feature flag `web_ide_extensions_marketplace` [enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/459028) in GitLab 17.4.
> - Feature flag `web_ide_oauth` [enabled on GitLab.com, self-managed, and GitLab Dedicated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/163181) in GitLab 17.4.
> - Feature flag `web_ide_extensions_marketplace` [enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/459028) in GitLab 17.4.
> - Feature flag `web_ide_oauth` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167464) in GitLab 17.5.
FLAG:

View File

@ -16,10 +16,9 @@ OKRs are an [experiment](../policy/development_stages_support.md#experiment).
For the OKR feature roadmap, see [epic 7864](https://gitlab.com/groups/gitlab-org/-/epics/7864).
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available per project, an administrator can [enable the featured flag](../administration/feature_flags.md) named `okrs_mvc`.
On GitLab.com, by default this feature is not available, but can be configured by GitLab.com administrators.
On GitLab Dedicated, this feature is not available.
This feature is not ready for production use.
The availability of this feature is controlled by a feature flag.
For more information, see the history.
This feature is available for testing, but not ready for production use.
[Objectives and key results](https://en.wikipedia.org/wiki/OKR) (OKRs) are a framework for setting
and tracking goals that are aligned with your organization's overall strategy and vision.
@ -113,6 +112,7 @@ To edit an OKR:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/378949) in GitLab 15.7 [with a flag](../administration/feature_flags.md) named `work_items_mvc_2`. Disabled by default.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/378949) to feature flag named `work_items_mvc` in GitLab 15.8. Disabled by default.
> - Feature flag [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144141) from `work_items_mvc` to `work_items_beta` in GitLab 16.10.
> - Changing activity sort order [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/378949) in GitLab 15.8.
> - Filtering activity [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/389971) in GitLab 15.10.
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/334812) in GitLab 15.10.
@ -382,9 +382,9 @@ To reorder them, drag them around.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/169256) the minimum user role from Reporter to Planner in GitLab 17.7.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../administration/feature_flags.md) named `okr_checkin_reminders`.
On GitLab.com and GitLab Dedicated, this feature is not available.
This feature is not ready for production use.
The availability of this feature is controlled by a feature flag.
For more information, see the history.
This feature is available for testing, but not ready for production use.
Schedule check-in reminders to remind your team to provide status updates on the key results you care
about.
@ -525,9 +525,9 @@ system note in the OKR's comments, for example:
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/169256) the minimum user role from Reporter to Planner in GitLab 17.7.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../administration/feature_flags.md) named `work_items_beta`.
On GitLab.com and GitLab Dedicated, this feature is not available.
This feature is not ready for production use.
The availability of this feature is controlled by a feature flag.
For more information, see the history.
This feature is available for testing, but not ready for production use.
You can prevent public comments in an OKR.
When you do, only project members can add and edit comments.

View File

@ -96,10 +96,6 @@ module API
params
]
end
def rescue_not_found?
Feature.disabled?(:handle_structured_gitaly_errors)
end
end
desc 'Get a project repository tree' do
@ -128,7 +124,7 @@ module API
end
end
get ':id/repository/tree', urgency: :low do
tree_finder = ::Repositories::TreeFinder.new(user_project, declared_params(include_missing: false).merge(rescue_not_found: rescue_not_found?))
tree_finder = ::Repositories::TreeFinder.new(user_project, declared_params(include_missing: false).merge(rescue_not_found: false))
not_found!("Tree") unless tree_finder.commit_exists?

View File

@ -42,6 +42,12 @@ module Gitlab
issue_event.issuable_type == MergeRequest.name
end
# `PruneOldEventsWorker` deletes Event records older than a cutoff date.
# Before importing Events, check if they would be pruned.
def event_outside_cutoff?(issue_event)
issue_event.created_at < PruneOldEventsWorker::CUTOFF_DATE.ago && PruneOldEventsWorker.pruning_enabled?
end
def resource_event_belongs_to(issue_event)
belongs_to_key = merge_request_event?(issue_event) ? :merge_request_id : :issue_id
{ belongs_to_key => issuable_db_id(issue_event) }

View File

@ -13,6 +13,8 @@ module Gitlab
private
def create_event(issue_event)
return if event_outside_cutoff?(issue_event)
created_event = Event.create!(
project_id: project.id,
author_id: author_id(issue_event),

View File

@ -14,6 +14,8 @@ module Gitlab
private
def create_event(issue_event)
return if event_outside_cutoff?(issue_event)
event = Event.create!(
project_id: project.id,
author_id: author_id(issue_event),

View File

@ -13,6 +13,8 @@ module Gitlab
private
def create_event(issue_event)
return if event_outside_cutoff?(issue_event)
created_event = Event.create!(
project_id: project.id,
author_id: author_id(issue_event),

View File

@ -18951,6 +18951,9 @@ msgstr ""
msgid "Deployment|Flux sync failed"
msgstr ""
msgid "Deployment|Flux sync is suspended"
msgstr ""
msgid "Deployment|Flux sync reconciled successfully"
msgstr ""

View File

@ -9,7 +9,8 @@ module RuboCop
include MigrationHelpers
MSG = 'Creating a table with more than one foreign key at once violates our migration style guide. ' \
'For more details check the https://docs.gitlab.com/ee/development/migration_style_guide.html#examples'
'For more details check the ' \
'https://docs.gitlab.com/ee/development/migration_style_guide.html#creating-a-new-table-when-we-have-two-foreign-keys'
def_node_matcher :create_table_with_block?, <<~PATTERN
(block

View File

@ -170,7 +170,6 @@ spec/frontend/ci/pipelines_page/components/pipeline_multi_actions_spec.js
spec/frontend/ci/pipelines_page/components/pipelines_artifacts_spec.js
spec/frontend/ci/runner/components/runner_details_spec.js
spec/frontend/ci/runner/components/runner_form_fields_spec.js
spec/frontend/ci/runner/components/runner_managers_table_spec.js
spec/frontend/ci/runner/components/runner_platforms_radio_spec.js
spec/frontend/ci/runner/group_runner_show/group_runner_show_app_spec.js
spec/frontend/ci_settings_pipeline_triggers/components/edit_trigger_modal_spec.js

View File

@ -61,7 +61,9 @@ RSpec.describe "User browses files", :js, feature_category: :source_code_managem
click_link("README.md")
end
click_link("History")
page.within(".commit-actions") do
click_link("History")
end
history_path = project_commits_path(project, "master/README.md")
expect(page).to have_current_path(history_path)

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe 'Secure Files', :js, feature_category: :source_code_management do
RSpec.describe 'Secure Files', :js, feature_category: :secrets_management do
let(:project) { create(:project) }
let(:user) { create(:user) }

View File

@ -162,7 +162,7 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_ov
namespace: kubernetesNamespace,
resourceType: k8sResourceType.k8sPods,
fluxApiError: '',
fluxResourceStatus: [],
fluxResourceStatus: { conditions: [] },
});
});
@ -240,7 +240,7 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_ov
});
it('provides empty `fluxResourceStatus` to KubernetesStatusBar', () => {
expect(findKubernetesStatusBar().props('fluxResourceStatus')).toEqual([]);
expect(findKubernetesStatusBar().props('fluxResourceStatus')).toEqual({ conditions: [] });
});
it('provides empty `fluxKustomization` to KubernetesTabs', () => {
@ -305,9 +305,9 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_ov
await waitForPromises();
});
it('provides correct `fluxResourceStatus` to KubernetesStatusBar', () => {
expect(findKubernetesStatusBar().props('fluxResourceStatus')).toEqual(
fluxResourceStatus,
);
expect(findKubernetesStatusBar().props('fluxResourceStatus')).toEqual({
conditions: fluxResourceStatus,
});
});
it('provides correct `fluxNamespace` to KubernetesStatusBar', () => {

View File

@ -43,7 +43,7 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_st
const createWrapper = ({
clusterHealthStatus = '',
fluxResourcePath = '',
fluxResourceStatus = [],
fluxResourceStatus = { conditions: [] },
fluxApiError = '',
namespace = kubernetesNamespace,
resourceType = k8sResourceType.k8sPods,
@ -189,14 +189,16 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_st
'renders sync status as $statusText when status is $status, type is $type, and reason is $reason',
({ status, type, reason, statusText, statusPopover }) => {
createWrapper({
fluxResourceStatus: [
{
status,
type,
reason,
message,
},
],
fluxResourceStatus: {
conditions: [
{
status,
type,
reason,
message,
},
],
},
});
expect(findSyncBadge().text()).toBe(statusText);
@ -206,7 +208,7 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_st
it('renders a clickable badge', () => {
createWrapper({
fluxResourceStatus: [{ status: 'True', type: 'Ready' }],
fluxResourceStatus: { conditions: [{ status: 'True', type: 'Ready' }] },
});
expect(findSyncBadge().attributes('href')).toBe('#');
@ -214,7 +216,7 @@ describe('~/environments/environment_details/components/kubernetes/kubernetes_st
it('emits `show-flux-resource-details` event when badge is clicked', () => {
createWrapper({
fluxResourceStatus: [{ status: 'True', type: 'Ready' }],
fluxResourceStatus: { conditions: [{ status: 'True', type: 'Ready' }] },
});
findSyncBadge().trigger('click');

View File

@ -67,16 +67,17 @@ describe('k8s_integration_helper', () => {
let fluxConditions;
it.each`
status | type | reason | statusText | statusMessage
${STATUS_TRUE} | ${'Stalled'} | ${''} | ${'stalled'} | ${{ message }}
${STATUS_TRUE} | ${'Reconciling'} | ${''} | ${'reconciling'} | ${''}
${STATUS_UNKNOWN} | ${'Ready'} | ${REASON_PROGRESSING} | ${'reconcilingWithBadConfig'} | ${{ message }}
${STATUS_TRUE} | ${'Ready'} | ${''} | ${'reconciled'} | ${''}
${STATUS_FALSE} | ${'Ready'} | ${''} | ${'failed'} | ${{ message }}
${STATUS_UNKNOWN} | ${'Ready'} | ${''} | ${'unknown'} | ${''}
suspended | status | type | reason | statusText | statusMessage
${false} | ${STATUS_TRUE} | ${'Stalled'} | ${''} | ${'stalled'} | ${{ message }}
${false} | ${STATUS_TRUE} | ${'Reconciling'} | ${''} | ${'reconciling'} | ${''}
${false} | ${STATUS_UNKNOWN} | ${'Ready'} | ${REASON_PROGRESSING} | ${'reconcilingWithBadConfig'} | ${{ message }}
${false} | ${STATUS_TRUE} | ${'Ready'} | ${''} | ${'reconciled'} | ${''}
${false} | ${STATUS_FALSE} | ${'Ready'} | ${''} | ${'failed'} | ${''}
${true} | ${STATUS_FALSE} | ${'Ready'} | ${''} | ${'suspended'} | ${''}
${false} | ${STATUS_UNKNOWN} | ${'Ready'} | ${''} | ${'unknown'} | ${''}
`(
'renders sync status as $statusText when status is $status, type is $type, and reason is $reason',
({ status, type, reason, statusText, statusMessage }) => {
({ suspended, status, type, reason, statusText, statusMessage }) => {
fluxConditions = [
{
status,
@ -86,7 +87,9 @@ describe('k8s_integration_helper', () => {
},
];
expect(fluxSyncStatus(fluxConditions)).toMatchObject({
const fluxResourceStatus = { conditions: fluxConditions, suspend: suspended };
expect(fluxSyncStatus(fluxResourceStatus)).toMatchObject({
status: statusText,
...statusMessage,
});

View File

@ -60,10 +60,10 @@ describe('Deployment target select', () => {
});
describe.each`
selectedTarget | formSubmitted | eventSent
${null} | ${true} | ${false}
${DEPLOYMENT_TARGET_SELECTIONS[0]} | ${false} | ${false}
${DEPLOYMENT_TARGET_SELECTIONS[0]} | ${true} | ${true}
selectedTarget | formSubmitted | eventSent
${null} | ${true} | ${false}
${DEPLOYMENT_TARGET_SELECTIONS[0].value} | ${false} | ${false}
${DEPLOYMENT_TARGET_SELECTIONS[0].value} | ${true} | ${true}
`('Snowplow tracking event', ({ selectedTarget, formSubmitted, eventSent }) => {
beforeEach(() => {
findSelect().vm.$emit('input', selectedTarget);
@ -89,10 +89,10 @@ describe('Deployment target select', () => {
});
describe.each`
selectedTarget | isTextShown
${null} | ${false}
${DEPLOYMENT_TARGET_SELECTIONS[0]} | ${true}
${DEPLOYMENT_TARGET_SELECTIONS[1]} | ${false}
selectedTarget | isTextShown
${null} | ${false}
${DEPLOYMENT_TARGET_SELECTIONS[0].value} | ${true}
${DEPLOYMENT_TARGET_SELECTIONS[1].value} | ${false}
`('K8s education text', ({ selectedTarget, isTextShown }) => {
beforeEach(() => {
findSelect().vm.$emit('input', selectedTarget);
@ -105,7 +105,7 @@ describe('Deployment target select', () => {
describe('when user clicks on the docs link', () => {
beforeEach(async () => {
findSelect().vm.$emit('input', K8S_OPTION);
findSelect().vm.$emit('input', K8S_OPTION.value);
await nextTick();
findLink().trigger('click');

View File

@ -1,60 +1,68 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Repository last commit component renders commit widget 1`] = `
<commit-info-stub
commit="[object Object]"
>
<div
class="commit-actions gl-flex gl-items-start"
<div>
<commit-info-stub
class="gl-hidden sm:gl-flex"
commit="[object Object]"
>
<div
class="gl-flex gl-h-7 gl-items-center gl-ml-5"
>
<ci-icon-stub
aria-label="Pipeline: failed"
class="js-commit-pipeline"
showtooltip="true"
status="[object Object]"
uselink="true"
/>
</div>
<gl-button-group-stub
class="gl-flex gl-items-center gl-ml-4 js-commit-sha-group"
class="commit-actions gl-flex gl-items-start"
>
<div
class="gl-flex gl-h-7 gl-items-center gl-ml-5"
>
<ci-icon-stub
aria-label="Pipeline: failed"
class="js-commit-pipeline"
showtooltip="true"
status="[object Object]"
uselink="true"
/>
</div>
<gl-button-group-stub
class="gl-flex gl-items-center gl-ml-4 js-commit-sha-group"
>
<gl-button-stub
buttontextclasses=""
category="primary"
class="gl-font-monospace"
data-testid="last-commit-id-label"
icon=""
label="true"
size="medium"
variant="default"
>
12345678
</gl-button-stub>
<clipboard-button-stub
category="secondary"
class="input-group-text"
size="medium"
text="123456789"
title="Copy commit SHA"
tooltipplacement="top"
variant="default"
/>
</gl-button-group-stub>
<gl-button-stub
buttontextclasses=""
category="primary"
class="gl-font-monospace"
data-testid="last-commit-id-label"
category="tertiary"
class="gl-ml-4"
data-testid="last-commit-history"
href="/history"
icon=""
label="true"
size="medium"
variant="default"
>
12345678
History
</gl-button-stub>
<clipboard-button-stub
category="secondary"
class="input-group-text"
size="medium"
text="123456789"
title="Copy commit SHA"
tooltipplacement="top"
variant="default"
/>
</gl-button-group-stub>
<gl-button-stub
buttontextclasses=""
category="tertiary"
class="gl-ml-4"
data-testid="last-commit-history"
href="/history"
icon=""
size="medium"
variant="default"
>
History
</gl-button-stub>
</div>
</commit-info-stub>
</div>
</commit-info-stub>
<collapsible-commit-info-stub
class="gl-block sm:gl-hidden"
commit="[object Object]"
historyurl="/history"
/>
</div>
`;

View File

@ -0,0 +1,142 @@
import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import CollapsibleCommitInfo from '~/repository/components/collapsible_commit_info.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
describe('CollapsibleCommitInfo', () => {
let wrapper;
const defaultProps = {
sha: '1234567890abcdef',
authorName: 'John Doe',
authoredDate: '2023-06-01T12:00:00Z',
titleHtml: 'Initial commit',
message: 'Commit message',
descriptionHtml: '&#x000A;This is a commit description',
webPath: '/commit/1234567890abcdef',
author: {
webPath: '/users/johndoe',
avatarUrl: 'https://example.com/avatar.jpg',
name: 'John Doe',
},
};
const createComponent = async (props = {}) => {
wrapper = shallowMountExtended(CollapsibleCommitInfo, {
propsData: { commit: { ...defaultProps, ...props }, historyUrl: '/project/history' },
});
await nextTick();
};
beforeEach(() => {
createComponent();
});
const findCommitterAvatar = () => wrapper.findComponent(UserAvatarLink);
const findDefaultAvatar = () => wrapper.findComponent(UserAvatarImage);
const findCommitTimeAgo = () => wrapper.findComponent(TimeagoTooltip);
const findCommitId = () => wrapper.findByText('12345678');
const findHistoryButton = () => wrapper.findByTestId('collapsible-commit-history');
const findTextExpander = () => wrapper.findByTestId('text-expander');
const findCommitRowDescription = () => wrapper.find('pre');
const findCommitTitle = () => wrapper.findByText('Initial commit');
const findCommitterName = () => wrapper.findByText('John Doe');
describe('renders user avatar correctly', () => {
it('renders avatar link if user has a custom profile photo', () => {
expect(findCommitterAvatar().exists()).toBe(true);
expect(findDefaultAvatar().exists()).toBe(false);
expect(findCommitterAvatar().props()).toStrictEqual({
imgAlt: "John Doe's avatar",
imgCssClasses: '',
imgCssWrapperClasses: '',
imgSize: 32,
imgSrc: 'https://example.com/avatar.jpg',
lazy: false,
linkHref: '/users/johndoe',
popoverUserId: '',
popoverUsername: '',
tooltipPlacement: 'top',
tooltipText: '',
username: '',
});
});
it('renders default avatar image if user does not have a custom profile photo', () => {
createComponent({
sha: '1234567890abcdef',
authorName: 'John Doe',
author: null,
authoredDate: '2023-06-01T12:00:00Z',
titleHtml: 'Initial commit',
descriptionHtml: '&#x000A;This is a commit description',
webPath: '/commit/1234567890abcdef',
});
expect(findCommitterAvatar().exists()).toBe(false);
expect(findDefaultAvatar().exists()).toBe(true);
expect(findDefaultAvatar().props()).toStrictEqual({
cssClasses: '',
imgAlt: 'user avatar',
imgSrc: 'file-mock',
pseudo: false,
size: 32,
lazy: false,
tooltipPlacement: 'top',
tooltipText: '',
});
});
});
it('renders commit details correctly', () => {
expect(findCommitTimeAgo().props().time).toBe('2023-06-01T12:00:00Z');
expect(findCommitId().exists()).toBe(true);
expect(findHistoryButton().exists()).toBe(true);
expect(findHistoryButton().attributes('href')).toBe('/project/history');
});
describe('text expander', () => {
it('renders commit details collapsed by default', () => {
expect(findTextExpander().exists()).toBe(true);
expect(findCommitTitle().exists()).toBe(false);
expect(findCommitterName().exists()).toBe(false);
expect(findCommitRowDescription().exists()).toBe(false);
});
it('shows commit details when clicking the expander button', async () => {
await findTextExpander().vm.$emit('click');
await nextTick();
expect(findTextExpander().classes('open')).toBe(true);
expect(findTextExpander().props('selected')).toBe(true);
expect(findCommitTitle().exists()).toBe(true);
expect(findCommitterName().exists()).toBe(true);
expect(findCommitRowDescription().html()).toBe(
'<pre class="commit-row-description gl-mb-3 gl-whitespace-pre-wrap">This is a commit description</pre>',
);
});
it('sets correct CSS class if the commit message is empty', async () => {
createComponent({ message: '' });
await findTextExpander().vm.$emit('click');
await nextTick();
expect(findCommitTitle().classes()).toContain('gl-italic');
});
it('does not render description when description is null', async () => {
createComponent({
sha: '1234567890abcdef',
authorName: 'John Doe',
authoredDate: '2023-06-01T12:00:00Z',
titleHtml: 'Initial commit',
descriptionHtml: null,
webPath: '/commit/1234567890abcdef',
});
await findTextExpander().vm.$emit('click');
await nextTick();
expect(findCommitRowDescription().exists()).toBe(false);
});
});
});

View File

@ -41,14 +41,8 @@ RSpec.describe Gitlab::Git::Tree, feature_category: :source_code_management do
context 'with an invalid ref' do
let(:sha) { 'foobar-does-not-exist' }
context 'when handle_structured_gitaly_errors feature is disabled' do
before do
stub_feature_flags(handle_structured_gitaly_errors: false)
end
it { expect(entries).to eq([]) }
it { expect(cursor).to be_nil }
end
it { expect(entries).to eq([]) }
it { expect(cursor).to be_nil }
end
context 'when path is provided' do

View File

@ -11,6 +11,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Closed, feature_category:
let(:client) { instance_double('Gitlab::GithubImport::Client') }
let(:issuable) { create(:issue, project: project) }
let(:commit_id) { nil }
let(:created_at) { 1.month.ago }
let(:issue_event) do
Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
@ -19,7 +20,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Closed, feature_category:
'url' => 'https://api.github.com/repos/elhowm/test-import/issues/events/6501124486',
'actor' => { 'id' => user.id, 'login' => user.username },
'event' => 'closed',
'created_at' => '2022-04-26 18:30:53 UTC',
'created_at' => created_at.iso8601,
'commit_id' => commit_id,
'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
@ -57,6 +58,29 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Closed, feature_category:
.to include expected_state_event_attrs
end
context 'when event is outside the cutoff date and would be pruned' do
let(:created_at) { (PruneOldEventsWorker::CUTOFF_DATE + 1.minute).ago }
it 'does not create the event, but does create the state event' do
importer.execute(issue_event)
expect(issuable.events.count).to eq 0
expect(issuable.resource_state_events.count).to eq 1
end
context 'when pruning events is disabled' do
before do
stub_feature_flags(ops_prune_old_events: false)
end
it 'creates the event' do
importer.execute(issue_event)
expect(issuable.events.count).to eq 1
end
end
end
context 'when closed by commit' do
let!(:closing_commit) { create(:commit, project: project) }
let(:commit_id) { closing_commit.id }

View File

@ -11,6 +11,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Merged, feature_category:
let(:client) { instance_double('Gitlab::GithubImport::Client') }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:commit_id) { nil }
let(:created_at) { 1.month.ago }
let(:issue_event) do
Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
@ -19,7 +20,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Merged, feature_category:
'url' => 'https://api.github.com/repos/elhowm/test-import/issues/events/6501124486',
'actor' => { 'id' => user.id, 'login' => user.username },
'event' => 'merged',
'created_at' => '2022-04-26 18:30:53 UTC',
'created_at' => created_at.iso8601,
'commit_id' => commit_id,
'issue' => { 'number' => merge_request.iid, pull_request: true }
)
@ -106,6 +107,29 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Merged, feature_category:
)
end
context 'when event is outside the cutoff date and would be pruned' do
let(:created_at) { (PruneOldEventsWorker::CUTOFF_DATE + 1.minute).ago }
it 'does not create the event, but does create the state event' do
importer.execute(issue_event)
expect(merge_request.events.count).to eq 0
expect(merge_request.resource_state_events.count).to eq 1
end
context 'when pruning events is disabled' do
before do
stub_feature_flags(ops_prune_old_events: false)
end
it 'creates the event' do
importer.execute(issue_event)
expect(merge_request.events.count).to eq 1
end
end
end
context 'when commit ID is present' do
let!(:commit) { create(:commit, project: project) }
let(:commit_id) { commit.id }

View File

@ -10,6 +10,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Reopened, :aggregate_fail
let(:client) { instance_double('Gitlab::GithubImport::Client') }
let(:issuable) { create(:issue, project: project) }
let(:created_at) { 1.month.ago }
let(:issue_event) do
Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
@ -18,7 +19,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Reopened, :aggregate_fail
'url' => 'https://api.github.com/repos/elhowm/test-import/issues/events/6501124486',
'actor' => { 'id' => user.id, 'login' => user.username },
'event' => 'reopened',
'created_at' => '2022-04-26 18:30:53 UTC',
'created_at' => created_at.iso8601,
'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
@ -59,6 +60,29 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Reopened, :aggregate_fail
end
end
context 'when event is outside the cutoff date and would be pruned' do
let(:created_at) { (PruneOldEventsWorker::CUTOFF_DATE + 1.minute).ago }
it 'does not create the event, but does create the state event' do
importer.execute(issue_event)
expect(issuable.events.count).to eq 0
expect(issuable.resource_state_events.count).to eq 1
end
context 'when pruning events is disabled' do
before do
stub_feature_flags(ops_prune_old_events: false)
end
it 'creates the event' do
importer.execute(issue_event)
expect(issuable.events.count).to eq 1
end
end
end
shared_examples 'push placeholder references' do
it 'pushes the references' do
expect(subject)

View File

@ -620,6 +620,19 @@ RSpec.describe Namespace, feature_category: :groups_and_projects do
end
end
describe '.project_namespaces' do
let_it_be(:user_namespace) { create(:user_namespace) }
let_it_be(:project) { create(:project) }
let_it_be(:project_namespace) { project.project_namespace }
let_it_be(:group_namespace) { create(:group) }
it 'only returns project namespaces' do
project_namespaces = described_class.project_namespaces
expect(project_namespaces).to include(project_namespace)
expect(project_namespaces).not_to include(group_namespace, user_namespace)
end
end
describe '.without_project_namespaces' do
let_it_be(:user_namespace) { create(:user_namespace) }
let_it_be(:project) { create(:project) }

View File

@ -40,46 +40,9 @@ RSpec.describe API::Repositories, feature_category: :source_code_management do
context 'when path does not exist' do
let(:path) { 'bogus' }
context 'when handle_structured_gitaly_errors feature is disabled' do
before do
stub_feature_flags(handle_structured_gitaly_errors: false)
end
it 'returns an empty array' do
get api("#{route}?path=#{path}", current_user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an(Array)
expect(json_response).to be_an_empty
end
end
context 'when handle_structured_gitaly_errors feature is enabled' do
before do
stub_feature_flags(handle_structured_gitaly_errors: true)
end
it_behaves_like '404 response' do
let(:request) { get api("#{route}?path=#{path}", current_user) }
let(:message) { '404 invalid revision or path Not Found' }
end
end
end
context 'when path is empty directory ' do
context 'when handle_structured_gitaly_errors feature is disabled' do
before do
stub_feature_flags(handle_structured_gitaly_errors: false)
end
it 'returns an empty array' do
get api(route, current_user)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
expect(json_response).to be_an(Array)
end
it_behaves_like '404 response' do
let(:request) { get api("#{route}?path=#{path}", current_user) }
let(:message) { '404 invalid revision or path Not Found' }
end
end

View File

@ -78,7 +78,7 @@ RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys do
context 'with more than one foreign keys' do
let(:offense) do
'Creating a table with more than one foreign key at once violates our migration style guide. ' \
'For more details check the https://docs.gitlab.com/ee/development/migration_style_guide.html#examples'
'For more details check the https://docs.gitlab.com/ee/development/migration_style_guide.html#creating-a-new-table-when-we-have-two-foreign-keys'
end
shared_examples 'target to high traffic table' do |dsl_method, table_name|

View File

@ -53,13 +53,13 @@ RSpec.describe ::Ml::CreateModelVersionService, feature_category: :mlops do
context 'when a version exist and no value is passed for version' do
before do
create(:ml_model_versions, model: model, version: '3.0.0')
create(:ml_model_versions, model: model, version: '1.2.3')
model.reload
end
it 'creates another model version and increments the version number' do
expect { service }.to change { Ml::ModelVersion.count }.by(1).and change { Ml::Candidate.count }.by(1)
expect(model.reload.latest_version.version).to eq('4.0.0')
expect(model.reload.latest_version.version).to eq('1.2.4')
expect(service).to be_success
expect(Gitlab::InternalEvents).to have_received(:track_event).with(

View File

@ -39,8 +39,8 @@ RSpec.describe ::Ml::IncrementVersionService, feature_category: :mlops do
where(:version, :increment_type, :result) do
nil | nil | '1.0.0'
'0.0.1' | nil | '1.0.1'
'1.0.0' | nil | '2.0.0'
'0.0.1' | nil | '0.0.2'
'1.0.0' | nil | '1.0.1'
'1.0.0' | :major | '2.0.0'
'1.0.0' | :minor | '1.1.0'
'1.0.0' | :patch | '1.0.1'

View File

@ -43,4 +43,18 @@ RSpec.describe PruneOldEventsWorker, feature_category: :user_profile do
end
end
end
describe '.pruning_enabled?' do
subject(:pruning_enabled) { described_class.pruning_enabled? }
it { is_expected.to be(true) }
context 'with ops_prune_old_events FF disabled' do
before do
stub_feature_flags(ops_prune_old_events: false)
end
it { is_expected.to be(false) }
end
end
end