Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-06-14 00:11:58 +00:00
parent 29a7132039
commit eca6018a19
28 changed files with 398 additions and 74 deletions

View File

@ -1,15 +1,30 @@
<script> <script>
import MergeRequestsWidget from './merge_requests_widget.vue';
import ActivityWidget from './activity_widget.vue'; import ActivityWidget from './activity_widget.vue';
import TodosWidget from './todos_widget.vue'; import TodosWidget from './todos_widget.vue';
export default { export default {
components: { ActivityWidget, TodosWidget }, components: { MergeRequestsWidget, ActivityWidget, TodosWidget },
props: {
reviewRequestedPath: {
type: String,
required: true,
},
assignedToYouPath: {
type: String,
required: true,
},
},
}; };
</script> </script>
<template> <template>
<div> <div>
<h1>{{ __("Today's highlights") }}</h1> <h1>{{ __("Today's highlights") }}</h1>
<merge-requests-widget
:review-requested-path="reviewRequestedPath"
:assigned-to-you-path="assignedToYouPath"
/>
<todos-widget /> <todos-widget />
<activity-widget /> <activity-widget />
</div> </div>

View File

@ -0,0 +1,37 @@
<script>
import { GlIcon, GlLink } from '@gitlab/ui';
export default {
name: 'MergeRequestsWidget',
components: {
GlIcon,
GlLink,
},
props: {
reviewRequestedPath: {
type: String,
required: true,
},
assignedToYouPath: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="gl-border gl-rounded-lg gl-px-4 gl-py-1">
<h4 class="gl-flex gl-items-center gl-gap-2">
<gl-icon name="merge-request" :size="16" />{{ __('Merge requests') }}
</h4>
<ul class="gl-list-none gl-p-0">
<li>
<gl-link :href="reviewRequestedPath">{{ __('Review requested') }}</gl-link>
</li>
<li>
<gl-link :href="assignedToYouPath">{{ __('Assigned to you') }}</gl-link>
</li>
</ul>
</div>
</template>

View File

@ -12,13 +12,20 @@ export default () => {
return false; return false;
} }
const { reviewRequestedPath, assignedToYouPath } = el.dataset;
return new Vue({ return new Vue({
el, el,
apolloProvider: new VueApollo({ apolloProvider: new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient(),
}), }),
render(createElement) { render(createElement) {
return createElement(HomepageApp); return createElement(HomepageApp, {
props: {
reviewRequestedPath,
assignedToYouPath,
},
});
}, },
}); });
}; };

View File

@ -1,5 +1,6 @@
import { GlToast } from '@gitlab/ui'; import { GlToast } from '@gitlab/ui';
import Vue from 'vue'; import Vue from 'vue';
import { groupsProvideData } from 'ee_else_ce/invite_members/utils';
import InviteGroupsModal from '~/invite_members/components/invite_groups_modal.vue'; import InviteGroupsModal from '~/invite_members/components/invite_groups_modal.vue';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
@ -28,12 +29,7 @@ export default function initInviteGroupsModal() {
return new Vue({ return new Vue({
el, el,
provide: { provide: groupsProvideData(el),
freeUsersLimit: parseInt(el.dataset.freeUsersLimit, 10),
overageMembersModalAvailable: parseBoolean(el.dataset.overageMembersModalAvailable),
hasGitlabSubscription: parseBoolean(el.dataset.hasGitlabSubscription),
inviteWithCustomRoleEnabled: parseBoolean(el.dataset.inviteWithCustomRoleEnabled),
},
render: (createElement) => render: (createElement) =>
createElement(InviteGroupsModal, { createElement(InviteGroupsModal, {
props: { props: {

View File

@ -1,5 +1,6 @@
import { GlToast } from '@gitlab/ui'; import { GlToast } from '@gitlab/ui';
import Vue from 'vue'; import Vue from 'vue';
import { membersProvideData } from 'ee_else_ce/invite_members/utils';
import InviteMembersModal from '~/invite_members/components/invite_members_modal.vue'; import InviteMembersModal from '~/invite_members/components/invite_members_modal.vue';
import { parseBoolean, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { parseBoolean, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
@ -23,14 +24,7 @@ export default (function initInviteMembersModal() {
inviteMembersModal = new Vue({ inviteMembersModal = new Vue({
el, el,
name: 'InviteMembersModalRoot', name: 'InviteMembersModalRoot',
provide: { provide: membersProvideData(el),
name: el.dataset.name,
overageMembersModalAvailable: parseBoolean(el.dataset.overageMembersModalAvailable),
hasGitlabSubscription: parseBoolean(el.dataset.hasGitlabSubscription),
addSeatsHref: el.dataset.addSeatsHref,
hasBsoEnabled: parseBoolean(el.dataset.hasBsoFeatureEnabled),
searchUrl: el.dataset.searchUrl,
},
render: (createElement) => render: (createElement) =>
createElement(InviteMembersModal, { createElement(InviteMembersModal, {
props: { props: {

View File

@ -0,0 +1,45 @@
import { parseBoolean } from '~/lib/utils/common_utils';
export function membersProvideData(el) {
if (!el) {
return false;
}
const {
name,
overageMembersModalAvailable,
hasGitlabSubscription,
addSeatsHref,
hasBsoFeatureEnabled,
searchUrl,
} = el.dataset;
return {
name,
overageMembersModalAvailable: parseBoolean(overageMembersModalAvailable),
hasGitlabSubscription: parseBoolean(hasGitlabSubscription),
addSeatsHref,
hasBsoEnabled: parseBoolean(hasBsoFeatureEnabled),
searchUrl,
};
}
export function groupsProvideData(el) {
if (!el) {
return false;
}
const {
freeUsersLimit,
overageMembersModalAvailable,
hasGitlabSubscription,
inviteWithCustomRoleEnabled,
} = el.dataset;
return {
freeUsersLimit: parseInt(freeUsersLimit, 10),
overageMembersModalAvailable: parseBoolean(overageMembersModalAvailable),
hasGitlabSubscription: parseBoolean(hasGitlabSubscription),
inviteWithCustomRoleEnabled: parseBoolean(inviteWithCustomRoleEnabled),
};
}

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
module HomepageData
extend ActiveSupport::Concern
private
def homepage_app_data(user)
{
review_requested_path: merge_requests_dashboard_path(reviewer_username: user.username),
assigned_to_you_path: merge_requests_dashboard_path(assignee_username: user.username)
}
end
end

View File

@ -9,6 +9,8 @@
# For users who haven't customized the setting, we simply delegate to # For users who haven't customized the setting, we simply delegate to
# `DashboardController#show`, which is the default. # `DashboardController#show`, which is the default.
class RootController < Dashboard::ProjectsController class RootController < Dashboard::ProjectsController
include HomepageData
skip_before_action :authenticate_user!, only: [:index] skip_before_action :authenticate_user!, only: [:index]
before_action :redirect_unlogged_user, if: -> { current_user.nil? } before_action :redirect_unlogged_user, if: -> { current_user.nil? }
@ -21,6 +23,7 @@ class RootController < Dashboard::ProjectsController
CACHE_CONTROL_HEADER = 'no-store' CACHE_CONTROL_HEADER = 'no-store'
def index def index
@homepage_app_data = @current_user.nil? ? {} : homepage_app_data(@current_user)
render('root/index') && return if Feature.enabled?(:personal_homepage, current_user) render('root/index') && return if Feature.enabled?(:personal_homepage, current_user)
super super

View File

@ -1562,9 +1562,11 @@ class Project < ApplicationRecord
end end
def merge_base_commit(first_commit_id, second_commit_id) def merge_base_commit(first_commit_id, second_commit_id)
strong_memoize(:"merge_base_commit_#{first_commit_id}_#{second_commit_id}") do
sha = repository.merge_base(first_commit_id, second_commit_id) sha = repository.merge_base(first_commit_id, second_commit_id)
commit_by(oid: sha) if sha commit_by(oid: sha) if sha
end end
end
def saved? def saved?
id && persisted? id && persisted?

View File

@ -1,3 +1,3 @@
- page_title _("Home") - page_title _("Home")
#js-homepage-app #js-homepage-app{ data: @homepage_app_data }

View File

@ -142,6 +142,7 @@ To impersonate a user:
1. On the left sidebar, select **Overview > Users**. 1. On the left sidebar, select **Overview > Users**.
1. From the list of users, select a user. 1. From the list of users, select a user.
1. On the top right, select **Impersonate**. 1. On the top right, select **Impersonate**.
1. To stop impersonating, on the left sidebar at the top, select **Stop impersonating** ({{< icon name="incognito">}}).
- With the API, using [impersonation tokens](../api/rest/authentication.md#impersonation-tokens). - With the API, using [impersonation tokens](../api/rest/authentication.md#impersonation-tokens).
All impersonation activities are [captured with audit events](compliance/audit_event_reports.md#user-impersonation). All impersonation activities are [captured with audit events](compliance/audit_event_reports.md#user-impersonation).

View File

@ -349,7 +349,7 @@ use `%{created_at}` in Ruby but `%{createdAt}` in JavaScript. Make sure to
... ...
computed: { computed: {
userWelcome() { userWelcome() {
sprintf(__('Hello %{username}'), { username: this.user.name }); return sprintf(__('Hello %{username}'), { username: this.user.name });
} }
} }
... ...

View File

@ -18,11 +18,11 @@ module Gitlab
GLAB_WARNING_MESSAGE = "Warning: release-cli will not be supported after 19.0. Please use glab version >= #{GLAB_REQUIRED_VERSION}. Troubleshooting: #{TROUBLESHOOTING_URL}".freeze GLAB_WARNING_MESSAGE = "Warning: release-cli will not be supported after 19.0. Please use glab version >= #{GLAB_REQUIRED_VERSION}. Troubleshooting: #{TROUBLESHOOTING_URL}".freeze
GLAB_ENV_SET_UNIX = 'export GITLAB_HOST=$CI_SERVER_URL' GLAB_ENV_SET_UNIX = 'export GITLAB_HOST=$CI_SERVER_URL'
GLAB_ENV_SET_WINDOWS = '$env:GITLAB_HOST = $env:CI_SERVER_URL' GLAB_ENV_SET_WINDOWS = '$$env:GITLAB_HOST = $$env:CI_SERVER_URL'
GLAB_LOGIN_UNIX = 'glab auth login --job-token $CI_JOB_TOKEN --hostname "$CI_SERVER_FQDN" --api-protocol $CI_SERVER_PROTOCOL' GLAB_LOGIN_UNIX = 'glab auth login --job-token $CI_JOB_TOKEN --hostname "$CI_SERVER_FQDN" --api-protocol $CI_SERVER_PROTOCOL'
GLAB_LOGIN_WINDOWS = 'glab auth login --job-token $env:CI_JOB_TOKEN --hostname "$env:CI_SERVER_FQDN" --api-protocol $env:CI_SERVER_PROTOCOL' GLAB_LOGIN_WINDOWS = 'glab auth login --job-token $$env:CI_JOB_TOKEN --hostname "$$env:CI_SERVER_FQDN" --api-protocol $$env:CI_SERVER_PROTOCOL'
GLAB_CREATE_UNIX = 'glab release create -R $CI_PROJECT_PATH' GLAB_CREATE_UNIX = 'glab release create -R $CI_PROJECT_PATH'
GLAB_CREATE_WINDOWS = 'glab release create -R $env:CI_PROJECT_PATH' GLAB_CREATE_WINDOWS = 'glab release create -R $$env:CI_PROJECT_PATH'
GLAB_PUBLISH_TO_CATALOG_FLAG = '--publish-to-catalog' # enables publishing to the catalog after creating the release GLAB_PUBLISH_TO_CATALOG_FLAG = '--publish-to-catalog' # enables publishing to the catalog after creating the release
GLAB_NO_UPDATE_FLAG = '--no-update' # disables updating the release if it already exists GLAB_NO_UPDATE_FLAG = '--no-update' # disables updating the release if it already exists
GLAB_NO_CLOSE_MILESTONE_FLAG = '--no-close-milestone' # disables closing the milestone after creating the release GLAB_NO_CLOSE_MILESTONE_FLAG = '--no-close-milestone' # disables closing the milestone after creating the release
@ -42,11 +42,11 @@ module Gitlab
fi fi
BASH BASH
GLAB_CA_CERT_CONFIG_WINDOWS = <<~POWERSHELL.chomp.freeze GLAB_CA_CERT_CONFIG_WINDOWS = <<~POWERSHELL.chomp.freeze
if ($env:ADDITIONAL_CA_CERT_BUNDLE) { if ($$env:ADDITIONAL_CA_CERT_BUNDLE) {
Write-Output "Setting CA certificate for $env:CI_SERVER_FQDN" Write-Output "Setting CA certificate for $$env:CI_SERVER_FQDN"
"$env:ADDITIONAL_CA_CERT_BUNDLE" > "#{GLAB_CA_CERT_FILENAME}" "$$env:ADDITIONAL_CA_CERT_BUNDLE" > "#{GLAB_CA_CERT_FILENAME}"
glab config set ca_cert "#{GLAB_CA_CERT_FILENAME}" --host "$env:CI_SERVER_FQDN" glab config set ca_cert "#{GLAB_CA_CERT_FILENAME}" --host "$$env:CI_SERVER_FQDN"
} }
POWERSHELL POWERSHELL
GLAB_CA_CERT_CLEANUP_WINDOWS = <<~POWERSHELL.chomp.freeze GLAB_CA_CERT_CLEANUP_WINDOWS = <<~POWERSHELL.chomp.freeze
@ -92,10 +92,10 @@ module Gitlab
def glab_windows_script def glab_windows_script
<<~POWERSHELL <<~POWERSHELL
if (Get-Command glab -ErrorAction SilentlyContinue) { if (Get-Command glab -ErrorAction SilentlyContinue) {
$glabVersionOutput = (glab --version | Select-Object -First 1) -as [string] $$glabVersionOutput = (glab --version | Select-Object -First 1) -as [string]
if ($glabVersionOutput -match 'glab (\\\d+\\\.\\\d+\\\.\\\d+)') { if ($$glabVersionOutput -match 'glab (\\\d+\\\.\\\d+\\\.\\\d+)') {
if ([version]$matches[1] -ge [version]"#{GLAB_REQUIRED_VERSION}") { if ([version]$$matches[1] -ge [version]"#{GLAB_REQUIRED_VERSION}") {
#{GLAB_ENV_SET_WINDOWS} #{GLAB_ENV_SET_WINDOWS}
#{GLAB_CA_CERT_CONFIG_WINDOWS} #{GLAB_CA_CERT_CONFIG_WINDOWS}
#{GLAB_LOGIN_WINDOWS} #{GLAB_LOGIN_WINDOWS}
@ -159,8 +159,8 @@ module Gitlab
command = GLAB_CREATE_WINDOWS.dup command = GLAB_CREATE_WINDOWS.dup
# More information: https://gitlab.com/groups/gitlab-org/-/epics/15437#note_2432564707 # More information: https://gitlab.com/groups/gitlab-org/-/epics/15437#note_2432564707
tag_name = config[:tag_name].presence || '$env:CI_COMMIT_TAG' tag_name = config[:tag_name].presence || '$$env:CI_COMMIT_TAG'
ref = config[:ref].presence || '$env:CI_COMMIT_SHA' ref = config[:ref].presence || '$$env:CI_COMMIT_SHA'
else else
command = GLAB_CREATE_UNIX.dup command = GLAB_CREATE_UNIX.dup

View File

@ -7,6 +7,22 @@ module Gitlab
Gitlab::Routing.url_helpers.help_page_path('ci/variables/_index.md', anchor: 'define-a-cicd-variable-in-the-ui') Gitlab::Routing.url_helpers.help_page_path('ci/variables/_index.md', anchor: 'define-a-cicd-variable-in-the-ui')
end end
def self.vulnerability_checks_documentation_link
Gitlab::Routing.url_helpers.help_page_path('user/application_security/dast/browser/checks/_index.md')
end
def self.secure_log_level_documentation_link
Gitlab::Routing.url_helpers.help_page_path('user/application_security/dast/browser/troubleshooting.md',
anchor: 'secure_log_level')
end
def self.authentication_actions_documentation_link
Gitlab::Routing.url_helpers.help_page_path(
'user/application_security/dast/browser/configuration/authentication.md',
anchor: 'taking-additional-actions-after-submitting-the-login-form'
)
end
# rubocop: disable Metrics/AbcSize -- Generate dynamic translation as per # rubocop: disable Metrics/AbcSize -- Generate dynamic translation as per
# https://docs.gitlab.com/ee/development/i18n/externalization.html#keep-translations-dynamic # https://docs.gitlab.com/ee/development/i18n/externalization.html#keep-translations-dynamic
def self.data def self.data
@ -35,11 +51,11 @@ module Gitlab
type: "string", type: "string",
example: "select(option=id:accept-yes),click(on=css:.continue)", example: "select(option=id:accept-yes),click(on=css:.continue)",
name: s_("DastProfiles|After-login actions"), name: s_("DastProfiles|After-login actions"),
description: s_( description: format(s_(
"DastProfiles|A comma-separated list of actions to take after login but before login verification. " \ "DastProfiles|A comma-separated list of actions to take after login but before login verification. " \
"Supports `click` and `select` actions. " \ "Supports `click` and `select` actions. " \
"See [Taking additional actions after submitting the login form]" \ "See [Taking additional actions after submitting the login form](%{documentation_link})."),
"(authentication.md#taking-additional-actions-after-submitting-the-login-form)." documentation_link: authentication_actions_documentation_link
) )
}, },
DAST_AUTH_BEFORE_LOGIN_ACTIONS: { DAST_AUTH_BEFORE_LOGIN_ACTIONS: {
@ -519,6 +535,7 @@ module Gitlab
}, },
scanner: { scanner: {
DAST_AUTH_REPORT: { DAST_AUTH_REPORT: {
additional: true,
auth: true, auth: true,
type: "boolean", type: "boolean",
example: true, example: true,
@ -531,24 +548,29 @@ module Gitlab
) )
}, },
DAST_CHECKS_TO_EXCLUDE: { DAST_CHECKS_TO_EXCLUDE: {
additional: true,
type: "string", type: "string",
example: "552.2,78.1", example: "552.2,78.1",
name: s_("DastProfiles|Excluded checks"), name: s_("DastProfiles|Excluded checks"),
description: s_( description: format(s_(
"DastProfiles|Comma-separated list of check identifiers to exclude from the scan. " \ "DastProfiles|Comma-separated list of check identifiers to exclude from the scan. " \
"For identifiers, see [vulnerability checks](../checks/_index.md)." "For identifiers, see [vulnerability checks](%{documentation_link})."),
documentation_link: vulnerability_checks_documentation_link
) )
}, },
DAST_CHECKS_TO_RUN: { DAST_CHECKS_TO_RUN: {
additional: true,
type: "List of strings", type: "List of strings",
example: "16.1,16.2,16.3", example: "16.1,16.2,16.3",
name: s_("DastProfiles|Included checks"), name: s_("DastProfiles|Included checks"),
description: s_( description: format(s_(
"DastProfiles|Comma-separated list of check identifiers to use for the scan. " \ "DastProfiles|Comma-separated list of check identifiers to use for the scan. " \
"For identifiers, see [vulnerability checks](../checks/_index.md)." "For identifiers, see [vulnerability checks](%{documentation_link})."),
documentation_link: vulnerability_checks_documentation_link
) )
}, },
DAST_CRAWL_GRAPH: { DAST_CRAWL_GRAPH: {
additional: true,
type: "boolean", type: "boolean",
example: true, example: true,
name: s_("DastProfiles|Generate graph"), name: s_("DastProfiles|Generate graph"),
@ -559,18 +581,21 @@ module Gitlab
) )
}, },
DAST_FULL_SCAN: { DAST_FULL_SCAN: {
additional: true,
type: "boolean", type: "boolean",
example: true, example: true,
name: s_("DastProfiles|Full scan"), name: s_("DastProfiles|Full scan"),
description: s_("DastProfiles|Set to `true` to run both passive and active checks. Default is `false`.") description: s_("DastProfiles|Set to `true` to run both passive and active checks. Default is `false`.")
}, },
DAST_LOG_BROWSER_OUTPUT: { DAST_LOG_BROWSER_OUTPUT: {
additional: true,
type: "boolean", type: "boolean",
example: true, example: true,
name: s_("DastProfiles|Log browser output"), name: s_("DastProfiles|Log browser output"),
description: s_("DastProfiles|Set to `true` to log Chromium `STDOUT` and `STDERR`.") description: s_("DastProfiles|Set to `true` to log Chromium `STDOUT` and `STDERR`.")
}, },
DAST_LOG_CONFIG: { DAST_LOG_CONFIG: {
additional: true,
type: "List of strings", type: "List of strings",
example: "brows:debug,auth:debug", example: "brows:debug,auth:debug",
name: s_("DastProfiles|Log levels"), name: s_("DastProfiles|Log levels"),
@ -578,12 +603,14 @@ module Gitlab
"DastProfiles|A list of modules and their intended logging level for use in the console log.") "DastProfiles|A list of modules and their intended logging level for use in the console log.")
}, },
DAST_LOG_DEVTOOLS_CONFIG: { DAST_LOG_DEVTOOLS_CONFIG: {
additional: true,
type: "string", type: "string",
example: "Default:messageAndBody,truncate:2000", example: "Default:messageAndBody,truncate:2000",
name: s_("DastProfiles|Log messages"), name: s_("DastProfiles|Log messages"),
description: s_("DastProfiles|Set to log protocol messages between DAST and the Chromium browser.") description: s_("DastProfiles|Set to log protocol messages between DAST and the Chromium browser.")
}, },
DAST_LOG_FILE_CONFIG: { DAST_LOG_FILE_CONFIG: {
additional: true,
type: "List of strings", type: "List of strings",
example: "brows:debug,auth:debug", example: "brows:debug,auth:debug",
name: s_("DastProfiles|Log file levels"), name: s_("DastProfiles|Log file levels"),
@ -591,24 +618,28 @@ module Gitlab
"DastProfiles|A list of modules and their intended logging level for use in the file log.") "DastProfiles|A list of modules and their intended logging level for use in the file log.")
}, },
DAST_LOG_FILE_PATH: { DAST_LOG_FILE_PATH: {
additional: true,
type: "string", type: "string",
example: "/output/browserker.log", example: "/output/browserker.log",
name: s_("DastProfiles|Log file path"), name: s_("DastProfiles|Log file path"),
description: s_("DastProfiles|Set to the path of the file log. Default is `gl-dast-scan.log`.") description: s_("DastProfiles|Set to the path of the file log. Default is `gl-dast-scan.log`.")
}, },
SECURE_ANALYZERS_PREFIX: { SECURE_ANALYZERS_PREFIX: {
additional: true,
type: "URL", type: "URL",
example: "registry.organization.com", example: "registry.organization.com",
name: s_("DastProfiles|Docker registry"), name: s_("DastProfiles|Docker registry"),
description: s_("DastProfiles|Set the Docker registry base address from which to download the analyzer.") description: s_("DastProfiles|Set the Docker registry base address from which to download the analyzer.")
}, },
SECURE_LOG_LEVEL: { SECURE_LOG_LEVEL: {
additional: true,
type: "string", type: "string",
example: "debug", example: "debug",
name: s_("DastProfiles|Default log level"), name: s_("DastProfiles|Default log level"),
description: s_( description: format(s_(
"DastProfiles|Set the default level for the file log. " \ "DastProfiles|Set the default level for the file log. " \
"See [SECURE_LOG_LEVEL](../troubleshooting.md#secure_log_level)." \ "See [SECURE_LOG_LEVEL](%{documentation_link})."),
documentation_link: secure_log_level_documentation_link
) )
} }
} }
@ -617,7 +648,7 @@ module Gitlab
# rubocop: enable Metrics/AbcSize # rubocop: enable Metrics/AbcSize
def self.additional_site_variables def self.additional_site_variables
data[:site].filter { |_, variable| variable[:additional] } data[:site].merge(data[:scanner]).filter { |_, variable| variable[:additional] }
end end
def self.auth_variables def self.auth_variables

View File

@ -136,6 +136,9 @@ module Gitlab
end end
def emitter_class def emitter_class
# Use test emitter in test environment to prevent HTTP requests
return SnowplowTestEmitter if Rails.env.test?
# snowplow_enabled? is true for gitlab.com and customers that configured their own Snowplow collector # snowplow_enabled? is true for gitlab.com and customers that configured their own Snowplow collector
# In both bases we do not want to log the events being sent as the instance is controlled by the same company # In both bases we do not want to log the events being sent as the instance is controlled by the same company
# controlling the Snowplow collector. # controlling the Snowplow collector.

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
module Gitlab
module Tracking
class SnowplowTestEmitter < SnowplowTracker::Emitter
extend ::Gitlab::Utils::Override
# Override send_requests to prevent HTTP requests in test environment
# This allows event tracking to work normally without making actual HTTP calls
override :send_requests
def send_requests(events)
events.size
end
end
end
end

View File

@ -19978,7 +19978,7 @@ msgstr ""
msgid "DastProfiles|A URL that is compared to the URL in the browser to determine if authentication has succeeded after the login form is submitted." msgid "DastProfiles|A URL that is compared to the URL in the browser to determine if authentication has succeeded after the login form is submitted."
msgstr "" msgstr ""
msgid "DastProfiles|A comma-separated list of actions to take after login but before login verification. Supports `click` and `select` actions. See [Taking additional actions after submitting the login form](authentication.md#taking-additional-actions-after-submitting-the-login-form)." msgid "DastProfiles|A comma-separated list of actions to take after login but before login verification. Supports `click` and `select` actions. See [Taking additional actions after submitting the login form](%{documentation_link})."
msgstr "" msgstr ""
msgid "DastProfiles|A comma-separated list of selectors representing elements to click on prior to entering the DAST_AUTH_USERNAME and DAST_AUTH_PASSWORD into the login form." msgid "DastProfiles|A comma-separated list of selectors representing elements to click on prior to entering the DAST_AUTH_USERNAME and DAST_AUTH_PASSWORD into the login form."
@ -20086,10 +20086,10 @@ msgstr ""
msgid "DastProfiles|Clear input fields" msgid "DastProfiles|Clear input fields"
msgstr "" msgstr ""
msgid "DastProfiles|Comma-separated list of check identifiers to exclude from the scan. For identifiers, see [vulnerability checks](../checks/_index.md)." msgid "DastProfiles|Comma-separated list of check identifiers to exclude from the scan. For identifiers, see [vulnerability checks](%{documentation_link})."
msgstr "" msgstr ""
msgid "DastProfiles|Comma-separated list of check identifiers to use for the scan. For identifiers, see [vulnerability checks](../checks/_index.md)." msgid "DastProfiles|Comma-separated list of check identifiers to use for the scan. For identifiers, see [vulnerability checks](%{documentation_link})."
msgstr "" msgstr ""
msgid "DastProfiles|Comma-separated list of selectors that are ignored when scanning." msgid "DastProfiles|Comma-separated list of selectors that are ignored when scanning."
@ -20434,7 +20434,7 @@ msgstr ""
msgid "DastProfiles|Set the Docker registry base address from which to download the analyzer." msgid "DastProfiles|Set the Docker registry base address from which to download the analyzer."
msgstr "" msgstr ""
msgid "DastProfiles|Set the default level for the file log. See [SECURE_LOG_LEVEL](../troubleshooting.md#secure_log_level)." msgid "DastProfiles|Set the default level for the file log. See [SECURE_LOG_LEVEL](%{documentation_link})."
msgstr "" msgstr ""
msgid "DastProfiles|Set to `false` to disable caching. Default: `true`. **Note**: Disabling cache can cause OOM events or DAST job timeouts." msgid "DastProfiles|Set to `false` to disable caching. Default: `true`. **Note**: Disabling cache can cause OOM events or DAST job timeouts."
@ -23325,6 +23325,27 @@ msgstr ""
msgid "Duo Workflow|Something went wrong saving Duo Workflow settings" msgid "Duo Workflow|Something went wrong saving Duo Workflow settings"
msgstr "" msgstr ""
msgid "DuoAgentsPlatform|Agents"
msgstr ""
msgid "DuoAgentsPlatform|Failed to fetch workflows"
msgstr ""
msgid "DuoAgentsPlatform|Last updated"
msgstr ""
msgid "DuoAgentsPlatform|New Agent runs will appear here."
msgstr ""
msgid "DuoAgentsPlatform|No Agent runs yet"
msgstr ""
msgid "DuoAgentsPlatform|Prompt"
msgstr ""
msgid "DuoAgentsPlatform|Status"
msgstr ""
msgid "DuoChat|%{linkStart}Learn how%{linkEnd} to set up Code Suggestions and Chat in your IDE. You can also use Chat in GitLab. Ask questions about:" msgid "DuoChat|%{linkStart}Learn how%{linkEnd} to set up Code Suggestions and Chat in your IDE. You can also use Chat in GitLab. Ask questions about:"
msgstr "" msgstr ""

View File

@ -0,0 +1,32 @@
import { shallowMount } from '@vue/test-utils';
import HomepageApp from '~/homepage/components/homepage_app.vue';
import MergeRequestsWidget from '~/homepage/components/merge_requests_widget.vue';
describe('HomepageApp', () => {
const MOCK_REVIEW_REQUESTED_PATH = '/review/requested/path';
const MOCK_ASSIGNED_TO_YOU_PATH = '/assigned/to/you/path';
let wrapper;
const findMergeRequestsWidget = () => wrapper.findComponent(MergeRequestsWidget);
function createWrapper() {
wrapper = shallowMount(HomepageApp, {
propsData: {
reviewRequestedPath: MOCK_REVIEW_REQUESTED_PATH,
assignedToYouPath: MOCK_ASSIGNED_TO_YOU_PATH,
},
});
}
beforeEach(() => {
createWrapper();
});
it('passes the correct props to the `MergeRequestsWidget` component', () => {
expect(findMergeRequestsWidget().props()).toEqual({
reviewRequestedPath: MOCK_REVIEW_REQUESTED_PATH,
assignedToYouPath: MOCK_ASSIGNED_TO_YOU_PATH,
});
});
});

View File

@ -0,0 +1,33 @@
import { shallowMount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import MergeRequestsWidget from '~/homepage/components/merge_requests_widget.vue';
describe('MergeRequestsWidget', () => {
const MOCK_REVIEW_REQUESTED_PATH = '/review/requested/path';
const MOCK_ASSIGNED_TO_YOU_PATH = '/assigned/to/you/path';
let wrapper;
const findGlLinks = () => wrapper.findAllComponents(GlLink);
function createWrapper() {
wrapper = shallowMount(MergeRequestsWidget, {
propsData: {
reviewRequestedPath: MOCK_REVIEW_REQUESTED_PATH,
assignedToYouPath: MOCK_ASSIGNED_TO_YOU_PATH,
},
});
}
beforeEach(() => {
createWrapper();
});
it('renders the "Review requested" link', () => {
expect(findGlLinks().at(0).props('href')).toBe(MOCK_REVIEW_REQUESTED_PATH);
});
it('renders the "Assigned to you" link', () => {
expect(findGlLinks().at(1).props('href')).toBe(MOCK_ASSIGNED_TO_YOU_PATH);
});
});

View File

@ -22,7 +22,7 @@ RSpec.describe InviteMembersHelper do
help_link: help_page_url('user/permissions.md'), help_link: help_page_url('user/permissions.md'),
is_project: 'true', is_project: 'true',
access_levels: ProjectMember.access_level_roles.to_json, access_levels: ProjectMember.access_level_roles.to_json,
full_path: project.full_path full_path: Gitlab.ee? ? project.root_ancestor.full_path : project.full_path
} }
expect(helper.common_invite_group_modal_data(project, ProjectMember)).to include(attributes) expect(helper.common_invite_group_modal_data(project, ProjectMember)).to include(attributes)
@ -60,7 +60,7 @@ RSpec.describe InviteMembersHelper do
root_id: project.root_ancestor.id, root_id: project.root_ancestor.id,
name: project.name, name: project.name,
default_access_level: Gitlab::Access::GUEST, default_access_level: Gitlab::Access::GUEST,
full_path: project.full_path full_path: Gitlab.ee? ? project.root_ancestor.full_path : project.full_path
} }
expect(helper.common_invite_modal_dataset(project)).to include(attributes) expect(helper.common_invite_modal_dataset(project)).to include(attributes)

View File

@ -40,7 +40,7 @@ RSpec.describe Gitlab::Ci::Build::Releaser, feature_category: :continuous_integr
result_for_release_cli_without_catalog_publish = "#{release_cli_command} #{release_cli_assets_links}" result_for_release_cli_without_catalog_publish = "#{release_cli_command} #{release_cli_assets_links}"
glab_create_unix = 'glab release create -R $CI_PROJECT_PATH' glab_create_unix = 'glab release create -R $CI_PROJECT_PATH'
glab_create_windows = 'glab release create -R $env:CI_PROJECT_PATH' glab_create_windows = 'glab release create -R $$env:CI_PROJECT_PATH'
glab_command = "\"release-$CI_COMMIT_SHA\" #{glab_assets_links} --milestone \"m1,m2,m3\" --name \"Release $CI_COMMIT_SHA\" --experimental-notes-text-or-file \"Created using the release-cli $EXTRA_DESCRIPTION\" --ref \"$CI_COMMIT_SHA\" --tag-message \"Annotated tag message\" --released-at \"2020-07-15T08:00:00Z\" --no-update --no-close-milestone" glab_command = "\"release-$CI_COMMIT_SHA\" #{glab_assets_links} --milestone \"m1,m2,m3\" --name \"Release $CI_COMMIT_SHA\" --experimental-notes-text-or-file \"Created using the release-cli $EXTRA_DESCRIPTION\" --ref \"$CI_COMMIT_SHA\" --tag-message \"Annotated tag message\" --released-at \"2020-07-15T08:00:00Z\" --no-update --no-close-milestone"
warning_message = "Warning: release-cli will not be supported after 19.0. Please use glab version >= #{described_class::GLAB_REQUIRED_VERSION}. Troubleshooting: http://localhost/help/user/project/releases/_index.md#gitlab-cli-version-requirement" warning_message = "Warning: release-cli will not be supported after 19.0. Please use glab version >= #{described_class::GLAB_REQUIRED_VERSION}. Troubleshooting: http://localhost/help/user/project/releases/_index.md#gitlab-cli-version-requirement"
@ -66,10 +66,10 @@ RSpec.describe Gitlab::Ci::Build::Releaser, feature_category: :continuous_integr
BASH BASH
windows_result_for_glab_or_release_cli_without_catalog_publish = <<~POWERSHELL windows_result_for_glab_or_release_cli_without_catalog_publish = <<~POWERSHELL
if (Get-Command glab -ErrorAction SilentlyContinue) { if (Get-Command glab -ErrorAction SilentlyContinue) {
$glabVersionOutput = (glab --version | Select-Object -First 1) -as [string] $$glabVersionOutput = (glab --version | Select-Object -First 1) -as [string]
if ($glabVersionOutput -match 'glab (\\\d+\\\.\\\d+\\\.\\\d+)') { if ($$glabVersionOutput -match 'glab (\\\d+\\\.\\\d+\\\.\\\d+)') {
if ([version]$matches[1] -ge [version]"#{described_class::GLAB_REQUIRED_VERSION}") { if ([version]$$matches[1] -ge [version]"#{described_class::GLAB_REQUIRED_VERSION}") {
#{described_class::GLAB_ENV_SET_WINDOWS} #{described_class::GLAB_ENV_SET_WINDOWS}
#{described_class::GLAB_CA_CERT_CONFIG_WINDOWS} #{described_class::GLAB_CA_CERT_CONFIG_WINDOWS}
#{described_class::GLAB_LOGIN_WINDOWS} #{described_class::GLAB_LOGIN_WINDOWS}
@ -145,10 +145,10 @@ RSpec.describe Gitlab::Ci::Build::Releaser, feature_category: :continuous_integr
BASH BASH
windows_result_for_glab_or_release_cli_with_catalog_publish = <<~POWERSHELL windows_result_for_glab_or_release_cli_with_catalog_publish = <<~POWERSHELL
if (Get-Command glab -ErrorAction SilentlyContinue) { if (Get-Command glab -ErrorAction SilentlyContinue) {
$glabVersionOutput = (glab --version | Select-Object -First 1) -as [string] $$glabVersionOutput = (glab --version | Select-Object -First 1) -as [string]
if ($glabVersionOutput -match 'glab (\\\d+\\\.\\\d+\\\.\\\d+)') { if ($$glabVersionOutput -match 'glab (\\\d+\\\.\\\d+\\\.\\\d+)') {
if ([version]$matches[1] -ge [version]"#{described_class::GLAB_REQUIRED_VERSION}") { if ([version]$$matches[1] -ge [version]"#{described_class::GLAB_REQUIRED_VERSION}") {
#{described_class::GLAB_ENV_SET_WINDOWS} #{described_class::GLAB_ENV_SET_WINDOWS}
#{described_class::GLAB_CA_CERT_CONFIG_WINDOWS} #{described_class::GLAB_CA_CERT_CONFIG_WINDOWS}
#{described_class::GLAB_LOGIN_WINDOWS} #{described_class::GLAB_LOGIN_WINDOWS}

View File

@ -6,9 +6,15 @@ RSpec.describe ::Gitlab::Security::DastVariables, feature_category: :dynamic_app
let(:dast_variables) { described_class } let(:dast_variables) { described_class }
describe '#additional_site_variables' do describe '#additional_site_variables' do
it 'contains only additional variables' do [:site, :scanner].each do |type|
described_class.additional_site_variables.each_value do |variable| it "contains additional #{type} variables" do
expect(variable[:additional]).to be(true) described_class.data[type].each do |key, variable|
if variable[:additional]
expect(described_class.additional_site_variables[key]).not_to be_nil
else
expect(described_class.additional_site_variables[key]).to be_nil
end
end
end end
end end
end end

View File

@ -106,7 +106,11 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow, :do_not_stub_snowplow_b
end end
end end
context "when in development or test environment" do context "when in development environment" do
before do
allow(Rails.env).to receive_messages(test?: false, development?: true)
end
it "initializes POST emitter with buffer_size 1" do it "initializes POST emitter with buffer_size 1" do
allow(SnowplowTracker::Tracker).to receive(:new).and_return(tracker) allow(SnowplowTracker::Tracker).to receive(:new).and_return(tracker)
allow(tracker).to receive(:track_struct_event).and_call_original allow(tracker).to receive(:track_struct_event).and_call_original
@ -174,7 +178,7 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow, :do_not_stub_snowplow_b
let(:payload) { { "event" => "page_view", "user_id" => "123" } } let(:payload) { { "event" => "page_view", "user_id" => "123" } }
before do before do
allow(SnowplowTracker::AsyncEmitter).to receive(:new).and_return(emitter) allow(Gitlab::Tracking::SnowplowTestEmitter).to receive(:new).and_return(emitter)
end end
it "forwards the payload to the emitter" do it "forwards the payload to the emitter" do
@ -333,6 +337,7 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow, :do_not_stub_snowplow_b
context 'when snowplow is enabled' do context 'when snowplow is enabled' do
before do before do
stub_application_setting(snowplow_enabled?: true) stub_application_setting(snowplow_enabled?: true)
allow(Rails.env).to receive(:test?).and_return(false)
end end
it 'uses AsyncEmitter' do it 'uses AsyncEmitter' do
@ -346,6 +351,7 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow, :do_not_stub_snowplow_b
context 'when snowplow is disabled' do context 'when snowplow is disabled' do
before do before do
stub_application_setting(snowplow_enabled?: false) stub_application_setting(snowplow_enabled?: false)
allow(Rails.env).to receive(:test?).and_return(false)
end end
context 'when GITLAB_DISABLE_PRODUCT_USAGE_EVENT_LOGGING env variable is true' do context 'when GITLAB_DISABLE_PRODUCT_USAGE_EVENT_LOGGING env variable is true' do
@ -379,5 +385,15 @@ RSpec.describe Gitlab::Tracking::Destinations::Snowplow, :do_not_stub_snowplow_b
end end
end end
end end
context 'in test environment' do
it 'uses SnowplowTestEmitter to prevent HTTP requests' do
# In test environment, we expect SnowplowTestEmitter to prevent HTTP requests
expect(Gitlab::Tracking::SnowplowTestEmitter).to receive(:new)
expect(SnowplowTracker::AsyncEmitter).not_to receive(:new)
expect(Gitlab::Tracking::SnowplowLoggingEmitter).not_to receive(:new)
subject.send(:emitter)
end
end
end end
end end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Tracking::SnowplowTestEmitter, feature_category: :service_ping do
describe '#send_requests' do
it 'returns the number of events' do
emitter = described_class.new(endpoint: 'test')
events = [{}, {}]
expect(emitter.send_requests(events)).to eq(2)
end
end
end

View File

@ -10241,4 +10241,16 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
expect(project.valid_lfs_oids(oids)).to eq([lfs_object.oid]) expect(project.valid_lfs_oids(oids)).to eq([lfs_object.oid])
end end
end end
describe '#merge_base_commit' do
let_it_be(:project) { create(:project, :repository) }
let(:commit1) { project.repository.commit }
let(:commit2) { project.repository.commit('feature') }
it 'memoizes the result' do
expect(project.repository).to receive(:merge_base).once.and_call_original
2.times { project.merge_base_commit(commit1.id, commit2.id) }
end
end
end end

View File

@ -2,17 +2,9 @@
module StubSnowplow module StubSnowplow
def stub_snowplow def stub_snowplow
# Using a high buffer size to not cause early flushes
buffer_size = 100
# WebMock is set up to allow requests to `localhost` # WebMock is set up to allow requests to `localhost`
host = 'localhost' host = 'localhost'
# rubocop:disable RSpec/AnyInstanceOf
allow_any_instance_of(Gitlab::Tracking::Destinations::Snowplow)
.to receive(:emitter)
.and_return(SnowplowTracker::Emitter.new(endpoint: host, options: { buffer_size: buffer_size }))
# rubocop:enable RSpec/AnyInstanceOf
stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: host) stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: host)
allow(SnowplowTracker::SelfDescribingJson).to receive(:new).and_call_original allow(SnowplowTracker::SelfDescribingJson).to receive(:new).and_call_original

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'root/index.html.haml', feature_category: :onboarding do
let_it_be(:mock_review_requested_path) { "review_requested_path" }
let_it_be(:mock_assigned_to_you_path) { "assigned_to_you_path" }
before do
@homepage_app_data = {
review_requested_path: mock_review_requested_path,
assigned_to_you_path: mock_assigned_to_you_path
}
render
end
it 'renders the app root element with the correct data attributes' do
expect(rendered).to have_css("[data-review-requested-path='#{mock_review_requested_path}']")
expect(rendered).to have_css("[data-assigned-to-you-path='#{mock_assigned_to_you_path}']")
end
end

View File

@ -44,12 +44,25 @@ module Tooling
end end
def render_description(description) def render_description(description)
# replace help_page_path-generated documentation link # replace help_page_path-generated documentation links
# with relative path to documentation file # with relative paths to documentation files
description.sub( description
.sub(
Gitlab::Security::DastVariables.ci_variables_documentation_link, Gitlab::Security::DastVariables.ci_variables_documentation_link,
'../../../../../ci/variables/_index.md#define-a-cicd-variable-in-the-ui' '../../../../../ci/variables/_index.md#define-a-cicd-variable-in-the-ui'
) )
.sub(
Gitlab::Security::DastVariables.vulnerability_checks_documentation_link,
'../checks/_index.md'
)
.sub(
Gitlab::Security::DastVariables.secure_log_level_documentation_link,
'../troubleshooting.md#secure_log_level'
)
.sub(
Gitlab::Security::DastVariables.authentication_actions_documentation_link,
'authentication.md#taking-additional-actions-after-submitting-the-login-form'
)
end end
def render_row(*values) def render_row(*values)