diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-24 12:09:10 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-24 12:09:10 +0300 |
commit | bd818d0618b0c1316c23f47f9ae41449e5d41fa8 (patch) | |
tree | 39909be45555a6ea4ee5143fc961caf33a76143f | |
parent | f1d39f5bea74d84c38b4713f66cfcc9fdbcd1c57 (diff) |
Add latest changes from gitlab-org/gitlab@master
29 files changed, 698 insertions, 49 deletions
@@ -440,7 +440,11 @@ end gem 'octokit', '~> 4.15' # https://gitlab.com/gitlab-org/gitlab/issues/207207 -gem 'gitlab-mail_room', '~> 0.0.9', require: 'mail_room' +gem 'gitlab-mail_room', '~> 0.0.10', require: false +# Ruby v3.0 promoted net-http and net-imap from stdlib. Adding net-http as a +# dependency avoids "already initialized constant errors" if net-imap is installed. +gem 'net-http', '~> 0.1' +gem 'uri', '= 0.10.0' # Lock this for bundler v2.1.4 gem 'email_reply_trimmer', '~> 0.1' gem 'html2text' diff --git a/Gemfile.lock b/Gemfile.lock index 5d63cb1cb2d..5a2787130ef 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -268,6 +268,7 @@ GEM diff-lcs (1.4.4) diff_match_patch (0.1.0) diffy (3.3.0) + digest (3.0.0) discordrb-webhooks (3.4.2) rest-client (>= 2.0.0) docile (1.3.2) @@ -464,7 +465,8 @@ GEM pg_query (~> 1.3) redis (> 3.0.0, < 5.0.0) gitlab-license (1.3.1) - gitlab-mail_room (0.0.9) + gitlab-mail_room (0.0.10) + net-imap (>= 0.2.1) gitlab-markup (1.7.1) gitlab-net-dns (0.9.1) gitlab-pry-byebug (3.9.0) @@ -763,10 +765,18 @@ GEM mustermann (>= 1.0.0) nap (1.1.0) nenv (0.3.0) + net-http (0.1.1) + net-protocol + uri net-http-persistent (4.0.1) connection_pool (~> 2.2) + net-imap (0.2.1) + digest + net-protocol + strscan net-ldap (0.16.3) net-ntp (2.1.3) + net-protocol (0.1.0) net-scp (3.0.0) net-ssh (>= 2.6.5, < 7.0.0) net-ssh (6.0.0) @@ -1208,6 +1218,7 @@ GEM state_machines-activerecord (0.8.0) activerecord (>= 5.1) state_machines-activemodel (>= 0.8.0) + strscan (3.0.0) swd (1.1.2) activesupport (>= 3) attr_required (>= 0.0.5) @@ -1278,6 +1289,7 @@ GEM equalizer (~> 0.0.9) parser (>= 2.6.5) procto (~> 0.0.2) + uri (0.10.0) uri_template (0.7.0) valid_email (0.1.3) activemodel @@ -1427,7 +1439,7 @@ DEPENDENCIES gitlab-fog-google (~> 1.13) gitlab-labkit (~> 0.16.2) gitlab-license (~> 1.3) - gitlab-mail_room (~> 0.0.9) + gitlab-mail_room (~> 0.0.10) gitlab-markup (~> 1.7.1) gitlab-net-dns (~> 0.9.1) gitlab-pry-byebug @@ -1489,6 +1501,7 @@ DEPENDENCIES mini_magick (~> 4.10.1) minitest (~> 5.11.0) multi_json (~> 1.14.1) + net-http (~> 0.1) net-ldap (~> 0.16.3) net-ntp net-ssh (~> 6.0) @@ -1597,6 +1610,7 @@ DEPENDENCIES unicorn (~> 5.5) unicorn-worker-killer (~> 0.4.4) unleash (~> 0.1.5) + uri (= 0.10.0) valid_email (~> 0.1) validates_hostname (~> 1.0.11) version_sorter (~> 2.2.4) diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue index 906965f4395..d00a0f1633b 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue @@ -222,9 +222,9 @@ export default { }, labels: { members: { - modalTitle: s__('InviteMembersModal|Invite team members'), - searchField: s__('InviteMembersModal|GitLab member or Email address'), - placeHolder: s__('InviteMembersModal|Search for members to invite'), + modalTitle: s__('InviteMembersModal|Invite members'), + searchField: s__('InviteMembersModal|GitLab member or email address'), + placeHolder: s__('InviteMembersModal|Select members or type email addresses'), toGroup: { introText: s__( "InviteMembersModal|You're inviting members to the %{strongStart}%{name}%{strongEnd} group.", diff --git a/app/finders/merge_requests/oldest_per_commit_finder.rb b/app/finders/merge_requests/oldest_per_commit_finder.rb index 5360f301036..5da7a08e36c 100644 --- a/app/finders/merge_requests/oldest_per_commit_finder.rb +++ b/app/finders/merge_requests/oldest_per_commit_finder.rb @@ -18,8 +18,8 @@ module MergeRequests mapping = {} shas = commits.map(&:id) - # To include merge requests by the commit SHA, we don't need to go through - # any diff rows. + # To include merge requests by the merge/squash SHA, we don't need to go + # through any diff rows. # # We can't squeeze all this into a single query, as the diff based data # relies on a GROUP BY. On the other hand, retrieving MRs by their merge @@ -27,12 +27,17 @@ module MergeRequests @project .merge_requests .preload_target_project - .by_merge_commit_sha(shas) + .by_merge_or_squash_commit_sha(shas) .each do |mr| - # Merge SHAs can't be in the merge request itself. It _is_ possible a - # newer merge request includes the merge commit, but in that case we - # still want the oldest merge request. - mapping[mr.merge_commit_sha] = mr + # Merge/squash SHAs can't be in the merge request itself. It _is_ + # possible a newer merge request includes the commit, but in that case + # we still want the oldest merge request. + # + # It's also possible that a merge request produces both a squashed + # commit and a merge commit. In that case we want to store the mapping + # for both the SHAs. + mapping[mr.squash_commit_sha] = mr if mr.squash_commit_sha + mapping[mr.merge_commit_sha] = mr if mr.merge_commit_sha end remaining = shas - mapping.keys diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 76f0417ab35..834d28f1e3c 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -276,6 +276,9 @@ class MergeRequest < ApplicationRecord scope :by_squash_commit_sha, -> (sha) do where(squash_commit_sha: sha) end + scope :by_merge_or_squash_commit_sha, -> (sha) do + from_union([by_squash_commit_sha(sha), by_merge_commit_sha(sha)]) + end scope :by_related_commit_sha, -> (sha) do from_union( [ diff --git a/app/models/packages/debian/file_entry.rb b/app/models/packages/debian/file_entry.rb new file mode 100644 index 00000000000..eb66f4acfa9 --- /dev/null +++ b/app/models/packages/debian/file_entry.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Packages + module Debian + class FileEntry + include ActiveModel::Model + + DIGESTS = %i[md5 sha1 sha256].freeze + FILENAME_REGEX = %r{\A[a-zA-Z0-9][a-zA-Z0-9_.~+-]*\z}.freeze + + attr_accessor :filename, + :size, + :md5sum, + :section, + :priority, + :sha1sum, + :sha256sum, + :package_file + + validates :filename, :size, :md5sum, :section, :priority, :sha1sum, :sha256sum, :package_file, presence: true + validates :filename, format: { with: FILENAME_REGEX } + validate :valid_package_file_digests, if: -> { md5sum.present? && sha1sum.present? && sha256sum.present? && package_file.present? } + + def component + return 'main' if section.blank? + return 'main' unless section.include?('/') + + section.split('/')[0] + end + + private + + def valid_package_file_digests + DIGESTS.each do |digest| + package_file_digest = package_file["file_#{digest}"] + sum = public_send("#{digest}sum") # rubocop:disable GitlabSecurity/PublicSend + next if package_file_digest == sum + + errors.add("#{digest}sum".to_sym, "mismatch for #{filename}: #{package_file_digest} != #{sum}") + end + end + end + end +end diff --git a/app/services/packages/debian/extract_changes_metadata_service.rb b/app/services/packages/debian/extract_changes_metadata_service.rb new file mode 100644 index 00000000000..242b4cc8e54 --- /dev/null +++ b/app/services/packages/debian/extract_changes_metadata_service.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +module Packages + module Debian + class ExtractChangesMetadataService + include Gitlab::Utils::StrongMemoize + + ExtractionError = Class.new(StandardError) + + def initialize(package_file) + @package_file = package_file + @entries = {} + end + + def execute + { + file_type: file_type, + architecture: metadata[:architecture], + fields: fields, + files: files + } + rescue ActiveModel::ValidationError => e + raise ExtractionError.new(e.message) + end + + private + + def metadata + strong_memoize(:metadata) do + ::Packages::Debian::ExtractMetadataService.new(@package_file).execute + end + end + + def file_type + metadata[:file_type] + end + + def fields + metadata[:fields] + end + + def files + strong_memoize(:files) do + raise ExtractionError.new("is not a changes file") unless file_type == :changes + raise ExtractionError.new("Files field is missing") if fields[:Files].blank? + raise ExtractionError.new("Checksums-Sha1 field is missing") if fields[:'Checksums-Sha1'].blank? + raise ExtractionError.new("Checksums-Sha256 field is missing") if fields[:'Checksums-Sha256'].blank? + + init_entries_from_files + entries_from_checksums_sha1 + entries_from_checksums_sha256 + entries_from_package_files + + @entries + end + end + + def init_entries_from_files + each_lines_for(:Files) do |line| + md5sum, size, section, priority, filename = line.split + entry = FileEntry.new( + filename: filename, + size: size.to_i, + md5sum: md5sum, + section: section, + priority: priority) + + @entries[filename] = entry + end + end + + def entries_from_checksums_sha1 + each_lines_for(:'Checksums-Sha1') do |line| + sha1sum, size, filename = line.split + entry = @entries[filename] + raise ExtractionError.new("#{filename} is listed in Checksums-Sha1 but not in Files") unless entry + raise ExtractionError.new("Size for #{filename} in Files and Checksums-Sha1 differ") unless entry.size == size.to_i + + entry.sha1sum = sha1sum + end + end + + def entries_from_checksums_sha256 + each_lines_for(:'Checksums-Sha256') do |line| + sha256sum, size, filename = line.split + entry = @entries[filename] + raise ExtractionError.new("#{filename} is listed in Checksums-Sha256 but not in Files") unless entry + raise ExtractionError.new("Size for #{filename} in Files and Checksums-Sha256 differ") unless entry.size == size.to_i + + entry.sha256sum = sha256sum + end + end + + def each_lines_for(field) + fields[field].split("\n").each do |line| + next if line.blank? + + yield(line) + end + end + + def entries_from_package_files + @entries.each do |filename, entry| + entry.package_file = ::Packages::PackageFileFinder.new(@package_file.package, filename).execute! + entry.validate! + rescue ActiveRecord::RecordNotFound + raise ExtractionError.new("#{filename} is listed in Files but was not uploaded") + end + end + end + end +end diff --git a/changelogs/unreleased/include-squash-commits-in-changelogs.yml b/changelogs/unreleased/include-squash-commits-in-changelogs.yml new file mode 100644 index 00000000000..cbcf2e8190b --- /dev/null +++ b/changelogs/unreleased/include-squash-commits-in-changelogs.yml @@ -0,0 +1,5 @@ +--- +title: Link squashed commits using the changelog API +merge_request: 56985 +author: +type: added diff --git a/changelogs/unreleased/inherited_issuable_templates_default_enabled.yml b/changelogs/unreleased/inherited_issuable_templates_default_enabled.yml new file mode 100644 index 00000000000..25bf183e441 --- /dev/null +++ b/changelogs/unreleased/inherited_issuable_templates_default_enabled.yml @@ -0,0 +1,5 @@ +--- +title: Configure issue and merge request description templates at group level and rolldown description templates in the group hierarchy +merge_request: 56737 +author: +type: added diff --git a/changelogs/unreleased/manifest-unsafe-scheme.yml b/changelogs/unreleased/manifest-unsafe-scheme.yml new file mode 100644 index 00000000000..8158af93b31 --- /dev/null +++ b/changelogs/unreleased/manifest-unsafe-scheme.yml @@ -0,0 +1,5 @@ +--- +title: Validate import manifest url scheme +merge_request: 57071 +author: +type: fixed diff --git a/changelogs/unreleased/sh-update-mailroom.yml b/changelogs/unreleased/sh-update-mailroom.yml new file mode 100644 index 00000000000..b53221ba787 --- /dev/null +++ b/changelogs/unreleased/sh-update-mailroom.yml @@ -0,0 +1,5 @@ +--- +title: Update mailroom to v0.0.10 +merge_request: 56913 +author: +type: fixed diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml index 774254d2ff1..326c7acb28a 100644 --- a/config/dependency_decisions.yml +++ b/config/dependency_decisions.yml @@ -331,3 +331,9 @@ :why: https://github.com/xijo/reverse_markdown/blob/master/LICENSE :versions: [] :when: 2021-02-03 08:47:28.792907000 Z +- - :approve + - net-protocol + - :who: Stan Hu + :why: "This license is the same as the Ruby license (BSD): https://github.com/ruby/net-protocol/blob/master/LICENSE.txt" + :versions: [] + :when: 2021-03-18 00:00:46.124036000 Z diff --git a/config/feature_flags/development/inherited_issuable_templates.yml b/config/feature_flags/development/inherited_issuable_templates.yml index fb322e9468a..403361ff784 100644 --- a/config/feature_flags/development/inherited_issuable_templates.yml +++ b/config/feature_flags/development/inherited_issuable_templates.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/321247 milestone: '13.9' type: development group: group::project management -default_enabled: false +default_enabled: true diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md index 45a1c5c29fa..887ecd858ff 100644 --- a/doc/development/usage_ping/dictionary.md +++ b/doc/development/usage_ping/dictionary.md @@ -9812,6 +9812,30 @@ Status: `implemented` Tiers: `premium`, `ultimate` +### `redis_hll_counters.epics_usage.g_project_management_epic_closed_monthly` + +Counts of MAU closing epics + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210310163213_g_project_management_epic_closed_monthly.yml) + +Group: `group::product planning` + +Status: `implemented` + +Tiers: `premium`, `ultimate` + +### `redis_hll_counters.epics_usage.g_project_management_epic_closed_weekly` + +Counts of WAU closing epics + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210310162703_g_project_management_epic_closed_weekly.yml) + +Group: `group::product planning` + +Status: `implemented` + +Tiers: `premium`, `ultimate` + ### `redis_hll_counters.epics_usage.g_project_management_epic_created_monthly` Count of MAU creating epics @@ -9860,6 +9884,30 @@ Status: `implemented` Tiers: `premium`, `ultimate` +### `redis_hll_counters.epics_usage.g_project_management_epic_reopened_monthly` + +Counts of MAU closing epics + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_28d/20210310164247_g_project_management_epic_reopened_monthly.yml) + +Group: `group::product planning` + +Status: `implemented` + +Tiers: `premium`, `ultimate` + +### `redis_hll_counters.epics_usage.g_project_management_epic_reopened_weekly` + +Counts of WAU re-opening epics + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/counts_7d/20210310164112_g_project_management_epic_reopened_weekly.yml) + +Group: `group::product planning` + +Status: `implemented` + +Tiers: `premium`, `ultimate` + ### `redis_hll_counters.epics_usage.g_project_management_users_destroying_epic_notes_monthly` Counts of MAU destroying epic notes diff --git a/doc/user/analytics/index.md b/doc/user/analytics/index.md index fe35d575373..bcc063dea28 100644 --- a/doc/user/analytics/index.md +++ b/doc/user/analytics/index.md @@ -48,9 +48,10 @@ The following analytics features are available at the group level: - [Contribution](../group/contribution_analytics/index.md). **(PREMIUM)** - [Insights](../group/insights/index.md). **(ULTIMATE)** - [Issue](../group/issues_analytics/index.md). **(PREMIUM)** -- [Productivity](productivity_analytics.md) **(PREMIUM)** -- [Repositories](../group/repositories_analytics/index.md) **(PREMIUM)** +- [Productivity](productivity_analytics.md). **(PREMIUM)** +- [Repositories](../group/repositories_analytics/index.md). **(PREMIUM)** - [Value Stream](../group/value_stream_analytics/index.md). **(PREMIUM)** +- [Application Security](../application_security/security_dashboard/#group-security-dashboard). **(ULTIMATE)** ## Project-level analytics @@ -64,3 +65,10 @@ The following analytics features are available at the project level: [feature flag](../../development/feature_flags/index.md#enabling-a-feature-flag-locally-in-development). **(PREMIUM)** - [Repository](repository_analytics.md). **(FREE)** - [Value Stream](value_stream_analytics.md). **(FREE)** +- [Application Security](../application_security/security_dashboard/#project-security-dashboard). **(ULTIMATE)** + +## User-configurable analytics + +The following analytics features are available for users to create personalized views: + +- [Application Security](../application_security/security_dashboard/#security-center). **(ULTIMATE)** diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md index 2267cb55f16..c45ce7a02d9 100644 --- a/doc/user/project/description_templates.md +++ b/doc/user/project/description_templates.md @@ -89,32 +89,27 @@ For example: `https://gitlab.com/gitlab-org/gitlab/-/issues/new?issuable_templat ![Description templates](img/description_templates.png) -### Set an issue and merge request description template at group level **(PREMIUM)** +You can set description templates at various levels: -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52360) in GitLab 13.9. -> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default. -> - It's disabled by default on GitLab.com. -> - To use it in GitLab self-managed instances, ask a GitLab administrator to - [enable it](#enable-or-disable-issue-and-merge-request-description-templates-at-group-and-instance-level). - -Templates can be useful because you can create a template once and use it multiple times. -To re-use templates [you've created](../project/description_templates.md#create-an-issue-template): - -1. Go to the group's **Settings > General > Templates**. -1. From the dropdown, select your template project as the template repository at group level. +- The entire [instance](#set-instance-level-description-templates) +- A specific [group or subgroup](#set-group-level-description-templates) +- A specific [project](#set-a-default-template-for-merge-requests-and-issues) -![Group template settings](../group/img/group_file_template_settings.png) +The templates are inherited. For example, in a project, you can also access templates set for the +instance or the project's parent groups. -### Set an issue and merge request description template at instance level **(PREMIUM ONLY)** +### Set instance-level description templates **(PREMIUM SELF)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52360) in GitLab 13.9. > - It's [deployed behind a feature flag](../feature_flags.md), disabled by default. -> - It's disabled by default on GitLab.com. -> - To use it in GitLab self-managed instances, ask a GitLab administrator to - [enable it](#enable-or-disable-issue-and-merge-request-description-templates-at-group-and-instance-level). +> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56737) in GitLab 13.11. +> - It's enabled by default on GitLab.com. +> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-issue-and-merge-request-description-templates-at-group-and-instance-level). **(PREMIUM SELF)** + +You can set a description template at the **instance level** for issues +and merge requests. +As a result, these templates are available in all projects within the instance. -Similar to group templates, issue and merge request templates can also be set up at the instance level. -This results in those templates being available in all projects within the instance. Only instance administrators can set instance-level templates. To set the instance-level description template repository: @@ -122,15 +117,41 @@ To set the instance-level description template repository: 1. Select the **Admin Area** icon (**{admin}**). 1. Go to **Settings > Templates**. 1. From the dropdown, select your template project as the template repository at instance level. +1. Select **Save changes**. + +![Setting templates in the Admin Area](../admin_area/settings/img/file_template_admin_area.png) Learn more about [instance template repository](../admin_area/settings/instance_template_repository.md). -![Setting templates in the Admin Area](../admin_area/settings/img/file_template_admin_area.png) +### Set group-level description templates **(PREMIUM)** + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52360) in GitLab 13.9. +> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default. +> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56737) in GitLab 13.11. +> - It's enabled by default on GitLab.com. +> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-issue-and-merge-request-description-templates-at-group-and-instance-level). **(PREMIUM SELF)** + +With **group-level** description templates, you can store your templates in a single repository and +configure the group file templates setting to point to that repository. +As a result, you can use the same templates in issues and merge requests in all the group's projects. + +To re-use templates [you've created](../project/description_templates.md#create-an-issue-template): + +1. Go to the group's **Settings > General > Templates**. +1. From the dropdown, select your template project as the template repository at group level. +1. Select **Save changes**. + +![Group template settings](../group/img/group_file_template_settings.png) ### Set a default template for merge requests and issues **(PREMIUM)** +In a project, you can choose a default description template for new issues and merge requests. +As a result, every time a new merge request or issue is created, it's pre-filled with the text you +entered in the template. + The visibility of issues or merge requests should be set to either "Everyone -with access" or "Only Project Members" in your project's **Settings / Visibility, project features, permissions** section, otherwise the +with access" or "Only Project Members" in your project's +**Settings / Visibility, project features, permissions** section. Otherwise, the template text areas don't show. This is the default behavior, so in most cases you should be fine. @@ -149,9 +170,6 @@ To set a default description template for issues: Because GitLab merge request and issues support [Markdown](../markdown.md), you can use it to format headings, lists, and so on. -Now, every time a new merge request or issue is created, it's pre-filled with the text you entered -in the templates. - [GitLab versions 13.10 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/885) provide `issues_template` and `merge_requests_template` attributes in the [Projects API](../../api/projects.md) to help you keep your templates up to date. @@ -211,9 +229,9 @@ it's very hard to read otherwise.) Setting issue and merge request description templates at group and instance levels is under development and not ready for production use. It is deployed behind a -feature flag that is **disabled by default**. +feature flag that is **enabled by default**. [GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) -can enable it. +can disable it. To enable it: diff --git a/lib/gitlab/manifest_import/manifest.rb b/lib/gitlab/manifest_import/manifest.rb index 7208fe5bbc5..618ddf37b88 100644 --- a/lib/gitlab/manifest_import/manifest.rb +++ b/lib/gitlab/manifest_import/manifest.rb @@ -47,6 +47,10 @@ module Gitlab @errors << 'Make sure every <project> tag has name and path attributes.' end + unless validate_scheme + @errors << 'Make sure the url does not start with javascript' + end + @errors.empty? end @@ -64,6 +68,10 @@ module Gitlab end end + def validate_scheme + remote !~ /\Ajavascript/i + end + def repository_url(name) Gitlab::Utils.append_path(remote, name) end diff --git a/lib/gitlab/usage_data_counters/known_events/epic_events.yml b/lib/gitlab/usage_data_counters/known_events/epic_events.yml index 79dfae98157..3defa3e932e 100644 --- a/lib/gitlab/usage_data_counters/known_events/epic_events.yml +++ b/lib/gitlab/usage_data_counters/known_events/epic_events.yml @@ -38,3 +38,15 @@ redis_slot: project_management aggregation: daily feature_flag: track_epics_activity + +- name: g_project_management_epic_closed + category: epics_usage + redis_slot: project_management + aggregation: daily + feature_flag: track_epics_activity + +- name: g_project_management_epic_reopened + category: epics_usage + redis_slot: project_management + aggregation: daily + feature_flag: track_epics_activity diff --git a/locale/gitlab.pot b/locale/gitlab.pot index cb7bdb7c1f5..118b971b35f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -16888,7 +16888,7 @@ msgstr "" msgid "InviteMembersModal|Close invite team members" msgstr "" -msgid "InviteMembersModal|GitLab member or Email address" +msgid "InviteMembersModal|GitLab member or email address" msgstr "" msgid "InviteMembersModal|Invite" @@ -16897,7 +16897,7 @@ msgstr "" msgid "InviteMembersModal|Invite a group" msgstr "" -msgid "InviteMembersModal|Invite team members" +msgid "InviteMembersModal|Invite members" msgstr "" msgid "InviteMembersModal|Members were successfully added" @@ -16906,10 +16906,10 @@ msgstr "" msgid "InviteMembersModal|Search for a group to invite" msgstr "" -msgid "InviteMembersModal|Search for members to invite" +msgid "InviteMembersModal|Select a group to invite" msgstr "" -msgid "InviteMembersModal|Select a group to invite" +msgid "InviteMembersModal|Select members or type email addresses" msgstr "" msgid "InviteMembersModal|Some of the members could not be added" diff --git a/qa/qa/page/component/invite_members_modal.rb b/qa/qa/page/component/invite_members_modal.rb index fbddb37f15e..9883ef22029 100644 --- a/qa/qa/page/component/invite_members_modal.rb +++ b/qa/qa/page/component/invite_members_modal.rb @@ -42,7 +42,7 @@ module QA within_element(:invite_members_modal_content) do fill_element :access_level_dropdown, with: access_level - fill_in 'Search for members to invite', with: username + fill_in 'Select members or type email addresses', with: username Support::WaitForRequests.wait_for_requests diff --git a/spec/factories/packages/package_file.rb b/spec/factories/packages/package_file.rb index 6d8b119040e..6cbe9e6219d 100644 --- a/spec/factories/packages/package_file.rb +++ b/spec/factories/packages/package_file.rb @@ -125,6 +125,9 @@ FactoryBot.define do trait(:source) do file_name { 'sample_1.2.3~alpha2.tar.xz' } + file_md5 { 'd79b34f58f61ff4ad696d9bd0b8daa68' } + file_sha1 { '5f8bba5574eb01ac3b1f5e2988e8c29307788236' } + file_sha256 { 'b5a599e88e7cbdda3bde808160a21ba1dd1ec76b2ec8d4912aae769648d68362' } transient do file_metadatum_trait { :source } @@ -133,6 +136,9 @@ FactoryBot.define do trait(:dsc) do file_name { 'sample_1.2.3~alpha2.dsc' } + file_md5 { '3b0817804f669e16cdefac583ad88f0e' } + file_sha1 { '32ecbd674f0bfd310df68484d87752490685a8d6' } + file_sha256 { '844f79825b7e8aaa191e514b58a81f9ac1e58e2180134b0c9512fa66d896d7ba' } transient do file_metadatum_trait { :dsc } @@ -141,6 +147,9 @@ FactoryBot.define do trait(:deb) do file_name { 'libsample0_1.2.3~alpha2_amd64.deb' } + file_md5 { 'fb0842b21adc44207996296fe14439dd' } + file_sha1 { '5248b95600e85bfe7f63c0dfce330a75f5777366' } + file_sha256 { '1c383a525bfcba619c7305ccd106d61db501a6bbaf0003bf8d0c429fbdb7fcc1' } transient do file_metadatum_trait { :deb } @@ -149,6 +158,9 @@ FactoryBot.define do trait(:deb2) do file_name { 'sample-dev_1.2.3~binary_amd64.deb' } + file_md5 { 'd2afbd28e4d74430d22f9504e18bfdf5' } + file_sha1 { 'f81e4f66c8c6bb899653a3340c157965ee69634f' } + file_sha256 { '9fbeee2191ce4dab5288fad5ecac1bd369f58fef9a992a880eadf0caf25f086d' } transient do file_metadatum_trait { :deb } @@ -157,6 +169,9 @@ FactoryBot.define do trait(:udeb) do file_name { 'sample-udeb_1.2.3~alpha2_amd64.udeb' } + file_md5 { '72b1dd7d98229e2fb0355feda1d3a165' } + file_sha1 { 'e42e8f2fe04ed1bb73b44a187674480d0e49dcba' } + file_sha256 { '2b0c152b3ab4cc07663350424de972c2b7621d69fe6df2e0b94308a191e4632f' } transient do file_metadatum_trait { :udeb } @@ -165,6 +180,9 @@ FactoryBot.define do trait(:buildinfo) do file_name { 'sample_1.2.3~alpha2_amd64.buildinfo' } + file_md5 { '4e085dd67c120ca967ec314f65770a42' } + file_sha1 { '0d47e899f3cc67a2253a4629456ff927e0db5c60' } + file_sha256 { 'f9900d3c94e94b329232668dcbef3dba2d96c07147b15b6dc0533452e4dd8a43' } transient do file_metadatum_trait { :buildinfo } diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb index ce22171a560..520cf850da2 100644 --- a/spec/features/import/manifest_import_spec.rb +++ b/spec/features/import/manifest_import_spec.rb @@ -42,6 +42,15 @@ RSpec.describe 'Import multiple repositories by uploading a manifest file', :js end end + it 'renders an error if the remote url scheme starts with javascript' do + visit new_import_manifest_path + + attach_file('manifest', Rails.root.join('spec/fixtures/unsafe_javascript.xml')) + click_on 'List available repositories' + + expect(page).to have_content 'Make sure the url does not start with javascript' + end + it 'renders an error if invalid file was provided' do visit new_import_manifest_path diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb index 0830585da9b..384b8ae9929 100644 --- a/spec/features/projects/members/list_spec.rb +++ b/spec/features/projects/members/list_spec.rb @@ -175,7 +175,7 @@ RSpec.describe 'Project members list', :js do click_on 'Invite members' page.within '#invite-members-modal' do - fill_in 'Search for members to invite', with: id + fill_in 'Select members or type email addresses', with: id wait_for_requests click_button id diff --git a/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb b/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb index 4724a8eb5c7..6dffaff294d 100644 --- a/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb +++ b/spec/finders/merge_requests/oldest_per_commit_finder_spec.rb @@ -77,6 +77,45 @@ RSpec.describe MergeRequests::OldestPerCommitFinder do expect(described_class.new(project).execute(commits)).to eq(sha => mr) end + it 'includes a merge request that was squashed into the target branch' do + project = create(:project) + sha = Digest::SHA1.hexdigest('foo') + mr = create( + :merge_request, + :merged, + target_project: project, + squash_commit_sha: sha + ) + + commits = [double(:commit, id: sha)] + + expect(MergeRequestDiffCommit) + .not_to receive(:oldest_merge_request_id_per_commit) + + expect(described_class.new(project).execute(commits)).to eq(sha => mr) + end + + it 'includes a merge request for both a squash and merge commit' do + project = create(:project) + sha1 = Digest::SHA1.hexdigest('foo') + sha2 = Digest::SHA1.hexdigest('bar') + mr = create( + :merge_request, + :merged, + target_project: project, + squash_commit_sha: sha1, + merge_commit_sha: sha2 + ) + + commits = [double(:commit1, id: sha1), double(:commit2, id: sha2)] + + expect(MergeRequestDiffCommit) + .not_to receive(:oldest_merge_request_id_per_commit) + + expect(described_class.new(project).execute(commits)) + .to eq(sha1 => mr, sha2 => mr) + end + it 'includes the oldest merge request when a merge commit is present in a newer merge request' do project = create(:project) sha = Digest::SHA1.hexdigest('foo') diff --git a/spec/fixtures/unsafe_javascript.xml b/spec/fixtures/unsafe_javascript.xml new file mode 100644 index 00000000000..0c23d1e07db --- /dev/null +++ b/spec/fixtures/unsafe_javascript.xml @@ -0,0 +1,10 @@ +<manifest> + <remote review="javascript://HelloTheGitlabSecurityTeam%0Aprompt(1)%0A" /> + + <project path="test1" name="manifest1" /> + <project path="test2" name="manifest2" /> + <project path="test3" name="manifest3" /> + <project path="test4" name="manifest4" /> + <project path="test5" name="manifest5" /> + <project path="test6" name="manifest6" /> +</manifest>
\ No newline at end of file diff --git a/spec/frontend/invite_members/components/invite_members_modal_spec.js b/spec/frontend/invite_members/components/invite_members_modal_spec.js index 7b2ea890d35..7ed18775693 100644 --- a/spec/frontend/invite_members/components/invite_members_modal_spec.js +++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js @@ -93,7 +93,7 @@ describe('InviteMembersModal', () => { }); it('renders the modal with the correct title', () => { - expect(wrapper.findComponent(GlModal).props('title')).toBe('Invite team members'); + expect(wrapper.findComponent(GlModal).props('title')).toBe('Invite members'); }); it('renders the Cancel button text correctly', () => { diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index b2c45135de1..d330169eab0 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -420,6 +420,19 @@ RSpec.describe MergeRequest, factory_default: :keep do end end + describe '.by_merge_or_squash_commit_sha' do + subject { described_class.by_merge_or_squash_commit_sha([sha1, sha2]) } + + let(:sha1) { '123abc' } + let(:sha2) { '456abc' } + let(:mr1) { create(:merge_request, :merged, squash_commit_sha: sha1) } + let(:mr2) { create(:merge_request, :merged, merge_commit_sha: sha2) } + + it 'returns merge requests that match the given squash and merge commits' do + is_expected.to include(mr1, mr2) + end + end + describe '.by_related_commit_sha' do subject { described_class.by_related_commit_sha(sha) } diff --git a/spec/models/packages/debian/file_entry_spec.rb b/spec/models/packages/debian/file_entry_spec.rb new file mode 100644 index 00000000000..7aa16bc0cce --- /dev/null +++ b/spec/models/packages/debian/file_entry_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Packages::Debian::FileEntry, type: :model do + let_it_be(:package_file) { create(:debian_package_file, :dsc) } + + let(:filename) { 'sample_1.2.3~alpha2.dsc' } + let(:size) { 671 } + let(:md5sum) { '3b0817804f669e16cdefac583ad88f0e' } + let(:section) { 'libs' } + let(:priority) { 'optional' } + let(:sha1sum) { '32ecbd674f0bfd310df68484d87752490685a8d6' } + let(:sha256sum) { '844f79825b7e8aaa191e514b58a81f9ac1e58e2180134b0c9512fa66d896d7ba' } + + let(:file_entry) do + described_class.new( + filename: filename, + size: size, + md5sum: md5sum, + section: section, + priority: priority, + sha1sum: sha1sum, + sha256sum: sha256sum, + package_file: package_file + ) + end + + subject { file_entry } + + describe 'validations' do + it { is_expected.to be_valid } + + describe '#filename' do + it { is_expected.to validate_presence_of(:filename) } + it { is_expected.not_to allow_value('Hé').for(:filename) } + end + + describe '#size' do + it { is_expected.to validate_presence_of(:size) } + end + + describe '#md5sum' do + it { is_expected.to validate_presence_of(:md5sum) } + it { is_expected.not_to allow_value('12345678901234567890123456789012').for(:md5sum).with_message('mismatch for sample_1.2.3~alpha2.dsc: 3b0817804f669e16cdefac583ad88f0e != 12345678901234567890123456789012') } + end + + describe '#section' do + it { is_expected.to validate_presence_of(:section) } + end + + describe '#priority' do + it { is_expected.to validate_presence_of(:priority) } + end + + describe '#sha1sum' do + it { is_expected.to validate_presence_of(:sha1sum) } + it { is_expected.not_to allow_value('1234567890123456789012345678901234567890').for(:sha1sum).with_message('mismatch for sample_1.2.3~alpha2.dsc: 32ecbd674f0bfd310df68484d87752490685a8d6 != 1234567890123456789012345678901234567890') } + end + + describe '#sha256sum' do + it { is_expected.to validate_presence_of(:sha256sum) } + it { is_expected.not_to allow_value('1234567890123456789012345678901234567890123456789012345678901234').for(:sha256sum).with_message('mismatch for sample_1.2.3~alpha2.dsc: 844f79825b7e8aaa191e514b58a81f9ac1e58e2180134b0c9512fa66d896d7ba != 1234567890123456789012345678901234567890123456789012345678901234') } + end + + describe '#package_file' do + it { is_expected.to validate_presence_of(:package_file) } + end + end + + describe '#component' do + subject { file_entry.component } + + context 'without section' do + let(:section) { nil } + + it { is_expected.to eq 'main' } + end + + context 'with empty section' do + let(:section) { '' } + + it { is_expected.to eq 'main' } + end + + context 'with ruby section' do + let(:section) { 'ruby' } + + it { is_expected.to eq 'main' } + end + + context 'with contrib/ruby section' do + let(:section) { 'contrib/ruby' } + + it { is_expected.to eq 'contrib' } + end + end +end diff --git a/spec/services/packages/debian/extract_changes_metadata_service_spec.rb b/spec/services/packages/debian/extract_changes_metadata_service_spec.rb new file mode 100644 index 00000000000..a7d121819a1 --- /dev/null +++ b/spec/services/packages/debian/extract_changes_metadata_service_spec.rb @@ -0,0 +1,160 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Packages::Debian::ExtractChangesMetadataService do + describe '#execute' do + let_it_be(:distribution) { create(:debian_project_distribution, codename: 'unstable') } + let_it_be(:incoming) { create(:debian_incoming, project: distribution.project) } + + let(:package_file) { incoming.package_files.last } + let(:service) { described_class.new(package_file) } + + subject { service.execute } + + context 'with valid package file' do + it 'extract metadata', :aggregate_failures do + expected_fields = { 'Architecture': 'source amd64', 'Binary': 'libsample0 sample-dev sample-udeb' } + + expect(subject[:file_type]).to eq(:changes) + expect(subject[:architecture]).to be_nil + expect(subject[:fields]).to include(expected_fields) + expect(subject[:files].count).to eq(6) + end + end + + context 'with invalid package file' do + let(:package_file) { incoming.package_files.first } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "is not a changes file") + end + end + + context 'with invalid metadata' do + let(:md5_dsc) { '3b0817804f669e16cdefac583ad88f0e 671 libs optional sample_1.2.3~alpha2.dsc' } + let(:md5_source) { 'd79b34f58f61ff4ad696d9bd0b8daa68 864 libs optional sample_1.2.3~alpha2.tar.xz' } + let(:md5s) { "#{md5_dsc}\n#{md5_source}" } + let(:sha1_dsc) { '32ecbd674f0bfd310df68484d87752490685a8d6 671 sample_1.2.3~alpha2.dsc' } + let(:sha1_source) { '5f8bba5574eb01ac3b1f5e2988e8c29307788236 864 sample_1.2.3~alpha2.tar.xz' } + let(:sha1s) { "#{sha1_dsc}\n#{sha1_source}" } + let(:sha256_dsc) { '844f79825b7e8aaa191e514b58a81f9ac1e58e2180134b0c9512fa66d896d7ba 671 sample_1.2.3~alpha2.dsc' } + let(:sha256_source) { 'b5a599e88e7cbdda3bde808160a21ba1dd1ec76b2ec8d4912aae769648d68362 864 sample_1.2.3~alpha2.tar.xz' } + let(:sha256s) { "#{sha256_dsc}\n#{sha256_source}" } + let(:fields) { { Files: md5s, 'Checksums-Sha1': sha1s, 'Checksums-Sha256': sha256s } } + let(:metadata) { { file_type: :changes, architecture: 'amd64', fields: fields } } + + before do + allow_next_instance_of(::Packages::Debian::ExtractMetadataService) do |extract_metadata_service| + allow(extract_metadata_service).to receive(:execute).and_return(metadata) + end + end + + context 'without Files field' do + let(:md5s) { nil } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Files field is missing") + end + end + + context 'without Checksums-Sha1 field' do + let(:sha1s) { nil } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Checksums-Sha1 field is missing") + end + end + + context 'without Checksums-Sha256 field' do + let(:sha256s) { nil } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Checksums-Sha256 field is missing") + end + end + + context 'with file in Checksums-Sha1 but not in Files' do + let(:md5_dsc) { '' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "sample_1.2.3~alpha2.dsc is listed in Checksums-Sha1 but not in Files") + end + end + + context 'with different size in Checksums-Sha1' do + let(:sha1_dsc) { '32ecbd674f0bfd310df68484d87752490685a8d6 42 sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Size for sample_1.2.3~alpha2.dsc in Files and Checksums-Sha1 differ") + end + end + + context 'with file in Checksums-Sha256 but not in Files' do + let(:md5_dsc) { '' } + let(:sha1_dsc) { '' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "sample_1.2.3~alpha2.dsc is listed in Checksums-Sha256 but not in Files") + end + end + + context 'with different size in Checksums-Sha256' do + let(:sha256_dsc) { '844f79825b7e8aaa191e514b58a81f9ac1e58e2180134b0c9512fa66d896d7ba 42 sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Size for sample_1.2.3~alpha2.dsc in Files and Checksums-Sha256 differ") + end + end + + context 'with file in Files but not in Checksums-Sha1' do + let(:sha1_dsc) { '' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Sha1sum can't be blank") + end + end + + context 'with file in Files but not in Checksums-Sha256' do + let(:sha256_dsc) { '' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Sha256sum can't be blank") + end + end + + context 'with invalid MD5' do + let(:md5_dsc) { '1234567890123456789012345678012 671 libs optional sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Md5sum mismatch for sample_1.2.3~alpha2.dsc: 3b0817804f669e16cdefac583ad88f0e != 1234567890123456789012345678012") + end + end + + context 'with invalid SHA1' do + let(:sha1_dsc) { '1234567890123456789012345678901234567890 671 sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Sha1sum mismatch for sample_1.2.3~alpha2.dsc: 32ecbd674f0bfd310df68484d87752490685a8d6 != 1234567890123456789012345678901234567890") + end + end + + context 'with invalid SHA256' do + let(:sha256_dsc) { '1234567890123456789012345678901234567890123456789012345678901234 671 sample_1.2.3~alpha2.dsc' } + + it 'raise ArgumentError', :aggregate_failures do + expect { subject }.to raise_error(described_class::ExtractionError, "Validation failed: Sha256sum mismatch for sample_1.2.3~alpha2.dsc: 844f79825b7e8aaa191e514b58a81f9ac1e58e2180134b0c9512fa66d896d7ba != 1234567890123456789012345678901234567890123456789012345678901234") + end + end + end + + context 'with missing package file' do + before do + incoming.package_files.first.destroy! + end + + it 'raise ArgumentError' do + expect { subject }.to raise_error(described_class::ExtractionError, "sample_1.2.3~alpha2.tar.xz is listed in Files but was not uploaded") + end + end + end +end |