diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index 65771f6b568..ce1c7ad2453 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -202,27 +202,31 @@ export default { return !discussionItem.resolved || isHashTargeted(discussionItem); }; - const addDiscussion = (discussions) => - discussions.filter(({ id }) => discussion.id !== id).concat(discussion); - const file = state.diffFiles.find((diff) => diff.file_hash === fileHash); // a file batch might not be loaded yet when we try to add a discussion if (!file) return; const diffLines = file[INLINE_DIFF_LINES_KEY]; + const addDiscussion = (discussions) => + discussions.filter(({ id }) => discussion.id !== id).concat(discussion); + if (diffLines.length && positionType !== FILE_DIFF_POSITION_TYPE) { const line = diffLines.find(isTargetLine); // skip if none of the discussion positions matched a diff position if (!line) return; - const discussions = addDiscussion(line.discussions || []); + const originalDiscussions = line.discussions || []; + if (originalDiscussions.includes(discussion)) return; + const discussions = addDiscussion(originalDiscussions); Object.assign(line, { discussions, discussionsExpanded: line.discussionsExpanded || discussions.some(isExpandedDiscussion), }); } else { + const originalDiscussions = file.discussions || []; + if (originalDiscussions.includes(discussion)) return; Object.assign(discussion, { expandedOnDiff: isExpandedDiscussion(discussion) }); Object.assign(file, { - discussions: addDiscussion(file.discussions || []), + discussions: addDiscussion(originalDiscussions), }); } }, diff --git a/app/assets/javascripts/diffs/stores/legacy_diffs/mutations.js b/app/assets/javascripts/diffs/stores/legacy_diffs/mutations.js index 44c9ee9b79f..1948a6b6bea 100644 --- a/app/assets/javascripts/diffs/stores/legacy_diffs/mutations.js +++ b/app/assets/javascripts/diffs/stores/legacy_diffs/mutations.js @@ -198,27 +198,31 @@ export default { return !discussionItem.resolved || isHashTargeted(discussionItem); }; - const addDiscussion = (discussions) => - discussions.filter(({ id }) => discussion.id !== id).concat(discussion); - const file = this.diffFiles.find((diff) => diff.file_hash === fileHash); // a file batch might not be loaded yet when we try to add a discussion if (!file) return; const diffLines = file[INLINE_DIFF_LINES_KEY]; + const addDiscussion = (discussions) => + discussions.filter(({ id }) => discussion.id !== id).concat(discussion); + if (diffLines.length && positionType !== FILE_DIFF_POSITION_TYPE) { const line = diffLines.find(isTargetLine); // skip if none of the discussion positions matched a diff position if (!line) return; - const discussions = addDiscussion(line.discussions || []); + const originalDiscussions = line.discussions || []; + if (originalDiscussions.includes(discussion)) return; + const discussions = addDiscussion(originalDiscussions); Object.assign(line, { discussions, discussionsExpanded: line.discussionsExpanded || discussions.some(isExpandedDiscussion), }); } else { + const originalDiscussions = file.discussions || []; + if (originalDiscussions.includes(discussion)) return; Object.assign(discussion, { expandedOnDiff: isExpandedDiscussion(discussion) }); Object.assign(file, { - discussions: addDiscussion(file.discussions || []), + discussions: addDiscussion(originalDiscussions), }); } }, diff --git a/app/assets/javascripts/glql/components/common/facade.vue b/app/assets/javascripts/glql/components/common/facade.vue index 7839d9768a4..c5a367427cb 100644 --- a/app/assets/javascripts/glql/components/common/facade.vue +++ b/app/assets/javascripts/glql/components/common/facade.vue @@ -3,6 +3,8 @@ import { GlAlert, GlLoadingIcon } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import { renderMarkdown } from '~/notes/utils'; import SafeHtml from '~/vue_shared/directives/safe_html'; +import { sha256 } from '~/lib/utils/text_utility'; +import { InternalEvents } from '~/tracking'; import { executeAndPresentQuery } from '../../core'; import Counter from '../../utils/counter'; @@ -17,6 +19,7 @@ export default { directives: { SafeHtml, }, + mixins: [InternalEvents.mixin()], props: { query: { required: true, @@ -62,6 +65,7 @@ export default { try { this.presenterComponent = await executeAndPresentQuery(this.query); + this.trackRender(); } catch (error) { this.handleQueryError(error.message); } finally { @@ -78,6 +82,13 @@ export default { this.error = {}; }, renderMarkdown, + async trackRender() { + try { + this.trackEvent('render_glql_block', { label: await sha256(this.query) }); + } catch (e) { + // ignore any tracking errors + } + }, }, safeHtmlConfig: { ALLOWED_TAGS: ['code'] }, i18n: { diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index 68ab3e15856..8b94c0075b6 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -592,3 +592,10 @@ export const wildcardMatch = (str, pattern) => { const regex = new RegExp(`^${escapedPattern.replace(/\\\*/g, '.*')}$`); return regex.test(lowerStr); }; + +export const sha256 = async (str) => { + const data = new TextEncoder().encode(str); + const hashBuffer = await crypto.subtle.digest('SHA-256', data); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + return hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); +}; diff --git a/config/events/render_glql_block.yml b/config/events/render_glql_block.yml new file mode 100644 index 00000000000..24013d92500 --- /dev/null +++ b/config/events/render_glql_block.yml @@ -0,0 +1,21 @@ +--- +description: GLQL blocks rendered +internal_events: true +action: render_glql_block +identifiers: +- project +- namespace +- user +additional_properties: + label: + description: SHA256 of the GLQL block content +product_group: knowledge +milestone: '17.5' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167165 +distributions: +- ce +- ee +tiers: +- free +- premium +- ultimate diff --git a/config/metrics/counts_28d/count_distinct_label_from_render_glql_block_monthly.yml b/config/metrics/counts_28d/count_distinct_label_from_render_glql_block_monthly.yml new file mode 100644 index 00000000000..f7ad5473c17 --- /dev/null +++ b/config/metrics/counts_28d/count_distinct_label_from_render_glql_block_monthly.yml @@ -0,0 +1,22 @@ +--- +key_path: redis_hll_counters.count_distinct_label_from_render_glql_block_monthly +description: Monthly count of unique GLQL blocks rendered +product_group: knowledge +performance_indicator_type: [] +value_type: number +status: active +milestone: '17.5' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167165 +time_frame: 28d +data_source: internal_events +data_category: optional +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +events: +- name: render_glql_block + unique: label diff --git a/config/metrics/counts_28d/count_distinct_user_id_from_render_glql_block_monthly.yml b/config/metrics/counts_28d/count_distinct_user_id_from_render_glql_block_monthly.yml new file mode 100644 index 00000000000..34ba8bb540e --- /dev/null +++ b/config/metrics/counts_28d/count_distinct_user_id_from_render_glql_block_monthly.yml @@ -0,0 +1,22 @@ +--- +key_path: redis_hll_counters.count_distinct_user_id_from_render_glql_block_monthly +description: Monthly count of unique users who rendered GLQL blocks +product_group: knowledge +performance_indicator_type: [] +value_type: number +status: active +milestone: '17.5' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167165 +time_frame: 28d +data_source: internal_events +data_category: optional +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +events: +- name: render_glql_block + unique: user.id diff --git a/config/metrics/counts_7d/count_distinct_label_from_render_glql_block_weekly.yml b/config/metrics/counts_7d/count_distinct_label_from_render_glql_block_weekly.yml new file mode 100644 index 00000000000..a22a15f96e2 --- /dev/null +++ b/config/metrics/counts_7d/count_distinct_label_from_render_glql_block_weekly.yml @@ -0,0 +1,22 @@ +--- +key_path: redis_hll_counters.count_distinct_label_from_render_glql_block_weekly +description: Weekly count of unique GLQL blocks rendered +product_group: knowledge +performance_indicator_type: [] +value_type: number +status: active +milestone: '17.5' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167165 +time_frame: 7d +data_source: internal_events +data_category: optional +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +events: +- name: render_glql_block + unique: label diff --git a/config/metrics/counts_7d/count_distinct_user_id_from_render_glql_block_weekly.yml b/config/metrics/counts_7d/count_distinct_user_id_from_render_glql_block_weekly.yml new file mode 100644 index 00000000000..eb37f289a27 --- /dev/null +++ b/config/metrics/counts_7d/count_distinct_user_id_from_render_glql_block_weekly.yml @@ -0,0 +1,22 @@ +--- +key_path: redis_hll_counters.count_distinct_user_id_from_render_glql_block_weekly +description: Weekly count of unique users who rendered GLQL blocks +product_group: knowledge +performance_indicator_type: [] +value_type: number +status: active +milestone: '17.5' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167165 +time_frame: 7d +data_source: internal_events +data_category: optional +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +events: +- name: render_glql_block + unique: user.id diff --git a/doc/administration/nfs.md b/doc/administration/nfs.md index 8c718d55208..da477b8781e 100644 --- a/doc/administration/nfs.md +++ b/doc/administration/nfs.md @@ -128,7 +128,6 @@ Here is an example snippet to add to `/etc/fstab`: 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs4 defaults,vers=4.1,hard,rsize=1048576,wsize=1048576,noatime,nofail,_netdev,lookupcache=positive 0 2 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs4 defaults,vers=4.1,hard,rsize=1048576,wsize=1048576,noatime,nofail,_netdev,lookupcache=positive 0 2 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs4 defaults,vers=4.1,hard,rsize=1048576,wsize=1048576,noatime,nofail,_netdev,lookupcache=positive 0 2 -10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,vers=4.1,hard,rsize=1048576,wsize=1048576,noatime,nofail,_netdev,lookupcache=positive 0 2 ``` You can view information and options set for each of the mounted NFS file @@ -208,7 +207,6 @@ restore of backups without manually moving existing data. mountpoint └── gitlab-data ├── builds - ├── git-data ├── shared └── uploads ``` @@ -220,7 +218,6 @@ Mount `/gitlab-nfs` then use the following Linux package configuration to move each data location to a subdirectory: ```ruby -git_data_dirs({"default" => { "path" => "/gitlab-nfs/gitlab-data/git-data"} }) gitlab_rails['uploads_directory'] = '/gitlab-nfs/gitlab-data/uploads' gitlab_rails['shared_path'] = '/gitlab-nfs/gitlab-data/shared' gitlab_ci['builds_directory'] = '/gitlab-nfs/gitlab-data/builds' @@ -242,7 +239,6 @@ NFS mount point is `/gitlab-nfs`. Then, add the following bind mounts in `/etc/fstab`: ```shell -/gitlab-nfs/gitlab-data/git-data /var/opt/gitlab/git-data none bind 0 0 /gitlab-nfs/gitlab-data/.ssh /var/opt/gitlab/.ssh none bind 0 0 /gitlab-nfs/gitlab-data/uploads /var/opt/gitlab/gitlab-rails/uploads none bind 0 0 /gitlab-nfs/gitlab-data/shared /var/opt/gitlab/gitlab-rails/shared none bind 0 0 @@ -255,9 +251,9 @@ are empty before attempting a restore. Read more about the ### Multiple NFS mounts -When using default Linux package configuration, you need to share 4 data locations +When using default Linux package configuration, you need to share 3 data locations between all GitLab cluster nodes. No other locations should be shared. The -following are the 4 locations need to be shared: +following are the 3 locations need to be shared: | Location | Description | Default configuration | | -------- | ----------- | --------------------- | diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index f0d3369d01b..683da223043 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -963,6 +963,17 @@ The following procedure includes steps to back up and edit the `gitlab-secrets.json` file. This file contains secrets that control database encryption. Proceed with caution. +1. Optionally, to enable [access control](#access-control), add the following to `/etc/gitlab/gitlab.rb` and [reconfigure the **GitLab server**](../restart_gitlab.md#reconfigure-a-linux-package-installation): + + ```ruby + gitlab_pages['access_control'] = true + ``` + + WARNING: + If you plan to use GitLab Pages with access control, you must enable it on the first GitLab server before copying `gitlab-secrets.json`. + Enabling access control generates a new OAuth application, and information about it propagates to `gitlab-secrets.json`. If it's not done + in the correct order, you may face issues with access control. + 1. Create a backup of the secrets file on the **GitLab server**: ```shell @@ -975,12 +986,6 @@ database encryption. Proceed with caution. pages_external_url "http://" ``` -1. Optionally, to enable [access control](#access-control), add the following to `/etc/gitlab/gitlab.rb`: - - ```ruby - gitlab_pages['access_control'] = true - ``` - 1. Set up object storage by either: - [Configuring the object storage and migrating GitLab Pages data to it](#object-storage-settings), or - [Configuring network storage](#enable-pages-network-storage-in-multi-node-environments). diff --git a/doc/api/discussions.md b/doc/api/discussions.md index 9956a6f7ea5..63625d94ff4 100644 --- a/doc/api/discussions.md +++ b/doc/api/discussions.md @@ -482,7 +482,9 @@ DETAILS: WARNING: The Epics REST API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/460668) in GitLab 17.0 -and is planned for removal in v5 of the API. Use the [Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. +and is planned for removal in v5 of the API. +In GitLab 17.4 or later, if your administrator [enabled the new look for epics](../user/group/epics/epic_work_items.md), use the +[Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. This change is a breaking change. ### List group epic discussion items diff --git a/doc/api/epic_issues.md b/doc/api/epic_issues.md index 861379ed144..0a67af08be0 100644 --- a/doc/api/epic_issues.md +++ b/doc/api/epic_issues.md @@ -12,7 +12,9 @@ DETAILS: WARNING: The Epics REST API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/460668) in GitLab 17.0 -and is planned for removal in v5 of the API. Use the [Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. +and is planned for removal in v5 of the API. +In GitLab 17.4 or later, if your administrator [enabled the new look for epics](../user/group/epics/epic_work_items.md), use the +[Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. This change is a breaking change. Every API call to the epic issues API endpoint must be authenticated. diff --git a/doc/api/epic_links.md b/doc/api/epic_links.md index 02b4d650fea..35d81884347 100644 --- a/doc/api/epic_links.md +++ b/doc/api/epic_links.md @@ -12,7 +12,9 @@ DETAILS: WARNING: The Epics REST API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/460668) in GitLab 17.0 -and is planned for removal in v5 of the API. Use the [Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. +and is planned for removal in v5 of the API. +In GitLab 17.4 or later, if your administrator [enabled the new look for epics](../user/group/epics/epic_work_items.md), use the +[Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. This change is a breaking change. Manages parent-child [epic relationships](../user/group/epics/manage_epics.md#multi-level-child-epics). diff --git a/doc/api/epics.md b/doc/api/epics.md index e2076d872a7..761b69ad063 100644 --- a/doc/api/epics.md +++ b/doc/api/epics.md @@ -12,7 +12,9 @@ DETAILS: WARNING: The Epics REST API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/460668) in GitLab 17.0 -and is planned for removal in v5 of the API. Use the [Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. +and is planned for removal in v5 of the API. +In GitLab 17.4 or later, if your administrator [enabled the new look for epics](../user/group/epics/epic_work_items.md), use the +[Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. This change is a breaking change. Every API call to epic must be authenticated. diff --git a/doc/api/events.md b/doc/api/events.md index 97e7198811f..9ba17e97c2d 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -17,21 +17,21 @@ DETAILS: See [User contribution events](../user/profile/contributions_calendar.md#user-contribution-events) for available types for the `action` parameter. These options are in lowercase. -### Target Types +### Target types -> - Support for epics [introduced](https://gitlab.com/groups/gitlab-org/-/epics/13056) in GitLab 17.3. +> - Support for epics [introduced](https://gitlab.com/groups/gitlab-org/-/epics/13056) in GitLab 17.3. Your administrator must have [enabled the new look for epics](../user/group/epics/epic_work_items.md). Available target types for the `target_type` parameter are: -- `epic` -- `issue` -- `milestone` -- `merge_request` +- `epic`. Your administrator must have [enabled the new look for epics](../user/group/epics/epic_work_items.md). +- `issue`. +- `milestone`. +- `merge_request`. - `note` - Some notes on merge requests may be of the type `DiscussionNote`, instead of `Note`. `DiscussionNote` items are [not available using the API](discussions.md#understand-note-types-in-the-api). -- `project` -- `snippet` -- `user` +- `project`. +- `snippet`. +- `user`. These options are in lowercase. Some epic features like child items, linked items, start dates, due dates, and health statuses are not available using the API. diff --git a/doc/api/linked_epics.md b/doc/api/linked_epics.md index b2a6899034d..4bcc2a617f0 100644 --- a/doc/api/linked_epics.md +++ b/doc/api/linked_epics.md @@ -15,7 +15,9 @@ DETAILS: WARNING: The Epics REST API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/460668) in GitLab 17.0 -and is planned for removal in v5 of the API. Use the [Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. +and is planned for removal in v5 of the API. +In GitLab 17.4 or later, if your administrator [enabled the new look for epics](../user/group/epics/epic_work_items.md), use the +[Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. This change is a breaking change. If the Related Epics feature is not available in your GitLab plan, a `403` status code is returned. diff --git a/doc/api/notes.md b/doc/api/notes.md index 19039572f96..afb3179e91a 100644 --- a/doc/api/notes.md +++ b/doc/api/notes.md @@ -482,7 +482,9 @@ DETAILS: WARNING: The Epics REST API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/460668) in GitLab 17.0 -and is planned for removal in v5 of the API. Use the [Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. +and is planned for removal in v5 of the API. +In GitLab 17.4 or later, if your administrator [enabled the new look for epics](../user/group/epics/epic_work_items.md), use the +[Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. This change is a breaking change. ### List all epic notes diff --git a/doc/api/resource_label_events.md b/doc/api/resource_label_events.md index 7b459360b54..bd452dd6bb4 100644 --- a/doc/api/resource_label_events.md +++ b/doc/api/resource_label_events.md @@ -107,7 +107,9 @@ DETAILS: WARNING: The Epics REST API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/460668) in GitLab 17.0 -and is planned for removal in v5 of the API. Use the [Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. +and is planned for removal in v5 of the API. +In GitLab 17.4 or later, if your administrator [enabled the new look for epics](../user/group/epics/epic_work_items.md), use the +[Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. This change is a breaking change. ### List group epic label events diff --git a/doc/api/resource_state_events.md b/doc/api/resource_state_events.md index 6a81d308d75..d1e953bd82d 100644 --- a/doc/api/resource_state_events.md +++ b/doc/api/resource_state_events.md @@ -224,7 +224,9 @@ Example response: WARNING: The Epics REST API was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/460668) in GitLab 17.0 -and is planned for removal in v5 of the API. Use the [Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. +and is planned for removal in v5 of the API. +In GitLab 17.4 or later, if your administrator [enabled the new look for epics](../user/group/epics/epic_work_items.md), use the +[Work Items API](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items/) instead. This change is a breaking change. ### List group epic state events diff --git a/doc/user/application_security/policies/pipeline_execution_policies.md b/doc/user/application_security/policies/pipeline_execution_policies.md index 05fa22b790f..a80b07dbb33 100644 --- a/doc/user/application_security/policies/pipeline_execution_policies.md +++ b/doc/user/application_security/policies/pipeline_execution_policies.md @@ -26,7 +26,7 @@ Use Pipeline execution policies to enforce CI/CD jobs for all applicable project The YAML file with pipeline execution policies consists of an array of objects matching pipeline execution policy schema nested under the `pipeline_execution_policy` key. You can configure a maximum of five -policies under the `pipeline_execution_policy` key. Any other policies configured after +policies under the `pipeline_execution_policy` key per security policy project. Any other policies configured after the first five are not applied. When you save a new policy, GitLab validates its contents against [this JSON schema](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/validators/json_schemas/security_orchestration_policy.json). diff --git a/doc/user/application_security/vulnerability_report/index.md b/doc/user/application_security/vulnerability_report/index.md index 6779579491c..c5fbdb1c0c1 100644 --- a/doc/user/application_security/vulnerability_report/index.md +++ b/doc/user/application_security/vulnerability_report/index.md @@ -179,11 +179,6 @@ You can group by: - Tool - OWASP top 10 2017 -WARNING: -Support for grouping by OWASP top 10 2017 was -[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/458835) in GitLab 17.0 and is planned for -removal in 17.6. Support for grouping by OWASP top 10 2021 is proposed in [issue 440182](https://gitlab.com/gitlab-org/gitlab/-/issues/440182) for GitLab 17.5. - ### Group vulnerabilities Group vulnerabilities on the vulnerability report page to more efficiently triage them. diff --git a/doc/user/project/time_tracking.md b/doc/user/project/time_tracking.md index 37c191885f7..1d258868a2b 100644 --- a/doc/user/project/time_tracking.md +++ b/doc/user/project/time_tracking.md @@ -11,14 +11,14 @@ DETAILS: **Offering:** GitLab.com, Self-managed, GitLab Dedicated > - Time tracking for tasks [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/438577) in GitLab 17.0. -> - Time tracking for epics [introduced](https://gitlab.com/groups/gitlab-org/-/epics/12396) in GitLab 17.5. +> - Time tracking for epics [introduced](https://gitlab.com/groups/gitlab-org/-/epics/12396) in GitLab 17.5. Your administrator must have [enabled the new look for epics](../group/epics/epic_work_items.md). You can estimate and track the time you spend on an item, such as: -- [Epic](../group/epics/index.md) -- [Issue](issues/index.md) -- [Task](../tasks.md) -- [Merge request](merge_requests/index.md) +- [Epic](../group/epics/index.md). Your administrator must have [enabled the new look for epics](../group/epics/epic_work_items.md). +- [Issue](issues/index.md). +- [Task](../tasks.md). +- [Merge request](merge_requests/index.md). Then you can [view a report](#view-an-items-time-tracking-report) that shows totals over time. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b9a992fbaf8..e6918631ebc 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -20298,88 +20298,70 @@ msgstr "" msgid "DuoFreeAccessEndingBanner|Starting %{date}, all GitLab Duo features, including Duo Chat, %{link_start}require a paid add-on subscription.%{link_end} To ensure uninterrupted access to Code Suggestions and Chat, buy Duo Pro and assign seats to your users. Or, for Duo Enterprise options, contact Sales." msgstr "" -msgid "DuoProDiscover|Accelerate your path to market" +msgid "DuoProDiscover|A single platform integrates the best AI model for each use case across the entire development workflow." msgstr "" -msgid "DuoProDiscover|Access Chat from the GitLab UI or your preferred IDE." +msgid "DuoProDiscover|Automate mundane tasks" msgstr "" -msgid "DuoProDiscover|Adopt AI with guardrails" +msgid "DuoProDiscover|Boost productivity with smart code assistance" msgstr "" -msgid "DuoProDiscover|Automates repetitive tasks and helps catch bugs early." +msgid "DuoProDiscover|Boost team collaboration" msgstr "" -msgid "DuoProDiscover|Automatically generate lines of code, including full functions, from single and multi-line comments as well as comment blocks." +msgid "DuoProDiscover|Catch bugs early in the workflow by generating tests for the selected content." msgstr "" -msgid "DuoProDiscover|Automatically generate new lines of code from a few typed characters." +msgid "DuoProDiscover|GitLab Duo Chat" msgstr "" -msgid "DuoProDiscover|Available in 15 languages, including C++, C#, Go, Java, JavaScript, Python, PHP, Ruby, Rust, Scala, Kotlin, and TypeScript. And you can use your favorite IDE — VS Code, Visual Studio, JetBrains' suite of IDEs, and Neovim are all supported." +msgid "DuoProDiscover|GitLab Duo Code Suggestions" msgstr "" -msgid "DuoProDiscover|Chat from any location" -msgstr "" - -msgid "DuoProDiscover|Code Explanation" -msgstr "" - -msgid "DuoProDiscover|Code Refactoring" -msgstr "" - -msgid "DuoProDiscover|Code Suggestions" -msgstr "" - -msgid "DuoProDiscover|Code completion" -msgstr "" - -msgid "DuoProDiscover|Code generation" -msgstr "" - -msgid "DuoProDiscover|Committed to transparent AI" -msgstr "" - -msgid "DuoProDiscover|Develop and deploy secure software faster with AI in every phase of the software development lifecycle — from planning and code creation to testing, security, and monitoring." -msgstr "" - -msgid "DuoProDiscover|For organizations and teams to trust AI, it must be transparent. GitLab's %{link_start}AI Transparency Center%{link_end} details how we uphold ethics and transparency in our AI-powered features." -msgstr "" - -msgid "DuoProDiscover|Give your developers a single platform that integrates the best AI model for each use case across the entire workflow, from understanding code to fixing security vulnerabilities." -msgstr "" - -msgid "DuoProDiscover|Helps you understand code by explaining it in natural language." +msgid "DuoProDiscover|GitLab Duo Test generation" msgstr "" msgid "DuoProDiscover|Improve developer experience" msgstr "" -msgid "DuoProDiscover|Language and IDE support" +msgid "DuoProDiscover|Maintain control over the data that's sent to an external large language model (LLM) service. Your organization's proprietary data or code are never used to train external AI models." msgstr "" -msgid "DuoProDiscover|Launch Demo" +msgid "DuoProDiscover|Modernize code faster" msgstr "" -msgid "DuoProDiscover|Read documentation" +msgid "DuoProDiscover|Privacy-first AI" +msgstr "" + +msgid "DuoProDiscover|Real-time guidance" +msgstr "" + +msgid "DuoProDiscover|Refactor code into modern languages with AI-powered GitLab Duo" msgstr "" msgid "DuoProDiscover|Ship software faster and more securely with AI integrated into your entire DevSecOps lifecycle." msgstr "" -msgid "DuoProDiscover|Test Generation" +msgid "DuoProDiscover|Streamline communication, facilitate knowledge sharing, and improve project management." msgstr "" -msgid "DuoProDiscover|What's new in GitLab Duo Chat" +msgid "DuoProDiscover|Streamline the refactoring process with AI-powered recommendations to optimize code structure and improve readability." msgstr "" -msgid "DuoProDiscover|Why GitLab Duo?" +msgid "DuoProDiscover|The GitLab %{link_start}AI Transparency Center%{link_end} details how we uphold ethics and transparency in our AI-powered features." msgstr "" -msgid "DuoProDiscover|With GitLab Duo, you control which users, projects, and groups can use AI-powered capabilities. Also, your organization's proprietary code and data aren't used to train AI models." +msgid "DuoProDiscover|Transparent AI" msgstr "" -msgid "DuoProDiscover|Work to improve existing code quality." +msgid "DuoProDiscover|Use Chat to get up to speed on the status of projects, and quickly learn about GitLab, directly in your IDE or web interface." +msgstr "" + +msgid "DuoProDiscover|Why GitLab Duo Pro?" +msgstr "" + +msgid "DuoProDiscover|Write secure code faster and save time with Code Suggestions in more than 20 languages, available in your favorite IDE." msgstr "" msgid "DuoProTrial|Activate my trial" diff --git a/package.json b/package.json index 886505cb1b1..801971ab320 100644 --- a/package.json +++ b/package.json @@ -273,6 +273,7 @@ "chalk": "^2.4.1", "chokidar": "^3.5.3", "commander": "^2.20.3", + "crypto": "^1.0.1", "custom-jquery-matchers": "^2.1.0", "eslint": "8.57.1", "eslint-import-resolver-jest": "3.0.2", diff --git a/spec/frontend/__helpers__/crypto.js b/spec/frontend/__helpers__/crypto.js new file mode 100644 index 00000000000..08af756b326 --- /dev/null +++ b/spec/frontend/__helpers__/crypto.js @@ -0,0 +1,9 @@ +import crypto from 'crypto'; + +export const stubCrypto = () => { + Object.defineProperty(global.self, 'crypto', { + value: { + subtle: crypto.webcrypto.subtle, + }, + }); +}; diff --git a/spec/frontend/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js index 8c71e47e969..722d54bdc41 100644 --- a/spec/frontend/diffs/store/mutations_spec.js +++ b/spec/frontend/diffs/store/mutations_spec.js @@ -834,6 +834,65 @@ describe('DiffsStoreMutations', () => { expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussionsExpanded).toBe(true); }); + + it('should keep expanded state when re-adding existing discussions', () => { + const diffPosition = { + base_sha: 'ed13df29948c41ba367caa757ab3ec4892509910', + head_sha: 'b921914f9a834ac47e6fd9420f78db0f83559130', + new_line: null, + new_path: '500-lines-4.txt', + old_line: 5, + old_path: '500-lines-4.txt', + start_sha: 'ed13df29948c41ba367caa757ab3ec4892509910', + }; + + const state = { + latestDiff: true, + diffFiles: [ + { + file_hash: 'ABC', + discussions: [], + [INLINE_DIFF_LINES_KEY]: [ + { + line_code: 'ABC_1', + discussions: [], + }, + ], + }, + ], + }; + const discussion = { + id: 1, + line_code: 'ABC_2', + line_codes: ['ABC_1'], + diff_discussion: true, + resolvable: true, + original_position: {}, + position: {}, + positions: [diffPosition], + diff_file: { + file_hash: state.diffFiles[0].file_hash, + }, + }; + + const diffPositionByLineCode = { + ABC_1: diffPosition, + }; + + mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { + discussion, + diffPositionByLineCode, + }); + + mutations[types.SET_EXPAND_ALL_DIFF_DISCUSSIONS](state, false); + + mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, { + discussion, + diffPositionByLineCode, + }); + + expect(state.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussionsExpanded).toBe(false); + }); }); }); diff --git a/spec/frontend/diffs/stores/legacy_diffs/mutations_spec.js b/spec/frontend/diffs/stores/legacy_diffs/mutations_spec.js index becb1e93987..929d44a4581 100644 --- a/spec/frontend/diffs/stores/legacy_diffs/mutations_spec.js +++ b/spec/frontend/diffs/stores/legacy_diffs/mutations_spec.js @@ -822,6 +822,65 @@ describe('DiffsStoreMutations', () => { expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussionsExpanded).toBe(true); }); + + it('should keep expanded state when re-adding existing discussions', () => { + const diffPosition = { + base_sha: 'ed13df29948c41ba367caa757ab3ec4892509910', + head_sha: 'b921914f9a834ac47e6fd9420f78db0f83559130', + new_line: null, + new_path: '500-lines-4.txt', + old_line: 5, + old_path: '500-lines-4.txt', + start_sha: 'ed13df29948c41ba367caa757ab3ec4892509910', + }; + + store.$patch({ + latestDiff: true, + diffFiles: [ + { + file_hash: 'ABC', + discussions: [], + [INLINE_DIFF_LINES_KEY]: [ + { + line_code: 'ABC_1', + discussions: [], + }, + ], + }, + ], + }); + const discussion = { + id: 1, + line_code: 'ABC_2', + line_codes: ['ABC_1'], + diff_discussion: true, + resolvable: true, + original_position: {}, + position: {}, + positions: [diffPosition], + diff_file: { + file_hash: store.diffFiles[0].file_hash, + }, + }; + + const diffPositionByLineCode = { + ABC_1: diffPosition, + }; + + store[types.SET_LINE_DISCUSSIONS_FOR_FILE]({ + discussion, + diffPositionByLineCode, + }); + + store[types.SET_EXPAND_ALL_DIFF_DISCUSSIONS](false); + + store[types.SET_LINE_DISCUSSIONS_FOR_FILE]({ + discussion, + diffPositionByLineCode, + }); + + expect(store.diffFiles[0][INLINE_DIFF_LINES_KEY][0].discussionsExpanded).toBe(false); + }); }); }); diff --git a/spec/frontend/glql/components/common/facade_spec.js b/spec/frontend/glql/components/common/facade_spec.js index 45e8631b9f0..f4d69dedb33 100644 --- a/spec/frontend/glql/components/common/facade_spec.js +++ b/spec/frontend/glql/components/common/facade_spec.js @@ -2,6 +2,8 @@ import { GlAlert, GlLoadingIcon } from '@gitlab/ui'; import { nextTick } from 'vue'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; +import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper'; +import { stubCrypto } from 'helpers/crypto'; import GlqlFacade from '~/glql/components/common/facade.vue'; import { executeAndPresentQuery } from '~/glql/core'; import Counter from '~/glql/utils/counter'; @@ -10,6 +12,8 @@ jest.mock('~/glql/core'); describe('GlqlFacade', () => { let wrapper; + + const { bindInternalEventDocument } = useMockInternalEventsTracking(); const createComponent = (props = {}) => { wrapper = mountExtended(GlqlFacade, { propsData: { @@ -19,6 +23,8 @@ describe('GlqlFacade', () => { }); }; + beforeEach(stubCrypto); + it('renders the query in a code block', () => { createComponent(); expect(wrapper.find('code').text()).toBe('assignee = "foo"'); @@ -41,13 +47,28 @@ describe('GlqlFacade', () => { expect(wrapper.emitted()).toHaveProperty('loaded'); }); - it('renders presenter component after successful query execution', async () => { + describe('when the query is successful', () => { const MockComponent = { render: (h) => h('div') }; - executeAndPresentQuery.mockResolvedValue(MockComponent); - createComponent(); - await waitForPromises(); - expect(wrapper.findComponent(MockComponent).exists()).toBe(true); + beforeEach(async () => { + executeAndPresentQuery.mockResolvedValue(MockComponent); + createComponent(); + await waitForPromises(); + }); + + it('renders presenter component after successful query execution', () => { + expect(wrapper.findComponent(MockComponent).exists()).toBe(true); + }); + + it('tracks GLQL render event', () => { + const { trackEventSpy } = bindInternalEventDocument(wrapper.element); + + expect(trackEventSpy).toHaveBeenCalledWith( + 'render_glql_block', + { label: '2962e3a32ad4bbe0d402e183b60ba858fe907e125df39f3221a01162959531b8' }, + undefined, + ); + }); }); describe('when the query results in an error', () => { diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js index ac0f948bbc5..97e77f41a4b 100644 --- a/spec/frontend/lib/utils/text_utility_spec.js +++ b/spec/frontend/lib/utils/text_utility_spec.js @@ -1,4 +1,5 @@ import * as textUtils from '~/lib/utils/text_utility'; +import { stubCrypto } from 'helpers/crypto'; describe('text_utility', () => { describe('addDelimiter', () => { @@ -458,4 +459,13 @@ describe('text_utility', () => { expect(textUtils.wildcardMatch(str, pattern)).toBe(result); }); }); + + describe('sha256', () => { + beforeEach(stubCrypto); + + it('returns a sha256 hash', async () => { + const hash = await textUtils.sha256('How vexingly quick daft zebras jump!'); + expect(hash).toBe('3f7282eed1c3cef3efc993275e9b9cc0cfe85927450d6b0e5d73a2c59663232e'); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index f6912ad231c..0ab64e067e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5255,6 +5255,11 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +crypto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" + integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== + css-functions-list@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/css-functions-list/-/css-functions-list-3.2.1.tgz#2eb205d8ce9f9ce74c5c1d7490b66b77c45ce3ea" @@ -13135,16 +13140,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -13197,7 +13193,7 @@ string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -13211,13 +13207,6 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -14909,7 +14898,7 @@ worker-loader@^3.0.8: loader-utils "^2.0.0" schema-utils "^3.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -14927,15 +14916,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"