Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2025-01-07 03:33:15 +00:00
parent d176911166
commit 91aa68c3ac
41 changed files with 515 additions and 184 deletions

View File

@ -6,7 +6,7 @@ workflow:
include:
- local: .gitlab/ci/version.yml
- component: "gitlab.com/gitlab-org/quality/pipeline-common/allure-report@9.9.0"
- component: "gitlab.com/gitlab-org/quality/pipeline-common/allure-report@9.10.0"
inputs:
job_name: "e2e-test-report"
job_stage: "report"
@ -16,7 +16,7 @@ include:
gitlab_auth_token_variable_name: "PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE"
allure_job_name: "${QA_RUN_TYPE}"
- project: gitlab-org/quality/pipeline-common
ref: 9.9.0
ref: 9.10.0
file:
- /ci/base.gitlab-ci.yml
- /ci/knapsack-report.yml

View File

@ -18,4 +18,4 @@ variables:
# Retry failed specs in separate process
QA_RETRY_FAILED_SPECS: "true"
# helm chart ref used by test-on-cng pipeline
GITLAB_HELM_CHART_REF: "35cdd0031eee7d33b6fdfbb35911fd8c3eb96b32"
GITLAB_HELM_CHART_REF: "56110ba29f58e91a94b119dd8911d241edb89d9a"

View File

@ -5,6 +5,7 @@ import DurationBadge from './duration_badge.vue';
import LineNumber from './line_number.vue';
export default {
name: 'LineHeader',
components: {
GlIcon,
LineNumber,

View File

@ -1,5 +1,6 @@
<script>
export default {
name: 'LineNumber',
functional: true,
props: {
lineNumber: {

View File

@ -30,15 +30,19 @@ export default {
if (window.location.hash) {
const lineNumber = getLocationHash();
this.unwatchJobLog = this.$watch('jobLog', async () => {
if (this.jobLog.length) {
await this.$nextTick();
this.unwatchJobLog = this.$watch(
'jobLog',
async () => {
if (this.jobLog.length) {
await this.$nextTick();
const el = document.getElementById(lineNumber);
scrollToElement(el);
this.unwatchJobLog();
}
});
const el = document.getElementById(lineNumber);
scrollToElement(el);
this.unwatchJobLog();
}
},
{ immediate: true },
);
}
this.setupFullScreenListeners();

View File

@ -86,6 +86,7 @@ export default {
canCreateSnippet: false,
isDeleteModalVisible: false,
isDropdownShown: false,
embedDropdown: false,
};
},
computed: {

View File

@ -42,6 +42,7 @@ export default {
data() {
return {
dropdownOpen: false,
showCreateWorkItemModal: false,
};
},
computed: {
@ -93,15 +94,22 @@ export default {
trigger-source="top_nav"
:trigger-element="$options.TRIGGER_ELEMENT_DISCLOSURE_DROPDOWN"
/>
<create-work-item-modal
<gl-disclosure-dropdown-item
v-else-if="isCreateWorkItem(groupItem)"
:key="`${groupItem.text}-modal-trigger`"
as-dropdown-item
is-group
:work-item-type-name="$options.WORK_ITEM_TYPE_ENUM_EPIC"
:item="groupItem"
@action="showCreateWorkItemModal = true"
/>
<gl-disclosure-dropdown-item v-else :key="groupItem.text" :item="groupItem" />
</template>
</gl-disclosure-dropdown-group>
<create-work-item-modal
v-if="showCreateWorkItemModal"
visible
hide-button
is-group
:work-item-type-name="$options.WORK_ITEM_TYPE_ENUM_EPIC"
@hideModal="showCreateWorkItemModal = false"
/>
</gl-disclosure-dropdown>
</template>

View File

@ -146,7 +146,7 @@ export default {
class="gl-flex gl-flex-wrap gl-items-center gl-gap-2"
data-testid="approvals-summary-content"
>
<span class="gl-font-bold">{{ approvalLeftMessage }}</span>
<span v-if="approvalLeftMessage" class="gl-font-bold">{{ approvalLeftMessage }}</span>
<template v-if="hasApprovers">
<span v-if="approvalLeftMessage">{{ message }}</span>
<span v-else class="gl-font-bold">{{ message }}</span>

View File

@ -137,7 +137,7 @@ class Issue < ApplicationRecord
validates :work_item_type, presence: true
validates :confidential, inclusion: { in: [true, false], message: 'must be a boolean' }
validate :allowed_work_item_type_change, on: :update, if: :work_item_type_id_changed?
validate :allowed_work_item_type_change, on: :update, if: :correct_work_item_type_id_changed?
validate :due_date_after_start_date, if: :validate_due_date?
validate :parent_link_confidentiality
@ -236,10 +236,10 @@ class Issue < ApplicationRecord
scope :service_desk, -> {
where(
"(author_id = ? AND work_item_type_id = ?) OR work_item_type_id = ?",
"(author_id = ? AND correct_work_item_type_id = ?) OR correct_work_item_type_id = ?",
Users::Internal.support_bot.id,
WorkItems::Type.default_issue_type.id,
WorkItems::Type.default_by_type(:ticket).id
WorkItems::Type.default_issue_type.correct_id,
WorkItems::Type.default_by_type(:ticket).correct_id
)
}
scope :inc_relations_for_view, -> do
@ -358,6 +358,11 @@ class Issue < ApplicationRecord
correct_work_item_type
end
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/499911
def work_item_type_id
correct_work_item_type&.id
end
def work_item_type_id=(input_work_item_type_id)
work_item_type = WorkItems::Type.find_by_correct_id_with_fallback(input_work_item_type_id)
@ -917,15 +922,19 @@ class Issue < ApplicationRecord
end
def ensure_work_item_type
return if work_item_type.present? || work_item_type_id.present? || work_item_type_id_change&.last.present?
return if work_item_type.present? ||
correct_work_item_type_id.present? ||
correct_work_item_type_id_change&.last.present?
self.work_item_type = WorkItems::Type.default_by_type(DEFAULT_ISSUE_TYPE)
end
def allowed_work_item_type_change
return unless changes[:work_item_type_id]
return unless changes[:correct_work_item_type_id]
involved_types = WorkItems::Type.where(id: changes[:work_item_type_id].compact).pluck(:base_type).uniq
involved_types = WorkItems::Type.where(
correct_id: changes[:correct_work_item_type_id].compact
).pluck(:base_type).uniq
disallowed_types = involved_types - WorkItems::Type::CHANGEABLE_BASE_TYPES
return if disallowed_types.empty?

View File

@ -235,17 +235,13 @@ class WorkItem < Issue
end
def max_depth_reached?(child_type)
# Using the association here temporarily. We should use the `work_item_type_id` column value after the cleanup
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/499911
restriction = ::WorkItems::HierarchyRestriction.find_by_parent_type_id_and_child_type_id(
work_item_type.id,
work_item_type_id,
child_type.id
)
return false unless restriction&.maximum_depth
# Using the association here temporarily. We should use the `work_item_type_id` column value after the cleanup
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/499911
if work_item_type.id == child_type.id
if work_item_type_id == child_type.id
same_type_base_and_ancestors.count >= restriction.maximum_depth
else
hierarchy(different_type_id: child_type.id).base_and_ancestors.count >= restriction.maximum_depth
@ -282,7 +278,7 @@ class WorkItem < Issue
override :allowed_work_item_type_change
def allowed_work_item_type_change
return unless work_item_type_id_changed?
return unless correct_work_item_type_id_changed?
child_links = WorkItems::ParentLink.for_parents(id)
parent_link = ::WorkItems::ParentLink.find_by(work_item: self)
@ -337,7 +333,7 @@ class WorkItem < Issue
return unless restriction&.maximum_depth
children_with_new_type = self.class.where(id: child_links.select(:work_item_id))
.where(work_item_type_id: work_item_type_id)
.where(correct_work_item_type_id: correct_work_item_type_id)
max_child_depth = ::Gitlab::WorkItems::WorkItemHierarchy.new(children_with_new_type).max_descendants_depth.to_i
ancestor_depth =

View File

@ -179,13 +179,15 @@ module Issues
end
def handle_issue_type_change(issue)
return unless issue.previous_changes.include?('work_item_type_id')
return unless issue.previous_changes.include?('correct_work_item_type_id')
do_handle_issue_type_change(issue)
end
def do_handle_issue_type_change(issue)
old_work_item_type = ::WorkItems::Type.find(issue.work_item_type_id_before_last_save).base_type
old_work_item_type = ::WorkItems::Type.find_by_correct_id(
issue.correct_work_item_type_id_before_last_save
).base_type
SystemNoteService.change_issue_type(issue, current_user, old_work_item_type)
::IncidentManagement::IssuableEscalationStatuses::CreateService.new(issue).execute if issue.supports_escalation?

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class RemoveIssuesWorkItemTypeIdFk < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
milestone '17.8'
def up
with_lock_retries do
remove_foreign_key_if_exists :issues, column: :work_item_type_id
end
end
def down
add_concurrent_foreign_key :issues,
:work_item_types,
column: :work_item_type_id,
target_column: :id,
on_delete: nil
end
end

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
class FixWorkItemTypesIdColumnValues < Gitlab::Database::Migration[2.2]
restrict_gitlab_migration gitlab_schema: :gitlab_main
milestone '17.8'
def up
connection.execute(
<<~SQL
UPDATE work_item_types SET id = correct_id;
SQL
)
end
def down
# For newer instances, old_id might be null as those instances did not go through the id cleanup process.
# These instances were created with all the records in the work_item_types table already in the correct state.
# So, running the migration would leave the records in the same state that they already had.
# correct_id was already eaqual to id in every record.
connection.execute(
<<~SQL
UPDATE work_item_types SET id = old_id WHERE old_id IS NOT NULL;
SQL
)
end
end

View File

@ -0,0 +1 @@
b1eb892643ab9436c79a5bdf6afa066d0f64dc7eba28f4dafe4e7d2c496c568c

View File

@ -0,0 +1 @@
9d90f065475e1710750b265edc8fc106f47ec28335e6923355f0226eb4892e60

View File

@ -37474,9 +37474,6 @@ ALTER TABLE ONLY member_approvals
ALTER TABLE ONLY related_epic_links
ADD CONSTRAINT fk_b30520b698 FOREIGN KEY (issue_link_id) REFERENCES issue_links(id) ON DELETE CASCADE;
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_b37be69be6 FOREIGN KEY (work_item_type_id) REFERENCES work_item_types(id);
ALTER TABLE ONLY duo_workflows_checkpoints
ADD CONSTRAINT fk_b3d9cea509 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;

View File

@ -827,7 +827,7 @@ However, you should avoid putting too many links on any page. Too many links can
To link to another documentation (`.md`) file in the same repository:
- Use an inline link with a relative file path. For example, `[GitLab.com settings](../user/gitlab_com/index.md)`.
- Put the entire link on a single line, even if the link is very long. ([Vale](../testing/vale.md) rule: [`SubstitutionWarning.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab_base/MultiLineLinks.yml)).
- Put the entire link on a single line, even if the link is very long. ([Vale](../testing/vale.md) rule: [`MultiLineLinks.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab_base/MultiLineLinks.yml)).
To link to a file outside of the documentation files, for example to link from development
documentation to a specific code file, you can:

View File

@ -48,7 +48,7 @@ If you are on a self-managed GitLab instance, create a personal access token.
After you configure the plugin in your IDE, connect it to your GitLab account:
1. Go to your IDE's top menu bar and select **Settings**.
1. In your IDE, on the top bar, select your IDE's name, then select **Settings**.
1. On the left sidebar, expand **Tools**, then select **GitLab Duo**.
1. Select an authentication method:
- For GitLab.com, use `OAuth`.

View File

@ -25,39 +25,20 @@ More logs are available in the **GitLab Extension Output** window:
1. Verify that the debug log contains similar output:
```shell
14:48:21:344 GitlabProposalSource.GetCodeSuggestionAsync
14:48:21:344 LsClient.SendTextDocumentCompletionAsync("GitLab.Extension.Test\TestData.cs", 34, 0)
14:48:21:346 LS(55096): time="2023-07-17T14:48:21-05:00" level=info msg="update context"
GetProposalManagerAsync: Code suggestions enabled. ContentType (csharp) or file extension (cs) is supported.
GitlabProposalSourceProvider.GetProposalSourceAsync
```
## Extension not loaded on startup
### View activity log
After restarting, the following error is displayed:
If your extension does not load or crashes, check the activity log for errors.
Your activity log is available in this location:
```plaintext
SetSite failed for package [VisualStudioPackage]Source: 'Microsoft.VisualStudio.Composition' Description: Expected 1 export(s) with contract name "Microsoft.VisualStudio.Language.Suggestions.SuggestionServiceBase" but found 0 after applying applicable constraints.
Microsoft.VisualStudio.Composition.CompositionFailedException: Expected 1 export(s) with contract name "Microsoft.VisualStudio.Language.Suggestions.SuggestionServiceBase" but found 0 after applying applicable constraints.
at Microsoft.VisualStudio.Composition.ExportProvider.GetExports(ImportDefinition importDefinition)
at Microsoft.VisualStudio.Composition.ExportProvider.GetExports[T,TMetadataView](String contractName, ImportCardinality cardinality)
at Microsoft.VisualStudio.Composition.ExportProvider.GetExport[T,TMetadataView](String contractName)
at Microsoft.VisualStudio.Composition.ExportProvider.GetExportedValue[T]()
at Microsoft.VisualStudio.ComponentModelHost.ComponentModel.GetService[T]()
[...]
C:\Users\WINDOWS_USERNAME\AppData\Roaming\Microsoft\VisualStudio\VS_VERSION\ActivityLog.xml
```
To fix this issue, install the IntelliCode component for Visual Studio.
Replace these values in the directory path:
## Error: unable to find last release
If you receive this error message, your commits are likely on the main branch of
your fork, instead of a feature branch:
```plaintext
buildtag.sh: Error: unable to find last release.
```
To resolve this issue:
1. Create a separate feature branch for your changes.
1. Cherry-pick your commits into your feature branch.
1. Retry your command.
- `WINDOWS_USERNAME`: Your Windows username.
- `VS_VERSION`: The version of your Visual Studio installation.

View File

@ -312,6 +312,11 @@ Your subscription cost is based on the maximum number of seats you use during th
- If restricted access is turned off, when there are no seats left in your subscription groups can continue to add billable
users. GitLab [bills you for the overage](../quarterly_reconciliation.md).
You cannot add seats to your subscription if either:
- You purchased your subscription through an [authorized reseller](../customers_portal.md#customers-that-purchased-through-a-reseller) (including GCP and AWS marketplaces). Contact the reseller to add more seats.
- You have a multi-year subscription. Contact the [sales team](https://about.gitlab.com/sales/) to add more seats.
To add seats to a subscription:
1. Sign in to the [Customers Portal](https://customers.gitlab.com/).
@ -325,8 +330,6 @@ To add seats to a subscription:
You receive the payment receipt by email. You can also access the receipt in the Customers Portal under [**Invoices**](https://customers.gitlab.com/invoices).
For multi-year subscriptions, the **Add seats** option is unavailable. To purchase additional seats, please contact the [sales team](https://about.gitlab.com/sales/).
## Remove users from subscription
To remove a billable user from your GitLab.com subscription:

View File

@ -204,6 +204,11 @@ period is prorated from the date of purchase through to the end of the subscript
period. You can continue to add users even if you reach the number of users in
license count. GitLab [bills you for the overage](../quarterly_reconciliation.md).
You cannot add seats to your subscription if either:
- You purchased your subscription through an [authorized reseller](../customers_portal.md#customers-that-purchased-through-a-reseller) (including GCP and AWS marketplaces). Contact the reseller to add more seats.
- You have a multi-year subscription. Contact the [sales team](https://about.gitlab.com/sales/) to add more seats.
To add seats to a subscription:
1. Sign in to the [Customers Portal](https://customers.gitlab.com/).
@ -223,10 +228,6 @@ your instance immediately. If you're using a license file, you receive an update
To add the seats, [add the license file](../../administration/license_file.md)
to your instance.
If you purchased your subscription through an [authorized reseller](../customers_portal.md#customers-that-purchased-through-a-reseller) (including GCP and AWS marketplaces), contact the reseller to add more seats.
For multi-year subscriptions, the **Add seats** option is unavailable. To purchase additional seats, please contact the [sales team](https://about.gitlab.com/sales/).
## Subscription data synchronization
Prerequisites:

View File

@ -169,7 +169,38 @@ GitLab Language Server process are invalid. To re-enable Code Suggestions:
The following documentation is for Code Suggestions-specific troubleshooting for
Microsoft Visual Studio.
For non-Code Suggestions troubleshooting for Microsoft Visual Studio, see [Visual Studio troubleshooting](../../../../editor_extensions/visual_studio/visual_studio_troubleshooting.md).
For non-Code Suggestions troubleshooting for Microsoft Visual Studio, see
[Visual Studio troubleshooting](../../../../editor_extensions/visual_studio/visual_studio_troubleshooting.md).
### IntelliCode is missing
Code Suggestions requires the **IntelliCode** component of Visual Studio. If the component
is missing, you might see an error like this when you start Visual Studio:
```plaintext
SetSite failed for package [VisualStudioPackage]Source: 'Microsoft.VisualStudio.Composition'
Description: Expected 1 export(s) with contract name "Microsoft.VisualStudio.Language.Suggestions.SuggestionServiceBase"
but found 0 after applying applicable constraints.
Microsoft.VisualStudio.Composition.CompositionFailedException:
Expected 1 export(s) with contract name "Microsoft.VisualStudio.Language.Suggestions.SuggestionServiceBase"
but found 0 after applying applicable constraints.
at Microsoft.VisualStudio.Composition.ExportProvider.GetExports(ImportDefinition importDefinition)
at Microsoft.VisualStudio.Composition.ExportProvider.GetExports[T,TMetadataView](String contractName, ImportCardinality cardinality)
at Microsoft.VisualStudio.Composition.ExportProvider.GetExport[T,TMetadataView](String contractName)
at Microsoft.VisualStudio.Composition.ExportProvider.GetExportedValue[T]()
at Microsoft.VisualStudio.ComponentModelHost.ComponentModel.GetService[T]()
[...]
```
To fix this problem, install the **IntelliCode** component:
1. In the Windows start menu, search for the **Visual Studio Installer** and open it.
1. Select your Visual Studio instance, then select **Modify**.
1. In the **Individual components** tab, search for **IntelliCode**.
1. Select the component's checkbox, then on the bottom right, select **Modify**.
1. Wait for the Visual Studio Installer to finish the installation.
### Suggestions not displayed in Microsoft Visual Studio

View File

@ -50618,6 +50618,9 @@ msgstr ""
msgid "SecurityOrchestration|Add regular branches"
msgstr ""
msgid "SecurityOrchestration|Additional configuration"
msgstr ""
msgid "SecurityOrchestration|After dismissing the alert, the information will never be shown again."
msgstr ""
@ -50651,6 +50654,9 @@ msgstr ""
msgid "SecurityOrchestration|Allow except on these packages"
msgstr ""
msgid "SecurityOrchestration|Allow users to skip pipelines"
msgstr ""
msgid "SecurityOrchestration|Allowed licenses"
msgstr ""
@ -50741,6 +50747,9 @@ msgstr ""
msgid "SecurityOrchestration|Configuration"
msgstr ""
msgid "SecurityOrchestration|Configure policies to allow individual users or service accounts to use %{linkStart}skip_ci%{linkEnd} to skip pipelines."
msgstr ""
msgid "SecurityOrchestration|Configure your conditions in the pipeline execution file. %{linkStart}What can pipeline execution do?%{linkEnd}"
msgstr ""
@ -51538,6 +51547,9 @@ msgstr ""
msgid "SecurityOrchestration|default"
msgstr ""
msgid "SecurityOrchestration|except for:"
msgstr ""
msgid "SecurityOrchestration|except groups"
msgstr ""

View File

@ -60,7 +60,7 @@
"@gitlab/duo-ui": "^6.0.0",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.3.0",
"@gitlab/query-language-rust": "0.3.0",
"@gitlab/query-language-rust": "0.3.1",
"@gitlab/svgs": "3.121.0",
"@gitlab/ui": "106.0.0",
"@gitlab/vue-router-vue3": "npm:vue-router@4.1.6",

View File

@ -8,9 +8,14 @@ module QA
GITLAB_PROJECT_ID = 278964
UPDATE_BRANCH_NAME = "qa-knapsack-master-report-update"
DEFAULT_WAIT_BEFORE_MERGE = 120
def self.run
new.update_master_report
def self.run(wait_before_merge: DEFAULT_WAIT_BEFORE_MERGE)
new(wait_before_merge: wait_before_merge).update_master_report
end
def initialize(wait_before_merge: DEFAULT_WAIT_BEFORE_MERGE)
@wait_before_merge = wait_before_merge
end
# Create master_report.json merge request
@ -20,10 +25,17 @@ module QA
create_branch
create_commit
create_mr
return unless auto_merge?
logger.info("Performing auto merge")
approve_mr
add_mr_to_merge_train
end
private
attr_reader :wait_before_merge, :mr_iid
# Knapsack report generator
#
# @return [QA::Support::KnapsackReport]
@ -31,13 +43,6 @@ module QA
@knapsack_reporter = Support::KnapsackReport.new(logger)
end
# Gitlab access token
#
# @return [String]
def gitlab_access_token
@gitlab_access_token ||= ENV["GITLAB_ACCESS_TOKEN"] || raise("Missing GITLAB_ACCESS_TOKEN env variable")
end
# Gitlab api url
#
# @return [String]
@ -45,13 +50,49 @@ module QA
@gitlab_api_url ||= ENV["CI_API_V4_URL"] || raise("Missing CI_API_V4_URL env variable")
end
# Api request headers
# Gitlab access token
#
# @return [String]
def gitlab_access_token
@gitlab_access_token ||= ENV["GITLAB_ACCESS_TOKEN"] || raise("Missing GITLAB_ACCESS_TOKEN env variable")
end
# Knapsack report approver token
#
# @return [String]
def approver_access_token
@approver_access_token ||= ENV["QA_KNAPSACK_REPORT_APPROVER_TOKEN"].tap do |token|
logger.warn("QA_KNAPSACK_REPORT_APPROVER_TOKEN is not set") unless token
end
end
# Update mr approver user id
#
# @return [Integer]
def approver_user_id
@approver_user_id ||= approver_access_token.then do |token|
next 0 unless token
resp = get("#{gitlab_api_url}/user", token_header(token))
next parse_body(resp)[:id] if success?(resp.code)
logger.error("Failed to fetch approver user id! Response: #{resp.body}")
0
end
end
# Valid approver user is set
#
# @return [Boolean]
def approver_user_valid?
approver_user_id != 0
end
# Api request private token header
#
# @return [Hash]
def api_headers
@api_headers ||= {
headers: { "PRIVATE-TOKEN" => gitlab_access_token }
}
def token_header(token = gitlab_access_token)
{ headers: { "PRIVATE-TOKEN" => token } }
end
# Create branch for knapsack report update
@ -59,16 +100,24 @@ module QA
# @return [void]
def create_branch
logger.info("Creating branch '#{UPDATE_BRANCH_NAME}' branch")
api_request(:post, "repository/branches", {
branch: UPDATE_BRANCH_NAME,
ref: "master"
})
rescue StandardError => e
raise e unless e.message.include?("Branch already exists")
retry_attempts = 0
logger.warn("Branch '#{UPDATE_BRANCH_NAME}' already exists, recreating it.")
api_request(:delete, "repository/branches/#{UPDATE_BRANCH_NAME}")
retry
begin
api_request(:post, "repository/branches", {
branch: UPDATE_BRANCH_NAME,
ref: "master"
})
rescue StandardError => e
raise e if retry_attempts > 2
if e.message.include?("Branch already exists")
logger.warn("Branch '#{UPDATE_BRANCH_NAME}' already exists, recreating it.")
api_request(:delete, "repository/branches/#{UPDATE_BRANCH_NAME}")
end
retry_attempts += 1
retry
end
end
# Create update commit for knapsack report
@ -104,28 +153,69 @@ module QA
resp = api_request(:post, "merge_requests", {
source_branch: UPDATE_BRANCH_NAME,
target_branch: "master",
title: "Update master_report.json for E2E tests",
title: "Update knapsack runtime data for E2E tests",
remove_source_branch: true,
squash: true,
labels: "Quality,team::Test and Tools Infrastructure,type::maintenance,maintenance::pipelines",
description: <<~DESCRIPTION
Update fallback knapsack report with latest spec runtime data.
reviewer_ids: approver_user_valid? ? [approver_user_id] : nil,
labels: "group::development analytics,type::maintenance,maintenance::pipelines",
description: "Update fallback knapsack report and example runtime data report.".then do |description|
next description if approver_user_valid?
@gl-dx/qe-maintainers please review and merge.
DESCRIPTION
})
"#{description}\n\ncc: @gl-dx/qe-maintainers"
end
}.compact)
@mr_iid = resp[:iid]
logger.info("Merge request created: #{resp[:web_url]}")
end
# Approve created merge request
#
# @return [void]
def approve_mr
logger.info(" approving merge request")
api_request(:post, "merge_requests/#{mr_iid}/approve", {}, token_header(approver_access_token))
end
# Add merge request to merge train
#
# @return [void]
def add_mr_to_merge_train
logger.info(" adding merge request to merge train")
sleep(wait_before_merge) # gitlab-org/gitlab takes a long time to create pipeline after approval
retry_attempts = 0
approver_header = token_header(approver_access_token)
begin
api_request(:post, "merge_trains/merge_requests/#{mr_iid}", { when_pipeline_succeeds: true }, approver_header)
rescue StandardError => e
raise e if retry_attempts > 2
logger.warn(" failed to add merge request to merge train, retrying...")
logger.warn(e.message)
retry_attempts += 1
sleep(10)
retry
end
end
# Attempt to automatically merge created mr
#
# @return [Boolean]
def auto_merge?
(mr_iid && approver_user_valid?).tap do |auto_merge|
logger.warn("Auto merge will not be performed!") unless auto_merge
end
end
# Api update request
#
# @param [String] verb
# @param [String] path
# @param [Hash] payload
# @return [Hash, Array]
def api_request(verb, path, payload = nil)
args = [verb, "#{gitlab_api_url}/projects/#{GITLAB_PROJECT_ID}/#{path}", payload, api_headers].compact
def api_request(verb, path, payload = nil, headers = token_header)
args = [verb, "#{gitlab_api_url}/projects/#{GITLAB_PROJECT_ID}/#{path}", payload, headers].compact
response = public_send(*args)
raise "Api request to #{path} failed! Body: #{response.body}" unless success?(response.code)
return {} if response.body.empty?

View File

@ -1,14 +1,19 @@
# frozen_string_literal: true
RSpec.describe QA::Tools::KnapsackReportUpdater do
RSpec.describe QA::Tools::KnapsackReportUpdater, :aggregate_failures do
include QA::Support::Helpers::StubEnv
let(:http_response) { instance_double("HTTPResponse", code: 200, body: {}.to_json) }
let(:logger) { instance_double("Logger", info: nil, warn: nil) }
subject(:report_updater) { described_class.new(wait_before_merge: 0) }
let(:approver_id) { 1 }
let(:mr_iid) { 1 }
let(:approver_token) { nil }
let(:merged_runtimes) { { "spec_file[1:1]": 0.0 } }
let(:merged_report) { { spec_file: 0.0 } }
let(:branch) { "qa-knapsack-master-report-update" }
let(:http_response) { mock_response(200, { id: approver_id, iid: mr_iid }) }
let(:logger) { instance_double(Logger, info: nil, warn: nil, error: nil) }
let(:knapsack_reporter) do
instance_double(
QA::Support::KnapsackReport,
@ -17,26 +22,37 @@ RSpec.describe QA::Tools::KnapsackReportUpdater do
)
end
def request_args(verb, path, payload)
# Instance double for rest client response
#
# @param code [Integer]
# @param body [Hash]
# @return [InstanceDouble]
def mock_response(code, body)
instance_double(RestClient::Response, code: code, body: body.to_json)
end
# Request args passed to rest client
#
# @param verb [Symbol]
# @param path [String]
# @param payload [Hash]
# @param token [String]
# @return [Hash]
def request_args(verb, path, payload, token = "token")
{
method: verb,
url: "https://gitlab.com/api/v4/projects/278964/#{path}",
payload: payload,
verify_ssl: false,
headers: { "PRIVATE-TOKEN" => "token" }
headers: { "PRIVATE-TOKEN" => token }
}.compact
end
before do
allow(RestClient::Request).to receive(:execute).and_return(http_response)
allow(Gitlab::QA::TestLogger).to receive(:logger).and_return(logger)
allow(QA::Support::KnapsackReport).to receive(:new).with(logger).and_return(knapsack_reporter)
stub_env("CI_API_V4_URL", "https://gitlab.com/api/v4")
stub_env("GITLAB_ACCESS_TOKEN", "token")
end
def expect_mr_created
# Expect merge request was created
#
# @param reviewer_ids [Array, nil]
# @return [void]
def expect_mr_created(reviewer_ids: nil)
expect(knapsack_reporter).to have_received(:create_knapsack_report).with(merged_runtimes)
expect(RestClient::Request).to have_received(:execute).with(request_args(:post, "repository/commits", {
branch: branch,
@ -57,21 +73,49 @@ RSpec.describe QA::Tools::KnapsackReportUpdater do
expect(RestClient::Request).to have_received(:execute).with(request_args(:post, "merge_requests", {
source_branch: branch,
target_branch: "master",
title: "Update master_report.json for E2E tests",
title: "Update knapsack runtime data for E2E tests",
remove_source_branch: true,
squash: true,
labels: "Quality,team::Test and Tools Infrastructure,type::maintenance,maintenance::pipelines",
description: <<~DESCRIPTION
Update fallback knapsack report with latest spec runtime data.
reviewer_ids: reviewer_ids,
labels: "group::development analytics,type::maintenance,maintenance::pipelines",
description: "Update fallback knapsack report and example runtime data report.".then do |description|
next description unless reviewer_ids.nil?
@gl-dx/qe-maintainers please review and merge.
DESCRIPTION
}))
"#{description}\n\ncc: @gl-dx/qe-maintainers"
end
}.compact))
end
before do
allow(RestClient::Request).to receive(:execute).and_return(http_response)
allow(Gitlab::QA::TestLogger).to receive(:logger).and_return(logger)
allow(QA::Support::KnapsackReport).to receive(:new).and_return(knapsack_reporter)
allow(QA::Runtime::Env).to receive(:canary_cookie).and_return({})
stub_env("CI_API_V4_URL", "https://gitlab.com/api/v4")
stub_env("GITLAB_ACCESS_TOKEN", "token")
stub_env("QA_KNAPSACK_REPORT_APPROVER_TOKEN", approver_token)
end
context "without approver token" do
it "does not attempt auto merge" do
report_updater.update_master_report
expect_mr_created
expect(RestClient::Request).not_to have_received(:execute).with(hash_including(
method: :post,
url: "merge_requests/#{mr_iid}/approve"
))
expect(RestClient::Request).not_to have_received(:execute).with(hash_including(
method: :post,
url: "merge_trains/merge_requests/#{mr_iid}"
))
end
end
context "without existing branch" do
it "creates master report merge request", :aggregate_failures do
described_class.run
it "creates master report merge request" do
report_updater.update_master_report
expect(RestClient::Request).to have_received(:execute).with(request_args(:post, "repository/branches", {
branch: branch,
@ -86,13 +130,13 @@ RSpec.describe QA::Tools::KnapsackReportUpdater do
allow(RestClient::Request).to receive(:execute)
.with(request_args(:post, "repository/branches", { branch: branch, ref: "master" }))
.and_return(
instance_double("HTTPResponse", code: 403, body: { message: "Branch already exists" }.to_json),
http_response
mock_response(403, { message: "Branch already exists" }),
mock_response(200, { name: branch })
)
end
it "recreates branch and creates master report merge request", :aggregate_failures do
described_class.run
report_updater.update_master_report
expect(RestClient::Request).to have_received(:execute).with(
request_args(:post, "repository/branches", { branch: branch, ref: "master" })
@ -103,4 +147,47 @@ RSpec.describe QA::Tools::KnapsackReportUpdater do
expect_mr_created
end
end
context "with approver token" do
let(:approver_token) { "approver_token" }
context "with approver id returned" do
it "creates merge request and adds it to merge train" do
report_updater.update_master_report
expect_mr_created(reviewer_ids: [approver_id])
expect(RestClient::Request).to have_received(:execute).with(
request_args(:post, "merge_requests/#{mr_iid}/approve", {}, approver_token)
)
expect(RestClient::Request).to have_received(:execute).with(
request_args(:post, "merge_trains/merge_requests/#{mr_iid}", { when_pipeline_succeeds: true }, approver_token)
)
end
end
context "without approver id returned" do
before do
allow(RestClient::Request).to receive(:execute).with({
method: :get,
url: "https://gitlab.com/api/v4/user",
verify_ssl: false,
headers: { "PRIVATE-TOKEN" => approver_token }
}).and_return(mock_response(401, { message: "401 Unauthorized" }))
end
it "does not attempt auto merge" do
report_updater.update_master_report
expect_mr_created(reviewer_ids: nil)
expect(RestClient::Request).not_to have_received(:execute).with(hash_including(
method: :post,
url: "merge_requests/#{mr_iid}/approve"
))
expect(RestClient::Request).not_to have_received(:execute).with(hash_including(
method: :post,
url: "merge_trains/merge_requests/#{mr_iid}"
))
end
end
end
end

View File

@ -54,7 +54,6 @@ ee/spec/frontend/members/components/modals/ldap_override_confirmation_modal_spec
ee/spec/frontend/members/components/table/drawer/role_details_drawer_spec.js
ee/spec/frontend/members/components/table/drawer/role_updater_spec.js
ee/spec/frontend/members/components/table/max_role_spec.js
ee/spec/frontend/members/components/table/member_action_buttons_spec.js
ee/spec/frontend/members/components/table/members_table_spec.js
ee/spec/frontend/metrics/details/filter_bar/groupby_filter_spec.js
ee/spec/frontend/metrics/details/metrics_details_spec.js
@ -126,18 +125,11 @@ spec/frontend/branches/components/delete_branch_modal_spec.js
spec/frontend/cascading_settings/components/cascading_lock_icon_spec.js
spec/frontend/cascading_settings/components/lock_tooltip_spec.js
spec/frontend/ci/artifacts/components/artifacts_bulk_delete_spec.js
spec/frontend/ci/catalog/index_spec.js
spec/frontend/ci/job_details/components/log/line_header_spec.js
spec/frontend/ci/job_details/components/log/line_spec.js
spec/frontend/ci/job_details/components/log/log_spec.js
spec/frontend/ci/job_details/components/sidebar/job_sidebar_retry_button_spec.js
spec/frontend/ci/pipeline_details/header/components/header_badges_spec.js
spec/frontend/ci/pipeline_editor/components/header/validation_segment_spec.js
spec/frontend/ci/pipeline_editor/components/job_assistant_drawer/accordion_items/rules_item_spec.js
spec/frontend/ci/pipeline_editor/components/lint/ci_lint_warnings_spec.js
spec/frontend/ci/pipeline_editor/components/pipeline_editor_tabs_spec.js
spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js
spec/frontend/ci/pipelines_page/components/pipeline_multi_actions_spec.js
spec/frontend/ci/pipelines_page/components/pipelines_artifacts_spec.js
spec/frontend/ci/runner/components/runner_form_fields_spec.js
spec/frontend/ci_settings_pipeline_triggers/components/edit_trigger_modal_spec.js
@ -273,7 +265,6 @@ spec/frontend/sidebar/components/lock/issuable_lock_form_spec.js
spec/frontend/sidebar/components/milestone/milestone_dropdown_spec.js
spec/frontend/sidebar/components/subscriptions/subscriptions_dropdown_spec.js
spec/frontend/sidebar/components/todo_toggle/sidebar_todo_widget_spec.js
spec/frontend/snippets/components/snippet_header_spec.js
spec/frontend/super_sidebar/components/nav_item_router_link_spec.js
spec/frontend/super_sidebar/components/organization_switcher_spec.js
spec/frontend/super_sidebar/components/sidebar_portal_spec.js

View File

@ -31,7 +31,6 @@ RSpec.describe 'Database schema',
ci_sources_pipelines: [%w[source_partition_id source_pipeline_id], %w[partition_id pipeline_id]],
ci_sources_projects: [%w[partition_id pipeline_id]], # index on pipeline_id is sufficient
ci_stages: [%w[partition_id pipeline_id]], # the index on pipeline_id is sufficient
issues: [%w[correct_work_item_type_id]],
notes: %w[namespace_id], # this index is added in an async manner, hence it needs to be ignored in the first phase.
p_ci_build_trace_metadata: [%w[partition_id build_id], %w[partition_id trace_artifact_id]], # the index on build_id is enough
p_ci_builds: [%w[partition_id stage_id], %w[partition_id execution_config_id], %w[auto_canceled_by_partition_id auto_canceled_by_id], %w[upstream_pipeline_partition_id upstream_pipeline_id], %w[partition_id commit_id]], # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142804#note_1745483081
@ -157,7 +156,7 @@ RSpec.describe 'Database schema',
gitlab_subscription_histories: %w[gitlab_subscription_id hosted_plan_id namespace_id],
identities: %w[user_id],
import_failures: %w[project_id],
issues: %w[last_edited_by_id state_id correct_work_item_type_id],
issues: %w[last_edited_by_id state_id work_item_type_id],
issue_emails: %w[email_message_id],
jira_tracker_data: %w[jira_issue_transition_id],
keys: %w[user_id],

View File

@ -125,5 +125,14 @@ FactoryBot.define do
end
end
end
after(:build) do |issue, _|
next if issue.attributes['work_item_type_id'].blank?
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/499911
issue.write_attribute(:work_item_type_id, -issue.attributes['work_item_type_id'])
# clearing attribute change to avoid update callbacks on initial save
issue.clear_work_item_type_id_change
end
end
end

View File

@ -85,6 +85,15 @@ FactoryBot.define do
end
end
after(:build) do |work_item, _|
next if work_item.attributes['work_item_type_id'].blank?
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/499911
work_item.write_attribute(:work_item_type_id, -work_item.attributes['work_item_type_id'])
# clearing attribute change to avoid update callbacks on initial save
work_item.clear_work_item_type_id_change
end
# Service Desk Ticket
factory :ticket do
association :work_item_type, :ticket

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import { initCatalog } from '~/ci/catalog/';
import * as Router from '~/ci/catalog/router';
import CiResourcesPage from '~/ci/catalog/components/pages/ci_resources_page.vue';
@ -30,7 +29,7 @@ describe('~/ci/catalog/index', () => {
});
it('returns a Vue Instance', () => {
expect(component).toBeInstanceOf(Vue);
expect(component.$options.name).toBe('GlobalCatalog');
});
it('creates a router with the received base path and component', () => {

View File

@ -1,4 +1,4 @@
import { shallowMount } from '@vue/test-utils';
import { shallowMount, mount } from '@vue/test-utils';
import Line from '~/ci/job_details/components/log/line.vue';
import LineNumber from '~/ci/job_details/components/log/line_number.vue';
import setWindowLocation from 'helpers/set_window_location_helper';
@ -24,8 +24,8 @@ describe('Job Log Line', () => {
let wrapper;
let data;
const createComponent = (props) => {
wrapper = shallowMount(Line, {
const createComponent = (props, mountFn = shallowMount) => {
wrapper = mountFn(Line, {
propsData: {
path: '/',
...props,
@ -215,14 +215,17 @@ describe('Job Log Line', () => {
const time = '00:00:01Z';
const text = 'text';
createComponent({
line: {
time,
content: [{ text }],
lineNumber,
createComponent(
{
line: {
time,
content: [{ text }],
lineNumber,
},
path: '/',
},
path: '/',
});
mount,
);
expect(wrapper.text()).toBe(`${lineNumber}${time}${text}`);
});

View File

@ -20,14 +20,14 @@ jest.mock('~/lib/utils/common_utils', () => ({
describe('Job Log', () => {
let wrapper;
let actions;
let state;
let initialState;
let store;
let toggleCollapsibleLineMock;
Vue.use(Vuex);
const createComponent = (props) => {
store = new Vuex.Store({ actions, state });
store = new Vuex.Store({ actions, state: initialState });
wrapper = mount(Log, {
provide: {
@ -49,7 +49,7 @@ describe('Job Log', () => {
const { lines, sections } = logLinesParser(mockJobLog);
state = {
initialState = {
jobLog: lines,
jobLogSections: sections,
};
@ -102,7 +102,8 @@ describe('Job Log', () => {
});
it('hides duration', () => {
state.jobLogSections['resolve-secrets'].hideDuration = true;
initialState.jobLogSections['resolve-secrets'].hideDuration = true;
createComponent();
expect(findLineHeader().props('duration')).toBe('00:00');
@ -112,7 +113,7 @@ describe('Job Log', () => {
describe('when a section is collapsed', () => {
beforeEach(() => {
state.jobLogSections['prepare-executor'].isClosed = true;
initialState.jobLogSections['prepare-executor'].isClosed = true;
createComponent();
});
@ -153,20 +154,21 @@ describe('Job Log', () => {
it('scrolls to line number', async () => {
createComponent();
await waitForPromises();
state.jobLog = logLinesParser(mockJobLog, [], '#L6').lines;
wrapper.vm.$store.state.jobLog = logLinesParser(mockJobLog, [], '#L6').lines;
await waitForPromises();
expect(scrollToElement).toHaveBeenCalledTimes(1);
state.jobLog = logLinesParser(mockJobLog, [], '#L6').lines;
wrapper.vm.$store.state.jobLog = logLinesParser(mockJobLog, [], '#L6').lines;
await waitForPromises();
expect(scrollToElement).toHaveBeenCalledTimes(1);
});
it('line number within collapsed section is visible', () => {
state.jobLog = logLinesParser(mockJobLog, [], '#L6').lines;
initialState.jobLog = logLinesParser(mockJobLog, [], '#L6').lines;
createComponent();

View File

@ -32,7 +32,9 @@ describe('Job Sidebar Retry Button', () => {
});
};
beforeEach(createWrapper);
beforeEach(() => {
createWrapper();
});
it.each([
[null, false, true],
@ -82,8 +84,9 @@ describe('Job Sidebar Retry Button', () => {
store,
});
};
it('should not render confirmation modal if confirmation message is null', () => {
findRetryLink().vm.$emit('click');
findRetryLink().trigger('click');
expect(confirmAction).not.toHaveBeenCalled();
});

View File

@ -46,6 +46,9 @@ describe('Job App', () => {
wrapper = shallowMountExtended(JobApp, {
propsData: { ...props },
store,
provide: {
glAbilities: { troubleshootJobWithAi: false },
},
});
};
@ -165,6 +168,9 @@ describe('Job App', () => {
details_path: 'path',
action: {
confirmation_message: null,
button_title: 'Retry job',
method: 'post',
path: '/path',
},
illustration: {
content: 'Run this job again in order to create the necessary resources.',

View File

@ -7,7 +7,7 @@ import { pipelineHeaderSuccess, pipelineHeaderTrigger } from '../../mock_data';
describe('Header badges', () => {
let wrapper;
const findAllBadges = () => wrapper.findAllComponents(GlBadge);
const findAllBadges = () => wrapper.findAllComponents(GlBadge).wrappers;
const findChildPipelineBadge = () =>
findAllBadges().filter((badge) => {
const sprintf = badge.findComponent(GlSprintf);
@ -62,7 +62,7 @@ describe('Header badges', () => {
});
expect(findAllBadges()).toHaveLength(4);
expect(findChildPipelineBadge().exists()).toBe(true);
expect(findChildPipelineBadge()).toHaveLength(1);
});
});
});

View File

@ -48,7 +48,6 @@ describe('Pipeline Multi Actions Dropdown', () => {
path: '/new/download/path-three',
},
];
const artifactItemTestId = 'artifact-item';
const artifactsEndpointPlaceholder = ':pipeline_artifacts_id';
const artifactsEndpoint = `endpoint/${artifactsEndpointPlaceholder}/artifacts.json`;
const pipelineId = 108;
@ -77,7 +76,6 @@ describe('Pipeline Multi Actions Dropdown', () => {
const findAlert = () => wrapper.findByTestId('artifacts-fetch-error');
const findDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findFirstArtifactItem = () => wrapper.findByTestId(artifactItemTestId);
const findAllArtifactItemsData = () =>
findDropdown()
.props('items')
@ -182,8 +180,7 @@ describe('Pipeline Multi Actions Dropdown', () => {
});
it('should render the correct artifact name and path', () => {
expect(findFirstArtifactItem().attributes('href')).toBe(artifacts[0].path);
expect(findFirstArtifactItem().text()).toBe(artifacts[0].name);
expect(findAllArtifactItemsData()).toEqual(artifacts);
});
describe('when opened again with new artifacts', () => {

View File

@ -18,6 +18,10 @@ describe('CreateMenu component', () => {
const findGlDisclosureDropdownGroups = () => wrapper.findAllComponents(GlDisclosureDropdownGroup);
const findGlDisclosureDropdownItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem);
const findInviteMembersTrigger = () => wrapper.findComponent(InviteMembersTrigger);
const findCreateWorkItemModalTrigger = () =>
findGlDisclosureDropdownItems()
.filter((item) => item.props('item').text === 'New epic')
.at(0);
const findCreateWorkItemModal = () => wrapper.findComponent(CreateWorkItemModal);
const createWrapper = ({ provide = {} } = {}) => {
@ -82,12 +86,34 @@ describe('CreateMenu component', () => {
});
describe('create new work item modal', () => {
it('renders the modal', () => {
expect(findCreateWorkItemModal().exists()).toBe(true);
it('renders work item menu item correctly', () => {
expect(findCreateWorkItemModalTrigger().exists()).toBe(true);
});
it('sets `isGroup` to `true`', () => {
it('does not render the modal by default', () => {
expect(findCreateWorkItemModal().exists()).toBe(false);
});
it('shows modal when clicking work item dropdown item', async () => {
findCreateWorkItemModalTrigger().vm.$emit('action');
await nextTick();
expect(findCreateWorkItemModal().exists()).toBe(true);
expect(findCreateWorkItemModal().props('isGroup')).toBe(true);
expect(findCreateWorkItemModal().props('visible')).toBe(true);
expect(findCreateWorkItemModal().props('hideButton')).toBe(true);
});
it('hides modal when hideModal event is emitted', async () => {
findCreateWorkItemModalTrigger().vm.$emit('action');
await nextTick();
expect(findCreateWorkItemModal().exists()).toBe(true);
findCreateWorkItemModal().vm.$emit('hideModal');
await nextTick();
expect(findCreateWorkItemModal().exists()).toBe(false);
});
});

View File

@ -52,7 +52,7 @@ RSpec.describe Issue, feature_category: :team_planning do
let(:type_attributes) { { work_item_type_id: type1.id, correct_work_item_type_id: type2.correct_id } }
it 'does not overwrite any of the provided values' do
expect(issue.work_item_type_id).to eq(type1.id)
expect(issue.attributes['work_item_type_id']).to eq(type1.id)
expect(issue.correct_work_item_type_id).to eq(type2.correct_id)
end
end
@ -90,7 +90,7 @@ RSpec.describe Issue, feature_category: :team_planning do
expect do
issue.update_columns(work_item_type_id: type1.id, correct_work_item_type_id: type2.correct_id)
issue.reload
end.to change { issue.work_item_type_id }.to(type1.id).and(
end.to change { issue.attributes['work_item_type_id'] }.to(type1.id).and(
change { issue.correct_work_item_type_id }.to(type2.correct_id)
)
end
@ -2599,6 +2599,20 @@ RSpec.describe Issue, feature_category: :team_planning do
end
end
describe '#work_item_type_id' do
let_it_be(:work_item_type) { create(:work_item_type, :non_default) }
let_it_be(:issue) { create(:issue, project: reusable_project) }
it 'returns the correct work_item_types.id value even if the value in the column is wrong' do
issue.update_columns(
work_item_type_id: non_existing_record_id,
correct_work_item_type_id: work_item_type.correct_id
)
expect(issue.work_item_type_id).to eq(work_item_type.id)
end
end
describe '#work_item_type_id=', :aggregate_failures do
let_it_be(:type1) do
create(:work_item_type, :non_default).tap do |type|

View File

@ -417,11 +417,11 @@ module TestEnv
Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
Gitlab::DatabaseImporters::WorkItems::RelatedLinksRestrictionsImporter.upsert_restrictions
# We need this temporarily to make sure the app works when work_item_types.id and work_item_types.correct_id
# are different (as in some production apps). We can remove when work_item_types.id values are fixed (1 - 9)
# Updating old_id to simulate an environment that has gone through the process of cleaning
# the issues.work_item_type_id column. old_id is used as a fallback id.
# TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/499911
WorkItems::Type.find_each do |work_item_type|
work_item_type.update!(id: -work_item_type.id, old_id: -work_item_type.id)
work_item_type.update!(old_id: -work_item_type.id)
end
end

View File

@ -1413,10 +1413,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/noop/-/noop-1.0.0.tgz#b1ecb8ae6b2abf9b2e28927e4fbb05b7a1b2704b"
integrity sha512-nOltttik5o2BjBo8LnyeTFzHoLpMY/XcCVOC+lm9ZwU+ivEam8wafacMF0KTbRn1KVrIoHYdo70QnqS+vJiOVw==
"@gitlab/query-language-rust@0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@gitlab/query-language-rust/-/query-language-rust-0.3.0.tgz#41ce042886bd0e87e372b1355963acaec6777ab9"
integrity sha512-zn43UMaHJKjFjerEMZWMqbWkZ05sX0U9F1D235kSY8zQgFk2qWu2rOE5GwJAmCgMd0vdZSHwaSWhzpsc3lX+cw==
"@gitlab/query-language-rust@0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@gitlab/query-language-rust/-/query-language-rust-0.3.1.tgz#eb758c9b21326765a541dc016e8d459ad4f6b8db"
integrity sha512-nXmLZHjEMJwiQbdwrTZiTCKkzLbyG7+XOUK/ARrRZGKHg775EhVCY5sj9i6eGGXwUBxnJ0Os2oH12Nc1/s47cQ==
"@gitlab/stylelint-config@6.2.2":
version "6.2.2"