diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-26 15:12:12 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-26 15:12:12 +0300 |
commit | ae436dd0c51ac75aadcc811c750b8625880919b8 (patch) | |
tree | 79394dd156361448d0dbfbb0c3f24f4408d590e7 | |
parent | 79cd3f3a38777b1436107bd1e3205f593e1a3bd1 (diff) |
Add latest changes from gitlab-org/gitlab@master
34 files changed, 272 insertions, 88 deletions
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue b/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue index de087a8fcc5..e15f204dc6e 100644 --- a/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue +++ b/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue @@ -3,6 +3,7 @@ import { GlTableLite, GlToggle } from '@gitlab/ui'; import { GENERIC_PACKAGE_FORMAT, MAVEN_PACKAGE_FORMAT, + NUGET_PACKAGE_FORMAT, PACKAGE_FORMATS_TABLE_HEADER, PACKAGE_SETTINGS_HEADER, PACKAGE_SETTINGS_DESCRIPTION, @@ -91,6 +92,18 @@ export default { }, testid: 'generic-settings', }, + { + id: 'nuget-duplicated-settings-regex-input', + format: NUGET_PACKAGE_FORMAT, + duplicatesAllowed: this.packageSettings.nugetDuplicatesAllowed, + duplicateExceptionRegex: this.packageSettings.nugetDuplicateExceptionRegex, + duplicateExceptionRegexError: this.errors.nugetDuplicateExceptionRegex, + modelNames: { + allowed: 'nugetDuplicatesAllowed', + exception: 'nugetDuplicateExceptionRegex', + }, + testid: 'nuget-settings', + }, ]; }, }, diff --git a/app/assets/javascripts/packages_and_registries/settings/group/constants.js b/app/assets/javascripts/packages_and_registries/settings/group/constants.js index bfb57e3ac1c..54b337a4296 100644 --- a/app/assets/javascripts/packages_and_registries/settings/group/constants.js +++ b/app/assets/javascripts/packages_and_registries/settings/group/constants.js @@ -10,6 +10,7 @@ export const MAVEN_PACKAGE_FORMAT = s__('PackageRegistry|Maven'); export const NPM_PACKAGE_FORMAT = s__('PackageRegistry|npm'); export const PYPI_PACKAGE_FORMAT = s__('PackageRegistry|PyPI'); export const GENERIC_PACKAGE_FORMAT = s__('PackageRegistry|Generic'); +export const NUGET_PACKAGE_FORMAT = s__('PackageRegistry|NuGet'); export const DUPLICATES_TOGGLE_LABEL = s__('PackageRegistry|Allow duplicates'); export const DUPLICATES_SETTING_EXCEPTION_TITLE = __('Exceptions'); diff --git a/app/assets/javascripts/packages_and_registries/settings/group/graphql/fragments/package_settings_fields.fragment.graphql b/app/assets/javascripts/packages_and_registries/settings/group/graphql/fragments/package_settings_fields.fragment.graphql index 267e40263f2..0e36f48e9a6 100644 --- a/app/assets/javascripts/packages_and_registries/settings/group/graphql/fragments/package_settings_fields.fragment.graphql +++ b/app/assets/javascripts/packages_and_registries/settings/group/graphql/fragments/package_settings_fields.fragment.graphql @@ -3,6 +3,8 @@ fragment PackageSettingsFields on PackageSettings { mavenDuplicateExceptionRegex genericDuplicatesAllowed genericDuplicateExceptionRegex + nugetDuplicatesAllowed + nugetDuplicateExceptionRegex mavenPackageRequestsForwarding lockMavenPackageRequestsForwarding mavenPackageRequestsForwardingLocked diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue index ff48b8d92cc..1e1ec185ee1 100644 --- a/app/assets/javascripts/super_sidebar/components/nav_item.vue +++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue @@ -220,7 +220,6 @@ export default { class="gl-relative gl-display-flex gl-align-items-center gl-min-h-7 gl-gap-3 gl-mb-1 gl-py-2 gl-text-black-normal! gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-text-decoration-none! gl-focus--focus show-on-focus-or-hover--control hide-on-focus-or-hover--control" :class="computedLinkClasses" data-testid="nav-item-link" - data-qa-selector="nav_item_link" > <div :class="[isActive ? 'gl-opacity-10' : 'gl-opacity-0']" diff --git a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue index 4a3c3cf0053..73c030b23dc 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue @@ -190,7 +190,7 @@ export default { renderMarkdown(markdown) { const url = setUrlParams( { render_quick_actions: this.supportsQuickActions }, - joinPaths(gon.relative_url_root || window.location.origin, this.renderMarkdownPath), + joinPaths(window.location.origin, gon.relative_url_root, this.renderMarkdownPath), ); return axios.post(url, { text: markdown }).then(({ data }) => data.body); }, diff --git a/app/services/import/validate_remote_git_endpoint_service.rb b/app/services/import/validate_remote_git_endpoint_service.rb index a994072c4aa..8297757997f 100644 --- a/app/services/import/validate_remote_git_endpoint_service.rb +++ b/app/services/import/validate_remote_git_endpoint_service.rb @@ -13,6 +13,8 @@ module Import GIT_PROTOCOL_PKT_LEN = 4 GIT_MINIMUM_RESPONSE_LENGTH = GIT_PROTOCOL_PKT_LEN + GIT_EXPECTED_FIRST_PACKET_LINE.length EXPECTED_CONTENT_TYPE = "application/x-#{GIT_SERVICE_NAME}-advertisement" + INVALID_BODY_MESSAGE = 'Not a git repository: Invalid response body' + INVALID_CONTENT_TYPE_MESSAGE = 'Not a git repository: Invalid content-type' def initialize(params) @params = params @@ -30,32 +32,35 @@ module Import uri.fragment = nil url = Gitlab::Utils.append_path(uri.to_s, "/info/refs?service=#{GIT_SERVICE_NAME}") - response_body = '' - result = nil - Gitlab::HTTP.try_get(url, stream_body: true, follow_redirects: false, basic_auth: auth) do |fragment| - response_body += fragment - next if response_body.length < GIT_MINIMUM_RESPONSE_LENGTH - - result = if status_code_is_valid(fragment) && content_type_is_valid(fragment) && response_body_is_valid(response_body) - :success - else - :error - end - - # We are interested only in the first chunks of the response - # So we're using stream_body: true and breaking when receive enough body - break - end + response, response_body = http_get_and_extract_first_chunks(url) - if result == :success - ServiceResponse.success - else - ServiceResponse.error(message: "#{uri} is not a valid HTTP Git repository") - end + validate(uri, response, response_body) + rescue *Gitlab::HTTP::HTTP_ERRORS => err + error_result("HTTP #{err.class.name.underscore} error: #{err.message}") + rescue StandardError => err + ServiceResponse.error( + message: "Internal #{err.class.name.underscore} error: #{err.message}", + reason: 500 + ) end private + def http_get_and_extract_first_chunks(url) + # We are interested only in the first chunks of the response + # So we're using stream_body: true and breaking when receive enough body + response = nil + response_body = '' + + Gitlab::HTTP.get(url, stream_body: true, follow_redirects: false, basic_auth: auth) do |response_chunk| + response = response_chunk + response_body += response_chunk + break if GIT_MINIMUM_RESPONSE_LENGTH <= response_body.length + end + + [response, response_body] + end + def auth unless @params[:user].to_s.blank? { @@ -65,15 +70,37 @@ module Import end end - def status_code_is_valid(fragment) - fragment.http_response.code == '200' + def validate(uri, response, response_body) + return status_code_error(uri, response) unless status_code_is_valid?(response) + return error_result(INVALID_CONTENT_TYPE_MESSAGE) unless content_type_is_valid?(response) + return error_result(INVALID_BODY_MESSAGE) unless response_body_is_valid?(response_body) + + ServiceResponse.success + end + + def status_code_error(uri, response) + http_code = response.http_response.code.to_i + message = response.http_response.message || Rack::Utils::HTTP_STATUS_CODES[http_code] + + error_result( + "#{uri} endpoint error: #{http_code}#{message.presence&.prepend(' ')}", + http_code + ) + end + + def error_result(message, reason = nil) + ServiceResponse.error(message: message, reason: reason) + end + + def status_code_is_valid?(response) + response.http_response.code == '200' end - def content_type_is_valid(fragment) - fragment.http_response['content-type'] == EXPECTED_CONTENT_TYPE + def content_type_is_valid?(response) + response.http_response['content-type'] == EXPECTED_CONTENT_TYPE end - def response_body_is_valid(response_body) + def response_body_is_valid?(response_body) response_body.match?(GIT_BODY_MESSAGE_REGEXP) end end diff --git a/doc/architecture/blueprints/cells/impacted_features/personal-access-tokens.md b/doc/architecture/blueprints/cells/impacted_features/personal-access-tokens.md index 3aca9f1e116..a493a1c4395 100644 --- a/doc/architecture/blueprints/cells/impacted_features/personal-access-tokens.md +++ b/doc/architecture/blueprints/cells/impacted_features/personal-access-tokens.md @@ -17,13 +17,37 @@ we can document the reasons for not choosing this approach. ## 1. Definition -Personal Access Tokens associated with a User are a way for Users to interact with the API of GitLab to perform operations. -Personal Access Tokens today are scoped to the User, and can access all Groups that a User has access to. +Personal Access Tokens (PATs) associated with a User are a way for Users to interact with the API of GitLab to perform operations. +PATs today are scoped to the User, and can access all Groups that a User has access to. ## 2. Data flow ## 3. Proposal +### 3.1. Organization-scoped PATs + +Pros: + +- Can be managed entirely from Rails application. +- Increased security. PAT is limited only to Organization. + +Cons: + +- Different PAT needed for different Organizations. +- Cannot tell at a glance if PAT will apply to a certain Project/Namespace. + +### 3.2. Cluster-wide PATs + +Pros: + +- User does not have to worry about which scope the PAT applies to. + +Cons: + +- User has to worry about wide-ranging scope of PAT (e.g. separation of personal items versus work items). +- Organization cannot limit scope of PAT to only their Organization. +- Increases complexity. All cluster-wide data likely will be moved to a separate [data access layer](../../cells/index.md#1-data-access-layer). + ## 4. Evaluation ## 4.1. Pros diff --git a/doc/architecture/blueprints/cells/index.md b/doc/architecture/blueprints/cells/index.md index 1366d308487..c9a03830a4a 100644 --- a/doc/architecture/blueprints/cells/index.md +++ b/doc/architecture/blueprints/cells/index.md @@ -338,6 +338,7 @@ Below is a list of known affected features with preliminary proposed solutions. - [Cells: Global Search](impacted_features/global-search.md) - [Cells: GraphQL](impacted_features/graphql.md) - [Cells: Organizations](impacted_features/organizations.md) +- [Cells: Personal Access Tokens](impacted_features/personal-access-tokens.md) - [Cells: Personal Namespaces](impacted_features/personal-namespaces.md) - [Cells: Secrets](impacted_features/secrets.md) - [Cells: Snippets](impacted_features/snippets.md) @@ -354,7 +355,6 @@ The following list of impacted features only represents placeholders that still - [Cells: Group Transfer](impacted_features/group-transfer.md) - [Cells: Issues](impacted_features/issues.md) - [Cells: Merge Requests](impacted_features/merge-requests.md) -- [Cells: Personal Access Tokens](impacted_features/personal-access-tokens.md) - [Cells: Project Transfer](impacted_features/project-transfer.md) - [Cells: Router Endpoints Classification](impacted_features/router-endpoints-classification.md) - [Cells: Schema changes (Postgres and Elasticsearch migrations)](impacted_features/schema-changes.md) diff --git a/doc/development/gems.md b/doc/development/gems.md index c9672483e8d..54d6e6dc30d 100644 --- a/doc/development/gems.md +++ b/doc/development/gems.md @@ -254,13 +254,12 @@ The project for a new Gem should always be created in [`gitlab-org/ruby/gems` na 1. Create a project in the [`gitlab-org/ruby/gems` group](https://gitlab.com/gitlab-org/ruby/gems/) (or in a subgroup of it): 1. Follow the [instructions for new projects](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#creating-a-new-project). 1. Follow the instructions for setting up a [CI/CD configuration](https://about.gitlab.com/handbook/engineering/gitlab-repositories/#cicd-configuration). - 1. Use the [shared CI/CD config](https://gitlab.com/gitlab-org/quality/pipeline-common/-/blob/master/ci/gem-release.yml) + 1. Use the [gem-release CI component](https://gitlab.com/gitlab-org/quality/pipeline-common/-/tree/master/gem-release) to release and publish new gem versions by adding the following to their `.gitlab-ci.yml`: ```yaml include: - - project: 'gitlab-org/quality/pipeline-common' - file: '/ci/gem-release.yml' + - component: gitlab.com/gitlab-org/quality/pipeline-common/gem-release@<REPLACE WITH LATEST TAG FROM https://gitlab.com/gitlab-org/quality/pipeline-common/-/releases> ``` This job will handle building and publishing the gem (it uses a `gitlab_rubygems` Rubygems.org diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md index 91fa91e3a6a..c46b89f7620 100644 --- a/doc/gitlab-basics/start-using-git.md +++ b/doc/gitlab-basics/start-using-git.md @@ -117,8 +117,10 @@ This connection requires you to add credentials. You can either use SSH or HTTPS Clone with SSH when you want to authenticate only one time. 1. Authenticate with GitLab by following the instructions in the [SSH documentation](../user/ssh.md). -1. Go to your project's landing page and select **Clone**. Copy the URL for **Clone with SSH**. -1. Open a terminal and go to the directory where you want to clone the files. Git automatically creates a folder with the repository name and downloads the files there. +1. On the left sidebar, select **Search or go to** and find the project you want to clone. +1. On the right-hand side of the page, select **Clone**, then copy the URL for **Clone with SSH**. +1. Open a terminal and go to the directory where you want to clone the files. + Git automatically creates a folder with the repository name and downloads the files there. 1. Run this command: ```shell @@ -139,7 +141,8 @@ You can also Clone with HTTPS when you want to authenticate each time you perform an operation between your computer and GitLab. -1. Go to your project's landing page and select **Clone**. Copy the URL for **Clone with HTTPS**. +1. On the left sidebar, select **Search or go to** and find the project you want to clone. +1. On the right-hand side of the page, select **Clone**, then copy the URL for **Clone with HTTPS**. 1. Open a terminal and go to the directory where you want to clone the files. 1. Run the following command. Git automatically creates a folder with the repository name and downloads the files there. diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md index 917508e8715..25fa1f5cbaf 100644 --- a/doc/user/application_security/index.md +++ b/doc/user/application_security/index.md @@ -270,9 +270,7 @@ In the Free tier, the reports above aren't parsed by GitLab. As a result, the wi A merge request contains a security widget which displays a summary of the _new_ results. New results are determined by comparing the findings of the merge request against the findings of the most recent completed pipeline (`success`, `failed`, `canceled` or `skipped`) for the commit when the feature branch was created from the target branch. -If the `check_multiple_pipelines_for_security_reports` [feature flag is enabled](../../administration/feature_flags.md), GitLab checks the last 10 pipelines for the commit when the feature was created from the target branch to find one with security reports to use in comparison logic. - -If security scans have not run for the completed pipeline in the target branch when the feature branch was created, there is no base for comparison. The vulnerabilities from the merge request findings are listed as new in the merge request security widget. We recommend you run a scan of the `default` (target) branch before enabling feature branch scans for your developers. +GitLab checks the last 10 pipelines for the commit when the feature was created from the target branch to find one with security reports to use in comparison logic. If security scans have not run for the last 10 completed pipelines in the target branch when the feature branch was created, there is no base for comparison. The vulnerabilities from the merge request findings are listed as new in the merge request security widget. We recommend you run a scan of the `default` (target) branch before enabling feature branch scans for your developers. The merge request security widget displays only a subset of the vulnerabilities in the generated JSON artifact because it contains both new and existing findings. diff --git a/doc/user/group/access_and_permissions.md b/doc/user/group/access_and_permissions.md index 966945b6b12..4396450eb85 100644 --- a/doc/user/group/access_and_permissions.md +++ b/doc/user/group/access_and_permissions.md @@ -118,7 +118,7 @@ To allow runner downloading, add the [outbound runner CIDR ranges](../gitlab_com > - Support for restricting access to projects in the group [added](https://gitlab.com/gitlab-org/gitlab/-/issues/14004) in GitLab 14.1.2. > - Support for restricting group memberships to groups with a subset of the allowed email domains [added](https://gitlab.com/gitlab-org/gitlab/-/issues/354791) in GitLab 15.1.1 -You can prevent users with email addresses in specific domains from being added to a group and its projects. +You can prevent users with email addresses in specific domains from being added to a group and its projects. You can define an email domain allowlist at the top-level namespace only. Subgroups do not offer the ability to define an alternative allowlist. To restrict group access by domain: diff --git a/doc/user/img/snippet_clone_button_v13_0.png b/doc/user/img/snippet_clone_button_v13_0.png Binary files differdeleted file mode 100644 index bf681e7349b..00000000000 --- a/doc/user/img/snippet_clone_button_v13_0.png +++ /dev/null diff --git a/doc/user/img/snippet_intro_v13_11.png b/doc/user/img/snippet_intro_v13_11.png Binary files differdeleted file mode 100644 index 4b6818341b7..00000000000 --- a/doc/user/img/snippet_intro_v13_11.png +++ /dev/null diff --git a/doc/user/img/snippet_sample_v16_6.png b/doc/user/img/snippet_sample_v16_6.png Binary files differnew file mode 100644 index 00000000000..035947a2b82 --- /dev/null +++ b/doc/user/img/snippet_sample_v16_6.png diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md index 9c7a8803961..8db79dc6c5f 100644 --- a/doc/user/packages/nuget_repository/index.md +++ b/doc/user/packages/nuget_repository/index.md @@ -437,7 +437,16 @@ the existing package is overwritten. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/293748) in GitLab 16.3 [with a flag](../../../administration/feature_flags.md) named `nuget_duplicates_option`. Disabled by default. > - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/419078) in GitLab 16.6. Feature flag `nuget_duplicates_option` removed. -To prevent users from publishing duplicate NuGet packages, you can use the [GraphQl API](../../../api/graphql/reference/index.md#packagesettings). +To prevent users from publishing duplicate NuGet packages, you can use the [GraphQl API](../../../api/graphql/reference/index.md#packagesettings) or the UI. + +In the UI: + +1. On the left sidebar, select **Search or go to** and find your group. +1. Select **Settings > Packages and registries**. +1. In the **NuGet** row of the **Duplicate packages** table, turn off the **Allow duplicates** toggle. +1. Optional. In the **Exceptions** text box, enter a regular expression that matches the names and versions of packages to allow. + +Your changes are automatically saved. WARNING: If the .nuspec file isn't located in the root of the package, the package might diff --git a/doc/user/snippets.md b/doc/user/snippets.md index dbcc90c26df..fe782227701 100644 --- a/doc/user/snippets.md +++ b/doc/user/snippets.md @@ -17,7 +17,7 @@ and you can maintain your snippets with the [snippets API](../api/snippets.md). You can create and manage your snippets through the GitLab user interface, or by using the [GitLab Workflow VS Code extension](project/repository/vscode.md). -![Example of snippet](img/snippet_intro_v13_11.png) +![Example of a snippet](img/snippet_sample_v16_6.png) GitLab provides two types of snippets: @@ -168,10 +168,11 @@ To delete a file from your snippet through the GitLab UI: ## Clone snippets To ensure you receive updates, clone the snippet instead of copying it locally. Cloning -maintains the snippet's connection with the repository. Select **Clone** on a snippet -to display the URLs to clone with SSH or HTTPS: +maintains the snippet's connection with the repository. -![Clone snippet](img/snippet_clone_button_v13_0.png) +To clone a snippet: + +- Select **Clone**, then copy the URL to clone with SSH or HTTPS. You can commit changes to a cloned snippet, and push the changes to GitLab. diff --git a/package.json b/package.json index b605a4ffbbd..90c4f6f096e 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@gitlab/favicon-overlay": "2.0.0", "@gitlab/fonts": "^1.3.0", "@gitlab/svgs": "3.67.0", - "@gitlab/ui": "66.36.1", + "@gitlab/ui": "66.37.0", "@gitlab/visual-review-tools": "1.7.3", "@gitlab/web-ide": "0.0.1-dev-20231004090414", "@mattiasbuelens/web-streams-adapter": "^0.1.0", @@ -167,7 +167,7 @@ "marked-bidi": "^1.0.3", "mathjax": "3", "mdurl": "^1.0.1", - "mermaid": "10.5.0", + "mermaid": "10.6.0", "micromatch": "^4.0.5", "minimatch": "^3.0.4", "monaco-editor": "^0.30.1", diff --git a/qa/qa/page/admin/menu.rb b/qa/qa/page/admin/menu.rb index 563650bbdf2..fea44a0ebca 100644 --- a/qa/qa/page/admin/menu.rb +++ b/qa/qa/page/admin/menu.rb @@ -21,7 +21,7 @@ module QA end def go_to_applications - click_element(:nav_item_link, submenu_item: 'Applications') + click_element('nav-item-link', submenu_item: 'Applications') end private diff --git a/qa/qa/page/group/sub_menus/main.rb b/qa/qa/page/group/sub_menus/main.rb index a9f2e11c532..b7b694335f9 100644 --- a/qa/qa/page/group/sub_menus/main.rb +++ b/qa/qa/page/group/sub_menus/main.rb @@ -16,7 +16,7 @@ module QA end def go_to_group_overview - click_element(:nav_item_link, submenu_item: 'group-overview') + click_element('nav-item-link', submenu_item: 'group-overview') end end end diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index 9900343600a..73d48c6fcbf 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -78,23 +78,23 @@ module QA end def go_to_projects - click_element(:nav_item_link, submenu_item: 'Projects') + click_element('nav-item-link', submenu_item: 'Projects') end def go_to_groups # This needs to be fixed in the tests themselves. Fullfillment tests try to go to groups view from the # group. Instead of having a global hack, explicit test should navigate to correct view first. # see: https://gitlab.com/gitlab-org/gitlab/-/issues/403589#note_1383040061 - go_to_your_work unless has_element?(:nav_item_link, submenu_item: 'Groups', wait: 0) - click_element(:nav_item_link, submenu_item: 'Groups') + go_to_your_work unless has_element?('nav-item-link', submenu_item: 'Groups', wait: 0) + click_element('nav-item-link', submenu_item: 'Groups') end def go_to_snippets - click_element(:nav_item_link, submenu_item: 'Snippets') + click_element('nav-item-link', submenu_item: 'Snippets') end def go_to_workspaces - click_element(:nav_item_link, submenu_item: 'Workspaces') + click_element('nav-item-link', submenu_item: 'Workspaces') end def go_to_menu_dropdown_option(option_name) diff --git a/qa/qa/page/profile/menu.rb b/qa/qa/page/profile/menu.rb index d3b08944914..37128571b7f 100644 --- a/qa/qa/page/profile/menu.rb +++ b/qa/qa/page/profile/menu.rb @@ -7,23 +7,23 @@ module QA include SubMenus::CreateNewMenu def click_ssh_keys - click_element(:nav_item_link, submenu_item: 'SSH Keys') + click_element('nav-item-link', submenu_item: 'SSH Keys') end def click_account - click_element(:nav_item_link, submenu_item: 'Account') + click_element('nav-item-link', submenu_item: 'Account') end def click_emails - click_element(:nav_item_link, submenu_item: 'Emails') + click_element('nav-item-link', submenu_item: 'Emails') end def click_password - click_element(:nav_item_link, submenu_item: 'Password') + click_element('nav-item-link', submenu_item: 'Password') end def click_access_tokens - click_element(:nav_item_link, submenu_item: 'Access Tokens') + click_element('nav-item-link', submenu_item: 'Access Tokens') end end end diff --git a/qa/qa/page/project/sub_menus/main.rb b/qa/qa/page/project/sub_menus/main.rb index a147f28eef0..193d58a2603 100644 --- a/qa/qa/page/project/sub_menus/main.rb +++ b/qa/qa/page/project/sub_menus/main.rb @@ -8,7 +8,7 @@ module QA extend QA::Page::PageConcern def click_project - click_element(:nav_item_link, submenu_item: 'project-overview') + click_element('nav-item-link', submenu_item: 'project-overview') end end end diff --git a/qa/qa/page/sub_menus/common.rb b/qa/qa/page/sub_menus/common.rb index dc878674877..4cfd4f04cbe 100644 --- a/qa/qa/page/sub_menus/common.rb +++ b/qa/qa/page/sub_menus/common.rb @@ -46,7 +46,7 @@ module QA end within_element(:menu_section, section_name: parent_menu_name) do - click_element(:nav_item_link, submenu_item: sub_menu) + click_element('nav-item-link', submenu_item: sub_menu) end end end diff --git a/qa/qa/page/sub_menus/main.rb b/qa/qa/page/sub_menus/main.rb index c8d7d27d930..25732672dd4 100644 --- a/qa/qa/page/sub_menus/main.rb +++ b/qa/qa/page/sub_menus/main.rb @@ -7,11 +7,11 @@ module QA extend QA::Page::PageConcern def go_to_issues - click_element(:nav_item_link, submenu_item: 'Issues') + click_element('nav-item-link', submenu_item: 'Issues') end def go_to_merge_requests - click_element(:nav_item_link, submenu_item: 'Merge requests') + click_element('nav-item-link', submenu_item: 'Merge requests') end end end diff --git a/qa/qa/page/user/show.rb b/qa/qa/page/user/show.rb index 62e08dc8c63..aba79256340 100644 --- a/qa/qa/page/user/show.rb +++ b/qa/qa/page/user/show.rb @@ -21,7 +21,7 @@ module QA end def click_following_tab - click_element(:nav_item_link, submenu_item: 'Following') + click_element('nav-item-link', submenu_item: 'Following') end def click_user_link(username) diff --git a/spec/features/projects/wikis_spec.rb b/spec/features/projects/wikis_spec.rb index 63714954c0c..5d950da6674 100644 --- a/spec/features/projects/wikis_spec.rb +++ b/spec/features/projects/wikis_spec.rb @@ -3,7 +3,7 @@ require "spec_helper" RSpec.describe 'Project wikis', :js, feature_category: :wiki do - let_it_be(:user) { create(:user, :no_super_sidebar) } + let_it_be(:user) { create(:user) } let(:wiki) { create(:project_wiki, user: user, project: project) } let(:project) { create(:project, namespace: user.namespace, creator: user) } diff --git a/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js index 49e76cfbae0..bf7abd353b6 100644 --- a/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js +++ b/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js @@ -60,13 +60,17 @@ describe('Packages Settings', () => { const findDescription = () => wrapper.findByTestId('description'); const findMavenSettings = () => wrapper.findByTestId('maven-settings'); const findGenericSettings = () => wrapper.findByTestId('generic-settings'); + const findNugetSettings = () => wrapper.findByTestId('nuget-settings'); const findMavenDuplicatedSettingsToggle = () => findMavenSettings().findComponent(GlToggle); const findGenericDuplicatedSettingsToggle = () => findGenericSettings().findComponent(GlToggle); + const findNugetDuplicatedSettingsToggle = () => findNugetSettings().findComponent(GlToggle); const findMavenDuplicatedSettingsExceptionsInput = () => findMavenSettings().findComponent(ExceptionsInput); const findGenericDuplicatedSettingsExceptionsInput = () => findGenericSettings().findComponent(ExceptionsInput); + const findNugetDuplicatedSettingsExceptionsInput = () => + findNugetSettings().findComponent(ExceptionsInput); const fillApolloCache = () => { apolloProvider.defaultClient.cache.writeQuery({ @@ -208,6 +212,58 @@ describe('Packages Settings', () => { }); }); + describe('nuget settings', () => { + it('exists', () => { + mountComponent({ mountFn: mountExtended }); + + expect(findNugetSettings().find('td').text()).toBe('NuGet'); + }); + + it('renders toggle', () => { + mountComponent({ mountFn: mountExtended }); + + const { nugetDuplicatesAllowed } = packageSettings; + + expect(findNugetDuplicatedSettingsToggle().exists()).toBe(true); + expect(findNugetDuplicatedSettingsToggle().props()).toMatchObject({ + label: DUPLICATES_TOGGLE_LABEL, + value: nugetDuplicatesAllowed, + disabled: false, + labelPosition: 'hidden', + }); + }); + + it('renders ExceptionsInput and assigns duplication allowness and exception props', () => { + mountComponent({ mountFn: mountExtended }); + + const { nugetDuplicatesAllowed, nugetDuplicateExceptionRegex } = packageSettings; + + expect(findNugetDuplicatedSettingsExceptionsInput().props()).toMatchObject({ + duplicatesAllowed: nugetDuplicatesAllowed, + duplicateExceptionRegex: nugetDuplicateExceptionRegex, + duplicateExceptionRegexError: '', + loading: false, + name: 'nugetDuplicateExceptionRegex', + id: 'nuget-duplicated-settings-regex-input', + }); + }); + + it('on update event calls the mutation', () => { + const mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock()); + mountComponent({ mountFn: mountExtended, mutationResolver }); + + fillApolloCache(); + + findNugetDuplicatedSettingsExceptionsInput().vm.$emit('update', { + nugetDuplicateExceptionRegex: ')', + }); + + expect(mutationResolver).toHaveBeenCalledWith({ + input: { nugetDuplicateExceptionRegex: ')', namespacePath: 'foo_group_path' }, + }); + }); + }); + describe('settings update', () => { describe('success state', () => { beforeEach(() => { diff --git a/spec/frontend/packages_and_registries/settings/group/mock_data.js b/spec/frontend/packages_and_registries/settings/group/mock_data.js index 1ca9dc6daeb..c68b0b8e23f 100644 --- a/spec/frontend/packages_and_registries/settings/group/mock_data.js +++ b/spec/frontend/packages_and_registries/settings/group/mock_data.js @@ -3,6 +3,8 @@ const packageDuplicateSettings = { mavenDuplicateExceptionRegex: '', genericDuplicatesAllowed: true, genericDuplicateExceptionRegex: '', + nugetDuplicatesAllowed: true, + nugetDuplicateExceptionRegex: '', }; export const packageForwardingSettings = { diff --git a/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js index b4c90fe49d1..edb11bd581b 100644 --- a/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js +++ b/spec/frontend/vue_shared/components/markdown/markdown_editor_spec.js @@ -137,7 +137,26 @@ describe('vue_shared/component/markdown/markdown_editor', () => { await enableContentEditor(); expect(mock.history.post).toHaveLength(1); - expect(mock.history.post[0].url).toContain(`render_quick_actions=true`); + expect(mock.history.post[0].url).toBe( + `${window.location.origin}/api/markdown?render_quick_actions=true`, + ); + }); + + describe('if gitlab is installed under a relative url', () => { + beforeEach(() => { + window.gon = { relative_url_root: '/gitlab' }; + }); + + it('passes render_quick_actions param to renderMarkdownPath if quick actions are enabled', async () => { + buildWrapper({ propsData: { supportsQuickActions: true } }); + + await enableContentEditor(); + + expect(mock.history.post).toHaveLength(1); + expect(mock.history.post[0].url).toBe( + `${window.location.origin}/gitlab/api/markdown?render_quick_actions=true`, + ); + }); }); it('does not pass render_quick_actions param to renderMarkdownPath if quick actions are disabled', async () => { @@ -146,7 +165,9 @@ describe('vue_shared/component/markdown/markdown_editor', () => { await enableContentEditor(); expect(mock.history.post).toHaveLength(1); - expect(mock.history.post[0].url).toContain(`render_quick_actions=false`); + expect(mock.history.post[0].url).toBe( + `${window.location.origin}/api/markdown?render_quick_actions=false`, + ); }); it('enables content editor switcher when contentEditorEnabled prop is true', () => { diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index ba3103fd52f..c6e1605010e 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1400,13 +1400,14 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and it 'disallows creating a project with an import_url that is not reachable' do url = 'http://example.com' endpoint_url = "#{url}/info/refs?service=git-upload-pack" - stub_full_request(endpoint_url, method: :get).to_return({ status: 301, body: '', headers: nil }) + error_response = { status: 301, body: '', headers: nil } + stub_full_request(endpoint_url, method: :get).to_return(error_response) project_params = { import_url: url, path: 'path-project-Foo', name: 'Foo Project' } expect { post api(path, user), params: project_params }.not_to change { Project.count } expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(json_response['message']).to eq("#{url} is not a valid HTTP Git repository") + expect(json_response['message']).to eq("#{url} endpoint error: #{error_response[:status]}") end it 'creates a project with an import_url that is valid' do diff --git a/spec/services/import/validate_remote_git_endpoint_service_spec.rb b/spec/services/import/validate_remote_git_endpoint_service_spec.rb index 1d2b3975832..15e80f2c85d 100644 --- a/spec/services/import/validate_remote_git_endpoint_service_spec.rb +++ b/spec/services/import/validate_remote_git_endpoint_service_spec.rb @@ -7,7 +7,9 @@ RSpec.describe Import::ValidateRemoteGitEndpointService, feature_category: :impo let_it_be(:base_url) { 'http://demo.host/path' } let_it_be(:endpoint_url) { "#{base_url}/info/refs?service=git-upload-pack" } - let_it_be(:error_message) { "#{base_url} is not a valid HTTP Git repository" } + let_it_be(:endpoint_error_message) { "#{base_url} endpoint error:" } + let_it_be(:body_error_message) { described_class::INVALID_BODY_MESSAGE } + let_it_be(:content_type_error_message) { described_class::INVALID_CONTENT_TYPE_MESSAGE } describe '#execute' do let(:valid_response) do @@ -70,13 +72,14 @@ RSpec.describe Import::ValidateRemoteGitEndpointService, feature_category: :impo end it 'reports error when status code is not 200' do - stub_full_request(endpoint_url, method: :get).to_return(valid_response.merge({ status: 301 })) + error_response = { status: 401 } + stub_full_request(endpoint_url, method: :get).to_return(error_response) result = subject.execute expect(result).to be_a(ServiceResponse) expect(result.error?).to be(true) - expect(result.message).to eq(error_message) + expect(result.message).to eq("#{endpoint_error_message} #{error_response[:status]}") end it 'reports error when invalid URL is provided' do @@ -94,27 +97,49 @@ RSpec.describe Import::ValidateRemoteGitEndpointService, feature_category: :impo expect(result).to be_a(ServiceResponse) expect(result.error?).to be(true) - expect(result.message).to eq(error_message) + expect(result.message).to eq(content_type_error_message) end - it 'reports error when body is in invalid format' do + it 'reports error when body is too short' do stub_full_request(endpoint_url, method: :get).to_return(valid_response.merge({ body: 'invalid content' })) result = subject.execute expect(result).to be_a(ServiceResponse) expect(result.error?).to be(true) - expect(result.message).to eq(error_message) + expect(result.message).to eq(body_error_message) + end + + it 'reports error when body is in invalid format' do + stub_full_request(endpoint_url, method: :get).to_return(valid_response.merge({ body: 'invalid long content with no git respons whatshowever' })) + + result = subject.execute + + expect(result).to be_a(ServiceResponse) + expect(result.error?).to be(true) + expect(result.message).to eq(body_error_message) + end + + it 'reports error when http exceptions are raised' do + err = SocketError.new('dummy message') + stub_full_request(endpoint_url, method: :get).to_raise(err) + + result = subject.execute + + expect(result).to be_a(ServiceResponse) + expect(result.error?).to be(true) + expect(result.message).to eq("HTTP #{err.class.name.underscore} error: #{err.message}") end - it 'reports error when exception is raised' do - stub_full_request(endpoint_url, method: :get).to_raise(SocketError.new('dummy message')) + it 'reports error when other exceptions are raised' do + err = StandardError.new('internal dummy message') + stub_full_request(endpoint_url, method: :get).to_raise(err) result = subject.execute expect(result).to be_a(ServiceResponse) expect(result.error?).to be(true) - expect(result.message).to eq(error_message) + expect(result.message).to eq("Internal #{err.class.name.underscore} error: #{err.message}") end end diff --git a/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb index 254682e1a3a..1d056592816 100644 --- a/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb +++ b/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb @@ -264,7 +264,10 @@ RSpec.shared_examples 'User views a wiki page' do it 'opens a default wiki page', :js do visit wiki.container.web_url - find('.shortcuts-wiki').click + within_testid('super-sidebar') do + click_button 'Plan' + click_link 'Wiki' + end wait_for_svg_to_be_loaded diff --git a/yarn.lock b/yarn.lock index dda9f763eee..3c5dad0346c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1274,10 +1274,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.67.0.tgz#72dc27e6b611acf6bdae2d7074fc413f24da6f1f" integrity sha512-N2otHyWJqnUpJcP/lz//KdAD6VV6za7zv7QQqpD9v/p/dzCneyEc4t52g09ERHV71kv9/pWOVPqqwk9eQ2iz9A== -"@gitlab/ui@66.36.1": - version "66.36.1" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-66.36.1.tgz#92a3ae8846066f55ec1ee0271055040f13da9c38" - integrity sha512-2qT2+k7VXyNJbkQblhRUFI1LCey9vn+11efF5snIYHP/cuFmo+OarogYt0fhDnR9gONNMjKBk6CLyvb2rGM8sA== +"@gitlab/ui@66.37.0": + version "66.37.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-66.37.0.tgz#0b785b5f4380b37fb4021246a102c7eca2e0bab5" + integrity sha512-yTyV9Ht5YPtofRHLfOtab08kNFG0Z+VK5/ZtVur/HmgZSMAZv20FfPAo3VqYaieVRYAkTgtLlxpXRO9S2pMpHg== dependencies: "@floating-ui/dom" "1.2.9" bootstrap-vue "2.23.1" @@ -9477,10 +9477,10 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -mermaid@10.5.0: - version "10.5.0" - resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.5.0.tgz#e90512a65b5c6e29bd86cd04ce45aa31da2be76d" - integrity sha512-9l0o1uUod78D3/FVYPGSsgV+Z0tSnzLBDiC9rVzvelPxuO80HbN1oDr9ofpPETQy9XpypPQa26fr09VzEPfvWA== +mermaid@10.6.0: + version "10.6.0" + resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-10.6.0.tgz#151af64fb7c6cf1f8a5c403c53c6151832268b87" + integrity sha512-Hcti+Q2NiWnb2ZCijSX89Bn2i7TCUwosBdIn/d+u63Sz7y40XU6EKMctT4UX4qZuZGfKGZpfOeim2/KTrdR7aQ== dependencies: "@braintree/sanitize-url" "^6.0.1" "@types/d3-scale" "^4.0.3" |