diff --git a/.gitlab/ci/release-environments.gitlab-ci.yml b/.gitlab/ci/release-environments.gitlab-ci.yml
index bcd1a3b047c..82de8b99b74 100644
--- a/.gitlab/ci/release-environments.gitlab-ci.yml
+++ b/.gitlab/ci/release-environments.gitlab-ci.yml
@@ -26,6 +26,7 @@ start-release-environments-pipeline:
- project: 'gitlab-org/gitlab'
ref: 'master'
file: '.gitlab/ci/release-environments/main.gitlab-ci.yml'
+ resource_group: release-environment-${CI_COMMIT_REF_SLUG}
start-release-environments-security-pipeline:
allow_failure: true
@@ -54,3 +55,4 @@ start-release-environments-security-pipeline:
- project: 'gitlab-org/security/gitlab'
ref: 'master'
file: '.gitlab/ci/release-environments/security.gitlab-ci.yml'
+ resource_group: release-environment-${CI_COMMIT_REF_SLUG}
diff --git a/.gitlab/ci/release-environments/main.gitlab-ci.yml b/.gitlab/ci/release-environments/main.gitlab-ci.yml
index df1f0b86dc0..c911da4618d 100644
--- a/.gitlab/ci/release-environments/main.gitlab-ci.yml
+++ b/.gitlab/ci/release-environments/main.gitlab-ci.yml
@@ -82,7 +82,6 @@ release-environments-deploy:
branch: main
strategy: depend
needs: ["release-environments-deploy-env"]
- resource_group: release-environment-${CI_COMMIT_REF_SLUG}
release-environments-qa:
stage: qa
diff --git a/.gitlab/ci/release-environments/security.gitlab-ci.yml b/.gitlab/ci/release-environments/security.gitlab-ci.yml
index cca5389d88a..7ad33654ed2 100644
--- a/.gitlab/ci/release-environments/security.gitlab-ci.yml
+++ b/.gitlab/ci/release-environments/security.gitlab-ci.yml
@@ -85,7 +85,6 @@ release-environments-deploy:
branch: main
strategy: depend
needs: ["release-environments-deploy-env"]
- resource_group: release-environment-${CI_COMMIT_REF_SLUG}
release-environments-qa:
stage: qa
diff --git a/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml b/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml
index 9c07e555ebc..6020ce1918b 100644
--- a/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml
+++ b/.gitlab/ci/test-on-gdk/main.gitlab-ci.yml
@@ -5,13 +5,23 @@ include:
- local: .gitlab/ci/qa-common/variables.gitlab-ci.yml
# code pattern changes
-.code-pattern-changes: &code-pattern-changes
- if: $MR_CODE_PATTERNS == "true"
+.code-pattern-changes-with-selective-disabled: &code-pattern-changes-with-selective-disabled
+ if: $MR_CODE_PATTERNS == "true" && $SELECTIVE_EXECUTION_IMPROVED != "true"
+
+# code pattern changes with selective execution enabled
+.code-pattern-with-selective-enabled-knapsack-set: &code-pattern-with-selective-enabled-knapsack-set
+ if: $MR_CODE_PATTERNS == "true" && $QA_TESTS != "" && $KNAPSACK_TEST_FILE_PATTERN != "" && $SELECTIVE_EXECUTION_IMPROVED == "true"
+
+.code-pattern-with-selective-enabled-knapsack-unset: &code-pattern-with-selective-enabled-knapsack-unset
+ if: $MR_CODE_PATTERNS == "true" && $QA_TESTS != "" && $KNAPSACK_TEST_FILE_PATTERN == "" && $SELECTIVE_EXECUTION_IMPROVED == "true"
.rules:gdk:qa-selective:
rules:
- - <<: *code-pattern-changes
+ - <<: *code-pattern-changes-with-selective-disabled
when: never
+ - <<: *code-pattern-with-selective-enabled-knapsack-set
+ when: never
+ - <<: *code-pattern-with-selective-enabled-knapsack-unset
- !reference [.rules:test:qa-selective, rules]
- if: $QA_SUITES =~ /Test::Instance::Blocking/
@@ -19,10 +29,13 @@ include:
rules:
# To account for cases where a group label is set which may trigger selective execution
# But we want to execute full blocking suite on gdk in case of code-pattern-changes
- - <<: *code-pattern-changes
+ - <<: *code-pattern-changes-with-selective-disabled
variables:
QA_TESTS: ""
KNAPSACK_TEST_FILE_PATTERN: ""
+ - <<: *code-pattern-with-selective-enabled-knapsack-unset
+ when: never
+ - <<: *code-pattern-with-selective-enabled-knapsack-set
- !reference [.rules:test:qa-parallel, rules]
- if: $QA_SUITES =~ /Test::Instance::Blocking/
- !reference [.rules:test:manual, rules]
diff --git a/Gemfile b/Gemfile
index 42ae2957646..8f1b16391f2 100644
--- a/Gemfile
+++ b/Gemfile
@@ -536,7 +536,7 @@ group :development, :test, :coverage do
gem 'simplecov', '~> 0.22', require: false, feature_category: :tooling
gem 'simplecov-lcov', '~> 0.8.0', require: false, feature_category: :tooling
gem 'simplecov-cobertura', '~> 2.1.0', require: false, feature_category: :tooling
- gem 'undercover', '~> 0.4.4', require: false, feature_category: :tooling
+ gem 'undercover', '~> 0.5.0', require: false, feature_category: :tooling
end
# Gems required in omnibus-gitlab pipeline
diff --git a/Gemfile.checksum b/Gemfile.checksum
index c62ad21033c..6328b384d82 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -726,7 +726,7 @@
{"name":"typhoeus","version":"1.4.0","platform":"ruby","checksum":"fff9880d5dc35950e7706cf132fd297f377c049101794be1cf01c95567f642d4"},
{"name":"tzinfo","version":"2.0.6","platform":"ruby","checksum":"8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b"},
{"name":"uber","version":"0.1.0","platform":"ruby","checksum":"5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc"},
-{"name":"undercover","version":"0.4.6","platform":"ruby","checksum":"7255959205be7ba492731d4376d89d7cceaed1e675a14ae55c90f71a1621e6ae"},
+{"name":"undercover","version":"0.5.0","platform":"ruby","checksum":"ef99a8478be5466fb13fcd199f659ae308b81f71145a5a4e57428ff67d109fae"},
{"name":"unf","version":"0.1.4","platform":"java","checksum":"49a5972ec0b3d091d3b0b2e00113f2f342b9b212f0db855eb30a629637f6d302"},
{"name":"unf","version":"0.1.4","platform":"ruby","checksum":"4999517a531f2a955750f8831941891f6158498ec9b6cb1c81ce89388e63022e"},
{"name":"unf_ext","version":"0.0.8.2","platform":"ruby","checksum":"90b9623ee359cc4878461c5d2eab7d3d3ce5801a680a9e7ac83b8040c5b742fa"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 9fa46ee0694..446bb0638a3 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1837,10 +1837,11 @@ GEM
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uber (0.1.0)
- undercover (0.4.6)
+ undercover (0.5.0)
+ bigdecimal
imagen (>= 0.1.8)
rainbow (>= 2.1, < 4.0)
- rugged (>= 0.27, < 1.7)
+ rugged (>= 0.27, < 1.8)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
@@ -2272,7 +2273,7 @@ DEPENDENCIES
truncato (~> 0.7.12)
tty-prompt (~> 0.23)
typhoeus (~> 1.4.0)
- undercover (~> 0.4.4)
+ undercover (~> 0.5.0)
unleash (~> 3.2.2)
valid_email (~> 0.1)
validates_hostname (~> 1.0.13)
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 4ae4d619774..73ddb96e000 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -17,6 +17,34 @@ import { fullBoardId } from './boards_util';
Vue.use(VueApollo);
Vue.use(PortalVue);
+defaultClient.cache.policies.addTypePolicies({
+ BoardList: {
+ fields: {
+ issues: {
+ keyArgs: ['filters'],
+ },
+ },
+ },
+ IssueConnection: {
+ merge(existing = { nodes: [] }, incoming, { args }) {
+ if (!args?.after) {
+ return incoming;
+ }
+ return {
+ ...incoming,
+ nodes: [...existing.nodes, ...incoming.nodes],
+ };
+ },
+ },
+ Board: {
+ fields: {
+ epics: {
+ keyArgs: ['boardId'],
+ },
+ },
+ },
+});
+
const apolloProvider = new VueApollo({
defaultClient,
});
diff --git a/app/assets/javascripts/ci/pipeline_mini_graph/graphql/queries/get_pipeline_mini_graph.query.graphql b/app/assets/javascripts/ci/pipeline_mini_graph/graphql/queries/get_pipeline_mini_graph.query.graphql
index 09977fed4ce..343afa99209 100644
--- a/app/assets/javascripts/ci/pipeline_mini_graph/graphql/queries/get_pipeline_mini_graph.query.graphql
+++ b/app/assets/javascripts/ci/pipeline_mini_graph/graphql/queries/get_pipeline_mini_graph.query.graphql
@@ -32,17 +32,21 @@ query getPipelineMiniGraph($fullPath: ID!, $iid: ID!) {
downstream {
nodes {
id
- path
- project {
- id
- name
- }
detailedStatus {
id
detailsPath
icon
label
}
+ path
+ project {
+ id
+ name
+ }
+ sourceJob {
+ id
+ retried
+ }
}
}
}
diff --git a/app/assets/javascripts/ci/pipeline_mini_graph/pipeline_mini_graph.vue b/app/assets/javascripts/ci/pipeline_mini_graph/pipeline_mini_graph.vue
index 6957034cdbd..4bc9dfad0bb 100644
--- a/app/assets/javascripts/ci/pipeline_mini_graph/pipeline_mini_graph.vue
+++ b/app/assets/javascripts/ci/pipeline_mini_graph/pipeline_mini_graph.vue
@@ -2,6 +2,7 @@
import { GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { createAlert } from '~/alert';
import { __ } from '~/locale';
+import { reportToSentry } from '~/ci/utils';
import { keepLatestDownstreamPipelines } from '~/ci/pipeline_details/utils/parsing_utils';
import { getQueryHeaders, toggleQueryPollingByVisibility } from '~/ci/pipeline_details/graph/utils';
import { PIPELINE_MINI_GRAPH_POLL_INTERVAL } from '~/ci/pipeline_details/constants';
@@ -13,6 +14,7 @@ import PipelineStages from './pipeline_stages.vue';
* Renders the GraphQL instance of the pipeline mini graph.
*/
export default {
+ name: 'PipelineMiniGraph',
i18n: {
pipelineMiniGraphFetchError: __('There was a problem fetching the pipeline mini graph.'),
},
@@ -74,8 +76,9 @@ export default {
update({ project }) {
return project?.pipeline || {};
},
- error() {
+ error(error) {
createAlert({ message: this.$options.i18n.pipelineMiniGraphFetchError });
+ reportToSentry(this.$options.name, error);
},
},
},
@@ -83,15 +86,15 @@ export default {
downstreamPipelines() {
return keepLatestDownstreamPipelines(this.pipeline?.downstream?.nodes);
},
- pipelineStages() {
- return this.pipeline?.stages?.nodes || [];
- },
hasDownstreamPipelines() {
return Boolean(this.downstreamPipelines.length);
},
pipelinePath() {
return this.pipeline?.path || '';
},
+ pipelineStages() {
+ return this.pipeline?.stages?.nodes || [];
+ },
upstreamPipeline() {
return this.pipeline?.upstream;
},
diff --git a/app/assets/javascripts/ci/pipeline_mini_graph/pipeline_stage.vue b/app/assets/javascripts/ci/pipeline_mini_graph/pipeline_stage.vue
index 3a0db53d375..5b8705c21a0 100644
--- a/app/assets/javascripts/ci/pipeline_mini_graph/pipeline_stage.vue
+++ b/app/assets/javascripts/ci/pipeline_mini_graph/pipeline_stage.vue
@@ -2,6 +2,7 @@
import { GlButton, GlDisclosureDropdown, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { createAlert } from '~/alert';
import { s__, __, sprintf } from '~/locale';
+import { reportToSentry } from '~/ci/utils';
import { PIPELINE_MINI_GRAPH_POLL_INTERVAL } from '~/ci/pipeline_details/constants';
import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
import { getQueryHeaders, toggleQueryPollingByVisibility } from '~/ci/pipeline_details/graph/utils';
@@ -74,8 +75,9 @@ export default {
update(data) {
return data?.ciPipelineStage?.jobs?.nodes || [];
},
- error() {
+ error(error) {
createAlert({ message: this.$options.i18n.stageJobsFetchError });
+ reportToSentry(this.$options.name, error);
},
},
},
diff --git a/app/assets/javascripts/ci/runner/components/runner_configuration_popover.vue b/app/assets/javascripts/ci/runner/components/runner_configuration_popover.vue
index 805dad07626..2837a33ad68 100644
--- a/app/assets/javascripts/ci/runner/components/runner_configuration_popover.vue
+++ b/app/assets/javascripts/ci/runner/components/runner_configuration_popover.vue
@@ -1,7 +1,7 @@
@@ -27,7 +24,7 @@ export default {
"
>
{{ content }}{{ content }}
{{ content }}
{
- this.settings = response.data;
- })
- .catch((e) => {
- logError(e);
- this.settings = [];
- });
+ settingsUrl = `${this.settingsPath}?project_id=${projectId}`;
+ } else if (groupId) {
+ settingsUrl = `${this.settingsPath}?group_id=${groupId}`;
+ } else {
+ this.settings = [];
+ return;
}
+
+ axios
+ .get(settingsUrl)
+ .then((response) => {
+ this.settings = response.data;
+ })
+ .catch((e) => {
+ logError(e);
+ this.settings = [];
+ });
},
filterBySearchQuery(items, key = 'keywords') {
return fuzzaldrinPlus.filter(items, this.searchQuery, { key });
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
index cae0abc682e..33467492032 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue
@@ -325,6 +325,7 @@ export default {
ref="wiLinksForm"
data-testid="add-links-form"
:full-path="fullPath"
+ :full-name="workItem.namespace.fullName"
:issuable-gid="issuableGid"
:work-item-iid="iid"
:children-ids="childrenIds"
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue
index 79a81fdcf56..5b6d98bb717 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue
@@ -83,6 +83,11 @@ export default {
required: false,
default: WORK_ITEM_TYPE_ENUM_TASK,
},
+ fullName: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
apollo: {
workItemTypes: {
@@ -106,7 +111,7 @@ export default {
error: null,
isInputValid: true,
search: '',
- selectedProject: null,
+ selectedProjectFullPath: this.isGroup ? null : this.fullPath,
childToCreateTitle: null,
confidential: this.parentConfidential,
submitInProgress: false,
@@ -126,10 +131,10 @@ export default {
confidential: this.parentConfidential || this.confidential,
};
- if (this.selectedProject && !this.workItemChildIsEpic) {
+ if (this.selectedProjectFullPath && !this.workItemChildIsEpic) {
workItemInput = {
...workItemInput,
- namespacePath: this.selectedProject.fullPath,
+ namespacePath: this.selectedProjectFullPath,
};
} else {
workItemInput = {
@@ -211,7 +216,7 @@ export default {
return this.search.length > 0;
},
hasSelectedProject() {
- return this.selectedProject !== null && this.selectedProject !== undefined;
+ return Boolean(this.selectedProjectFullPath);
},
canSubmitForm() {
if (this.isCreateForm) {
@@ -385,9 +390,10 @@ export default {
:description="$options.i18n.projectValidationMessage"
>
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_projects_listbox.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_projects_listbox.vue
index e0008e200e2..6c452c8b1d4 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_projects_listbox.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_projects_listbox.vue
@@ -15,7 +15,7 @@ export default {
ProjectAvatar,
},
model: {
- prop: 'selectedProject',
+ prop: 'selectedProjectFullPath',
event: 'selectProject',
},
props: {
@@ -23,14 +23,19 @@ export default {
required: true,
type: String,
},
+ currentProjectName: {
+ required: false,
+ type: String,
+ default: '',
+ },
isGroup: {
required: false,
type: Boolean,
default: false,
},
- selectedProject: {
+ selectedProjectFullPath: {
required: false,
- type: Object,
+ type: String,
default: null,
},
},
@@ -38,8 +43,8 @@ export default {
return {
projects: [],
frequentProjects: [],
- selectedProjectFullPath: null,
searchKey: '',
+ selectedProject: null,
};
},
apollo: {
@@ -57,14 +62,15 @@ export default {
return data.namespace?.projects?.nodes;
},
result() {
- if (this.selectedProject === null) {
- this.selectedProjectFullPath = this.fullPath;
- }
+ this.selectedProject = this.findSelectedProject(this.selectedProjectFullPath);
},
debounce: SEARCH_DEBOUNCE,
},
},
computed: {
+ projectsLoading() {
+ return this.$apollo.queries.projects.loading;
+ },
dropdownToggleText() {
if (this.selectedProject) {
/** When selectedProject is fetched from localStorage
@@ -73,7 +79,9 @@ export default {
* */
return this.selectedProject.nameWithNamespace || this.selectedProject.namespace;
}
- return s__('WorkItem|Select a project');
+ return this.selectedProjectFullPath && this.currentProjectName
+ ? this.currentProjectName
+ : s__('WorkItem|Select a project');
},
listItems() {
const items = [];
@@ -122,17 +130,15 @@ export default {
return items;
},
},
- watch: {
- selectedProjectFullPath(projectFullPath) {
- const project = this.findSelectedProject(projectFullPath);
- this.$emit('selectProject', project);
- },
- },
methods: {
handleSearch(keyword) {
this.searchKey = keyword;
this.setFrequentProjects(keyword);
},
+ handleSelect(projectFullPath) {
+ this.selectedProject = this.findSelectedProject(projectFullPath);
+ this.$emit('selectProject', projectFullPath);
+ },
findSelectedProject(projectFullPath) {
const project = this.projects.find((proj) => proj.fullPath === projectFullPath);
if (project) {
@@ -200,15 +206,17 @@ export default {
diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
index 2f9e9d8d4f7..d75801bef50 100644
--- a/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_tree.vue
@@ -105,6 +105,7 @@ export default {
widgetName: CHILD_ITEMS_ANCHOR,
showLabels: true,
fetchNextPageInProgress: false,
+ workItem: null,
};
},
apollo: {
@@ -123,6 +124,7 @@ export default {
update({ workItem = {} }) {
const { children } = findHierarchyWidgets(workItem.widgets);
this.$emit('childrenLoaded', Boolean(children?.count));
+ this.workItem = workItem;
return children || {};
},
error() {
@@ -191,6 +193,9 @@ export default {
hasNextPage() {
return this.pageInfo?.hasNextPage;
},
+ workItemNamespaceName() {
+ return this.workItem?.namespace.fullName;
+ },
},
methods: {
genericActionItems(workItem) {
@@ -306,6 +311,7 @@ export default {
ref="wiLinksForm"
data-testid="add-tree-form"
:full-path="fullPath"
+ :full-name="workItemNamespaceName"
:issuable-gid="workItemId"
:work-item-iid="workItemIid"
:form-type="formType"
diff --git a/app/assets/javascripts/work_items/graphql/namespace_projects_for_links_widget.query.graphql b/app/assets/javascripts/work_items/graphql/namespace_projects_for_links_widget.query.graphql
index 65c7edd5b99..6c130886415 100644
--- a/app/assets/javascripts/work_items/graphql/namespace_projects_for_links_widget.query.graphql
+++ b/app/assets/javascripts/work_items/graphql/namespace_projects_for_links_widget.query.graphql
@@ -1,7 +1,12 @@
query namespaceProjectsForLinksWidget($fullPath: ID!, $projectSearch: String) {
namespace(fullPath: $fullPath) {
id
- projects(search: $projectSearch, includeSubgroups: true, includeSiblingProjects: true) {
+ projects(
+ search: $projectSearch
+ includeSubgroups: true
+ includeSiblingProjects: true
+ sort: ACTIVITY_DESC
+ ) {
nodes {
id
name
diff --git a/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql b/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql
index 876f563d1e1..2ca72cc114f 100644
--- a/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item_tree.query.graphql
@@ -2,6 +2,10 @@
query workItemTreeQuery($id: WorkItemID!, $pageSize: Int = 100, $endCursor: String) {
workItem(id: $id) {
+ namespace {
+ id
+ fullName
+ }
...WorkItemHierarchy
}
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 1e3c34f32c9..16151dd97fc 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -147,15 +147,7 @@
line-height: $gl-btn-small-line-height;
}
- &.btn-success {
- @include btn-green;
- }
-
&.btn-inverted:not(.disabled):not(:disabled) {
- &.btn-success {
- @include btn-outline($white, $green-600, $green-500, $green-100, $green-700, $green-500, $green-200, $green-600, $green-800);
- }
-
&.btn-danger {
@include btn-outline($white, $red-500, $red-500, $red-100, $red-700, $red-500, $red-200, $red-600, $red-800);
}
diff --git a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss
index 83bb50bcca0..3d23b0ac2a6 100644
--- a/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss
+++ b/app/assets/stylesheets/page_bundles/_ide_theme_overrides.scss
@@ -356,24 +356,6 @@
}
}
- // todo: remove this block after all success buttons have been migrated to gl-button
- .btn-success:not(.gl-button) {
- background-color: var(--ide-btn-success-background, $green-500);
- border-color: var(--ide-btn-success-border, $green-600) !important;
-
- &:hover,
- &:focus {
- background-color: var(--ide-btn-success-background, $green-600);
- border-color: var(--ide-btn-success-hover-border, $green-700) !important;
- }
-
- &:active,
- &.active {
- background-color: var(--ide-btn-success-background, $green-700);
- border-color: var(--ide-btn-success-hover-border, $green-800) !important;
- }
- }
-
// todo: remove this block after all disabled buttons have been migrated to gl-button
.btn[disabled]:not(.gl-button) {
background-color: var(--ide-btn-default-background, $gray-10) !important;
@@ -417,10 +399,6 @@
@include ide-gl-button(default, $white, $gray-50, $gray-100, $gray-200, $gray-300, $gray-300, $gray-400);
}
- .btn-success.gl-button.gl-button {
- @include ide-gl-button(success, $green-500, $green-600, $green-800, $green-600, $green-800, $green-800, $green-900);
- }
-
.btn-info.gl-button.gl-button,
.btn-primary.gl-button.gl-button {
@include ide-gl-button(primary, $blue-500, $blue-600, $blue-800, $blue-600, $blue-800, $blue-800, $blue-900);
diff --git a/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss b/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss
index c7aae77c412..461e054ec85 100644
--- a/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss
+++ b/app/assets/stylesheets/page_bundles/ide_themes/_dark.scss
@@ -30,12 +30,6 @@
--ide-btn-primary-hover-border-width: 2px;
--ide-btn-primary-focus-box-shadow: 0 0 0 1px #63a6e9;
- --ide-btn-success-background: #217645;
- --ide-btn-success-border: #108548;
- --ide-btn-success-hover-border: #2da160;
- --ide-btn-success-hover-border-width: 2px;
- --ide-btn-success-focus-box-shadow: 0 0 0 1px #2da160;
-
// Danger styles should be the same as default styles in dark theme
--ide-btn-danger-secondary-background: var(--ide-btn-default-background);
--ide-btn-danger-secondary-border: var(--ide-btn-default-border);
diff --git a/app/assets/stylesheets/page_bundles/ide_themes/_monokai.scss b/app/assets/stylesheets/page_bundles/ide_themes/_monokai.scss
index f53ace0b6c2..3bf0856b392 100644
--- a/app/assets/stylesheets/page_bundles/ide_themes/_monokai.scss
+++ b/app/assets/stylesheets/page_bundles/ide_themes/_monokai.scss
@@ -30,12 +30,6 @@
--ide-btn-primary-hover-border-width: 2px;
--ide-btn-primary-focus-box-shadow: 0 0 0 1px #63a6e9;
- --ide-btn-success-background: #217645;
- --ide-btn-success-border: #108548;
- --ide-btn-success-hover-border: #2da160;
- --ide-btn-success-hover-border-width: 2px;
- --ide-btn-success-focus-box-shadow: 0 0 0 1px #2da160;
-
// Danger styles should be the same as default styles in dark theme
--ide-btn-danger-secondary-background: var(--ide-btn-default-background);
--ide-btn-danger-secondary-border: var(--ide-btn-default-border);
diff --git a/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss b/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss
index 1906b3ca938..d7985601e8d 100644
--- a/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss
+++ b/app/assets/stylesheets/page_bundles/ide_themes/_solarized-dark.scss
@@ -30,12 +30,6 @@
--ide-btn-primary-hover-border-width: 2px;
--ide-btn-primary-focus-box-shadow: 0 0 0 1px #63a6e9;
- --ide-btn-success-background: #217645;
- --ide-btn-success-border: #108548;
- --ide-btn-success-hover-border: #2da160;
- --ide-btn-success-hover-border-width: 2px;
- --ide-btn-success-focus-box-shadow: 0 0 0 1px #2da160;
-
// Danger styles should be the same as default styles in dark theme
--ide-btn-danger-secondary-background: var(--ide-btn-default-background);
--ide-btn-danger-secondary-border: var(--ide-btn-default-border);
diff --git a/app/assets/stylesheets/page_bundles/members.scss b/app/assets/stylesheets/page_bundles/members.scss
index ff9ee6e31ca..000edc84ab4 100644
--- a/app/assets/stylesheets/page_bundles/members.scss
+++ b/app/assets/stylesheets/page_bundles/members.scss
@@ -1,11 +1,5 @@
@import 'mixins_and_variables_and_functions';
-.invite-users-form {
- .btn-success {
- margin-right: 10px;
- }
-}
-
.member {
.controls {
@include media-breakpoint-up(sm) {
diff --git a/app/assets/stylesheets/page_bundles/search.scss b/app/assets/stylesheets/page_bundles/search.scss
index f906a8f3875..34802b5a14d 100644
--- a/app/assets/stylesheets/page_bundles/search.scss
+++ b/app/assets/stylesheets/page_bundles/search.scss
@@ -275,7 +275,6 @@ $language-filter-max-height: 20rem;
// stylelint-disable-next-line gitlab/no-gl-class
.btn-search,
- .btn-success,
.dropdown-menu-toggle,
.gl-dropdown {
width: 100%;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 9f6ed745aa8..ff2877c271a 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -432,12 +432,6 @@ table {
width: 100%;
margin-bottom: 10px;
- .btn-success {
- flex-grow: 1;
- flex-shrink: 0;
- width: auto;
- }
-
.dropdown-toggle {
flex-grow: 0;
flex-shrink: 1;
diff --git a/app/assets/stylesheets/themes/dark_mode_overrides.scss b/app/assets/stylesheets/themes/dark_mode_overrides.scss
index 7ed644969be..6bd89c6ba9d 100644
--- a/app/assets/stylesheets/themes/dark_mode_overrides.scss
+++ b/app/assets/stylesheets/themes/dark_mode_overrides.scss
@@ -27,7 +27,6 @@
&.btn-default,
&.btn-dashed,
&.btn-info,
- &.btn-success,
&.btn-danger,
&.btn-confirm {
&-tertiary {
diff --git a/app/controllers/projects/mirrors_controller.rb b/app/controllers/projects/mirrors_controller.rb
index 7a89fb378b2..31b393e9587 100644
--- a/app/controllers/projects/mirrors_controller.rb
+++ b/app/controllers/projects/mirrors_controller.rb
@@ -37,7 +37,14 @@ class Projects::MirrorsController < Projects::ApplicationController
end
end
else
- deprecated_pull_mirror_procedure
+ flash[:alert] = alert_error('Invalid mirror update request')
+
+ respond_to do |format|
+ format.html { redirect_to_repository_settings(project, anchor: 'js-push-remote-settings') }
+ format.json do
+ render json: { error: flash[:alert] }, status: :bad_request
+ end
+ end
end
end
@@ -77,7 +84,7 @@ class Projects::MirrorsController < Projects::ApplicationController
end
def push_mirror_destroy?
- mirror_params.dig(:remote_mirrors_attributes, '_destroy') == '1'
+ ::Gitlab::Utils.to_boolean(mirror_params.dig(:remote_mirrors_attributes, '_destroy'))
end
def push_mirror_attributes
@@ -108,27 +115,6 @@ class Projects::MirrorsController < Projects::ApplicationController
project.remote_mirrors.find(push_mirror_to_destroy_id)
end
- def deprecated_pull_mirror_procedure
- result = ::Projects::UpdateService.new(project, current_user, safe_mirror_params).execute
-
- if result[:status] == :success
- flash[:notice] = notice_message
- else
- flash[:alert] = project.errors.full_messages.join(', ').html_safe
- end
-
- respond_to do |format|
- format.html { redirect_to_repository_settings(project, anchor: 'js-push-remote-settings') }
- format.json do
- if project.errors.present?
- render json: project.errors, status: :unprocessable_entity
- else
- render json: ProjectMirrorSerializer.new.represent(project)
- end
- end
- end
- end
-
def remote_mirror
@remote_mirror = project.remote_mirrors.first_or_initialize
end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 9f81a8850c1..056e76e9a80 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -127,13 +127,14 @@ class SearchController < ApplicationController
def settings
return render(json: []) unless current_user
- project_id = params.require(:project_id)
- project = Project.find_by(id: project_id) # rubocop: disable CodeReuse/ActiveRecord -- Using `find_by` as `find` would raise 404s
+ project_id, group_id = params.permit(:project_id, :group_id).values_at(:project_id, :group_id)
- if project && current_user.can?(:admin_project, project)
- render json: Search::Settings.new.for_project(project)
+ if project_id
+ render json: settings_for_project(project_id)
+ elsif group_id
+ render json: settings_for_group(group_id)
else
- render json: []
+ head :bad_request
end
end
@@ -311,6 +312,20 @@ class SearchController < ApplicationController
def filter_params
params.permit(:confidential, :state, language: [])
end
+
+ def settings_for_project(project_id)
+ project = Project.find_by(id: project_id) # rubocop: disable CodeReuse/ActiveRecord -- Using `find` would raise 404s
+ return [] unless project && current_user.can?(:admin_project, project)
+
+ Search::ProjectSettings.new(project).all
+ end
+
+ def settings_for_group(group_id)
+ group = Group.find_by(id: group_id) # rubocop: disable CodeReuse/ActiveRecord -- Using `find` would raise 404s
+ return [] unless group && current_user.can?(:admin_group, group)
+
+ Search::GroupSettings.new(group).all
+ end
end
SearchController.prepend_mod_with('SearchController')
diff --git a/app/finders/organizations/groups_finder.rb b/app/finders/organizations/groups_finder.rb
index dd6113bf5b9..89cd6fa9b4c 100644
--- a/app/finders/organizations/groups_finder.rb
+++ b/app/finders/organizations/groups_finder.rb
@@ -33,5 +33,3 @@ module Organizations
end
end
end
-
-Organizations::GroupsFinder.prepend_mod
diff --git a/app/graphql/mutations/notes/convert_to_thread.rb b/app/graphql/mutations/notes/convert_to_thread.rb
new file mode 100644
index 00000000000..88fa1e9674a
--- /dev/null
+++ b/app/graphql/mutations/notes/convert_to_thread.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Notes
+ class ConvertToThread < Mutations::Notes::Base
+ graphql_name 'NoteConvertToThread'
+ description 'Convert a standard comment to a resolvable thread.'
+
+ # Permissions are more lenient for converting to a thread because we do not
+ # change the note body. Any user that can resolve notes can convert the note
+ # to a thread.
+ authorize :resolve_note
+
+ argument :id,
+ Types::GlobalIDType[Note],
+ loads: Types::Notes::NoteType,
+ as: :note,
+ required: true,
+ description: 'Global ID of the Note to convert.'
+
+ def resolve(note:)
+ authorize!(note)
+
+ discussion = note.to_discussion
+
+ unless discussion.can_convert_to_discussion?
+ raise Gitlab::Graphql::Errors::ArgumentError, 'Note cannot be converted to a resolvable thread'
+ end
+
+ discussion = discussion.convert_to_discussion!
+
+ if discussion.save
+ { note: discussion.first_note, errors: [] }
+ else
+ { errors: errors_on_object(discussion.first_note) }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/organizations/groups_resolver.rb b/app/graphql/resolvers/organizations/groups_resolver.rb
index 03bdab4c923..50b3fa6de7e 100644
--- a/app/graphql/resolvers/organizations/groups_resolver.rb
+++ b/app/graphql/resolvers/organizations/groups_resolver.rb
@@ -14,9 +14,14 @@ module Resolvers
alias_method :organization, :object
def resolve_groups(**args)
- finder_params = args.merge(organization: organization)
- ::Organizations::GroupsFinder.new(context[:current_user], finder_params).execute
+ ::Organizations::GroupsFinder.new(current_user, finder_params(args)).execute
+ end
+
+ def finder_params(args)
+ args.merge(organization: organization)
end
end
end
end
+
+Resolvers::Organizations::GroupsResolver.prepend_mod
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index dd3ffd485f6..1154a3cff1c 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -111,6 +111,7 @@ module Types
mount_mutation Mutations::Notes::Create::Discussion, calls_gitaly: true
mount_mutation Mutations::Notes::Update::Note
mount_mutation Mutations::Notes::Update::ImageDiffNote
+ mount_mutation Mutations::Notes::ConvertToThread
mount_mutation Mutations::Notes::RepositionImageDiffNote
mount_mutation Mutations::Notes::Destroy
mount_mutation Mutations::Organizations::Create, alpha: { milestone: '16.6' }
diff --git a/app/models/integration.rb b/app/models/integration.rb
index 6d621288d5e..89b6adea248 100644
--- a/app/models/integration.rb
+++ b/app/models/integration.rb
@@ -379,7 +379,9 @@ class Integration < ApplicationRecord
def self.disabled_integration_names
# The GitLab for Slack app integration is only available when enabled through settings.
# The Slack Slash Commands integration is only available for customers who cannot use the GitLab for Slack app.
- Gitlab::CurrentSettings.slack_app_enabled ? ['slack_slash_commands'] : ['gitlab_slack_application']
+ disabled = Gitlab::CurrentSettings.slack_app_enabled ? ['slack_slash_commands'] : ['gitlab_slack_application']
+ disabled += ['jira_cloud_app'] unless Gitlab::CurrentSettings.jira_connect_application_key.present?
+ disabled
end
private_class_method :disabled_integration_names
diff --git a/app/services/jira_connect_subscriptions/create_service.rb b/app/services/jira_connect_subscriptions/create_service.rb
index 1e22bf82587..70f4895b4d5 100644
--- a/app/services/jira_connect_subscriptions/create_service.rb
+++ b/app/services/jira_connect_subscriptions/create_service.rb
@@ -61,6 +61,8 @@ module JiraConnectSubscriptions
group_id: namespace.id
)
+ return unless integration
+
Integrations::JiraCloudApp.transaction do
integration.inherit_from_id = nil
integration.activate!
diff --git a/db/docs/sbom_components.yml b/db/docs/sbom_components.yml
index 4c22d906966..c005afa3494 100644
--- a/db/docs/sbom_components.yml
+++ b/db/docs/sbom_components.yml
@@ -7,5 +7,5 @@ feature_categories:
description: Stores information about software components produced by a Software Bill of Materials (SBoM)
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90809
milestone: '15.2'
-gitlab_schema: gitlab_main
+gitlab_schema: gitlab_sec
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/457096
diff --git a/db/post_migrate/20240223130548_queue_update_sbom_components_name_based_on_pep503.rb b/db/post_migrate/20240223130548_queue_update_sbom_components_name_based_on_pep503.rb
index aae29af7b74..59f8beaab1c 100644
--- a/db/post_migrate/20240223130548_queue_update_sbom_components_name_based_on_pep503.rb
+++ b/db/post_migrate/20240223130548_queue_update_sbom_components_name_based_on_pep503.rb
@@ -3,7 +3,7 @@
class QueueUpdateSbomComponentsNameBasedOnPep503 < Gitlab::Database::Migration[2.2]
milestone '16.11'
- restrict_gitlab_migration gitlab_schema: :gitlab_main
+ restrict_gitlab_migration gitlab_schema: :gitlab_sec
MIGRATION = "UpdateSbomComponentsNameBasedOnPep503"
DELAY_INTERVAL = 2.minutes
diff --git a/db/post_migrate/20240425205205_queue_remove_namespace_from_os_type_sbom_components.rb b/db/post_migrate/20240425205205_queue_remove_namespace_from_os_type_sbom_components.rb
index 8920006c316..5028f281117 100644
--- a/db/post_migrate/20240425205205_queue_remove_namespace_from_os_type_sbom_components.rb
+++ b/db/post_migrate/20240425205205_queue_remove_namespace_from_os_type_sbom_components.rb
@@ -3,7 +3,7 @@
class QueueRemoveNamespaceFromOsTypeSbomComponents < Gitlab::Database::Migration[2.2]
milestone '17.0'
- restrict_gitlab_migration gitlab_schema: :gitlab_main
+ restrict_gitlab_migration gitlab_schema: :gitlab_sec
MIGRATION = "RemoveNamespaceFromOsTypeSbomComponents"
DELAY_INTERVAL = 2.minutes
diff --git a/db/post_migrate/20240507152320_queue_migrate_os_sbom_occurrences_to_components_without_prefix.rb b/db/post_migrate/20240507152320_queue_migrate_os_sbom_occurrences_to_components_without_prefix.rb
index 51795a676ec..85a23b768c5 100644
--- a/db/post_migrate/20240507152320_queue_migrate_os_sbom_occurrences_to_components_without_prefix.rb
+++ b/db/post_migrate/20240507152320_queue_migrate_os_sbom_occurrences_to_components_without_prefix.rb
@@ -8,7 +8,7 @@ class QueueMigrateOsSbomOccurrencesToComponentsWithoutPrefix < Gitlab::Database:
# to the components that have the correct prefix.
DEPENDENT_BACKGROUND_MIGRATIONS = %w[20240425205205]
- restrict_gitlab_migration gitlab_schema: :gitlab_main
+ restrict_gitlab_migration gitlab_schema: :gitlab_sec
MIGRATION = "MigrateOsSbomOccurrencesToComponentsWithoutPrefix"
DELAY_INTERVAL = 2.minutes
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 1249fe42b7a..57e7f548345 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -7136,6 +7136,27 @@ Input type: `NamespaceDeleteRemoteDevelopmentClusterAgentMappingInput`
| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.noteConvertToThread`
+
+Convert a standard comment to a resolvable thread.
+
+Input type: `NoteConvertToThreadInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `id` | [`NoteID!`](#noteid) | Global ID of the Note to convert. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| `note` | [`Note`](#note) | Note after mutation. |
+
### `Mutation.oncallRotationCreate`
Input type: `OncallRotationCreateInput`
diff --git a/doc/user/project/codeowners/code_owners_troubleshooting.md b/doc/user/project/codeowners/troubleshooting.md
similarity index 100%
rename from doc/user/project/codeowners/code_owners_troubleshooting.md
rename to doc/user/project/codeowners/troubleshooting.md
diff --git a/lib/gitlab/background_migration/update_sbom_occurrences_component_name_based_on_pep503.rb b/lib/gitlab/background_migration/update_sbom_occurrences_component_name_based_on_pep503.rb
index 852663faeb5..35626040699 100644
--- a/lib/gitlab/background_migration/update_sbom_occurrences_component_name_based_on_pep503.rb
+++ b/lib/gitlab/background_migration/update_sbom_occurrences_component_name_based_on_pep503.rb
@@ -22,6 +22,7 @@ module Gitlab
occurrences = batch
.joins("INNER JOIN sbom_components ON sbom_occurrences.component_id = sbom_components.id")
.where("sbom_components.purl_type = 8 AND sbom_occurrences.component_name LIKE '%.%'")
+ .allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/473758')
return if occurrences.blank?
diff --git a/lib/gitlab/search/params.rb b/lib/gitlab/search/params.rb
index 7fc72ce70ef..99eaebcb809 100644
--- a/lib/gitlab/search/params.rb
+++ b/lib/gitlab/search/params.rb
@@ -59,9 +59,7 @@ module Gitlab
end
def validate
- if detect_abuse?
- abuse_detection.validate
- end
+ abuse_detection.validate if detect_abuse?
super
end
@@ -85,9 +83,9 @@ module Gitlab
end
def not_too_many_terms
- if search_terms.count > SEARCH_TERM_LIMIT
- errors.add :query_string, "has too many search terms (maximum is #{SEARCH_TERM_LIMIT})"
- end
+ return unless search_terms.count > SEARCH_TERM_LIMIT
+
+ errors.add :query_string, "has too many search terms (maximum is #{SEARCH_TERM_LIMIT})"
end
def strip_surrounding_whitespace(obj)
@@ -101,7 +99,7 @@ module Gitlab
def convert_not_params(params)
not_params = params.delete(:not)
- return params unless not_params
+ return params unless not_params&.respond_to?(:to_h)
not_params.each do |key, value|
params[:"not_#{key}"] = value
diff --git a/lib/search/group_settings.rb b/lib/search/group_settings.rb
new file mode 100644
index 00000000000..32903860e7d
--- /dev/null
+++ b/lib/search/group_settings.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+module Search
+ class GroupSettings
+ include Rails.application.routes.url_helpers
+
+ attr_reader :group
+
+ def initialize(group)
+ @group = group
+ end
+
+ def all
+ general_settings.concat(
+ repository_settings,
+ ci_cd_settings,
+ packages_settings
+ )
+ end
+
+ def general_settings
+ [
+ { text: _('Naming, visibility'), href: edit_group_path(group, anchor: 'js-general-settings') },
+ { text: _('Permissions and group features'), href: edit_group_path(group, anchor: 'js-permissions-settings') },
+ { text: _('Badges'), href: edit_group_path(group, anchor: 'js-badge-settings') },
+ { text: _('Advanced'), href: edit_group_path(group, anchor: 'js-advanced-settings') }
+ ]
+ end
+
+ def repository_settings
+ [
+ { text: _('Deploy tokens'), href: group_settings_repository_path(group, anchor: 'js-deploy-tokens') },
+ { text: _('Default branch'), href: group_settings_repository_path(group, anchor: 'js-default-branch-name') }
+ ]
+ end
+
+ def ci_cd_settings
+ [
+ { text: _('General pipelines'),
+ href: group_settings_ci_cd_path(group, anchor: 'js-general-pipeline-settings') },
+ { text: _('Variables'), href: group_settings_ci_cd_path(group, anchor: 'ci-variables') },
+ { text: _('Runners'), href: group_settings_ci_cd_path(group, anchor: 'runners-settings') },
+ { text: _('Auto DevOps'), href: group_settings_ci_cd_path(group, anchor: 'auto-devops-settings') }
+ ]
+ end
+
+ def packages_settings
+ [
+ { text: _('Duplicate packages'),
+ href: group_settings_packages_and_registries_path(group, anchor: 'packages-settings') },
+ { text: _('Package forwarding'),
+ href: group_settings_packages_and_registries_path(group, anchor: 'packages-forwarding-settings') },
+ { text: _('Dependency Proxy'),
+ href: group_settings_packages_and_registries_path(group, anchor: 'dependency-proxy-settings') }
+ ]
+ end
+ end
+end
+
+Search::GroupSettings.prepend_mod
diff --git a/lib/search/settings.rb b/lib/search/project_settings.rb
similarity index 86%
rename from lib/search/settings.rb
rename to lib/search/project_settings.rb
index cb18b0d27a0..d2c4b365f83 100644
--- a/lib/search/settings.rb
+++ b/lib/search/project_settings.rb
@@ -1,19 +1,25 @@
# frozen_string_literal: true
module Search
- class Settings
+ class ProjectSettings
include Rails.application.routes.url_helpers
- def for_project(project)
- project_general_settings(project).concat(
- project_repository_settings(project),
- project_merge_request_settings(project),
- project_ci_cd_settings(project),
- project_monitor_settings(project)
+ attr_reader :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def all
+ general_settings.concat(
+ repository_settings,
+ merge_request_settings,
+ ci_cd_settings,
+ monitor_settings
)
end
- def project_general_settings(project)
+ def general_settings
[
{ text: _("Naming, topics, avatar"), href: edit_project_path(project, anchor: 'js-general-settings') },
{ text: _("Visibility, project features, permissions"),
@@ -24,7 +30,7 @@ module Search
]
end
- def project_repository_settings(project)
+ def repository_settings
[
{ text: _("Branch defaults"),
href: project_settings_repository_path(project, anchor: 'branch-defaults-settings') },
@@ -39,14 +45,14 @@ module Search
]
end
- def project_merge_request_settings(project)
+ def merge_request_settings
[
{ text: _("Merge requests"),
href: project_settings_merge_requests_path(project, anchor: 'js-merge-request-settings') }
]
end
- def project_ci_cd_settings(project)
+ def ci_cd_settings
[
{ text: _("General pipelines"),
href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings') },
@@ -64,7 +70,7 @@ module Search
]
end
- def project_monitor_settings(project)
+ def monitor_settings
[
{ text: _("Error tracking"),
href: project_settings_operations_path(project, anchor: 'js-error-tracking-settings') },
@@ -77,4 +83,4 @@ module Search
end
end
-Search::Settings.prepend_mod
+Search::ProjectSettings.prepend_mod
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f6b19602f06..5a0b18cd472 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -17944,6 +17944,9 @@ msgstr ""
msgid "Deploy to..."
msgstr ""
+msgid "Deploy tokens"
+msgstr ""
+
msgid "DeployBoards|To see deployment progress for your environments, make sure you are deploying to %{codeStart}$KUBE_NAMESPACE%{codeEnd} and annotating with %{codeStart}app.gitlab.com/app=$CI_PROJECT_PATH_SLUG%{codeEnd} and %{codeStart}app.gitlab.com/env=$CI_ENVIRONMENT_SLUG%{codeEnd}."
msgstr ""
@@ -19624,6 +19627,9 @@ msgstr ""
msgid "DuoProTrial|Your trial ends on %{strongStart}%{trialEndDate}%{strongEnd}. To continue using features in GitLab Duo Pro, purchase a subscription add-on."
msgstr ""
+msgid "Duplicate packages"
+msgstr ""
+
msgid "Duplicate page: A page with that title already exists in the file %{file}"
msgstr ""
@@ -37296,6 +37302,9 @@ msgstr ""
msgid "Package file size limits"
msgstr ""
+msgid "Package forwarding"
+msgstr ""
+
msgid "Package name of the app in Google Play."
msgstr ""
diff --git a/package.json b/package.json
index c935c6120f9..e8d4bae7b22 100644
--- a/package.json
+++ b/package.json
@@ -73,7 +73,7 @@
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
"@rails/actioncable": "7.0.8-4",
"@rails/ujs": "7.0.8-4",
- "@sentry/browser": "8.19.0",
+ "@sentry/browser": "8.20.0",
"@snowplow/browser-plugin-client-hints": "^3.9.0",
"@snowplow/browser-plugin-form-tracking": "^3.9.0",
"@snowplow/browser-plugin-ga-cookies": "^3.9.0",
diff --git a/qa/tasks/ci.rake b/qa/tasks/ci.rake
index 8024777aa5d..5421a0f6a47 100644
--- a/qa/tasks/ci.rake
+++ b/qa/tasks/ci.rake
@@ -42,17 +42,23 @@ namespace :ci do
tests = run_all_label_present || qa_changes.framework_changes? ? nil : qa_changes.qa_tests
# When QA_TESTS only contain folders and no specific specs, populate KNAPSACK_TEST_FILE_PATTERN
+ files_pattern = ""
if tests && tests.split(' ').none? { |item| item.include?('_spec') }
test_paths = tests.split(' ').map { |item| "#{item}**/*" }
files_pattern = "{#{test_paths.join(',')}}"
- logger.info(" Files pattern for tests: #{files_pattern}")
- append_to_file(env_file, <<~TXT)
- KNAPSACK_TEST_FILE_PATTERN='#{files_pattern}'
- TXT
+ elsif tests && tests.split(' ').size > 50
+ # When number of QA_TESTS exceeds threshold, set KNAPSACK_FILE_PATTERN for parallel execution
+ files_pattern = "{#{tests.split(' ').join(',')}}"
end
+ logger.info(" Files pattern for tests: #{files_pattern}")
+
+ append_to_file(env_file, <<~TXT)
+ KNAPSACK_TEST_FILE_PATTERN='#{files_pattern}'
+ TXT
+
if run_all_label_present
logger.info(" merge request has pipeline:run-all-e2e label, full test suite will be executed")
append_to_file(env_file, "QA_RUN_ALL_E2E_LABEL=true\n")
diff --git a/scripts/rspec_check_order_dependence b/scripts/rspec_check_order_dependence
index 92a7f84acaf..b6a7b6073ac 100755
--- a/scripts/rspec_check_order_dependence
+++ b/scripts/rspec_check_order_dependence
@@ -21,7 +21,7 @@ if [ $# -eq 0 ]; then
fi
TODO_YAML='./spec/support/rspec_order_todo.yml'
-RSPEC_ARGS=(--format progress --tag ~quarantine)
+RSPEC_ARGS=(--fail-fast --format progress --tag ~quarantine)
RANDOM_ORDER_RUNS=${RANDOM_ORDER_RUNS:-5}
abort() {
diff --git a/spec/controllers/jira_connect/subscriptions_controller_spec.rb b/spec/controllers/jira_connect/subscriptions_controller_spec.rb
index 335ee3f84ca..a05f18f1a16 100644
--- a/spec/controllers/jira_connect/subscriptions_controller_spec.rb
+++ b/spec/controllers/jira_connect/subscriptions_controller_spec.rb
@@ -77,7 +77,6 @@ RSpec.describe JiraConnect::SubscriptionsController, feature_category: :integrat
before do
group.add_maintainer(user)
- allow(PropagateIntegrationWorker).to receive(:perform_async)
end
subject { post :create, params: { jwt: jwt, namespace_path: group.path, format: :json } }
diff --git a/spec/controllers/projects/mirrors_controller_spec.rb b/spec/controllers/projects/mirrors_controller_spec.rb
index c8081dca880..ff7796e4b99 100644
--- a/spec/controllers/projects/mirrors_controller_spec.rb
+++ b/spec/controllers/projects/mirrors_controller_spec.rb
@@ -220,6 +220,26 @@ RSpec.describe Projects::MirrorsController, feature_category: :source_code_manag
end
end
end
+
+ context 'when request is invalid' do
+ context 'with html format' do
+ it 'returns an error' do
+ do_put(project, { wrong: :params })
+
+ expect(response).to redirect_to(project_settings_repository_path(project, anchor: 'js-push-remote-settings'))
+ expect(flash[:alert]).to match(/Invalid mirror update request/)
+ end
+ end
+
+ context 'with json format' do
+ it 'processes an unsuccessful update' do
+ do_put(project, { wrong: :params }, { format: :json })
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response).to eq('error' => 'Invalid mirror update request')
+ end
+ end
+ end
end
describe '#ssh_host_keys', :use_clean_rails_memory_store_caching do
diff --git a/spec/frontend/ci/pipeline_mini_graph/mock_data.js b/spec/frontend/ci/pipeline_mini_graph/mock_data.js
index ca87c722c68..a9d4f3664c3 100644
--- a/spec/frontend/ci/pipeline_mini_graph/mock_data.js
+++ b/spec/frontend/ci/pipeline_mini_graph/mock_data.js
@@ -15,6 +15,11 @@ export const mockDownstreamPipelinesGraphql = () => ({
label: 'passed',
__typename: 'DetailedStatus',
},
+ sourceJob: {
+ id: 'gid://gitlab/Ci::Bridge/5785',
+ retried: false,
+ __typename: 'CiJob',
+ },
__typename: 'Pipeline',
},
{
@@ -32,6 +37,11 @@ export const mockDownstreamPipelinesGraphql = () => ({
label: 'passed',
__typename: 'DetailedStatus',
},
+ sourceJob: {
+ id: 'gid://gitlab/Ci::Bridge/5786',
+ retried: false,
+ __typename: 'CiJob',
+ },
__typename: 'Pipeline',
},
{
@@ -49,6 +59,11 @@ export const mockDownstreamPipelinesGraphql = () => ({
label: 'passed',
__typename: 'DetailedStatus',
},
+ sourceJob: {
+ id: 'gid://gitlab/Ci::Bridge/5787',
+ retried: true,
+ __typename: 'CiJob',
+ },
__typename: 'Pipeline',
},
],
@@ -259,12 +274,12 @@ export const mockPipelineMiniGraphQueryResponse = {
};
export const mockPMGQueryNoDownstreamResponse = {
- ...mockPipelineMiniGraphQueryResponse,
+ ...mockPipelineMiniGraphQueryResponse.data.project.pipeline,
downstream: { nodes: [] },
};
export const mockPMGQueryNoUpstreamResponse = {
- ...mockPipelineMiniGraphQueryResponse,
+ ...mockPipelineMiniGraphQueryResponse.data.project.pipeline,
upstream: null,
};
@@ -305,6 +320,10 @@ export const downstreamPipelines = [
icon: 'status_success',
label: 'passed',
},
+ sourceJob: {
+ id: 'gid://gitlab/Ci::Bridge/5785',
+ retried: false,
+ },
},
{
id: 'gid://gitlab/Ci::Pipeline/611',
@@ -319,6 +338,10 @@ export const downstreamPipelines = [
icon: 'status_success',
label: 'passed',
},
+ sourceJob: {
+ id: 'gid://gitlab/Ci::Bridge/5785',
+ retried: false,
+ },
},
{
id: 'gid://gitlab/Ci::Pipeline/609',
@@ -333,6 +356,10 @@ export const downstreamPipelines = [
icon: 'status_success',
label: 'passed',
},
+ sourceJob: {
+ id: 'gid://gitlab/Ci::Bridge/5785',
+ retried: false,
+ },
},
{
id: 'gid://gitlab/Ci::Pipeline/610',
@@ -347,6 +374,10 @@ export const downstreamPipelines = [
icon: 'status_success',
label: 'passed',
},
+ sourceJob: {
+ id: 'gid://gitlab/Ci::Bridge/5785',
+ retried: false,
+ },
},
];
diff --git a/spec/frontend/ci/pipeline_mini_graph/pipeline_mini_graph_spec.js b/spec/frontend/ci/pipeline_mini_graph/pipeline_mini_graph_spec.js
index 1de34dec526..e69545fab7e 100644
--- a/spec/frontend/ci/pipeline_mini_graph/pipeline_mini_graph_spec.js
+++ b/spec/frontend/ci/pipeline_mini_graph/pipeline_mini_graph_spec.js
@@ -112,7 +112,8 @@ describe('PipelineMiniGraph', () => {
it('does not render upstream if not available', () => {
pipelineMiniGraphResponse.mockResolvedValue(mockPMGQueryNoUpstreamResponse);
- expect(findUpstream().exists()).toBe(true);
+ createComponent();
+ expect(findUpstream().exists()).toBe(false);
});
});
@@ -128,9 +129,14 @@ describe('PipelineMiniGraph', () => {
});
});
+ it('keeps the latest downstream pipelines', () => {
+ expect(findDownstream().props('pipelines')).toHaveLength(2);
+ });
+
it('does not render downstream if not available', () => {
pipelineMiniGraphResponse.mockResolvedValue(mockPMGQueryNoDownstreamResponse);
- expect(findUpstream().exists()).toBe(true);
+ createComponent();
+ expect(findDownstream().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/ci/runner/components/runner_configuration_popover_spec.js b/spec/frontend/ci/runner/components/runner_configuration_popover_spec.js
index ecb277b468c..fc0550b30ac 100644
--- a/spec/frontend/ci/runner/components/runner_configuration_popover_spec.js
+++ b/spec/frontend/ci/runner/components/runner_configuration_popover_spec.js
@@ -28,8 +28,8 @@ describe('RunnerConfigurationPopover', () => {
createComponent();
expect(findHelpLink().text()).toBe('single runner entry');
- expect(findHelpLink().attributes('href')).toBe(
- '/help/runner/configuration/advanced-configuration.md#the-runners-section',
+ expect(findHelpLink().attributes('href')).toContain(
+ '/runner/configuration/advanced-configuration#the-runners-section',
);
});
diff --git a/spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js b/spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js
index 32ddc087b32..3ec7031274d 100644
--- a/spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js
+++ b/spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js
@@ -73,11 +73,11 @@ describe('Group Settings App', () => {
};
describe.each`
- finder | entitySpecificProps
- ${findPackageSettings} | ${packageSettingsProps}
- ${findPackageForwardingSettings} | ${packageForwardingSettingsProps}
- ${findDependencyProxySettings} | ${dependencyProxyProps}
- `('settings blocks', ({ finder, entitySpecificProps }) => {
+ finder | entitySpecificProps | id
+ ${findPackageSettings} | ${packageSettingsProps} | ${'packages-settings'}
+ ${findPackageForwardingSettings} | ${packageForwardingSettingsProps} | ${'packages-forwarding-settings'}
+ ${findDependencyProxySettings} | ${dependencyProxyProps} | ${'dependency-proxy-settings'}
+ `('settings blocks', ({ finder, entitySpecificProps, id }) => {
beforeEach(() => {
mountComponent();
return waitForApolloQueryAndRender();
@@ -87,6 +87,10 @@ describe('Group Settings App', () => {
expect(finder().exists()).toBe(true);
});
+ it('has the correct id', () => {
+ expect(finder().attributes('id')).toBe(id);
+ });
+
it('binds the correctProps', () => {
expect(finder().props()).toMatchObject(entitySpecificProps);
});
diff --git a/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js b/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js
index 4786b9f5964..59e43aea8a5 100644
--- a/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js
+++ b/spec/frontend/super_sidebar/components/global_search/command_palette/command_palette_items_spec.js
@@ -69,6 +69,7 @@ describe('CommandPaletteItems', () => {
beforeEach(() => {
mockAxios = new MockAdapter(axios);
mockAxios.onGet('/settings?project_id=1').reply(HTTP_STATUS_OK, SETTINGS);
+ mockAxios.onGet('/settings?group_id=2').reply(HTTP_STATUS_OK, SETTINGS);
});
describe('Commands and links', () => {
@@ -231,7 +232,7 @@ describe('CommandPaletteItems', () => {
describe('Settings search', () => {
describe('when in a project', () => {
- it('fetches settings when entering command mode', async () => {
+ it('fetches project settings when entering command mode', async () => {
jest.spyOn(axios, 'get');
createComponent({ handle: COMMAND_HANDLE });
@@ -265,8 +266,8 @@ describe('CommandPaletteItems', () => {
});
});
- describe('when not in a project', () => {
- it('does not fetch settings when entering command mode', () => {
+ describe('when in a group', () => {
+ it('fetches group settings when entering command mode', async () => {
jest.spyOn(axios, 'get');
createComponent(
@@ -274,6 +275,22 @@ describe('CommandPaletteItems', () => {
{},
{ searchContext: { project: { id: null }, group: { id: 2 } } },
);
+ await waitForPromises();
+
+ expect(axios.get).toHaveBeenCalledTimes(1);
+ expect(axios.get).toHaveBeenCalledWith('/settings?group_id=2');
+ });
+ });
+
+ describe('when not in a project or group', () => {
+ it('does not fetch settings when entering command mode', () => {
+ jest.spyOn(axios, 'get');
+
+ createComponent(
+ { handle: COMMAND_HANDLE },
+ {},
+ { searchContext: { project: { id: null }, group: { id: null } } },
+ );
expect(axios.get).not.toHaveBeenCalled();
});
});
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
index d3b8adef64e..6bd7064c29c 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_links_form_spec.js
@@ -192,7 +192,7 @@ describe('WorkItemLinksForm', () => {
expect(createMutationResolver).toHaveBeenCalledWith({
input: {
title: 'Create task test',
- projectPath: 'group-a',
+ namespacePath: 'group-a',
workItemTypeId: workItemTypeIdForTask,
hierarchyWidget: {
parentId: 'gid://gitlab/WorkItem/1',
@@ -216,7 +216,7 @@ describe('WorkItemLinksForm', () => {
expect(createMutationResolver).toHaveBeenCalledWith({
input: {
title: 'Create confidential task',
- projectPath: 'group-a',
+ namespacePath: 'group-a',
workItemTypeId: workItemTypeIdForTask,
hierarchyWidget: {
parentId: 'gid://gitlab/WorkItem/1',
@@ -247,7 +247,7 @@ describe('WorkItemLinksForm', () => {
it('creates child issue in non confidential parent', async () => {
findInput().vm.$emit('input', 'Create issue test');
- findProjectSelector().vm.$emit('selectProject', projectData[0]);
+ findProjectSelector().vm.$emit('selectProject', projectData[0].fullPath);
findForm().vm.$emit('submit', {
preventDefault: jest.fn(),
@@ -279,7 +279,7 @@ describe('WorkItemLinksForm', () => {
findInput().vm.$emit('input', 'Create confidential issue');
- findProjectSelector().vm.$emit('selectProject', projectData[0]);
+ findProjectSelector().vm.$emit('selectProject', projectData[0].fullPath);
findForm().vm.$emit('submit', {
preventDefault: jest.fn(),
@@ -385,7 +385,7 @@ describe('WorkItemLinksForm', () => {
expect(findAddChildButton().props('disabled')).toBe(true);
- findProjectSelector().vm.$emit('selectProject', projectData[0]);
+ findProjectSelector().vm.$emit('selectProject', projectData[0].fullPath);
await nextTick();
@@ -501,7 +501,7 @@ describe('WorkItemLinksForm', () => {
expect(createMutationResolver).toHaveBeenCalledWith({
input: {
title: 'Create task test',
- projectPath: 'group-a',
+ namespacePath: 'group-a',
workItemTypeId: workItemTypeIdForTask,
hierarchyWidget: {
parentId: 'gid://gitlab/WorkItem/1',
@@ -527,7 +527,7 @@ describe('WorkItemLinksForm', () => {
expect(createMutationResolver).not.toHaveBeenCalledWith({
input: {
title: 'Create task test',
- projectPath: 'group-a',
+ namespacePath: 'group-a',
workItemTypeId: 'gid://gitlab/WorkItems::Type/3',
hierarchyWidget: {
parentId: 'gid://gitlab/WorkItem/1',
diff --git a/spec/frontend/work_items/components/work_item_links/work_item_projects_listbox_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_projects_listbox_spec.js
index 84a19b7b895..489ade892de 100644
--- a/spec/frontend/work_items/components/work_item_links/work_item_projects_listbox_spec.js
+++ b/spec/frontend/work_items/components/work_item_links/work_item_projects_listbox_spec.js
@@ -40,8 +40,13 @@ describe('WorkItemProjectsListbox', () => {
const findRecentDropdownItems = () => findDropdown().find('ul').findAll('[role=option]');
const findRecentDropdownItemAt = (index) => findRecentDropdownItems().at(index);
const findAllDropdownItemsFor = (fullPath) => wrapper.findAllByTestId(`listbox-item-${fullPath}`);
+ const findDropdownToggle = () => wrapper.findByTestId('base-dropdown-toggle');
- const createComponent = async (isGroup = true, fullPath = 'group-a') => {
+ const createComponent = async (
+ isGroup = true,
+ fullPath = 'group-a',
+ selectedProjectFullPath = null,
+ ) => {
wrapper = mountExtended(WorkItemProjectsListbox, {
apolloProvider: createMockApollo([
[namespaceProjectsForLinksWidgetQuery, namespaceProjectsFormLinksWidgetResolver],
@@ -49,6 +54,7 @@ describe('WorkItemProjectsListbox', () => {
propsData: {
fullPath,
isGroup,
+ selectedProjectFullPath,
},
});
@@ -86,7 +92,7 @@ describe('WorkItemProjectsListbox', () => {
const emitted = wrapper.emitted('selectProject');
- expect(emitted[1][0]).toEqual(namespaceProjectsData[0]);
+ expect(emitted[0][0]).toBe(namespaceProjectsData[0].fullPath);
});
it('renders recent projects if present', async () => {
@@ -117,7 +123,7 @@ describe('WorkItemProjectsListbox', () => {
const emitted = wrapper.emitted('selectProject');
- expect(emitted[1][0]).toEqual(namespaceProjectsData[1]);
+ expect(emitted[0][0]).toBe(namespaceProjectsData[1].fullPath);
});
it('supports filtering recent projects via search input', async () => {
@@ -144,7 +150,7 @@ describe('WorkItemProjectsListbox', () => {
describe('project level work items', () => {
beforeEach(async () => {
- await createComponent(false, 'group-a/example-project-a');
+ await createComponent(false, 'group-a/example-project-a', 'group-a/example-project-a');
gon.current_username = 'root';
});
@@ -162,9 +168,7 @@ describe('WorkItemProjectsListbox', () => {
it('auto-selects the current project', async () => {
await nextTick();
- const emitted = wrapper.emitted('selectProject');
-
- expect(emitted[0][0]).toEqual(namespaceProjectsData[0]);
+ expect(findDropdownToggle().text()).toContain(namespaceProjectsData[0].nameWithNamespace);
});
it('supports selecting a project', async () => {
@@ -180,7 +184,7 @@ describe('WorkItemProjectsListbox', () => {
const emitted = wrapper.emitted('selectProject');
- expect(emitted[1][0]).toEqual(namespaceProjectsData[1]);
+ expect(emitted[0][0]).toBe(namespaceProjectsData[1].fullPath);
});
it('renders recent projects if present', async () => {
@@ -211,7 +215,7 @@ describe('WorkItemProjectsListbox', () => {
const emitted = wrapper.emitted('selectProject');
- expect(emitted[1][0]).toEqual(namespaceProjectsData[1]);
+ expect(emitted[0][0]).toBe(namespaceProjectsData[1].fullPath);
});
it('supports filtering recent projects via search input', async () => {
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index 31efaef323b..f8d2bfcf825 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -1834,6 +1834,7 @@ export const workItemHierarchyTreeEmptyResponse = {
id: '1',
fullPath: 'test-project-path',
name: 'Project name',
+ fullName: 'Project name',
},
widgets: [
{
@@ -1946,6 +1947,7 @@ export const workItemHierarchyTreeResponse = {
id: '1',
fullPath: 'test-project-path',
name: 'Project name',
+ fullName: 'Project name',
},
widgets: [
{
diff --git a/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb b/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb
index 2b2bf97c64b..d29bb4e2f78 100644
--- a/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb
+++ b/spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb
@@ -31,8 +31,10 @@ RSpec.describe 'cross-database foreign keys' do
'user_group_callouts.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/421287
'subscription_user_add_on_assignments.user_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/444666
'subscription_add_on_purchases.subscription_add_on_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/444666
- 'sbom_occurrences.source_package_id', # https://gitlab.com/groups/gitlab-org/-/epics/14116#identified-cross-joins
+ 'sbom_component_versions.component_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/469535
+ 'sbom_occurrences.component_id', # https://gitlab.com/gitlab-org/gitlab/-/issues/469535
'sbom_occurrences.source_id', # https://gitlab.com/groups/gitlab-org/-/epics/14116#identified-cross-joins
+ 'sbom_occurrences.source_package_id', # https://gitlab.com/groups/gitlab-org/-/epics/14116#identified-cross-joins
'vulnerability_export_parts.vulnerability_export_id' # https://gitlab.com/groups/gitlab-org/-/epics/14197#cross-db-issues-to-be-resolved
]
end
diff --git a/spec/lib/gitlab/search/params_spec.rb b/spec/lib/gitlab/search/params_spec.rb
index 3f2ec8b178d..03d0a779f3d 100644
--- a/spec/lib/gitlab/search/params_spec.rb
+++ b/spec/lib/gitlab/search/params_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
let(:search) { 'search' }
let(:group_id) { 123 }
- let(:params) { { group_id: 123, search: search } }
+ let(:params) { ActionController::Parameters.new(group_id: 123, search: search) }
let(:detect_abuse) { true }
describe 'detect_abuse conditional' do
@@ -172,7 +172,7 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
end
describe 'for confidential' do
- let(:params) { { group_id: 123, search: search, confidential: input } }
+ let(:params) { ActionController::Parameters.new(group_id: 123, search: search, confidential: input) }
include_context 'with inputs'
@@ -184,7 +184,7 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
end
describe 'for include_archived' do
- let(:params) { { group_id: 123, search: search, include_archived: input } }
+ let(:params) { ActionController::Parameters.new(group_id: 123, search: search, include_archived: input) }
include_context 'with inputs'
@@ -196,7 +196,7 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
end
describe 'for include_forked' do
- let(:params) { { group_id: 123, search: search, include_forked: input } }
+ let(:params) { ActionController::Parameters.new(group_id: 123, search: search, include_forked: input) }
include_context 'with inputs'
@@ -207,4 +207,31 @@ RSpec.describe Gitlab::Search::Params, feature_category: :global_search do
end
end
end
+
+ describe 'converts not params' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:input, :expected_key, :expected_value) do
+ { not: { source_branch: 'good-bye' } } | 'not_source_branch' | 'good-bye'
+ { not: { label_name: %w[hello-world labelName] } } | 'not_label_name' | %w[hello-world labelName]
+ { label_name: %w[hello-world labelName] } | 'label_name' | %w[hello-world labelName]
+ { source_branch: 'foo-bar' } | 'source_branch' | 'foo-bar'
+ end
+
+ let(:params) { ActionController::Parameters.new(group_id: 123, search: search, **input) }
+
+ with_them do
+ it 'transforms param' do
+ expect(search_params[expected_key]).to eq(expected_value)
+ end
+ end
+
+ context 'when not param is not a hash' do
+ let(:params) { ActionController::Parameters.new(group_id: 123, search: search, not: 'test') }
+
+ it 'ignores the not param and removes it from params' do
+ expect(search_params['not']).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/lib/search/group_settings_spec.rb b/spec/lib/search/group_settings_spec.rb
new file mode 100644
index 00000000000..f55eb136d11
--- /dev/null
+++ b/spec/lib/search/group_settings_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe "Search results for group settings", :js, feature_category: :global_search, type: :feature do
+ it_behaves_like 'all group settings sections exist and have correct anchor links'
+end
diff --git a/spec/lib/search/project_settings_spec.rb b/spec/lib/search/project_settings_spec.rb
new file mode 100644
index 00000000000..953d85154f4
--- /dev/null
+++ b/spec/lib/search/project_settings_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe "Search results for project settings", :js, feature_category: :global_search, type: :feature do
+ it_behaves_like 'all project settings sections exist and have correct anchor links'
+end
diff --git a/spec/lib/search/settings_spec.rb b/spec/lib/search/settings_spec.rb
deleted file mode 100644
index bee9aafc4fb..00000000000
--- a/spec/lib/search/settings_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe "Search results for settings", :js, feature_category: :global_search, type: :feature do
- it_behaves_like 'all sections exist and have correct anchor links'
-end
diff --git a/spec/migrations/20230815160428_rename_plans_titles_with_legacy_plan_names_spec.rb b/spec/migrations/20230815160428_rename_plans_titles_with_legacy_plan_names_spec.rb
index 21c17a60e90..00a721ee859 100644
--- a/spec/migrations/20230815160428_rename_plans_titles_with_legacy_plan_names_spec.rb
+++ b/spec/migrations/20230815160428_rename_plans_titles_with_legacy_plan_names_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require_migration!
-RSpec.describe RenamePlansTitlesWithLegacyPlanNames, feature_category: :subscription_management do
+RSpec.describe RenamePlansTitlesWithLegacyPlanNames, feature_category: :subscription_management, allowed_to_be_slow: true do
let(:plans) { table(:plans) }
let!(:premium_plan) { plans.create!(name: 'premium', title: 'Premium (Formerly Silver)') }
diff --git a/spec/migrations/20240223130548_queue_update_sbom_components_name_based_on_pep503_spec.rb b/spec/migrations/20240223130548_queue_update_sbom_components_name_based_on_pep503_spec.rb
index 943be3e8a52..5ded14185c8 100644
--- a/spec/migrations/20240223130548_queue_update_sbom_components_name_based_on_pep503_spec.rb
+++ b/spec/migrations/20240223130548_queue_update_sbom_components_name_based_on_pep503_spec.rb
@@ -18,7 +18,8 @@ RSpec.describe QueueUpdateSbomComponentsNameBasedOnPep503, feature_category: :so
column_name: :id,
interval: described_class::DELAY_INTERVAL,
batch_size: described_class::BATCH_SIZE,
- sub_batch_size: described_class::SUB_BATCH_SIZE
+ sub_batch_size: described_class::SUB_BATCH_SIZE,
+ gitlab_schema: :gitlab_sec
)
}
end
diff --git a/spec/migrations/20240425205205_queue_remove_namespace_from_os_type_sbom_components_spec.rb b/spec/migrations/20240425205205_queue_remove_namespace_from_os_type_sbom_components_spec.rb
index 4755254fbe3..00f7a6aa42d 100644
--- a/spec/migrations/20240425205205_queue_remove_namespace_from_os_type_sbom_components_spec.rb
+++ b/spec/migrations/20240425205205_queue_remove_namespace_from_os_type_sbom_components_spec.rb
@@ -18,7 +18,8 @@ RSpec.describe QueueRemoveNamespaceFromOsTypeSbomComponents, feature_category: :
column_name: :id,
interval: described_class::DELAY_INTERVAL,
batch_size: described_class::BATCH_SIZE,
- sub_batch_size: described_class::SUB_BATCH_SIZE
+ sub_batch_size: described_class::SUB_BATCH_SIZE,
+ gitlab_schema: :gitlab_sec
)
}
end
diff --git a/spec/migrations/20240507152320_queue_migrate_os_sbom_occurrences_to_components_without_prefix_spec.rb b/spec/migrations/20240507152320_queue_migrate_os_sbom_occurrences_to_components_without_prefix_spec.rb
index 4206a8bc157..d173e2da885 100644
--- a/spec/migrations/20240507152320_queue_migrate_os_sbom_occurrences_to_components_without_prefix_spec.rb
+++ b/spec/migrations/20240507152320_queue_migrate_os_sbom_occurrences_to_components_without_prefix_spec.rb
@@ -18,7 +18,8 @@ RSpec.describe QueueMigrateOsSbomOccurrencesToComponentsWithoutPrefix, feature_c
column_name: :id,
interval: described_class::DELAY_INTERVAL,
batch_size: described_class::BATCH_SIZE,
- sub_batch_size: described_class::SUB_BATCH_SIZE
+ sub_batch_size: described_class::SUB_BATCH_SIZE,
+ gitlab_schema: :gitlab_sec
)
}
end
diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb
index 184d0cac6ec..b32386aa873 100644
--- a/spec/models/integration_spec.rb
+++ b/spec/models/integration_spec.rb
@@ -1314,9 +1314,25 @@ RSpec.describe Integration, feature_category: :integrations do
describe '.integration_names' do
subject { described_class.integration_names }
- it { is_expected.to include(*described_class::INTEGRATION_NAMES) }
+ it { is_expected.to include(*described_class::INTEGRATION_NAMES - ['jira_cloud_app']) }
it { is_expected.to include('gitlab_slack_application') }
+ context 'when instance is configured for Jira Cloud app' do
+ before do
+ stub_application_setting(jira_connect_application_key: 'mock_app_oauth_key')
+ end
+
+ it { is_expected.to include('jira_cloud_app') }
+
+ context 'when the enable_jira_connect_configuration flag is disabled' do
+ before do
+ stub_feature_flags(enable_jira_connect_configuration: false)
+ end
+
+ it { is_expected.not_to include('jira_cloud_app') }
+ end
+ end
+
context 'when Rails.env is not test' do
before do
allow(Rails.env).to receive(:test?).and_return(false)
diff --git a/spec/requests/api/graphql/mutations/notes/convert_to_thread_spec.rb b/spec/requests/api/graphql/mutations/notes/convert_to_thread_spec.rb
new file mode 100644
index 00000000000..02b9b80e058
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/notes/convert_to_thread_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Mutations::Notes::ConvertToThread, feature_category: :team_planning do
+ include GraphqlHelpers
+
+ let_it_be(:noteable) { create(:work_item) }
+
+ let(:project) { noteable.project }
+ let(:current_user) { noteable.author }
+ let(:note) { create(:note, noteable: noteable, project: project) }
+
+ let(:mutation) do
+ graphql_mutation(:note_convert_to_thread, { id: global_id_of(note) }) do
+ <<~QL
+ note {
+ id
+ resolvable
+ }
+ errors
+ QL
+ end
+ end
+
+ def mutation_response
+ graphql_mutation_response(:note_convert_to_thread)
+ end
+
+ it 'converts to resolvable thread' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['note']).to match a_graphql_entity_for(note.becomes!(DiscussionNote))
+ expect(mutation_response.dig('note', 'resolvable')).to eq(true)
+ expect(mutation_response['errors']).to be_empty
+ end
+
+ context 'when discussion note is given' do
+ let(:note) { create(:discussion_note, noteable: noteable, project: project) }
+
+ it_behaves_like 'a mutation that returns top-level errors' do
+ let(:match_errors) { include(/Note cannot be converted to a resolvable thread/) }
+ end
+ end
+
+ context 'when noteable does not support resolvable notes' do
+ let(:noteable) { create(:project_snippet) }
+
+ it_behaves_like 'a mutation that returns top-level errors' do
+ let(:match_errors) { include(/Note cannot be converted to a resolvable thread/) }
+ end
+ end
+
+ context 'when note is internal and user does not have access' do
+ let(:current_user) { create(:user, guest_of: project) }
+ let(:note) { create(:note, :internal, noteable: noteable, project: project) }
+
+ it_behaves_like 'a mutation that returns top-level errors' do
+ let(:match_errors) { include(/you don't have permission to perform this action/) }
+ end
+ end
+
+ context 'when saving fails with validation error' do
+ before do
+ note.update_column(:project_id, nil)
+ end
+
+ it 'returns the validation error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response['errors']).to include("Project can't be blank")
+ end
+ end
+end
diff --git a/spec/requests/search_controller_spec.rb b/spec/requests/search_controller_spec.rb
index d43a243b12a..50957f1caf2 100644
--- a/spec/requests/search_controller_spec.rb
+++ b/spec/requests/search_controller_spec.rb
@@ -214,9 +214,10 @@ RSpec.describe SearchController, type: :request, feature_category: :global_searc
login_as(user)
end
- context 'when project_id param is missing' do
- it 'raises an error' do
- expect { request }.to raise_error(ActionController::ParameterMissing)
+ context 'when neither project_id nor group_id param is given' do
+ it 'responds with Bad Request' do
+ request
+ expect(response).to have_gitlab_http_status(:bad_request)
end
end
@@ -246,8 +247,43 @@ RSpec.describe SearchController, type: :request, feature_category: :global_searc
let(:params) { { project_id: project.id } }
it 'returns all available settings results' do
- expect_next_instance_of(Search::Settings) do |settings|
- expect(settings).to receive(:for_project).with(project).and_return(%w[foo bar])
+ expect_next_instance_of(Search::ProjectSettings) do |settings|
+ expect(settings).to receive(:all).and_return(%w[foo bar])
+ end
+
+ request
+ expect(response.body).to eq '["foo","bar"]'
+ end
+ end
+
+ context 'when given group is not found' do
+ let(:params) { { group_id: non_existing_record_id } }
+
+ it 'returns an empty array' do
+ request
+ expect(response.body).to eq '[]'
+ end
+ end
+
+ context 'when user is not allowed to change settings in given group' do
+ let(:params) { { group_id: group.id } }
+
+ it 'returns an empty array' do
+ request
+ expect(response.body).to eq '[]'
+ end
+ end
+
+ context 'when user is allowed to change settings in given group' do
+ before_all do
+ group.add_owner(user)
+ end
+
+ let(:params) { { group_id: group.id } }
+
+ it 'returns all available settings results' do
+ expect_next_instance_of(Search::GroupSettings) do |settings|
+ expect(settings).to receive(:all).and_return(%w[foo bar])
end
request
diff --git a/spec/services/jira_connect_subscriptions/create_service_spec.rb b/spec/services/jira_connect_subscriptions/create_service_spec.rb
index b029e57944f..9753652420c 100644
--- a/spec/services/jira_connect_subscriptions/create_service_spec.rb
+++ b/spec/services/jira_connect_subscriptions/create_service_spec.rb
@@ -47,6 +47,7 @@ RSpec.describe JiraConnectSubscriptions::CreateService, feature_category: :integ
context 'when user does have access' do
before do
allow(PropagateIntegrationWorker).to receive(:perform_async)
+ stub_application_setting(jira_connect_application_key: 'mock_key')
end
it 'creates a subscription' do
@@ -71,6 +72,15 @@ RSpec.describe JiraConnectSubscriptions::CreateService, feature_category: :integ
expect(PropagateIntegrationWorker).not_to have_received(:perform_async)
end
+ it 'does not create integration when instance is not configured for Jira Cloud app and returns success' do
+ stub_application_setting(jira_connect_application_key: nil)
+
+ expect(subject[:status]).to eq(:success)
+
+ expect { subject }.not_to change { Integrations::JiraCloudApp.count }
+ expect(PropagateIntegrationWorker).not_to have_received(:perform_async)
+ end
+
context 'when group has an existing inactive integration' do
let_it_be(:integration) { create(:jira_cloud_app_integration, :group, :inactive, group: group) }
@@ -130,6 +140,7 @@ RSpec.describe JiraConnectSubscriptions::CreateService, feature_category: :integ
before do
allow(PropagateIntegrationWorker).to receive(:perform_async)
+ stub_application_setting(jira_connect_application_key: 'mock_key')
end
it 'activate existing jira cloud app integrations if subscription saved successfully' do
diff --git a/spec/support/shared_examples/lib/search/settings_shared_examples.rb b/spec/support/shared_examples/lib/search/settings_shared_examples.rb
index 47ab46ebdea..e1def7cb615 100644
--- a/spec/support/shared_examples/lib/search/settings_shared_examples.rb
+++ b/spec/support/shared_examples/lib/search/settings_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-RSpec.shared_examples 'all sections exist and have correct anchor links' do
- let(:settings) { Search::Settings.new.for_project(project) }
+RSpec.shared_examples 'all project settings sections exist and have correct anchor links' do
+ let(:settings) { Search::ProjectSettings.new(project).all }
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
@@ -21,10 +21,34 @@ RSpec.shared_examples 'all sections exist and have correct anchor links' do
expect(page).to have_css "##{URI.parse(setting[:href]).fragment}"
end
end
+end
- def remove_anchor_from_url(url)
- uri = URI.parse(url)
- uri.fragment = nil
- uri.to_s
+RSpec.shared_examples 'all group settings sections exist and have correct anchor links' do
+ let(:settings) { Search::GroupSettings.new(group).all }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:admin) } # some group settings require instance admin rights
+
+ it 'has only valid settings sections' do
+ sign_in(user)
+ enable_admin_mode!(user)
+ group.add_owner(user)
+
+ current_href = nil
+ settings.each do |setting|
+ # This speeds up the spec by not repeatedly visiting the same page.
+ if current_href != remove_anchor_from_url(setting[:href])
+ visit setting[:href]
+ current_href = remove_anchor_from_url(setting[:href])
+ end
+
+ expect(page).to have_content setting[:text]
+ expect(page).to have_css "##{URI.parse(setting[:href]).fragment}", visible: :all
+ end
end
end
+
+def remove_anchor_from_url(url)
+ uri = URI.parse(url)
+ uri.fragment = nil
+ uri.to_s
+end
diff --git a/spec/workers/jira_connect/jira_cloud_app_deactivation_worker_spec.rb b/spec/workers/jira_connect/jira_cloud_app_deactivation_worker_spec.rb
index 873a8457752..cd6587c6c47 100644
--- a/spec/workers/jira_connect/jira_cloud_app_deactivation_worker_spec.rb
+++ b/spec/workers/jira_connect/jira_cloud_app_deactivation_worker_spec.rb
@@ -36,6 +36,10 @@ RSpec.describe JiraConnect::JiraCloudAppDeactivationWorker, feature_category: :i
subject(:perform) { described_class.new.perform(group.id) }
+ before do
+ stub_application_setting(jira_connect_application_key: 'mock_key')
+ end
+
it 'deactivates all subgroup and sub project JiraCloudApp integrations' do
expect { perform }.not_to change { Integration.count }
diff --git a/yarn.lock b/yarn.lock
index 400e43642b7..f85dea110e7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2031,76 +2031,76 @@
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz#5b2fb4d8cd44c05deef8a7b0e6deb9ccb8939d18"
integrity sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==
-"@sentry-internal/browser-utils@8.19.0":
- version "8.19.0"
- resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.19.0.tgz#7a25111c5c3430c07b881d5fc83d1598af6d5676"
- integrity sha512-kM/2KlikKuBR63nFi2q7MGS3V9K9hakjvUknhr/jHZqDVfEuBKmp1ZlHFAdJtglKHHJy07gPj/XqDH7BbYh5yg==
+"@sentry-internal/browser-utils@8.20.0":
+ version "8.20.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.20.0.tgz#26837f889cff1caf09ddfd6ca7f0adad9256b981"
+ integrity sha512-GGYNiELnT4ByidHyS4/M8UF8Oxagm5R13QyTncQGq8nZcQhcFZ9mdxLnf1/R4+j44Fph2Cgzafe8jGP/AMA9zw==
dependencies:
- "@sentry/core" "8.19.0"
- "@sentry/types" "8.19.0"
- "@sentry/utils" "8.19.0"
+ "@sentry/core" "8.20.0"
+ "@sentry/types" "8.20.0"
+ "@sentry/utils" "8.20.0"
-"@sentry-internal/feedback@8.19.0":
- version "8.19.0"
- resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.19.0.tgz#7efef695fd4a058b36ef5c98145c4a990bbe877c"
- integrity sha512-Jc77H8fEaGcBhERc2U/o7Q8CZHvlZLT9vAlzq0ZZR20v/1vwYcJW1ysKfTuvmw22hCR6ukhFNl6pqJocXFVhvA==
+"@sentry-internal/feedback@8.20.0":
+ version "8.20.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.20.0.tgz#8e4ab43bb4048951f6670bd48b2af04deb75eff4"
+ integrity sha512-mFvAoVpVShkDB2AgEr/dE96NSTPKI/lGMBznZMg7ZEcwZhLfH7HvLYCadIskRfzqFTLOUpbm9ciIO4SyR/4bDA==
dependencies:
- "@sentry/core" "8.19.0"
- "@sentry/types" "8.19.0"
- "@sentry/utils" "8.19.0"
+ "@sentry/core" "8.20.0"
+ "@sentry/types" "8.20.0"
+ "@sentry/utils" "8.20.0"
-"@sentry-internal/replay-canvas@8.19.0":
- version "8.19.0"
- resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.19.0.tgz#4afe06acadf14a709e36efe5ad7da350e3ce0815"
- integrity sha512-l4pKJDHrXEctxrK7Xme/+fKToXpGwr/G2t77BzeE1WEw9LwSwADz/hi8HoMdZzuKWriM2BNbz20tpVS84sODxA==
+"@sentry-internal/replay-canvas@8.20.0":
+ version "8.20.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.20.0.tgz#abaa845278dd397670fb01baed505f751b1c9989"
+ integrity sha512-LXV/pMH9KMw6CtImenMsiBkYIFIc97pDJ/rC7mVImKIROQ45fxGp/JBXM4Id0GENyA2+SySMWVQCAAapSfHZTw==
dependencies:
- "@sentry-internal/replay" "8.19.0"
- "@sentry/core" "8.19.0"
- "@sentry/types" "8.19.0"
- "@sentry/utils" "8.19.0"
+ "@sentry-internal/replay" "8.20.0"
+ "@sentry/core" "8.20.0"
+ "@sentry/types" "8.20.0"
+ "@sentry/utils" "8.20.0"
-"@sentry-internal/replay@8.19.0":
- version "8.19.0"
- resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.19.0.tgz#7b290b19d6ba5c0ab742c4c48839cce329129b3f"
- integrity sha512-EW9e1J6XbqXUXQST1AfSIzT9O8OwPyeFOkhkn9/gqOQv08TJvQEIBtWJEoJS+XFMEUuB8IqIzVWNVko/DnGt9A==
+"@sentry-internal/replay@8.20.0":
+ version "8.20.0"
+ resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.20.0.tgz#1e1b514651a6c499609cb90cf8df3ee18a1df157"
+ integrity sha512-sCiI7SOAHq5XsxkixtoMofeSyKd/hVgDV+4145f6nN9m7nLzig4PBQwh2SgK2piJ2mfaXfqcdzA1pShPYldaJA==
dependencies:
- "@sentry-internal/browser-utils" "8.19.0"
- "@sentry/core" "8.19.0"
- "@sentry/types" "8.19.0"
- "@sentry/utils" "8.19.0"
+ "@sentry-internal/browser-utils" "8.20.0"
+ "@sentry/core" "8.20.0"
+ "@sentry/types" "8.20.0"
+ "@sentry/utils" "8.20.0"
-"@sentry/browser@8.19.0":
- version "8.19.0"
- resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.19.0.tgz#64551150cda979728297035f9a367ed344c7d586"
- integrity sha512-ZC1HxIFm4TIGONyy9MkPG6Dw8IAhzq43t5mq9PqrB1ehuWj8GX6Vk3E26kuc2sydAm4AXbj0562OmvZHsAJpUA==
+"@sentry/browser@8.20.0":
+ version "8.20.0"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.20.0.tgz#6644d3223f891b031684210d89511275a5d6c657"
+ integrity sha512-JDZbCreY44/fHYN28QzsAwEHXa2rc1hzM6GE4RSlXCdAhNfrjVxyYDxhw/50pVEHZg1WXxf7ZmERjocV5VJHsw==
dependencies:
- "@sentry-internal/browser-utils" "8.19.0"
- "@sentry-internal/feedback" "8.19.0"
- "@sentry-internal/replay" "8.19.0"
- "@sentry-internal/replay-canvas" "8.19.0"
- "@sentry/core" "8.19.0"
- "@sentry/types" "8.19.0"
- "@sentry/utils" "8.19.0"
+ "@sentry-internal/browser-utils" "8.20.0"
+ "@sentry-internal/feedback" "8.20.0"
+ "@sentry-internal/replay" "8.20.0"
+ "@sentry-internal/replay-canvas" "8.20.0"
+ "@sentry/core" "8.20.0"
+ "@sentry/types" "8.20.0"
+ "@sentry/utils" "8.20.0"
-"@sentry/core@8.19.0":
- version "8.19.0"
- resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.19.0.tgz#427d09ca27557ddc7c1bfa5e810b7f802836e0b4"
- integrity sha512-MrgjsZCEjOJgQjIznnDSrLEy7qL+4LVpNieAvr49cV1rzBNSwGmWRnt/puVaPsLyCUgupVx/43BPUHB/HtKNUw==
+"@sentry/core@8.20.0":
+ version "8.20.0"
+ resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.20.0.tgz#c50d082033a44295d2fe9140319f912ba1f946d3"
+ integrity sha512-R81snuw+67VT4aCxr6ShST/s0Y6FlwN2YczhDwaGyzumn5rlvA6A4JtQDeExduNoDDyv4T3LrmW8wlYZn3CJJw==
dependencies:
- "@sentry/types" "8.19.0"
- "@sentry/utils" "8.19.0"
+ "@sentry/types" "8.20.0"
+ "@sentry/utils" "8.20.0"
-"@sentry/types@8.19.0":
- version "8.19.0"
- resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.19.0.tgz#26a5d56c823c5eabbb7d6f53112da335b6d96dcb"
- integrity sha512-52C8X5V7mK2KIxMJt8MV5TxXAFHqrQR1RKm1oPTwKVWm8hKr1ZYJXINymNrWvpAc3oVIKLC/sa9WFYgXQh+YlA==
+"@sentry/types@8.20.0":
+ version "8.20.0"
+ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.20.0.tgz#f0f50c84eb768df8b55ee7b41459fec2d39d0d5e"
+ integrity sha512-6IP278KojOpiAA7vrd1hjhUyn26cl0n0nGsShzic5ztCVs92sTeVRnh7MTB9irDVtAbOEyt/YH6go3h+Jia1pA==
-"@sentry/utils@8.19.0":
- version "8.19.0"
- resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.19.0.tgz#f22df2a38327b1cff1e04ba7f11fdf1a32d3ba22"
- integrity sha512-8dWJJKaUN6Hf92Oxw2TBmHchGua2W3ZmonrZTTwLvl06jcAigbiQD0MGuF5ytZP8PHx860orV+SbTGKFzfU3Pg==
+"@sentry/utils@8.20.0":
+ version "8.20.0"
+ resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.20.0.tgz#fcbf46c8e8c8eccbf1db532b087547eb4f6c449c"
+ integrity sha512-+1I5H8dojURiEUGPliDwheQk8dhjp8uV1sMccR/W/zjFrt4wZyPs+Ttp/V7gzm9LDJoNek9tmELert/jQqWTgg==
dependencies:
- "@sentry/types" "8.19.0"
+ "@sentry/types" "8.20.0"
"@sinclair/typebox@^0.27.8":
version "0.27.8"