mirror of
https://gitlab.com/gitlab-org/gitlab-foss.git
synced 2025-08-15 21:39:00 +00:00
Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
<script>
|
||||
import VueRouter from 'vue-router';
|
||||
import { GlTab, GlTabs, GlButton, GlSprintf, GlIcon, GlLink } from '@gitlab/ui';
|
||||
import { GlAvatar, GlTab, GlTabs, GlButton, GlSprintf, GlIcon, GlLink } from '@gitlab/ui';
|
||||
import TitleArea from '~/vue_shared/components/registry/title_area.vue';
|
||||
import * as Sentry from '~/sentry/sentry_browser_wrapper';
|
||||
import { createAlert, VARIANT_DANGER } from '~/alert';
|
||||
@ -35,6 +35,7 @@ const routes = [
|
||||
export default {
|
||||
name: 'ShowMlModelVersionApp',
|
||||
components: {
|
||||
GlAvatar,
|
||||
GlButton,
|
||||
LoadOrErrorOrShow,
|
||||
ModelVersionActionsDropdown,
|
||||
@ -166,6 +167,12 @@ export default {
|
||||
tabIndex() {
|
||||
return routes.findIndex(({ name }) => name === this.$route.name);
|
||||
},
|
||||
showAuthor() {
|
||||
return Boolean(this.modelVersion?.author);
|
||||
},
|
||||
author() {
|
||||
return this.modelVersion?.author;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleError(error) {
|
||||
@ -216,6 +223,7 @@ export default {
|
||||
},
|
||||
i18n: {
|
||||
editModelVersionButtonLabel: s__('MlModelRegistry|Edit model version'),
|
||||
authorTitle: s__('MlModelRegistry|Publisher'),
|
||||
},
|
||||
ROUTE_DETAILS,
|
||||
ROUTE_PERFORMANCE,
|
||||
@ -241,10 +249,10 @@ export default {
|
||||
<template #author>
|
||||
<gl-link
|
||||
class="js-user-link gl-font-bold !gl-text-gray-500"
|
||||
:href="modelVersion.author.webUrl"
|
||||
:href="author.webUrl"
|
||||
:data-user-id="authorId"
|
||||
>
|
||||
<span class="sm:gl-inline">{{ modelVersion.author.name }}</span>
|
||||
<span class="sm:gl-inline">{{ author.name }}</span>
|
||||
</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
@ -264,23 +272,41 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<load-or-error-or-show :is-loading="isLoading" :error-message="errorMessage">
|
||||
<gl-tabs class="gl-mt-4" :value="tabIndex">
|
||||
<gl-tab
|
||||
:title="s__('MlModelRegistry|Version card')"
|
||||
@click="goTo($options.ROUTE_DETAILS)"
|
||||
/>
|
||||
<gl-tab
|
||||
:title="s__('MlModelRegistry|Performance')"
|
||||
@click="goTo($options.ROUTE_PERFORMANCE)"
|
||||
/>
|
||||
</gl-tabs>
|
||||
<router-view
|
||||
:model-version="modelVersion"
|
||||
can-write-model-registry
|
||||
import-path
|
||||
allow-artifact-import
|
||||
/>
|
||||
</load-or-error-or-show>
|
||||
<div class="gl-grid gl-gap-3 md:gl-grid-cols-4">
|
||||
<div class="md:gl-col-span-3 md:gl-pr-8">
|
||||
<load-or-error-or-show :is-loading="isLoading" :error-message="errorMessage">
|
||||
<gl-tabs class="gl-mt-4" :value="tabIndex">
|
||||
<gl-tab
|
||||
:title="s__('MlModelRegistry|Version card')"
|
||||
@click="goTo($options.ROUTE_DETAILS)"
|
||||
/>
|
||||
<gl-tab
|
||||
:title="s__('MlModelRegistry|Performance')"
|
||||
@click="goTo($options.ROUTE_PERFORMANCE)"
|
||||
/>
|
||||
</gl-tabs>
|
||||
<router-view
|
||||
:model-version="modelVersion"
|
||||
can-write-model-registry
|
||||
import-path
|
||||
allow-artifact-import
|
||||
/>
|
||||
</load-or-error-or-show>
|
||||
</div>
|
||||
|
||||
<div class="gl-pt-6 md:gl-col-span-1">
|
||||
<div class="gl-text-lg gl-font-bold">{{ $options.i18n.authorTitle }}</div>
|
||||
<div v-if="showAuthor" class="gl-mt-3 gl-text-gray-500">
|
||||
<gl-link
|
||||
data-testid="sidebar-author-link"
|
||||
class="js-user-link gl-font-bold !gl-text-gray-500"
|
||||
:href="author.webUrl"
|
||||
>
|
||||
<gl-avatar :label="author.name" :src="author.avatarUrl" :size="24" />
|
||||
{{ author.name }}
|
||||
</gl-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -22,6 +22,7 @@ class WorkItem < Issue
|
||||
foreign_key: 'issue_id',
|
||||
inverse_of: :work_item,
|
||||
autosave: true
|
||||
has_one :weights_source, class_name: 'WorkItems::WeightsSource'
|
||||
|
||||
has_many :child_links, class_name: '::WorkItems::ParentLink', foreign_key: :work_item_parent_id
|
||||
has_many :work_item_children, through: :child_links, class_name: 'WorkItem',
|
||||
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
migration_job_name: BackfillDastScannerProfilesBuildsProjectId
|
||||
description: Backfills sharding key `dast_scanner_profiles_builds.project_id` from `dast_scanner_profiles`.
|
||||
description: Backfills sharding key `dast_scanner_profiles_builds.project_id` from
|
||||
`dast_scanner_profiles`.
|
||||
feature_category: dynamic_application_security_testing
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167626
|
||||
milestone: '17.5'
|
||||
queued_migration_version: 20240930135301
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
finalized_by: '20241027232438'
|
||||
|
12
db/docs/work_item_weights_sources.yml
Normal file
12
db/docs/work_item_weights_sources.yml
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
table_name: work_item_weights_sources
|
||||
classes:
|
||||
- WorkItems::WeightsSource
|
||||
feature_categories:
|
||||
- team_planning
|
||||
description: Weight data associated to work items.
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/170371
|
||||
milestone: '17.6'
|
||||
gitlab_schema: gitlab_main_cell
|
||||
sharding_key:
|
||||
namespace_id: namespaces
|
@ -0,0 +1,20 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CreateWorkItemWeightsSourcesTable < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.6'
|
||||
|
||||
def change
|
||||
create_table :work_item_weights_sources, id: false do |t| # rubocop:disable Migration/EnsureFactoryForTable -- factory is in ee/spec/factories/work_items/weights_sources.rb
|
||||
t.references :work_item,
|
||||
primary_key: true,
|
||||
default: nil,
|
||||
foreign_key: { on_delete: :cascade, to_table: :issues }
|
||||
t.bigint :namespace_id, null: false
|
||||
t.bigint :rolled_up_weight
|
||||
t.bigint :rolled_up_completed_weight
|
||||
t.timestamps_with_timezone null: false
|
||||
|
||||
t.index :namespace_id
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class AddWorkItemWeightsSourcesNamespaceFk < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.6'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :work_item_weights_sources, :namespaces,
|
||||
column: :namespace_id, on_delete: :cascade
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_foreign_key :work_item_weights_sources, column: :namespace_id
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,21 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class FinalizeBackfillDastScannerProfilesBuildsProjectId < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.6'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_sec
|
||||
|
||||
def up
|
||||
ensure_batched_background_migration_is_finished(
|
||||
job_class_name: 'BackfillDastScannerProfilesBuildsProjectId',
|
||||
table_name: :dast_scanner_profiles_builds,
|
||||
column_name: :ci_build_id,
|
||||
job_arguments: [:project_id, :dast_scanner_profiles, :project_id, :dast_scanner_profile_id],
|
||||
finalize: true
|
||||
)
|
||||
end
|
||||
|
||||
def down; end
|
||||
end
|
1
db/schema_migrations/20241023090142
Normal file
1
db/schema_migrations/20241023090142
Normal file
@ -0,0 +1 @@
|
||||
be5034e209a4f141ef3dd8530aee4337b4cce96010fc1132a661e930cf9bad2f
|
1
db/schema_migrations/20241023091855
Normal file
1
db/schema_migrations/20241023091855
Normal file
@ -0,0 +1 @@
|
||||
3092506e75b1aebe2f9e6047629316331eaf22e080043567c1c492b91aed9e86
|
1
db/schema_migrations/20241027232438
Normal file
1
db/schema_migrations/20241027232438
Normal file
@ -0,0 +1 @@
|
||||
bf583f1f7f53e25e861091a8afdf962d2fcc701f294620bbd9d4c3c9b8c12ae1
|
@ -21674,6 +21674,15 @@ CREATE SEQUENCE work_item_types_id_seq
|
||||
|
||||
ALTER SEQUENCE work_item_types_id_seq OWNED BY work_item_types.id;
|
||||
|
||||
CREATE TABLE work_item_weights_sources (
|
||||
work_item_id bigint NOT NULL,
|
||||
namespace_id bigint NOT NULL,
|
||||
rolled_up_weight bigint,
|
||||
rolled_up_completed_weight bigint,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE work_item_widget_definitions (
|
||||
id bigint NOT NULL,
|
||||
work_item_type_id bigint NOT NULL,
|
||||
@ -26525,6 +26534,9 @@ ALTER TABLE ONLY work_item_type_custom_fields
|
||||
ALTER TABLE ONLY work_item_types
|
||||
ADD CONSTRAINT work_item_types_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY work_item_weights_sources
|
||||
ADD CONSTRAINT work_item_weights_sources_pkey PRIMARY KEY (work_item_id);
|
||||
|
||||
ALTER TABLE ONLY work_item_widget_definitions
|
||||
ADD CONSTRAINT work_item_widget_definitions_pkey PRIMARY KEY (id);
|
||||
|
||||
@ -32342,6 +32354,10 @@ CREATE UNIQUE INDEX index_work_item_types_on_correct_id_unique ON work_item_type
|
||||
|
||||
CREATE UNIQUE INDEX index_work_item_types_on_name_unique ON work_item_types USING btree (TRIM(BOTH FROM lower(name)));
|
||||
|
||||
CREATE INDEX index_work_item_weights_sources_on_namespace_id ON work_item_weights_sources USING btree (namespace_id);
|
||||
|
||||
CREATE INDEX index_work_item_weights_sources_on_work_item_id ON work_item_weights_sources USING btree (work_item_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_work_item_widget_definitions_on_type_id_and_name ON work_item_widget_definitions USING btree (work_item_type_id, TRIM(BOTH FROM lower(name)));
|
||||
|
||||
CREATE INDEX index_work_item_widget_definitions_on_work_item_type_id ON work_item_widget_definitions USING btree (work_item_type_id);
|
||||
@ -35619,6 +35635,9 @@ ALTER TABLE ONLY sprints
|
||||
ALTER TABLE ONLY alert_management_alert_metric_images
|
||||
ADD CONSTRAINT fk_80b75a6094 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY work_item_weights_sources
|
||||
ADD CONSTRAINT fk_815ba3b395 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY alert_management_alert_user_mentions
|
||||
ADD CONSTRAINT fk_8175238264 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
@ -37278,6 +37297,9 @@ ALTER TABLE ONLY ci_cost_settings
|
||||
ALTER TABLE ONLY operations_feature_flags_issues
|
||||
ADD CONSTRAINT fk_rails_6a8856ca4f FOREIGN KEY (feature_flag_id) REFERENCES operations_feature_flags(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY work_item_weights_sources
|
||||
ADD CONSTRAINT fk_rails_6ac227847a FOREIGN KEY (work_item_id) REFERENCES issues(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY import_source_users
|
||||
ADD CONSTRAINT fk_rails_6aee6cd676 FOREIGN KEY (placeholder_user_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
|
@ -349,6 +349,9 @@ To migrate back to local storage:
|
||||
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/11872) in GitLab 17.2.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/charts/gitlab/-/merge_requests/3845) for Helm chart (Kubernetes) in GitLab 17.3.
|
||||
|
||||
WARNING:
|
||||
This feature is affected by [a known issue](https://github.com/git-lfs/git-lfs/issues/5880). If you clone a repository with multiple Git LFS objects using the pure SSH protocol, the client might crash due to a `nil` pointer reference.
|
||||
|
||||
[`git-lfs` 3.0.0](https://github.com/git-lfs/git-lfs/blob/main/CHANGELOG.md#300-24-sep-2021)
|
||||
released support for using SSH as the transfer protocol instead of HTTP.
|
||||
SSH is handled transparently by the `git-lfs` command line tool.
|
||||
|
@ -848,8 +848,8 @@ The script does not print job templates that:
|
||||
```shell
|
||||
export GL_GROUP_ID=56595735
|
||||
|
||||
# Script requires pyyaml too.
|
||||
pip3 install python-gitlab pyyaml
|
||||
# Install script dependencies
|
||||
python3 -m pip install 'python-gitlab[yaml]'
|
||||
|
||||
python3 get_all_cicd_config_artifacts_expiry.py
|
||||
|
||||
|
@ -1,251 +1,249 @@
|
||||
{
|
||||
"qa/specs/features/api/10_govern/group_access_token_spec.rb": 22.368781745999968,
|
||||
"qa/specs/features/api/10_govern/project_access_token_spec.rb": 23.633304253000006,
|
||||
"qa/specs/features/api/1_manage/rate_limits_spec.rb": 3.1467832400003317,
|
||||
"qa/specs/features/api/2_plan/closes_issue_via_pushing_a_commit_spec.rb": 7.066938178999862,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb": 8.327959042000202,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb": 8.461132950999854,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb": 26.816687255000033,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb": 8.936014013000204,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb": 22.04809043399996,
|
||||
"qa/specs/features/api/3_create/merge_request/view_merge_requests_spec.rb": 0.07425793599986719,
|
||||
"qa/specs/features/api/3_create/repository/add_list_delete_branches_spec.rb": 5.418077632999939,
|
||||
"qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb": 9.446346610999626,
|
||||
"qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb": 8.764005738999913,
|
||||
"qa/specs/features/api/3_create/repository/files_spec.rb": 4.811035699000058,
|
||||
"qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb": 4.926575522999883,
|
||||
"qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb": 6.722170169000037,
|
||||
"qa/specs/features/api/3_create/repository/storage_size_spec.rb": 15.720025386000088,
|
||||
"qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb": 2.1978715340001145,
|
||||
"qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb": 45.16411672999993,
|
||||
"qa/specs/features/api/4_verify/cancel_pipeline_when_block_user_spec.rb": 10.504823287000363,
|
||||
"qa/specs/features/api/4_verify/file_variable_spec.rb": 34.60797590999982,
|
||||
"qa/specs/features/api/4_verify/job_downloads_artifacts_spec.rb": 25.90519738099988,
|
||||
"qa/specs/features/api/9_data_stores/user_inherited_access_spec.rb": 72.58973470299998,
|
||||
"qa/specs/features/api/9_data_stores/users_spec.rb": 0.46943177399998604,
|
||||
"qa/specs/features/browser_ui/10_govern/group/group_access_token_spec.rb": 10.867392854000173,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_recovery_spec.rb": 44.48518719200001,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_ssh_recovery_spec.rb": 45.29727074599987,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_spec.rb": 9.24467735899998,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_with_2fa_spec.rb": 71.21219507099977,
|
||||
"qa/specs/features/browser_ui/10_govern/login/login_via_oauth_and_oidc_with_gitlab_as_idp_spec.rb": 434.290598865,
|
||||
"qa/specs/features/browser_ui/10_govern/login/register_spec.rb": 167.21751289999997,
|
||||
"qa/specs/features/browser_ui/10_govern/project/project_access_token_spec.rb": 15.002181600999847,
|
||||
"qa/specs/features/browser_ui/10_govern/user/impersonation_token_spec.rb": 34.3730335500004,
|
||||
"qa/specs/features/browser_ui/10_govern/user/user_access_termination_spec.rb": 42.11820167299993,
|
||||
"qa/specs/features/browser_ui/14_analytics/performance_bar_spec.rb": 18.42159530700019,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_default_enabled_spec.rb": 12.865294517000166,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb": 64.23631289100013,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb": 13.557514046000051,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb": 15.577577431000009,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb": 16.93965877000005,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb": 15.712710897999841,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb": 11.492959395999605,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb": 14.383327321999786,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb": 47.38104187599993,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb": 18.069448444000045,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/export_as_csv_spec.rb": 20.876328290999936,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb": 21.686897792999844,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb": 17.499067247999847,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb": 20.052594967999994,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb": 15.281954894000137,
|
||||
"qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb": 9.612242246999813,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb": 58.40833293900005,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb": 12.194349004999822,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb": 16.66858077699999,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_creation_spec.rb": 57.8606801320002,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_manipulation_spec.rb": 36.7572706560004,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_directory_management_spec.rb": 13.130093501000374,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_file_upload_spec.rb": 26.96827703400004,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_list_spec.rb": 47.06445215099984,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_page_deletion_spec.rb": 32.69071444200017,
|
||||
"qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb": 12.757383355999991,
|
||||
"qa/specs/features/browser_ui/2_plan/transient/comment_on_discussion_spec.rb": 94.82951411399995,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_a_merge_spec.rb": 28.79146358800017,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb": 15.203331360999982,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb": 34.60106437700006,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb": 16.876695956000276,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb": 20.24183390600001,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb": 39.35212773400008,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb": 16.664726423000047,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb": 29.294655512999725,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb": 29.818330108000055,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb": 49.977204337999865,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb": 74.05581426499998,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb": 25.886177607000036,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb": 58.32857243300032,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_new_branch_rule_spec.rb": 19.121554731999822,
|
||||
"qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb": 12.009725652999805,
|
||||
"qa/specs/features/browser_ui/3_create/repository/clone_spec.rb": 14.347447782000017,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb": 16.534865420999722,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb": 13.119983175000016,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb": 15.378666402999897,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb": 15.938765588000024,
|
||||
"qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb": 21.63542792399994,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb": 79.99040074799996,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb": 12.59656118699968,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb": 14.377605238999877,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb": 12.259645183999965,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb": 61.57848500299997,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb": 58.59291245999975,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb": 40.70022904899997,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb": 12.597955936999824,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb": 23.335798235999846,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb": 11.16296970899998,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb": 19.33630398599996,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_create_spec.rb": 15.139340509000021,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_delete_spec.rb": 15.598802374000115,
|
||||
"qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb": 33.16634144599993,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb": 43.07075570100005,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb": 32.04896657299969,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb": 44.12173341700009,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb": 45.1201057369999,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb": 35.046242132000316,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb": 11.228186354000172,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb": 11.679584620000014,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb": 17.142693512999813,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb": 25.128736303000096,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb": 22.43362897299994,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb": 20.63210230699997,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb": 51.18759698200006,
|
||||
"qa/specs/features/browser_ui/3_create/source_editor/source_editor_toolbar_spec.rb": 18.490141503999894,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb": 47.41684779299976,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb": 55.95471532700003,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/closing_web_ide_with_unsaved_changes_spec.rb": 17.89687930900027,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb": 127.97824422899998,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/ci_catalog_sorting_spec.rb": 92.95931840900016,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/run_component_in_project_pipeline_spec.rb": 35.63051462899966,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/expose_job_artifacts_in_mr_spec.rb": 28.20214109099993,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/job_artifacts_access_keyword_spec.rb": 110.66918478300022,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_pipelines_spec.rb": 217.47925253999983,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_project_artifacts/user_can_bulk_delete_artifacts_spec.rb": 26.754828502000237,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb": 31.085558825000135,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb": 44.06894120200013,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb": 35.78982095099991,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb": 29.708999760000097,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb": 50.36197774100037,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb": 13.341353649999746,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb": 42.411010002999774,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_multiple_projects_spec.rb": 29.5737167530001,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_independent_relationship_spec.rb": 48.13610776800033,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb": 28.524868175999927,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb": 106.57642176700028,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb": 43.70788082299987,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb": 36.24359708399993,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb": 34.50332342399997,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb": 24.002371265999955,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_management/group_runner_counts_spec.rb": 10.757894206999936,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_management/group_runner_status_counts_spec.rb": 10.301866036000092,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb": 12.12438019199999,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb": 15.005740251000134,
|
||||
"qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb": 29.49941567299993,
|
||||
"qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb": 51.479649964000146,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/composer_registry_spec.rb": 27.352062939999996,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/conan_repository_spec.rb": 81.84952514700012,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/generic_repository_spec.rb": 28.553728266000235,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb": 168.17629702600016,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb": 371.8446552810001,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb": 147.34913712900016,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb": 143.3438839700002,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_group_level_spec.rb": 212.5769153199999,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb": 154.38543904599965,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb": 65.49403680400019,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb": 14.485062738000124,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb": 95.86104218500031,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb": 7.291155398999763,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb": 28.596902782000143,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb": 51.41727816599996,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb": 30.579300772999886,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/recovery_alert_resolves_correct_alert_spec.rb": 17.057656027999656,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/group_member_access_request_spec.rb": 62.32897362400013,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/transfer_project_spec.rb": 18.02293271999997,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/add_project_member_spec.rb": 17.59251898699995,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/create_project_badge_spec.rb": 15.446313674000066,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/create_project_spec.rb": 18.019042763000016,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/dashboard_images_spec.rb": 14.63526036400026,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/invite_group_to_project_spec.rb": 50.74090813900011,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/project_owner_permissions_spec.rb": 44.86465373500005,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/view_project_activity_spec.rb": 10.57122799900003,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/follow_user_activity_spec.rb": 17.56207801699975,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/parent_group_access_termination_spec.rb": 27.12611723300006,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/user_inherited_access_spec.rb": 22.777019481000025,
|
||||
"qa/specs/features/ee/api/10_govern/compliance_pipeline_spec.rb": 9.899398154999744,
|
||||
"qa/specs/features/ee/api/10_govern/instance_audit_event_streaming_spec.rb": 13.41729055199994,
|
||||
"qa/specs/features/ee/api/10_govern/user/minimal_access_user_spec.rb": 52.863767513999846,
|
||||
"qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 24.66561908299991,
|
||||
"qa/specs/features/ee/api/3_create/code_suggestions_spec.rb": 9.734530240000026,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/change_vulnerability_status_spec.rb": 78.78103058800025,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb": 41.02528116299982,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/dismissed_vulnerabilities_in_security_widget_spec.rb": 75.75522501800015,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb": 21.53629811100018,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb": 87.46915877299989,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_event_streaming_spec.rb": 19.81644356400011,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_logs_1_spec.rb": 94.59999707999987,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/restrict_by_ip_address_spec.rb": 244.94209053500003,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/instance/instance_audit_logs_spec.rb": 149.11489667600017,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/policies_list_spec.rb": 23.17459256999996,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project/project_audit_logs_spec.rb": 142.88678456800017,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project_security_dashboard_spec.rb": 32.639712987000166,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_execution_policy_vulnerabilities_spec.rb": 87.48531807299992,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb": 100.16423248799993,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb": 219.48511522400008,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/user/minimal_access_user_spec.rb": 10.48683282300044,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb": 223.67916750899985,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerability_security_training_spec.rb": 90.11761338999986,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb": 6.416189227000359,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/utilization/user_registration_billing_spec.rb": 25.087748015999978,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/enable_advanced_sast_spec.rb": 98.2445041179999,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb": 50.96775453700002,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/on_demand_dast_spec.rb": 95.48979141200016,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/contribution_analytics_spec.rb": 31.527241697000136,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/mr_analytics_spec.rb": 25.38604634799981,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/value_stream_analytics_spec.rb": 23.01250367600005,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb": 10.041722617999994,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb": 12.056798871999945,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb": 134.200256243,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/promote_issue_to_epic_spec.rb": 28.479518154999823,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb": 10.634338394000224,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/create_group_wiki_page_spec.rb": 29.492960455999764,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/delete_group_wiki_page_spec.rb": 9.413657116000195,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/file_upload_group_wiki_page_spec.rb": 14.95102097400013,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/insights/default_insights_spec.rb": 24.128866205000122,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue/default_issue_template_spec.rb": 15.553754882000248,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configurable_issue_board_spec.rb": 11.234492763999697,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configure_issue_board_by_label_spec.rb": 18.468924121000327,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/create_group_issue_board_spec.rb": 17.354496787000244,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/group_issue_boards_spec.rb": 18.92236214400009,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb": 39.630014759000005,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/read_only_board_configuration_spec.rb": 24.101322554000035,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/sum_of_issues_weights_spec.rb": 12.440506615000004,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_analytics/issues_analytics_spec.rb": 21.37253022599998,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_weight/issue_weight_visualization_spec.rb": 12.460709697999846,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/assign_group_iteration_spec.rb": 13.901354654999977,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/create_group_iteration_spec.rb": 33.006181079000044,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/four_assignees_spec.rb": 12.56144702499978,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb": 32.16122686400013,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb": 13.211152739000227,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 57.75658679099979,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/approval_rules_spec.rb": 96.94270416100017,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/default_merge_request_template_spec.rb": 33.12321659999998,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/assign_code_owners_spec.rb": 28.152587653999944,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb": 16.502650385000152,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_with_protected_branch_and_squashed_commits_spec.rb": 25.722663802999932,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb": 200.75515444999974,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/group_file_template_spec.rb": 92.8232680189999,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_root_group_spec.rb": 81.4973934489999,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_subgroup_spec.rb": 141.39839299200003,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/project_templates_spec.rb": 72.26127357299993,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_http_spec.rb": 29.386763341999995,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb": 45.32656895399987,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb": 244.24686534299963,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb": 224.68295732599972,
|
||||
"qa/specs/features/ee/browser_ui/3_create/web_ide/code_suggestions_in_web_ide_spec.rb": 70.40918833,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/multi-project_pipelines_spec.rb": 16.11996115000011,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/parent_child_pipelines_dependent_relationship_spec.rb": 46.88418564700032,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb": 36.758229692000214,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipelines_for_merged_results_and_merge_trains_spec.rb": 79.00419656800022,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/transient/merge_trains_transient_bug_spec.rb": 102.89802900000018,
|
||||
"qa/specs/features/ee/browser_ui/8_monitor/incident_management/incident_quick_action_spec.rb": 14.259584643000153,
|
||||
"qa/specs/features/ee/browser_ui/9_data_stores/group/prevent_forking_outside_group_spec.rb": 38.22584831700033,
|
||||
"qa/specs/features/ee/browser_ui/9_data_stores/group/share_group_with_group_spec.rb": 24.902903569000046
|
||||
"qa/specs/features/api/10_govern/group_access_token_spec.rb": 12.751388505000023,
|
||||
"qa/specs/features/api/10_govern/project_access_token_spec.rb": 32.89926324599992,
|
||||
"qa/specs/features/api/1_manage/rate_limits_spec.rb": 29.133628446999865,
|
||||
"qa/specs/features/api/2_plan/closes_issue_via_pushing_a_commit_spec.rb": 13.026944022999942,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb": 11.490352364000046,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb": 6.069066840000005,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_remove_source_branch_spec.rb": 24.042001749000065,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_target_branch_spec.rb": 24.645955939000032,
|
||||
"qa/specs/features/api/3_create/merge_request/push_options_title_description_spec.rb": 24.601507323999954,
|
||||
"qa/specs/features/api/3_create/merge_request/view_merge_requests_spec.rb": 0.01137709700014966,
|
||||
"qa/specs/features/api/3_create/repository/add_list_delete_branches_spec.rb": 6.916183379000017,
|
||||
"qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb": 14.377443990999836,
|
||||
"qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb": 5.819943247000083,
|
||||
"qa/specs/features/api/3_create/repository/files_spec.rb": 5.173241866999888,
|
||||
"qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb": 4.929177706000246,
|
||||
"qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb": 9.340088966999929,
|
||||
"qa/specs/features/api/3_create/repository/storage_size_spec.rb": 11.24625387900005,
|
||||
"qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb": 4.532630837999932,
|
||||
"qa/specs/features/api/4_verify/api_variable_inheritance_with_forward_pipeline_variables_spec.rb": 46.805881252999825,
|
||||
"qa/specs/features/api/4_verify/cancel_pipeline_when_block_user_spec.rb": 8.743665168999996,
|
||||
"qa/specs/features/api/4_verify/file_variable_spec.rb": 24.386426346999997,
|
||||
"qa/specs/features/api/4_verify/job_downloads_artifacts_spec.rb": 16.590649373000133,
|
||||
"qa/specs/features/api/9_data_stores/user_inherited_access_spec.rb": 71.2855975110001,
|
||||
"qa/specs/features/api/9_data_stores/users_spec.rb": 0.4440915310001401,
|
||||
"qa/specs/features/browser_ui/10_govern/group/group_access_token_spec.rb": 12.470258933999958,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_recovery_spec.rb": 36.39139395800021,
|
||||
"qa/specs/features/browser_ui/10_govern/login/2fa_ssh_recovery_spec.rb": 41.08415781800022,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_spec.rb": 8.281860885000015,
|
||||
"qa/specs/features/browser_ui/10_govern/login/log_in_with_2fa_spec.rb": 86.3487416539997,
|
||||
"qa/specs/features/browser_ui/10_govern/login/login_via_oauth_and_oidc_with_gitlab_as_idp_spec.rb": 499.21237998800007,
|
||||
"qa/specs/features/browser_ui/10_govern/login/register_spec.rb": 200.75919483400003,
|
||||
"qa/specs/features/browser_ui/10_govern/project/project_access_token_spec.rb": 11.893035009999949,
|
||||
"qa/specs/features/browser_ui/10_govern/user/impersonation_token_spec.rb": 29.84007964699981,
|
||||
"qa/specs/features/browser_ui/10_govern/user/user_access_termination_spec.rb": 40.08391615200003,
|
||||
"qa/specs/features/browser_ui/14_analytics/performance_bar_spec.rb": 21.119296476000272,
|
||||
"qa/specs/features/browser_ui/14_analytics/service_ping_default_enabled_spec.rb": 10.500143610000123,
|
||||
"qa/specs/features/browser_ui/1_manage/integrations/jenkins/jenkins_build_status_spec.rb": 65.06015029199989,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/add_design_content_spec.rb": 17.374500398000237,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/archive_design_content_spec.rb": 16.848764783999968,
|
||||
"qa/specs/features/browser_ui/2_plan/design_management/modify_design_content_spec.rb": 15.021202979999998,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb": 18.450994444000116,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/collapse_comments_in_discussions_spec.rb": 17.622998689000042,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/comment_issue_spec.rb": 14.794653964999952,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb": 41.69908395199991,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/custom_issue_template_spec.rb": 20.513510600000018,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/export_as_csv_spec.rb": 18.107505599999968,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/filter_issue_comments_spec.rb": 16.460536250999894,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb": 15.750375492000103,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/mentions_spec.rb": 17.857268017000024,
|
||||
"qa/specs/features/browser_ui/2_plan/issue/real_time_assignee_spec.rb": 13.975939827000275,
|
||||
"qa/specs/features/browser_ui/2_plan/issue_boards/focus_mode_spec.rb": 10.05644193199987,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/assign_milestone_spec.rb": 67.57972961699988,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_group_milestone_spec.rb": 13.48369254499994,
|
||||
"qa/specs/features/browser_ui/2_plan/milestone/create_project_milestone_spec.rb": 20.18888547100005,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_creation_spec.rb": 71.71342247399991,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_content_manipulation_spec.rb": 37.97556997300012,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_directory_management_spec.rb": 13.762443853000036,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_file_upload_spec.rb": 29.93055537700002,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_list_spec.rb": 41.261750471999676,
|
||||
"qa/specs/features/browser_ui/2_plan/project_wiki/project_based_page_deletion_spec.rb": 38.11781402800011,
|
||||
"qa/specs/features/browser_ui/2_plan/related_issues/related_issues_spec.rb": 12.23750995599994,
|
||||
"qa/specs/features/browser_ui/2_plan/transient/comment_on_discussion_spec.rb": 133.01163271799987,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_a_merge_spec.rb": 23.313274683000145,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/cherry_pick/cherry_pick_commit_spec.rb": 14.570134598999857,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb": 40.756809996000015,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb": 29.887464119000015,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb": 20.59689329899993,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb": 62.44206307600007,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/revert_commit_spec.rb": 17.132142855999973,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb": 36.95017465400019,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb": 22.052299305999895,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/batch_suggestion_spec.rb": 49.84356878200015,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/suggestions/custom_commit_suggestion_spec.rb": 33.78413305000004,
|
||||
"qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb": 44.768547936000004,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb": 63.99649399300006,
|
||||
"qa/specs/features/browser_ui/3_create/repository/add_new_branch_rule_spec.rb": 20.69057171999998,
|
||||
"qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb": 25.212562218000016,
|
||||
"qa/specs/features/browser_ui/3_create/repository/clone_spec.rb": 7.695580884999913,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb": 14.542094647999875,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb": 16.766271739999866,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb": 17.48617004699986,
|
||||
"qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb": 15.972208421000005,
|
||||
"qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb": 32.49958670500018,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb": 82.62286975099994,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb": 11.582223320000026,
|
||||
"qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb": 17.065084866000007,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb": 12.421405112000002,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb": 50.74149139600013,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb": 56.192172966000044,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb": 43.43877877099999,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb": 12.306967781000026,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb": 22.123440633999962,
|
||||
"qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb": 11.930819230999987,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_create_spec.rb": 14.466449840999985,
|
||||
"qa/specs/features/browser_ui/3_create/repository/ssh_key_support_delete_spec.rb": 13.404772087999845,
|
||||
"qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb": 28.492135840000174,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb": 40.902203811999925,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb": 28.4565982299996,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_personal_snippet_spec.rb": 44.05035067500012,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/clone_push_pull_project_snippet_spec.rb": 52.8828863990002,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb": 32.79950173500015,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb": 10.373941559000059,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb": 9.907039061999967,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb": 14.810275069,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb": 18.689862891999837,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb": 25.652545791000193,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb": 22.147358860000168,
|
||||
"qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb": 48.514524907999885,
|
||||
"qa/specs/features/browser_ui/3_create/source_editor/source_editor_toolbar_spec.rb": 16.087555477000024,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb": 40.66486228200006,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/add_new_directory_in_web_ide_spec.rb": 62.267841525999984,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/closing_web_ide_with_unsaved_changes_spec.rb": 17.63566870900013,
|
||||
"qa/specs/features/browser_ui/3_create/web_ide/upload_new_file_in_web_ide_spec.rb": 133.5650063160001,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/ci_catalog_sorting_spec.rb": 68.8284702850001,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_components_catalog/run_component_in_project_pipeline_spec.rb": 40.266767914999946,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/expose_job_artifacts_in_mr_spec.rb": 36.87972815300009,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/job_artifacts_access_keyword_spec.rb": 154.66687935100026,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_job_artifacts/unlocking_job_artifacts_across_pipelines_spec.rb": 266.6912154249999,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_project_artifacts/user_can_bulk_delete_artifacts_spec.rb": 24.51923005200001,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/custom_variable_spec.rb": 32.529648410999926,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb": 47.52443747999996,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb": 33.065717594000034,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/raw_variables_defined_in_yaml_spec.rb": 20.569682358000136,
|
||||
"qa/specs/features/browser_ui/4_verify/ci_variable/ui_variable_inheritable_when_forward_pipeline_variables_true_spec.rb": 49.354740160999995,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb": 23.049214602999882,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb": 46.987583461999975,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_multiple_projects_spec.rb": 36.45673636599986,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/parent_child_pipelines_independent_relationship_spec.rb": 58.82185278499992,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb": 42.403081290000046,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb": 149.85470755100005,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/run_pipeline_with_manual_jobs_spec.rb": 50.89883141200016,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_child_pipeline_with_manual_spec.rb": 44.78671075300008,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb": 32.12375235100001,
|
||||
"qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb": 20.63341818699996,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_management/group_runner_counts_spec.rb": 10.157025357999828,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/fleet_management/group_runner_status_counts_spec.rb": 14.983221184000058,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_group_runner_spec.rb": 10.514455229000077,
|
||||
"qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb": 18.13358168099967,
|
||||
"qa/specs/features/browser_ui/4_verify/testing/endpoint_coverage_spec.rb": 35.76304038599983,
|
||||
"qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb": 42.05816078700013,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/composer_registry_spec.rb": 31.686695261000068,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/conan_repository_spec.rb": 49.87885218400004,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/generic_repository_spec.rb": 34.36321494200001,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/helm_registry_spec.rb": 144.980374235,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb": 360.92151482300005,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven/maven_project_level_spec.rb": 150.15723534799986,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/maven_gradle_repository_spec.rb": 136.13819214399996,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_group_level_spec.rb": 192.52999090000003,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/npm/npm_instance_level_spec.rb": 183.16166350100002,
|
||||
"qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb": 50.5702839329997,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/add_deploy_key_spec.rb": 14.971665118000033,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb": 110.33423504300026,
|
||||
"qa/specs/features/browser_ui/6_release/deploy_token/add_deploy_token_spec.rb": 10.932732566999903,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/alert_settings_create_new_alerts_spec.rb": 28.898326267000357,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/automatically_creates_incident_for_alert_spec.rb": 32.88440417899983,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/create_alert_using_authorization_key_spec.rb": 30.428553857000225,
|
||||
"qa/specs/features/browser_ui/8_monitor/alert_management/recovery_alert_resolves_correct_alert_spec.rb": 40.65821105299983,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/group_member_access_request_spec.rb": 69.0673923659997,
|
||||
"qa/specs/features/browser_ui/9_data_stores/group/transfer_project_spec.rb": 22.13317842100014,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/add_project_member_spec.rb": 14.512742338999942,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/create_project_badge_spec.rb": 16.419005202000335,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/create_project_spec.rb": 23.331705375999718,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/dashboard_images_spec.rb": 16.30081602300004,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/invite_group_to_project_spec.rb": 58.298898994999945,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/project_owner_permissions_spec.rb": 51.102430441000024,
|
||||
"qa/specs/features/browser_ui/9_data_stores/project/view_project_activity_spec.rb": 14.038119108000046,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/follow_user_activity_spec.rb": 18.661561350000056,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/parent_group_access_termination_spec.rb": 26.20017358599989,
|
||||
"qa/specs/features/browser_ui/9_data_stores/user/user_inherited_access_spec.rb": 23.727169318999586,
|
||||
"qa/specs/features/ee/api/10_govern/compliance_pipeline_spec.rb": 14.230563544999768,
|
||||
"qa/specs/features/ee/api/10_govern/instance_audit_event_streaming_spec.rb": 14.22414006200006,
|
||||
"qa/specs/features/ee/api/10_govern/user/minimal_access_user_spec.rb": 48.28243208599997,
|
||||
"qa/specs/features/ee/api/2_plan/epics_milestone_dates_spec.rb": 24.16354903199999,
|
||||
"qa/specs/features/ee/api/3_create/code_suggestions_spec.rb": 12.572733440000036,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/change_vulnerability_status_spec.rb": 68.61389828400024,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb": 50.011880293999866,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/export_vulnerability_report_spec.rb": 19.24138949500002,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/fix_vulnerability_workflow_spec.rb": 85.92190458799996,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_event_streaming_spec.rb": 25.921691762000137,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/group_audit_logs_1_spec.rb": 109.84475951299999,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group/restrict_by_ip_address_spec.rb": 209.49803926899995,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/group_pipeline_execution_policy_spec.rb": 127.72439350100012,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/instance/instance_audit_logs_spec.rb": 132.9845808350001,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project/project_audit_logs_spec.rb": 124.79136410800015,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/project_security_dashboard_spec.rb": 32.404924101000006,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_execution_policy_vulnerabilities_spec.rb": 90.34270750299993,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/scan_result_policy_vulnerabilities_spec.rb": 73.92341833399996,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_policies_spec.rb": 49.21381796800006,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/security_reports_spec.rb": 172.6255000389997,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/user/minimal_access_user_spec.rb": 20.043817780999916,
|
||||
"qa/specs/features/ee/browser_ui/10_govern/vulnerability_management_spec.rb": 261.28464171700034,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb": 7.501284284999883,
|
||||
"qa/specs/features/ee/browser_ui/11_fulfillment/utilization/user_registration_billing_spec.rb": 15.795260276999898,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/enable_advanced_sast_spec.rb": 74.9842102069997,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb": 50.713501423000025,
|
||||
"qa/specs/features/ee/browser_ui/13_secure/on_demand_dast_spec.rb": 94.28897494800003,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/contribution_analytics_spec.rb": 28.131016550000027,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/mr_analytics_spec.rb": 43.30277173699983,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/analytics/value_stream_analytics_spec.rb": 33.68210793000026,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb": 12.955446931000097,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb": 12.208983900000021,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb": 158.4215048420001,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/promote_issue_to_epic_spec.rb": 33.01069102800011,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb": 8.259949558000017,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/create_group_wiki_page_spec.rb": 25.861441487999855,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/delete_group_wiki_page_spec.rb": 10.608916837999914,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/group_wiki/file_upload_group_wiki_page_spec.rb": 28.67545881199976,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/insights/default_insights_spec.rb": 27.49661202900006,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue/default_issue_template_spec.rb": 17.888848881000058,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configurable_issue_board_spec.rb": 11.857604426999842,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/configure_issue_board_by_label_spec.rb": 13.318511218000367,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/create_group_issue_board_spec.rb": 18.71652893999999,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/group_issue_boards_spec.rb": 19.94479222999962,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/project_issue_boards_spec.rb": 47.67097838999962,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/read_only_board_configuration_spec.rb": 20.099411936000024,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issue_boards/sum_of_issues_weights_spec.rb": 12.640246061000084,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_analytics/issues_analytics_spec.rb": 30.00575052099998,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/issues_weight/issue_weight_visualization_spec.rb": 12.941392978000067,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/assign_group_iteration_spec.rb": 16.032257479000236,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/iterations/create_group_iteration_spec.rb": 38.549332582000034,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/four_assignees_spec.rb": 14.220769124999833,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/multiple_assignees_for_issues/more_than_four_assignees_spec.rb": 37.273302462999936,
|
||||
"qa/specs/features/ee/browser_ui/2_plan/scoped_labels/editing_scoped_labels_spec.rb": 12.753879795000103,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/add_batch_comments_in_merge_request_spec.rb": 49.9373758429997,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/approval_rules_spec.rb": 83.34420719600007,
|
||||
"qa/specs/features/ee/browser_ui/3_create/merge_request/default_merge_request_template_spec.rb": 35.74193098000001,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/assign_code_owners_spec.rb": 31.49351207000018,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_spec.rb": 20.113167300999976,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/code_owners_with_protected_branch_and_squashed_commits_spec.rb": 22.566818372999933,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/file_locking_spec.rb": 148.86333409300005,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/group_file_template_spec.rb": 96.78819865899993,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_root_group_spec.rb": 91.5671666899998,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/merge_with_code_owner_in_subgroup_spec.rb": 126.88817487399979,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/project_templates_spec.rb": 63.82498676599971,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_http_spec.rb": 30.712793918999978,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/pull_mirroring_over_ssh_with_key_spec.rb": 37.274831491999976,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb": 305.9995597960001,
|
||||
"qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb": 266.46528617599984,
|
||||
"qa/specs/features/ee/browser_ui/3_create/web_ide/code_suggestions_in_web_ide_spec.rb": 57.34347684400018,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/multi-project_pipelines_spec.rb": 32.21118624199994,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/parent_child_pipelines_dependent_relationship_spec.rb": 75.41926421400012,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipeline_subscription_with_group_owned_project_spec.rb": 44.61706870600028,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/pipelines_for_merged_results_and_merge_trains_spec.rb": 73.5016436740002,
|
||||
"qa/specs/features/ee/browser_ui/4_verify/transient/merge_trains_transient_bug_spec.rb": 93.12980337499994,
|
||||
"qa/specs/features/ee/browser_ui/8_monitor/incident_management/incident_quick_action_spec.rb": 11.34621108899978,
|
||||
"qa/specs/features/ee/browser_ui/9_data_stores/group/prevent_forking_outside_group_spec.rb": 32.30694202300003,
|
||||
"qa/specs/features/ee/browser_ui/9_data_stores/group/share_group_with_group_spec.rb": 25.535255447000054
|
||||
}
|
166
qa/qa/runtime/user_store.rb
Normal file
166
qa/qa/runtime/user_store.rb
Normal file
@ -0,0 +1,166 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
module Runtime
|
||||
# Helper class to create and store globally accessible test users
|
||||
#
|
||||
class UserStore
|
||||
InvalidTokenError = Class.new(StandardError)
|
||||
ExpiredAdminPasswordError = Class.new(StandardError)
|
||||
|
||||
# @return [String] default admin api token pre-seeded on ephemeral test environments
|
||||
DEFAULT_ADMIN_API_TOKEN = "ypCa3Dzb23o5nvsixwPA" # gitleaks:allow
|
||||
# @return [String] default username for admin user
|
||||
DEFAULT_ADMIN_USERNAME = "root"
|
||||
# @return [String] default password for admin user
|
||||
DEFAULT_ADMIN_PASSWORD = "5iveL!fe"
|
||||
|
||||
class << self
|
||||
# Global admin client
|
||||
#
|
||||
# @return [QA::Runtime::API::Client]
|
||||
def admin_api_client
|
||||
return @admin_api_client if @admin_api_client
|
||||
|
||||
info("Creating admin api client for api fabrications")
|
||||
|
||||
if Env.admin_personal_access_token
|
||||
info("Admin api token variable is set, using it for default admin api fabrications")
|
||||
@admin_api_client = API::Client
|
||||
.new(personal_access_token: Env.admin_personal_access_token)
|
||||
.tap { |client| validate_admin_client!(client) }
|
||||
elsif default_admin_token_valid?
|
||||
info("Admin api token variable is not set, using default - '#{DEFAULT_ADMIN_API_TOKEN}'")
|
||||
@admin_api_client = API::Client.new(personal_access_token: DEFAULT_ADMIN_API_TOKEN)
|
||||
else
|
||||
@admin_api_client = create_admin_api_client(admin_user)
|
||||
end
|
||||
|
||||
info("Admin token set up successfully")
|
||||
@admin_api_client
|
||||
end
|
||||
alias_method :initialize_admin_api_client, :admin_api_client
|
||||
|
||||
# Global admin user
|
||||
#
|
||||
# @return [QA::Resource::User]
|
||||
def admin_user
|
||||
return @admin_user if @admin_user
|
||||
|
||||
@admin_user = Resource::User.init do |user|
|
||||
user.username = if Env.admin_username
|
||||
Env.admin_username
|
||||
else
|
||||
debug("Admin username variable not set, using default - '#{DEFAULT_ADMIN_USERNAME}'")
|
||||
DEFAULT_ADMIN_USERNAME
|
||||
end
|
||||
|
||||
user.password = if Env.admin_password
|
||||
Env.admin_password
|
||||
else
|
||||
debug("Admin password variable not set, using default - '#{DEFAULT_ADMIN_PASSWORD}'")
|
||||
DEFAULT_ADMIN_PASSWORD
|
||||
end
|
||||
end
|
||||
|
||||
if @admin_api_client && client_belongs_to_user?(@admin_api_client, @admin_user)
|
||||
@admin_user.api_client = @admin_api_client
|
||||
@admin_user.reload!
|
||||
elsif @admin_api_client
|
||||
warn(<<~WARN)
|
||||
Configured global admin token does not belong to configured admin user
|
||||
Please check values for GITLAB_QA_ADMIN_ACCESS_TOKEN, GITLAB_ADMIN_USERNAME and GITLAB_ADMIN_PASSWORD variables
|
||||
WARN
|
||||
end
|
||||
|
||||
@admin_user
|
||||
end
|
||||
alias_method :initialize_admin_user, :admin_user
|
||||
|
||||
private
|
||||
|
||||
delegate :debug, :info, :warn, :error, to: Logger
|
||||
|
||||
# Check if default admin token is present in environment and valid
|
||||
#
|
||||
# @return [Boolean]
|
||||
def default_admin_token_valid?
|
||||
debug("Validating presence of default admin api token in environment")
|
||||
validate_admin_client!(API::Client.new(personal_access_token: DEFAULT_ADMIN_API_TOKEN))
|
||||
debug("Default admin token is present in environment and is valid")
|
||||
true
|
||||
rescue InvalidTokenError
|
||||
debug("Default admin token is not valid or present in environment, skipping...")
|
||||
false
|
||||
end
|
||||
|
||||
# Create admin access client and validate it
|
||||
#
|
||||
# @param [QA::Resource::User] user
|
||||
# @return [QA::Runtime::API::Client]
|
||||
def create_admin_api_client(user)
|
||||
info("Creating admin token via ui")
|
||||
admin_token = Flow::Login.while_signed_in(as: user) do
|
||||
Resource::PersonalAccessToken.fabricate_via_browser_ui! { |pat| pat.user = user }.token
|
||||
end
|
||||
|
||||
API::Client.new(:gitlab, personal_access_token: admin_token).tap do |client|
|
||||
validate_admin_client!(client)
|
||||
user.api_client = client
|
||||
user.reload!
|
||||
end
|
||||
end
|
||||
|
||||
# Validate if client belongs to an admin user
|
||||
#
|
||||
# @param [QA::Runtime::API::Client] client
|
||||
# @return [void]
|
||||
def validate_admin_client!(client)
|
||||
debug("Validating admin access token")
|
||||
resp = fetch_user_details(client)
|
||||
|
||||
if resp.code == 403 && resp.body.include?("Your password expired")
|
||||
raise ExpiredAdminPasswordError, "Admin password has expired and must be reset"
|
||||
elsif !status_ok?(resp)
|
||||
raise InvalidTokenError, "Admin token validation failed! Code: #{resp.code}, Err: '#{resp.body}'"
|
||||
end
|
||||
|
||||
is_admin = Support::API.parse_body(resp)[:is_admin]
|
||||
raise InvalidTokenError, "Admin token does not belong to admin user" unless is_admin
|
||||
|
||||
debug("Admin token is valid")
|
||||
end
|
||||
|
||||
# Check if token belongs to specific user
|
||||
#
|
||||
# @param [QA::Runtime::API::Client] client
|
||||
# @param [QA::Resource::User] user
|
||||
# @return [Boolean]
|
||||
def client_belongs_to_user?(client, user)
|
||||
resp = fetch_user_details(client)
|
||||
unless status_ok?(resp)
|
||||
raise InvalidTokenError, "Token validation failed! Code: #{resp.code}, Err: '#{resp.body}'"
|
||||
end
|
||||
|
||||
Support::API.parse_body(resp)[:username] == user.username
|
||||
end
|
||||
|
||||
# Fetch user details of given api client
|
||||
#
|
||||
# @param [QA::Runtime::API::Client] client
|
||||
# @return [RestClient::Response]
|
||||
def fetch_user_details(client)
|
||||
Support::API.get(API::Request.new(client, "/user").url)
|
||||
end
|
||||
|
||||
# Validate 200 HTTP status code of response
|
||||
#
|
||||
# @param [RestClient::Response] resp
|
||||
# @return [Boolean]
|
||||
def status_ok?(resp)
|
||||
resp.code == Support::API::HTTP_STATUS_OK
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
189
qa/spec/runtime/user_store_spec.rb
Normal file
189
qa/spec/runtime/user_store_spec.rb
Normal file
@ -0,0 +1,189 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe Runtime::UserStore do
|
||||
let(:default_admin_token) { "ypCa3Dzb23o5nvsixwPA" }
|
||||
|
||||
before do
|
||||
allow(Runtime::Scenario).to receive(:send).with("gitlab_address").and_return("https://example.com")
|
||||
allow(Runtime::Logger).to receive_messages({
|
||||
debug: nil,
|
||||
info: nil,
|
||||
warn: nil,
|
||||
error: nil
|
||||
})
|
||||
allow(Runtime::Env).to receive_messages({
|
||||
admin_username: nil,
|
||||
admin_password: nil,
|
||||
admin_personal_access_token: nil
|
||||
})
|
||||
|
||||
described_class.instance_variable_set(:@admin_api_client, nil)
|
||||
described_class.instance_variable_set(:@admin_user, nil)
|
||||
end
|
||||
|
||||
def mock_user_get(token:, code: 200, body: { is_admin: true, id: 1, username: "root" }.to_json)
|
||||
allow(Support::API).to receive(:get).with("https://example.com/api/v4/user?private_token=#{token}").and_return(
|
||||
instance_double(RestClient::Response, code: code, body: body)
|
||||
)
|
||||
end
|
||||
|
||||
describe "#admin_api_client" do
|
||||
let(:admin_token) { nil }
|
||||
|
||||
before do
|
||||
allow(Runtime::Env).to receive(:admin_personal_access_token).and_return(admin_token)
|
||||
end
|
||||
|
||||
context "when admin token variable is set" do
|
||||
let(:admin_token) { "admin-token" }
|
||||
|
||||
before do
|
||||
mock_user_get(token: admin_token)
|
||||
end
|
||||
|
||||
it "creates admin api client with configured token" do
|
||||
expect(described_class.admin_api_client.personal_access_token).to eq(admin_token)
|
||||
end
|
||||
end
|
||||
|
||||
context "with valid default admin token and no token configured" do
|
||||
before do
|
||||
mock_user_get(token: default_admin_token)
|
||||
end
|
||||
|
||||
it "creates admin api client with default admin token" do
|
||||
expect(described_class.admin_api_client.personal_access_token).to eq(default_admin_token)
|
||||
end
|
||||
end
|
||||
|
||||
context "with invalid token set via environment variable" do
|
||||
let(:admin_token) { "admin-token" }
|
||||
|
||||
before do
|
||||
mock_user_get(token: admin_token, code: 401, body: "401 Unauthorized")
|
||||
end
|
||||
|
||||
it "raises InvalidTokenError" do
|
||||
expect { described_class.admin_api_client }.to raise_error(
|
||||
described_class::InvalidTokenError, "Admin token validation failed! Code: 401, Err: '401 Unauthorized'"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "with expired admin password" do
|
||||
let(:admin_token) { "admin-token" }
|
||||
|
||||
before do
|
||||
mock_user_get(token: admin_token, code: 403, body: "Your password expired")
|
||||
end
|
||||
|
||||
it "raises ExpiredAdminPasswordError" do
|
||||
expect { described_class.admin_api_client }.to raise_error(
|
||||
described_class::ExpiredAdminPasswordError, "Admin password has expired and must be reset"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "with token creation via UI" do
|
||||
let(:admin_user) { Resource::User.new }
|
||||
let(:pat) { Resource::PersonalAccessToken.init { |pat| pat.token = "test" } }
|
||||
|
||||
before do
|
||||
allow(Resource::User).to receive(:init).and_yield(admin_user).and_return(admin_user)
|
||||
allow(Resource::PersonalAccessToken).to receive(:fabricate_via_browser_ui!).and_yield(pat).and_return(pat)
|
||||
allow(Flow::Login).to receive(:while_signed_in).with(as: admin_user).and_yield
|
||||
allow(admin_user).to receive(:reload!)
|
||||
|
||||
mock_user_get(token: default_admin_token, code: 401)
|
||||
mock_user_get(token: pat.token)
|
||||
end
|
||||
|
||||
it "creates admin api client with token created from UI" do
|
||||
expect(described_class.admin_api_client.personal_access_token).to eq(pat.token)
|
||||
expect(admin_user.username).to eq("root")
|
||||
expect(admin_user.password).to eq("5iveL!fe")
|
||||
expect(admin_user).to have_received(:reload!)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#admin_user" do
|
||||
context "when admin client has not been initialized" do
|
||||
context "with admin user variables set" do
|
||||
let(:username) { "admin-username" }
|
||||
let(:password) { "admin-password" }
|
||||
|
||||
before do
|
||||
allow(Runtime::Env).to receive_messages({ admin_username: username, admin_password: password })
|
||||
end
|
||||
|
||||
it "returns admin user with configured credentials" do
|
||||
expect(described_class.admin_user.username).to eq(username)
|
||||
expect(described_class.admin_user.password).to eq(password)
|
||||
end
|
||||
end
|
||||
|
||||
context "without admin user variables set" do
|
||||
let(:username) { "root" }
|
||||
let(:password) { "5iveL!fe" }
|
||||
|
||||
it "returns admin user with default credentials" do
|
||||
expect(described_class.admin_user.username).to eq(username)
|
||||
expect(described_class.admin_user.password).to eq(password)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when admin client has been initialized" do
|
||||
let(:admin_user) { Resource::User.new }
|
||||
let(:admin_client) { Runtime::API::Client.new(personal_access_token: default_admin_token) }
|
||||
|
||||
before do
|
||||
allow(Resource::User).to receive(:init).and_yield(admin_user).and_return(admin_user)
|
||||
allow(admin_user).to receive(:reload!)
|
||||
|
||||
described_class.instance_variable_set(:@admin_api_client, admin_client)
|
||||
end
|
||||
|
||||
context "with valid admin client belonging to user" do
|
||||
before do
|
||||
mock_user_get(token: default_admin_token)
|
||||
end
|
||||
|
||||
it "sets api client on admin user and reloads it" do
|
||||
expect(described_class.admin_user.instance_variable_get(:@api_client)).to eq(admin_client)
|
||||
expect(admin_user).to have_received(:reload!)
|
||||
end
|
||||
end
|
||||
|
||||
context "with valid admin client not belonging to user" do
|
||||
before do
|
||||
mock_user_get(token: default_admin_token, body: { username: "test" }.to_json)
|
||||
end
|
||||
|
||||
it "prints warning message" do
|
||||
described_class.initialize_admin_user
|
||||
|
||||
expect(Runtime::Logger).to have_received(:warn).with(<<~WARN)
|
||||
Configured global admin token does not belong to configured admin user
|
||||
Please check values for GITLAB_QA_ADMIN_ACCESS_TOKEN, GITLAB_ADMIN_USERNAME and GITLAB_ADMIN_PASSWORD variables
|
||||
WARN
|
||||
end
|
||||
end
|
||||
|
||||
context "with invalid admin client" do
|
||||
before do
|
||||
mock_user_get(token: default_admin_token, code: 403, body: "Unauthorized")
|
||||
end
|
||||
|
||||
it "raises invalid token error" do
|
||||
expect { described_class.admin_user }.to raise_error(
|
||||
described_class::InvalidTokenError, "Token validation failed! Code: 403, Err: 'Unauthorized'"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -10,14 +10,13 @@ require 'json_schemer'
|
||||
require 'delegate'
|
||||
|
||||
require_relative './cli/helpers'
|
||||
require_relative './cli/flows/usage_viewer'
|
||||
require_relative './cli/flows/metric_definer'
|
||||
require_relative './cli/flows/event_definer'
|
||||
require_relative './cli/flows/flow_advisor'
|
||||
require_relative './cli/flows/metric_definer'
|
||||
require_relative './cli/flows/usage_viewer'
|
||||
require_relative './cli/global_state'
|
||||
require_relative './cli/metric'
|
||||
require_relative './cli/event'
|
||||
require_relative './cli/text'
|
||||
|
||||
class Cli
|
||||
include ::InternalEventsCli::Helpers
|
||||
@ -29,8 +28,8 @@ class Cli
|
||||
end
|
||||
|
||||
def run
|
||||
cli.say InternalEventsCli::Text::FEEDBACK_NOTICE
|
||||
cli.say InternalEventsCli::Text::CLI_INSTRUCTIONS
|
||||
cli.say feedback_notice
|
||||
cli.say instructions
|
||||
|
||||
task = cli.select("What would you like to do?", **select_opts) do |menu|
|
||||
menu.enum "."
|
||||
@ -54,6 +53,23 @@ class Cli
|
||||
InternalEventsCli::Flows::FlowAdvisor.new(cli).run
|
||||
end
|
||||
end
|
||||
|
||||
def instructions
|
||||
cli.say <<~TEXT.freeze
|
||||
#{format_info('INSTRUCTIONS:')}
|
||||
To start tracking usage of a feature...
|
||||
|
||||
1) Define event (using CLI)
|
||||
2) Trigger event (from code)
|
||||
3) Define metric (using CLI)
|
||||
4) View data in Tableau (after merge & deploy)
|
||||
|
||||
This CLI will help you create the correct defintion files, then provide code examples for instrumentation and testing.
|
||||
|
||||
Learn more: https://docs.gitlab.com/ee/development/internal_analytics/#fundamental-concepts
|
||||
|
||||
TEXT
|
||||
end
|
||||
end
|
||||
|
||||
class GitlabPrompt < SimpleDelegator
|
||||
|
@ -1,12 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../helpers'
|
||||
require_relative '../text/event_definer'
|
||||
|
||||
# Entrypoint for flow to create an event definition file
|
||||
module InternalEventsCli
|
||||
module Flows
|
||||
class EventDefiner
|
||||
include Helpers
|
||||
include Text::EventDefiner
|
||||
|
||||
SCHEMA = ::JSONSchemer.schema(Pathname('config/events/schema.json'))
|
||||
STEPS = [
|
||||
@ -20,23 +22,6 @@ module InternalEventsCli
|
||||
'Save files'
|
||||
].freeze
|
||||
|
||||
IDENTIFIER_OPTIONS = {
|
||||
%w[project namespace user] =>
|
||||
'Use case: For project-level user actions (ex - issue_assignee_changed) [MOST COMMON]',
|
||||
%w[namespace user] =>
|
||||
'Use case: For namespace-level user actions (ex - epic_assigned_to_milestone)',
|
||||
%w[user] =>
|
||||
'Use case: For user-only actions (ex - admin_impersonated_user)',
|
||||
%w[project namespace] =>
|
||||
'Use case: For project-level events without user interaction (ex - service_desk_request_received)',
|
||||
%w[namespace] =>
|
||||
'Use case: For namespace-level events without user interaction (ex - stale_runners_cleaned_up)',
|
||||
%w[feature_enabled_by_namespace_ids user] =>
|
||||
'Use case: For user actions attributable to multiple namespaces (ex - Code-Suggestions / Duo Pro)',
|
||||
%w[] =>
|
||||
'Use case: For instance-level events without user interaction [LEAST COMMON]'
|
||||
}.freeze
|
||||
|
||||
IDENTIFIER_FORMATTING_BUFFER = "[#{IDENTIFIER_OPTIONS.keys.map { |k| k.join(', ') }.max_by(&:length)}]".length
|
||||
|
||||
attr_reader :cli, :event
|
||||
@ -64,18 +49,18 @@ module InternalEventsCli
|
||||
|
||||
def prompt_for_description
|
||||
new_page!(1, 7, STEPS)
|
||||
cli.say Text::EVENT_DESCRIPTION_INTRO
|
||||
cli.say DESCRIPTION_INTRO
|
||||
|
||||
event.description = cli.ask("Describe what the event tracks: #{input_required_text}", **input_opts) do |q|
|
||||
q.required true
|
||||
q.modify :trim
|
||||
q.messages[:required?] = Text::EVENT_DESCRIPTION_HELP
|
||||
q.messages[:required?] = DESCRIPTION_HELP
|
||||
end
|
||||
end
|
||||
|
||||
def prompt_for_action
|
||||
new_page!(2, 7, STEPS)
|
||||
cli.say Text::EVENT_ACTION_INTRO
|
||||
cli.say ACTION_INTRO
|
||||
|
||||
event.action = cli.ask("Define the event name: #{input_required_text}", **input_opts) do |q|
|
||||
q.required true
|
||||
@ -84,7 +69,7 @@ module InternalEventsCli
|
||||
q.messages[:valid?] = format_warning(
|
||||
"Invalid event name. Only lowercase/numbers/underscores allowed. " \
|
||||
"Ensure %{value} is not an existing event.")
|
||||
q.messages[:required?] = Text::EVENT_ACTION_HELP
|
||||
q.messages[:required?] = ACTION_HELP
|
||||
end
|
||||
end
|
||||
|
||||
@ -99,7 +84,7 @@ module InternalEventsCli
|
||||
end
|
||||
|
||||
def prompt_for_identifiers
|
||||
cli.say Text::EVENT_IDENTIFIERS_INTRO % event.action
|
||||
cli.say IDENTIFIERS_INTRO % event.action
|
||||
|
||||
identifiers = prompt_for_array_selection(
|
||||
'Which identifiers are available when the event occurs?',
|
||||
@ -118,7 +103,7 @@ module InternalEventsCli
|
||||
end
|
||||
|
||||
def prompt_for_additional_properties
|
||||
cli.say Text::ADDITIONAL_PROPERTIES_INTRO
|
||||
cli.say ADDITIONAL_PROPERTIES_INTRO
|
||||
|
||||
available_props = [:label, :property, :value, :add_extra_prop]
|
||||
|
||||
@ -191,7 +176,7 @@ module InternalEventsCli
|
||||
q.required true
|
||||
q.validate ->(input) { input =~ NAME_REGEX && primary_props.none?(input) }
|
||||
q.modify :trim
|
||||
q.messages[:required?] = Text::ADDITIONAL_PROPERTIES_ADD_MORE_HELP
|
||||
q.messages[:required?] = ADDITIONAL_PROPERTIES_ADD_MORE_HELP
|
||||
q.messages[:valid?] = format_warning(
|
||||
"Invalid property name. Only lowercase/numbers/underscores allowed. " \
|
||||
"Ensure %{value} is not one of `property, label, value`.")
|
||||
@ -280,7 +265,7 @@ module InternalEventsCli
|
||||
when :view_usage
|
||||
UsageViewer.new(cli, event.file_path, event).run
|
||||
when :exit
|
||||
cli.say Text::FEEDBACK_NOTICE
|
||||
cli.say feedback_notice
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../helpers'
|
||||
require_relative '../text/flow_advisor'
|
||||
|
||||
# Entrypoint for help flow, which directs the user to the
|
||||
# correct flow or documentation based on their goal
|
||||
@ -8,6 +9,7 @@ module InternalEventsCli
|
||||
module Flows
|
||||
class FlowAdvisor
|
||||
include Helpers
|
||||
include Text::FlowAdvisor
|
||||
|
||||
attr_reader :cli
|
||||
|
||||
@ -36,7 +38,7 @@ module InternalEventsCli
|
||||
new_page!
|
||||
|
||||
cli.say format_info("Excellent! Let's check that this tool will fit your needs.\n")
|
||||
cli.say Text::EVENT_TRACKING_EXAMPLES
|
||||
cli.say EVENT_TRACKING_EXAMPLES
|
||||
|
||||
cli.yes?(
|
||||
'Can usage for the feature be measured with a count of specific user actions or events? ' \
|
||||
@ -49,7 +51,7 @@ module InternalEventsCli
|
||||
new_page!
|
||||
|
||||
cli.say format_info("Super! Let's figure out if the event is already tracked & usable.\n")
|
||||
cli.say Text::EVENT_EXISTENCE_CHECK_INSTRUCTIONS
|
||||
cli.say EVENT_EXISTENCE_CHECK_INSTRUCTIONS
|
||||
|
||||
cli.yes?('Is the event already tracked?', **yes_no_opts)
|
||||
end
|
||||
@ -58,8 +60,8 @@ module InternalEventsCli
|
||||
new_page!
|
||||
|
||||
cli.error("Oh no! This probably isn't the tool you need!\n")
|
||||
cli.say Text::ALTERNATE_RESOURCES_NOTICE
|
||||
cli.say Text::FEEDBACK_NOTICE
|
||||
cli.say ALTERNATE_RESOURCES_NOTICE
|
||||
cli.say feedback_notice
|
||||
end
|
||||
|
||||
def proceed_to_metric_definition
|
||||
@ -84,7 +86,7 @@ module InternalEventsCli
|
||||
|
||||
def not_ready_error(description)
|
||||
cli.say "\nNo problem! When you're ready, run the CLI & select '#{description}'\n"
|
||||
cli.say Text::FEEDBACK_NOTICE
|
||||
cli.say feedback_notice
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,13 +1,14 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../helpers'
|
||||
require_relative '../text'
|
||||
require_relative '../text/metric_definer'
|
||||
|
||||
# Entrypoint for flow to create an metric definition file
|
||||
module InternalEventsCli
|
||||
module Flows
|
||||
class MetricDefiner
|
||||
include Helpers
|
||||
include Text::MetricDefiner
|
||||
|
||||
SCHEMA = ::JSONSchemer.schema(Pathname('config/metrics/schema/base.json'))
|
||||
STEPS = [
|
||||
@ -23,21 +24,6 @@ module InternalEventsCli
|
||||
'Save files'
|
||||
].freeze
|
||||
|
||||
NAME_REQUIREMENT_REASONS = {
|
||||
filters: {
|
||||
text: 'Metrics using filters are too complex for default naming.',
|
||||
help: Text::METRIC_NAME_FILTER_HELP
|
||||
},
|
||||
length: {
|
||||
text: 'The default filename will be too long.',
|
||||
help: Text::METRIC_NAME_LENGTH_HELP
|
||||
},
|
||||
conflict: {
|
||||
text: 'The default key path is already in use.',
|
||||
help: Text::METRIC_NAME_CONFLICT_HELP
|
||||
}
|
||||
}.freeze
|
||||
|
||||
attr_reader :cli
|
||||
|
||||
def initialize(cli, starting_event = nil)
|
||||
@ -123,8 +109,8 @@ module InternalEventsCli
|
||||
**filter_opts(header_size: 7)
|
||||
)
|
||||
when :database_metric
|
||||
cli.error Text::DATABASE_METRIC_NOTICE
|
||||
cli.say Text::FEEDBACK_NOTICE
|
||||
cli.error DATABASE_METRIC_NOTICE
|
||||
cli.say feedback_notice
|
||||
end
|
||||
end
|
||||
|
||||
@ -132,8 +118,8 @@ module InternalEventsCli
|
||||
eligible_metrics = get_metric_options(selected_events)
|
||||
|
||||
if eligible_metrics.all? { |metric| metric[:disabled] }
|
||||
cli.error Text::ALL_METRICS_EXIST_NOTICE
|
||||
cli.say Text::FEEDBACK_NOTICE
|
||||
cli.error ALL_METRICS_EXIST_NOTICE
|
||||
cli.say feedback_notice
|
||||
|
||||
return
|
||||
end
|
||||
@ -206,7 +192,7 @@ module InternalEventsCli
|
||||
if idx == 0 || separate_page_per_metric
|
||||
new_page!(4, 9, STEPS)
|
||||
|
||||
cli.say Text::METRIC_DESCRIPTION_INTRO
|
||||
cli.say DESCRIPTION_INTRO
|
||||
cli.say selected_event_descriptions.join
|
||||
end
|
||||
|
||||
@ -359,7 +345,7 @@ module InternalEventsCli
|
||||
when :view_usage
|
||||
UsageViewer.new(cli, @selected_event_paths.first, selected_events.first).run
|
||||
when :exit
|
||||
cli.say Text::FEEDBACK_NOTICE
|
||||
cli.say feedback_notice
|
||||
end
|
||||
end
|
||||
|
||||
@ -487,7 +473,7 @@ module InternalEventsCli
|
||||
prompt_for_text(" Finish the description: #{description_start}", default, multiline: true) do |q|
|
||||
q.required true
|
||||
q.modify :trim
|
||||
q.messages[:required?] = Text::METRIC_DESCRIPTION_HELP
|
||||
q.messages[:required?] = DESCRIPTION_HELP
|
||||
end
|
||||
end
|
||||
|
||||
@ -515,7 +501,7 @@ module InternalEventsCli
|
||||
prompt_for_text(' Replace with: ', default, multiline: true) do |q|
|
||||
q.required true
|
||||
q.messages[:required?] = name_reason[:help] % help_tokens
|
||||
q.messages[:valid?] = Text::METRIC_NAME_ERROR % help_tokens
|
||||
q.messages[:valid?] = NAME_ERROR % help_tokens
|
||||
q.validate ->(input) do
|
||||
input.length <= max_length &&
|
||||
input.match?(NAME_REGEX) &&
|
||||
|
@ -96,7 +96,7 @@ module InternalEventsCli
|
||||
when :other_event
|
||||
self.class.new(cli).run
|
||||
when :exit
|
||||
cli.say(Text::FEEDBACK_NOTICE)
|
||||
cli.say(feedback_notice)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,5 +26,18 @@ module InternalEventsCli
|
||||
cli.say TTY::Cursor.move_to(0, 0)
|
||||
cli.say "#{progress_bar(page, total, steps)}\n" if page && total
|
||||
end
|
||||
|
||||
def feedback_notice
|
||||
format_heading <<~TEXT.chomp
|
||||
Thanks for using the Internal Events CLI!
|
||||
|
||||
Please reach out with any feedback!
|
||||
About Internal Events: https://gitlab.com/gitlab-org/analytics-section/analytics-instrumentation/internal/-/issues/687
|
||||
About CLI: https://gitlab.com/gitlab-org/gitlab/-/issues/434038
|
||||
In Slack: #g_analyze_analytics_instrumentation
|
||||
|
||||
Let us know that you used the CLI! React with 👍 on the feedback issue or post in Slack!
|
||||
TEXT
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,274 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Blocks of text rendered in CLI
|
||||
module InternalEventsCli
|
||||
module Text
|
||||
extend Helpers
|
||||
|
||||
CLI_INSTRUCTIONS = <<~TEXT.freeze
|
||||
#{format_info('INSTRUCTIONS:')}
|
||||
To start tracking usage of a feature...
|
||||
|
||||
1) Define event (using CLI)
|
||||
2) Trigger event (from code)
|
||||
3) Define metric (using CLI)
|
||||
4) View data in Tableau (after merge & deploy)
|
||||
|
||||
This CLI will help you create the correct defintion files, then provide code examples for instrumentation and testing.
|
||||
|
||||
Learn more: https://docs.gitlab.com/ee/development/internal_analytics/#fundamental-concepts
|
||||
|
||||
TEXT
|
||||
|
||||
FEEDBACK_NOTICE = format_heading <<~TEXT.chomp
|
||||
Thanks for using the Internal Events CLI!
|
||||
|
||||
Please reach out with any feedback!
|
||||
About Internal Events: https://gitlab.com/gitlab-org/analytics-section/analytics-instrumentation/internal/-/issues/687
|
||||
About CLI: https://gitlab.com/gitlab-org/gitlab/-/issues/434038
|
||||
In Slack: #g_analyze_analytics_instrumentation
|
||||
|
||||
Let us know that you used the CLI! React with 👍 on the feedback issue or post in Slack!
|
||||
TEXT
|
||||
|
||||
ALTERNATE_RESOURCES_NOTICE = <<~TEXT.freeze
|
||||
Other resources:
|
||||
|
||||
#{format_warning('Tracking GitLab feature usage from database info:')}
|
||||
https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_instrumentation.html#database-metrics
|
||||
|
||||
#{format_warning('Migrating existing metrics to use Internal Events:')}
|
||||
https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html
|
||||
|
||||
#{format_warning('Remove an existing metric:')}
|
||||
https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_lifecycle.html
|
||||
|
||||
#{format_warning('Finding existing usage data for GitLab features:')}
|
||||
https://metrics.gitlab.com/ (Customize Table > Snowflake query)
|
||||
https://10az.online.tableau.com/#/site/gitlab/views/SnowplowEventExplorationLast30Days/SnowplowEventExplorationLast30D
|
||||
https://10az.online.tableau.com/#/site/gitlab/views/PDServicePingExplorationDashboard/MetricsExploration
|
||||
|
||||
#{format_warning('Customer wants usage data for their own GitLab instance:')}
|
||||
https://docs.gitlab.com/ee/user/analytics/
|
||||
|
||||
#{format_warning('Customer wants usage data for their own products:')}
|
||||
https://docs.gitlab.com/ee/operations/product_analytics/
|
||||
TEXT
|
||||
|
||||
EVENT_TRACKING_EXAMPLES = <<~TEXT
|
||||
Product usage can be tracked in several ways.
|
||||
|
||||
By tracking events: ex) a user changes the assignee on an issue
|
||||
ex) a user uploads a CI template
|
||||
ex) a service desk request is received
|
||||
ex) all stale runners are cleaned up
|
||||
ex) a user copies code to the clipboard from markdown
|
||||
ex) a user uploads an issue template OR a user uploads an MR template
|
||||
|
||||
From database data: ex) track whether each gitlab instance allows signups
|
||||
ex) query how many projects are on each gitlab instance
|
||||
|
||||
TEXT
|
||||
|
||||
EVENT_EXISTENCE_CHECK_INSTRUCTIONS = <<~TEXT.freeze
|
||||
To determine what to do next, let's figure out if the event is already tracked & usable.
|
||||
|
||||
If you're unsure whether an event exists, you can check the existing defintions.
|
||||
|
||||
#{format_info('FROM GDK')}: Check `config/events/` or `ee/config/events`
|
||||
#{format_info('FROM BROWSER')}: Check https://metrics.gitlab.com/snowplow
|
||||
|
||||
Find one? Create a new metric for the event.
|
||||
Otherwise? Create a new event.
|
||||
|
||||
If you find a relevant event that does not have the property `internal_events: true`, it can be migrated to
|
||||
Internal Events. See https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html
|
||||
|
||||
TEXT
|
||||
|
||||
EVENT_DESCRIPTION_INTRO = <<~TEXT.freeze
|
||||
#{format_info('EVENT DESCRIPTION')}
|
||||
Include what the event is supposed to track, where, and when.
|
||||
|
||||
The description field helps others find & reuse this event. This will be used by Engineering, Product, Data team, Support -- and also GitLab customers directly. Be specific and explicit.
|
||||
ex - Debian package published to the registry using a deploy token
|
||||
ex - Issue confidentiality was changed
|
||||
|
||||
TEXT
|
||||
|
||||
EVENT_DESCRIPTION_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. 10+ words likely, but length may vary.')}
|
||||
|
||||
#{format_info('GOOD EXAMPLES:')}
|
||||
- Pipeline is created with a CI Template file included in its configuration
|
||||
- Quick action `/assign @user1` used to assign a single individual to an issuable
|
||||
- Quick action `/target_branch` used on a Merge Request
|
||||
- Quick actions `/unlabel` or `/remove_label` used to remove one or more specific labels
|
||||
- User edits file using the single file editor
|
||||
- User edits file using the Web IDE
|
||||
- User removed issue link between issue and incident
|
||||
- Debian package published to the registry using a deploy token
|
||||
|
||||
#{format_info('GUT CHECK:')}
|
||||
For your description...
|
||||
1. Would two different engineers likely instrument the event from the same code locations?
|
||||
2. Would a new GitLab user find where the event is triggered in the product?
|
||||
3. Would a GitLab customer understand what the description says?
|
||||
|
||||
|
||||
TEXT
|
||||
|
||||
EVENT_ACTION_INTRO = <<~TEXT.freeze
|
||||
#{format_info('EVENT NAME')}
|
||||
The event name is a unique identifier used from both a) app code and b) metric definitions.
|
||||
The name should concisely communicate the same information as the event description.
|
||||
|
||||
ex - change_time_estimate_on_issue
|
||||
ex - push_package_to_repository
|
||||
ex - publish_go_module_to_the_registry_from_pipeline
|
||||
ex - admin_user_comments_on_issue_while_impersonating_blocked_user
|
||||
|
||||
#{format_info('EXPECTED FORMAT:')} #{format_selection('<action>_<target_of_action>_<where/when>')}
|
||||
|
||||
ex) click_save_button_in_issue_description_within_15s_of_page_load
|
||||
- ACTION: click
|
||||
- TARGET: save button
|
||||
- WHERE: in issue description
|
||||
- WHEN: within 15s of page load
|
||||
|
||||
TEXT
|
||||
|
||||
EVENT_ACTION_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Must be globally unique. Must use only letters/numbers/underscores.')}
|
||||
|
||||
#{format_info('FAQs:')}
|
||||
- Q: Present tense or past tense?
|
||||
A: Prefer present tense! But it's up to you.
|
||||
- Q: Other event names have prefixes like `i_` or the `g_group_name`. Why?
|
||||
A: Those are leftovers from legacy naming schemes. Changing the names of old events/metrics can break dashboards, so stability is better than uniformity.
|
||||
|
||||
|
||||
TEXT
|
||||
|
||||
EVENT_IDENTIFIERS_INTRO = <<~TEXT.freeze
|
||||
#{format_info('KEY IDENTIFIERS')}
|
||||
Indicates the attributes recorded when the event occurs. Generally, we want to include every identifier available to us when the event is triggered.
|
||||
|
||||
#{format_info('BACKEND')}: Attributes must be specified when the event is triggered
|
||||
ex) User, project, and namespace are the identifiers available for backend instrumentation:
|
||||
track_internal_event(
|
||||
'%s',
|
||||
user: user,
|
||||
project: project,
|
||||
namespace: project.namespace
|
||||
)
|
||||
|
||||
#{format_info('FRONTEND')}: Attributes are automatically included from the URL
|
||||
ex) When a user takes an action on the MR list page, the URL is https://gitlab.com/gitlab-org/gitlab/-/merge_requests
|
||||
Because this URL is for a project, we know that all of user/project/namespace are available for the event
|
||||
|
||||
#{format_info('NOTE')}: If you're planning to instrument a unique-by-user metric, you should still include project & namespace when possible. This is especially helpful in the data warehouse, where namespace and project can make events relevant for CSM use-cases.
|
||||
|
||||
TEXT
|
||||
|
||||
ADDITIONAL_PROPERTIES_INTRO = <<~TEXT.freeze
|
||||
#{format_info('ADDITIONAL PROPERTIES')}
|
||||
Describe any related attributes or information which should be tracked when the event occurs. This enables extra capabilities:
|
||||
- Service Ping: define metrics filtered to a specific subset of events (built-in properties only)
|
||||
- Snowflake: view/sort/group individual events from GitLab.com
|
||||
|
||||
BUILT-IN PROPERTIES (recommended)
|
||||
For the best performance and flexibility, provide event context using:
|
||||
|
||||
property (string), label (string), value (numeric)
|
||||
|
||||
These attribute names correspond to repurposed fields in Snowflake. They have no special meaning other than data type.
|
||||
|
||||
ex) To add a metric like "Monthly count of unique users who changed an MR status to closed" using a 'change_merge_request_status' event, define an additional property like:
|
||||
Attribute: label (string)
|
||||
Description: Status of merge request after update (one of opened, merged, closed)
|
||||
|
||||
CUSTOM PROPERTIES (as-needed)
|
||||
When the built-in properties are insufficient, properties of any name can be provided.
|
||||
This option becomes available after both property and label are defined, or after value is defined.
|
||||
|
||||
TEXT
|
||||
|
||||
ADDITIONAL_PROPERTIES_ADD_MORE_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Must be unique within the event context. Must use only letters/numbers/underscores.')}
|
||||
|
||||
#{format_info('It should not be named any of the following:')}
|
||||
- property#{' '}
|
||||
- label
|
||||
- value
|
||||
|
||||
TEXT
|
||||
|
||||
DATABASE_METRIC_NOTICE = <<~TEXT
|
||||
|
||||
For right now, this script can only define metrics for internal events.
|
||||
|
||||
For more info on instrumenting database-backed metrics, see https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_instrumentation.html
|
||||
TEXT
|
||||
|
||||
ALL_METRICS_EXIST_NOTICE = <<~TEXT
|
||||
|
||||
Looks like the potential metrics for this event either already exist or are unsupported.
|
||||
|
||||
Check out https://metrics.gitlab.com/ for improved event/metric search capabilities.
|
||||
TEXT
|
||||
|
||||
METRIC_DESCRIPTION_INTRO = <<~TEXT.freeze
|
||||
#{format_info('METRIC DESCRIPTION')}
|
||||
Describes which occurrences of an event are tracked in the metric and how they're grouped.
|
||||
|
||||
The description field is critical for helping others find & reuse this event. This will be used by Engineering, Product, Data team, Support -- and also GitLab customers directly. Be specific and explicit.
|
||||
|
||||
#{format_info('GOOD EXAMPLES:')}
|
||||
- Total count of analytics dashboard list views
|
||||
- Weekly count of unique users who viewed the analytics dashboard list
|
||||
- Monthly count of unique projects where the analytics dashboard list was viewed
|
||||
- Total count of issue updates
|
||||
|
||||
#{format_info('SELECTED EVENT(S):')}
|
||||
TEXT
|
||||
|
||||
METRIC_DESCRIPTION_HELP = <<~TEXT.chomp.freeze
|
||||
#{format_warning('Required. 10+ words likely, but length may vary.')}
|
||||
|
||||
An event description can often be rearranged to work as a metric description.
|
||||
|
||||
ex) Event description: A merge request was created
|
||||
Metric description: Total count of merge requests created
|
||||
Metric description: Weekly count of unqiue users who created merge requests
|
||||
|
||||
Look at the event descriptions above to get ideas!
|
||||
TEXT
|
||||
|
||||
METRIC_NAME_FILTER_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
|
||||
|
||||
Metrics with filters must manually define this portion of their key path.
|
||||
|
||||
Auto-generated key paths for metrics filters results in long & confusing naming. By defining them manually, clarity and discoverability should be better.
|
||||
TEXT
|
||||
|
||||
METRIC_NAME_CONFLICT_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
|
||||
|
||||
Conflict! A metric with the same name already exists: %{name}
|
||||
TEXT
|
||||
|
||||
METRIC_NAME_LENGTH_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
|
||||
|
||||
Filenames cannot exceed 100 characters. The key path (ID) is not restricted, but keeping them aligned is recommended.
|
||||
|
||||
If needed, you can modify the key path and filename further after saving.
|
||||
TEXT
|
||||
|
||||
METRIC_NAME_ERROR = <<~TEXT.freeze
|
||||
#{format_warning('Input is invalid. Max %{count} characters. Only lowercase/numbers/underscores allowed. Ensure this key path (ID) is not already in use.')}
|
||||
TEXT
|
||||
end
|
||||
end
|
144
scripts/internal_events/cli/text/event_definer.rb
Normal file
144
scripts/internal_events/cli/text/event_definer.rb
Normal file
@ -0,0 +1,144 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module InternalEventsCli
|
||||
module Text
|
||||
module EventDefiner
|
||||
extend Helpers
|
||||
|
||||
DESCRIPTION_INTRO = <<~TEXT.freeze
|
||||
#{format_info('EVENT DESCRIPTION')}
|
||||
Include what the event is supposed to track, where, and when.
|
||||
|
||||
The description field helps others find & reuse this event. This will be used by Engineering, Product, Data team, Support -- and also GitLab customers directly. Be specific and explicit.
|
||||
ex - Debian package published to the registry using a deploy token
|
||||
ex - Issue confidentiality was changed
|
||||
|
||||
TEXT
|
||||
|
||||
DESCRIPTION_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. 10+ words likely, but length may vary.')}
|
||||
|
||||
#{format_info('GOOD EXAMPLES:')}
|
||||
- Pipeline is created with a CI Template file included in its configuration
|
||||
- Quick action `/assign @user1` used to assign a single individual to an issuable
|
||||
- Quick action `/target_branch` used on a Merge Request
|
||||
- Quick actions `/unlabel` or `/remove_label` used to remove one or more specific labels
|
||||
- User edits file using the single file editor
|
||||
- User edits file using the Web IDE
|
||||
- User removed issue link between issue and incident
|
||||
- Debian package published to the registry using a deploy token
|
||||
|
||||
#{format_info('GUT CHECK:')}
|
||||
For your description...
|
||||
1. Would two different engineers likely instrument the event from the same code locations?
|
||||
2. Would a new GitLab user find where the event is triggered in the product?
|
||||
3. Would a GitLab customer understand what the description says?
|
||||
|
||||
|
||||
TEXT
|
||||
|
||||
ACTION_INTRO = <<~TEXT.freeze
|
||||
#{format_info('EVENT NAME')}
|
||||
The event name is a unique identifier used from both a) app code and b) metric definitions.
|
||||
The name should concisely communicate the same information as the event description.
|
||||
|
||||
ex - change_time_estimate_on_issue
|
||||
ex - push_package_to_repository
|
||||
ex - publish_go_module_to_the_registry_from_pipeline
|
||||
ex - admin_user_comments_on_issue_while_impersonating_blocked_user
|
||||
|
||||
#{format_info('EXPECTED FORMAT:')} #{format_selection('<action>_<target_of_action>_<where/when>')}
|
||||
|
||||
ex) click_save_button_in_issue_description_within_15s_of_page_load
|
||||
- ACTION: click
|
||||
- TARGET: save button
|
||||
- WHERE: in issue description
|
||||
- WHEN: within 15s of page load
|
||||
|
||||
TEXT
|
||||
|
||||
ACTION_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Must be globally unique. Must use only letters/numbers/underscores.')}
|
||||
|
||||
#{format_info('FAQs:')}
|
||||
- Q: Present tense or past tense?
|
||||
A: Prefer present tense! But it's up to you.
|
||||
- Q: Other event names have prefixes like `i_` or the `g_group_name`. Why?
|
||||
A: Those are leftovers from legacy naming schemes. Changing the names of old events/metrics can break dashboards, so stability is better than uniformity.
|
||||
|
||||
|
||||
TEXT
|
||||
|
||||
IDENTIFIERS_INTRO = <<~TEXT.freeze
|
||||
#{format_info('KEY IDENTIFIERS')}
|
||||
Indicates the attributes recorded when the event occurs. Generally, we want to include every identifier available to us when the event is triggered.
|
||||
|
||||
#{format_info('BACKEND')}: Attributes must be specified when the event is triggered
|
||||
ex) User, project, and namespace are the identifiers available for backend instrumentation:
|
||||
track_internal_event(
|
||||
'%s',
|
||||
user: user,
|
||||
project: project,
|
||||
namespace: project.namespace
|
||||
)
|
||||
|
||||
#{format_info('FRONTEND')}: Attributes are automatically included from the URL
|
||||
ex) When a user takes an action on the MR list page, the URL is https://gitlab.com/gitlab-org/gitlab/-/merge_requests
|
||||
Because this URL is for a project, we know that all of user/project/namespace are available for the event
|
||||
|
||||
#{format_info('NOTE')}: If you're planning to instrument a unique-by-user metric, you should still include project & namespace when possible. This is especially helpful in the data warehouse, where namespace and project can make events relevant for CSM use-cases.
|
||||
|
||||
TEXT
|
||||
|
||||
IDENTIFIER_OPTIONS = {
|
||||
%w[project namespace user] =>
|
||||
'Use case: For project-level user actions (ex - issue_assignee_changed) [MOST COMMON]',
|
||||
%w[namespace user] =>
|
||||
'Use case: For namespace-level user actions (ex - epic_assigned_to_milestone)',
|
||||
%w[user] =>
|
||||
'Use case: For user-only actions (ex - admin_impersonated_user)',
|
||||
%w[project namespace] =>
|
||||
'Use case: For project-level events without user interaction (ex - service_desk_request_received)',
|
||||
%w[namespace] =>
|
||||
'Use case: For namespace-level events without user interaction (ex - stale_runners_cleaned_up)',
|
||||
%w[feature_enabled_by_namespace_ids user] =>
|
||||
'Use case: For user actions attributable to multiple namespaces (ex - Code-Suggestions / Duo Pro)',
|
||||
%w[] =>
|
||||
'Use case: For instance-level events without user interaction [LEAST COMMON]'
|
||||
}.freeze
|
||||
|
||||
ADDITIONAL_PROPERTIES_INTRO = <<~TEXT.freeze
|
||||
#{format_info('ADDITIONAL PROPERTIES')}
|
||||
Describe any related attributes or information which should be tracked when the event occurs. This enables extra capabilities:
|
||||
- Service Ping: define metrics filtered to a specific subset of events (built-in properties only)
|
||||
- Snowflake: view/sort/group individual events from GitLab.com
|
||||
|
||||
BUILT-IN PROPERTIES (recommended)
|
||||
For the best performance and flexibility, provide event context using:
|
||||
|
||||
property (string), label (string), value (numeric)
|
||||
|
||||
These attribute names correspond to repurposed fields in Snowflake. They have no special meaning other than data type.
|
||||
|
||||
ex) To add a metric like "Monthly count of unique users who changed an MR status to closed" using a 'change_merge_request_status' event, define an additional property like:
|
||||
Attribute: label (string)
|
||||
Description: Status of merge request after update (one of opened, merged, closed)
|
||||
|
||||
CUSTOM PROPERTIES (as-needed)
|
||||
When the built-in properties are insufficient, properties of any name can be provided.
|
||||
This option becomes available after both property and label are defined, or after value is defined.
|
||||
|
||||
TEXT
|
||||
|
||||
ADDITIONAL_PROPERTIES_ADD_MORE_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Must be unique within the event context. Must use only letters/numbers/underscores.')}
|
||||
|
||||
#{format_info('It should not be named any of the following:')}
|
||||
- property#{' '}
|
||||
- label
|
||||
- value
|
||||
|
||||
TEXT
|
||||
end
|
||||
end
|
||||
end
|
65
scripts/internal_events/cli/text/flow_advisor.rb
Executable file
65
scripts/internal_events/cli/text/flow_advisor.rb
Executable file
@ -0,0 +1,65 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Blocks of text rendered in CLI
|
||||
module InternalEventsCli
|
||||
module Text
|
||||
module FlowAdvisor
|
||||
extend Helpers
|
||||
|
||||
ALTERNATE_RESOURCES_NOTICE = <<~TEXT.freeze
|
||||
Other resources:
|
||||
|
||||
#{format_warning('Tracking GitLab feature usage from database info:')}
|
||||
https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_instrumentation.html#database-metrics
|
||||
|
||||
#{format_warning('Migrating existing metrics to use Internal Events:')}
|
||||
https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html
|
||||
|
||||
#{format_warning('Remove an existing metric:')}
|
||||
https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_lifecycle.html
|
||||
|
||||
#{format_warning('Finding existing usage data for GitLab features:')}
|
||||
https://metrics.gitlab.com/ (Customize Table > Snowflake query)
|
||||
https://10az.online.tableau.com/#/site/gitlab/views/SnowplowEventExplorationLast30Days/SnowplowEventExplorationLast30D
|
||||
https://10az.online.tableau.com/#/site/gitlab/views/PDServicePingExplorationDashboard/MetricsExploration
|
||||
|
||||
#{format_warning('Customer wants usage data for their own GitLab instance:')}
|
||||
https://docs.gitlab.com/ee/user/analytics/
|
||||
|
||||
#{format_warning('Customer wants usage data for their own products:')}
|
||||
https://docs.gitlab.com/ee/operations/product_analytics/
|
||||
TEXT
|
||||
|
||||
EVENT_TRACKING_EXAMPLES = <<~TEXT
|
||||
Product usage can be tracked in several ways.
|
||||
|
||||
By tracking events: ex) a user changes the assignee on an issue
|
||||
ex) a user uploads a CI template
|
||||
ex) a service desk request is received
|
||||
ex) all stale runners are cleaned up
|
||||
ex) a user copies code to the clipboard from markdown
|
||||
ex) a user uploads an issue template OR a user uploads an MR template
|
||||
|
||||
From database data: ex) track whether each gitlab instance allows signups
|
||||
ex) query how many projects are on each gitlab instance
|
||||
|
||||
TEXT
|
||||
|
||||
EVENT_EXISTENCE_CHECK_INSTRUCTIONS = <<~TEXT.freeze
|
||||
To determine what to do next, let's figure out if the event is already tracked & usable.
|
||||
|
||||
If you're unsure whether an event exists, you can check the existing defintions.
|
||||
|
||||
#{format_info('FROM GDK')}: Check `config/events/` or `ee/config/events`
|
||||
#{format_info('FROM BROWSER')}: Check https://metrics.gitlab.com/snowplow
|
||||
|
||||
Find one? Create a new metric for the event.
|
||||
Otherwise? Create a new event.
|
||||
|
||||
If you find a relevant event that does not have the property `internal_events: true`, it can be migrated to
|
||||
Internal Events. See https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html
|
||||
|
||||
TEXT
|
||||
end
|
||||
end
|
||||
end
|
92
scripts/internal_events/cli/text/metric_definer.rb
Executable file
92
scripts/internal_events/cli/text/metric_definer.rb
Executable file
@ -0,0 +1,92 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Blocks of text rendered in CLI
|
||||
module InternalEventsCli
|
||||
module Text
|
||||
module MetricDefiner
|
||||
extend Helpers
|
||||
|
||||
DATABASE_METRIC_NOTICE = <<~TEXT
|
||||
|
||||
For right now, this script can only define metrics for internal events.
|
||||
|
||||
For more info on instrumenting database-backed metrics, see https://docs.gitlab.com/ee/development/internal_analytics/metrics/metrics_instrumentation.html
|
||||
TEXT
|
||||
|
||||
ALL_METRICS_EXIST_NOTICE = <<~TEXT
|
||||
|
||||
Looks like the potential metrics for this event either already exist or are unsupported.
|
||||
|
||||
Check out https://metrics.gitlab.com/ for improved event/metric search capabilities.
|
||||
TEXT
|
||||
|
||||
DESCRIPTION_INTRO = <<~TEXT.freeze
|
||||
#{format_info('METRIC DESCRIPTION')}
|
||||
Describes which occurrences of an event are tracked in the metric and how they're grouped.
|
||||
|
||||
The description field is critical for helping others find & reuse this event. This will be used by Engineering, Product, Data team, Support -- and also GitLab customers directly. Be specific and explicit.
|
||||
|
||||
#{format_info('GOOD EXAMPLES:')}
|
||||
- Total count of analytics dashboard list views
|
||||
- Weekly count of unique users who viewed the analytics dashboard list
|
||||
- Monthly count of unique projects where the analytics dashboard list was viewed
|
||||
- Total count of issue updates
|
||||
|
||||
#{format_info('SELECTED EVENT(S):')}
|
||||
TEXT
|
||||
|
||||
DESCRIPTION_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. 10+ words likely, but length may vary.')}
|
||||
|
||||
An event description can often be rearranged to work as a metric description.
|
||||
|
||||
ex) Event description: A merge request was created
|
||||
Metric description: Total count of merge requests created
|
||||
Metric description: Weekly count of unqiue users who created merge requests
|
||||
|
||||
Look at the event descriptions above to get ideas!
|
||||
TEXT
|
||||
|
||||
NAME_FILTER_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
|
||||
|
||||
Metrics with filters must manually define this portion of their key path.
|
||||
|
||||
Auto-generated key paths for metrics filters results in long & confusing naming. By defining them manually, clarity and discoverability should be better.
|
||||
TEXT
|
||||
|
||||
NAME_CONFLICT_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
|
||||
|
||||
Conflict! A metric with the same name already exists: %{name}
|
||||
TEXT
|
||||
|
||||
NAME_LENGTH_HELP = <<~TEXT.freeze
|
||||
#{format_warning('Required. Max %{count} characters. Only lowercase/numbers/underscores allowed.')}
|
||||
|
||||
Filenames cannot exceed 100 characters. The key path (ID) is not restricted, but keeping them aligned is recommended.
|
||||
|
||||
If needed, you can modify the key path and filename further after saving.
|
||||
TEXT
|
||||
|
||||
NAME_REQUIREMENT_REASONS = {
|
||||
filters: {
|
||||
text: 'Metrics using filters are too complex for default naming.',
|
||||
help: NAME_FILTER_HELP
|
||||
},
|
||||
length: {
|
||||
text: 'The default filename will be too long.',
|
||||
help: NAME_LENGTH_HELP
|
||||
},
|
||||
conflict: {
|
||||
text: 'The default key path is already in use.',
|
||||
help: NAME_CONFLICT_HELP
|
||||
}
|
||||
}.freeze
|
||||
|
||||
NAME_ERROR = <<~TEXT.freeze
|
||||
#{format_warning('Input is invalid. Max %{count} characters. Only lowercase/numbers/underscores allowed. Ensure this key path (ID) is not already in use.')}
|
||||
TEXT
|
||||
end
|
||||
end
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
import { GlBadge, GlTab, GlTabs, GlIcon, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import { GlAvatar, GlBadge, GlTab, GlTabs, GlIcon, GlSprintf, GlLink } from '@gitlab/ui';
|
||||
import VueRouter from 'vue-router';
|
||||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
@ -140,6 +140,31 @@ describe('ml/model_registry/apps/show_model_version.vue', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sidebar', () => {
|
||||
const findSidebarAuthorLink = () => wrapper.findByTestId('sidebar-author-link');
|
||||
const findAvatar = () => wrapper.findComponent(GlAvatar);
|
||||
|
||||
it('displays sidebar author link', async () => {
|
||||
const resolver = jest.fn().mockResolvedValue(modelVersionQueryWithAuthor);
|
||||
|
||||
createWrapper({ resolver });
|
||||
|
||||
await waitForPromises();
|
||||
|
||||
expect(findSidebarAuthorLink().attributes('href')).toBe('path/to/user');
|
||||
expect(findSidebarAuthorLink().text()).toBe('Root');
|
||||
expect(findAvatar().props('src')).toBe('path/to/avatar');
|
||||
});
|
||||
|
||||
describe('when model does not get loaded', () => {
|
||||
it('does not displays sidebar author link', async () => {
|
||||
createWrapper({ resolver: jest.fn().mockRejectedValue(new Error('Failure!')) });
|
||||
await waitForPromises();
|
||||
expect(findSidebarAuthorLink().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Requests data with the right parameters', async () => {
|
||||
const resolver = jest.fn().mockResolvedValue(modelVersionQueryWithAuthor);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
1149
spec/support/shared_examples/models/wiki_page_shared_examples.rb
Normal file
1149
spec/support/shared_examples/models/wiki_page_shared_examples.rb
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user