Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-02 03:08:36 +00:00
parent 47daa6f9b3
commit 7f119dc263
49 changed files with 899 additions and 103 deletions

View File

@ -11,11 +11,6 @@ Gitlab/PolicyRuleBoolean:
Exclude:
- 'ee/app/policies/ee/identity_provider_policy.rb'
# Offense count: 200
# Cop supports --auto-correct.
Lint/RedundantCopDisableDirective:
Enabled: false
# Offense count: 22
# Cop supports --auto-correct.
# Configuration parameters: AllowComments.
@ -68,10 +63,6 @@ RSpec/PredicateMatcher:
RSpec/RepeatedExampleGroupBody:
Enabled: false
# Offense count: 225
RSpec/RepeatedExampleGroupDescription:
Enabled: false
# Offense count: 667
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.

View File

@ -0,0 +1,211 @@
---
# Cop supports --auto-correct.
Lint/RedundantCopDisableDirective:
# This cop can only be enabled after enabling all cops which are currently
# disabled. Otherwise we'll see RuboCop complaining depending on
# REVEAL_RUBOCOP_TODO environment variable.
Enabled: false
Exclude:
- 'app/controllers/admin/dashboard_controller.rb'
- 'app/controllers/concerns/enforces_two_factor_authentication.rb'
- 'app/controllers/concerns/integrations/actions.rb'
- 'app/controllers/concerns/issues_calendar.rb'
- 'app/controllers/concerns/snippets_actions.rb'
- 'app/controllers/concerns/wiki_actions.rb'
- 'app/controllers/groups/autocomplete_sources_controller.rb'
- 'app/controllers/groups/labels_controller.rb'
- 'app/controllers/import/fogbugz_controller.rb'
- 'app/controllers/import/github_controller.rb'
- 'app/controllers/projects/issues_controller.rb'
- 'app/controllers/projects/jobs_controller.rb'
- 'app/controllers/projects/pipeline_schedules_controller.rb'
- 'app/controllers/projects/pipelines/tests_controller.rb'
- 'app/controllers/search_controller.rb'
- 'app/controllers/sessions_controller.rb'
- 'app/finders/autocomplete/acts_as_taggable_on/tags_finder.rb'
- 'app/finders/autocomplete/move_to_project_finder.rb'
- 'app/finders/autocomplete/routes_finder.rb'
- 'app/finders/autocomplete/users_finder.rb'
- 'app/finders/ci/daily_build_group_report_results_finder.rb'
- 'app/finders/groups_finder.rb'
- 'app/finders/users_finder.rb'
- 'app/graphql/resolvers/concerns/caching_array_resolver.rb'
- 'app/graphql/resolvers/project_milestones_resolver.rb'
- 'app/graphql/types/base_enum.rb'
- 'app/graphql/types/ci/runner_web_url_edge.rb'
- 'app/graphql/types/packages/helm/dependency_type.rb'
- 'app/graphql/types/projects/service_type_enum.rb'
- 'app/helpers/diff_helper.rb'
- 'app/helpers/search_helper.rb'
- 'app/models/concerns/cascading_namespace_setting_attribute.rb'
- 'app/models/concerns/from_except.rb'
- 'app/models/concerns/from_intersect.rb'
- 'app/models/concerns/from_union.rb'
- 'app/models/user.rb'
- 'app/presenters/dev_ops_report/metric_presenter.rb'
- 'app/serializers/diffs_entity.rb'
- 'app/serializers/fork_namespace_entity.rb'
- 'app/services/ci/job_artifacts/destroy_batch_service.rb'
- 'app/services/ci/register_job_service.rb'
- 'app/services/ci/retry_job_service.rb'
- 'app/services/database/consistency_check_service.rb'
- 'app/services/issues/export_csv_service.rb'
- 'app/services/labels/transfer_service.rb'
- 'app/services/projects/auto_devops/disable_service.rb'
- 'app/services/projects/open_issues_count_service.rb'
- 'app/services/spam/spam_action_service.rb'
- 'app/services/users/migrate_to_ghost_user_service.rb'
- 'app/services/web_hooks/destroy_service.rb'
- 'app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb'
- 'app/workers/bulk_imports/entity_worker.rb'
- 'app/workers/container_expiration_policy_worker.rb'
- 'app/workers/create_note_diff_file_worker.rb'
- 'app/workers/expire_job_cache_worker.rb'
- 'app/workers/import_issues_csv_worker.rb'
- 'app/workers/incident_management/process_alert_worker_v2.rb'
- 'app/workers/merge_worker.rb'
- 'app/workers/remove_unaccepted_member_invites_worker.rb'
- 'config/initializers/warden.rb'
- 'config/initializers/wikicloth_redos_patch.rb'
- 'config/routes/api.rb'
- 'db/migrate/20210303193544_add_concurrent_fields_to_bulk_imports_trackers.rb'
- 'db/migrate/20210917134321_remove_temporary_index_for_project_topics_on_taggings.rb'
- 'db/migrate/20211013014228_add_content_validation_endpoint_to_application_settings.rb'
- 'db/post_migrate/20210610042700_remove_clusters_applications_fluentd_table.rb'
- 'db/post_migrate/20210708011426_finalize_ci_builds_metadata_bigint_conversion.rb'
- 'db/post_migrate/20210730104800_schedule_extract_project_topics_into_separate_table.rb'
- 'db/post_migrate/20210806131706_finalize_taggins_bigint_conversion.rb'
- 'db/post_migrate/20210906130643_drop_temporary_columns_and_triggers_for_taggings.rb'
- 'db/post_migrate/20211028100303_tmp_index_for_delete_issue_merge_request_taggings_records.rb'
- 'db/post_migrate/20220328100456_schedule20220328_reset_duplicate_ci_runners_token_encrypted_values_on_projects.rb'
- 'db/post_migrate/20220328100457_schedule20220328_reset_duplicate_ci_runners_token_values_on_projects.rb'
- 'ee/app/controllers/ee/groups/group_members_controller.rb'
- 'ee/app/controllers/groups/todos_controller.rb'
- 'ee/app/finders/geo/file_registry_finder.rb'
- 'ee/app/finders/geo/project_registry_finder.rb'
- 'ee/app/finders/geo/registry_finder.rb'
- 'ee/app/finders/status_page/incident_comments_finder.rb'
- 'ee/app/finders/status_page/incidents_finder.rb'
- 'ee/app/graphql/types/ci/minutes/namespace_monthly_usage_type.rb'
- 'ee/app/graphql/types/incident_management/oncall_rotation_active_period_input_type.rb'
- 'ee/app/graphql/types/scan_type.rb'
- 'ee/app/helpers/ee/boards_helper.rb'
- 'ee/app/helpers/ee/namespaces_helper.rb'
- 'ee/app/helpers/projects/on_demand_scans_helper.rb'
- 'ee/app/models/ee/vulnerability.rb'
- 'ee/app/models/geo/event_log.rb'
- 'ee/app/services/analytics/cycle_analytics/data_loader_service.rb'
- 'ee/app/services/ee/boards/issues/list_service.rb'
- 'ee/app/services/ee/search_service.rb'
- 'ee/app/services/ee/users/migrate_to_ghost_user_service.rb'
- 'ee/app/services/geo/repository_base_sync_service.rb'
- 'ee/app/workers/ee/issuable_export_csv_worker.rb'
- 'ee/app/workers/ee/namespaces/in_product_marketing_emails_worker.rb'
- 'ee/app/workers/geo/design_repository_shard_sync_worker.rb'
- 'ee/app/workers/geo/repository_shard_sync_worker.rb'
- 'ee/app/workers/geo/repository_verification/secondary/shard_worker.rb'
- 'ee/app/workers/scan_security_report_secrets_worker.rb'
- 'ee/app/workers/security/orchestration_policy_rule_schedule_worker.rb'
- 'ee/db/geo/migrate/20210504143244_add_verification_to_merge_request_diff_registry.rb'
- 'ee/lib/analytics/merge_request_metrics_calculator.rb'
- 'ee/lib/api/audit_events.rb'
- 'ee/lib/ee/api/entities/analytics/code_review/merge_request.rb'
- 'ee/lib/ee/api/settings.rb'
- 'ee/lib/ee/gitlab/usage_data.rb'
- 'ee/lib/gitlab/analytics/type_of_work/tasks_by_type.rb'
- 'ee/spec/helpers/ee/releases_helper_spec.rb'
- 'ee/spec/lib/ee/gitlab/issuable_metadata_spec.rb'
- 'ee/spec/lib/elastic/latest/project_instance_proxy_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/boards/epic_boards/epic_move_list_spec.rb'
- 'ee/spec/services/security/merge_reports_service_spec.rb'
- 'ee/spec/support/shared_examples/models/elasticsearch_indexed_container_shared_examples.rb'
- 'ee/spec/support/shared_examples/models/geo_verifiable_registry_shared_examples.rb'
- 'lib/api/api.rb'
- 'lib/api/ci/variables.rb'
- 'lib/api/entities/environment.rb'
- 'lib/api/entities/issuable_time_stats.rb'
- 'lib/api/helpers.rb'
- 'lib/bulk_imports/common/transformers/user_reference_transformer.rb'
- 'lib/bulk_imports/pipeline/runner.rb'
- 'lib/container_registry/tag.rb'
- 'lib/event_filter.rb'
- 'lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder.rb'
- 'lib/gitlab/analytics/cycle_analytics/base_query_builder.rb'
- 'lib/gitlab/analytics/cycle_analytics/records_fetcher.rb'
- 'lib/gitlab/background_migration/backfill_issue_search_data.rb'
- 'lib/gitlab/background_migration/drop_invalid_vulnerabilities.rb'
- 'lib/gitlab/background_migration/fix_merge_request_diff_commit_users.rb'
- 'lib/gitlab/background_migration/migrate_personal_namespace_project_maintainer_to_owner.rb'
- 'lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb'
- 'lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb'
- 'lib/gitlab/bitbucket_import/importer.rb'
- 'lib/gitlab/cache/request_cache.rb'
- 'lib/gitlab/ci/build/artifacts/metadata/entry.rb'
- 'lib/gitlab/ci/pipeline/duration.rb'
- 'lib/gitlab/ci/reports/accessibility_reports.rb'
- 'lib/gitlab/ci/reports/test_reports.rb'
- 'lib/gitlab/ci/reports/test_reports_comparer.rb'
- 'lib/gitlab/ci/reports/test_suite.rb'
- 'lib/gitlab/ci/reports/test_suite_summary.rb'
- 'lib/gitlab/composer/cache.rb'
- 'lib/gitlab/database/consistency_checker.rb'
- 'lib/gitlab/database/migration.rb'
- 'lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb'
- 'lib/gitlab/diff/file.rb'
- 'lib/gitlab/diff/file_collection/merge_request_diff_batch.rb'
- 'lib/gitlab/diff/pair_selector.rb'
- 'lib/gitlab/diff/parser.rb'
- 'lib/gitlab/encrypted_ldap_command.rb'
- 'lib/gitlab/encrypted_smtp_command.rb'
- 'lib/gitlab/git/patches/collection.rb'
- 'lib/gitlab/github_import/user_finder.rb'
- 'lib/gitlab/gitlab_import/importer.rb'
- 'lib/gitlab/graphql/pagination/keyset/connection.rb'
- 'lib/gitlab/legacy_github_import/user_formatter.rb'
- 'lib/gitlab/object_hierarchy.rb'
- 'lib/gitlab/pagination/keyset/pager.rb'
- 'lib/gitlab/performance_bar/redis_adapter_when_peek_enabled.rb'
- 'lib/gitlab/profiler.rb'
- 'lib/gitlab/project_search_results.rb'
- 'lib/gitlab/redis/hll.rb'
- 'lib/gitlab/request_profiler.rb'
- 'lib/gitlab/slash_commands/issue_search.rb'
- 'lib/gitlab/usage_data.rb'
- 'lib/gitlab/usage_data_queries.rb'
- 'lib/gitlab/utils/usage_data.rb'
- 'lib/tasks/gitlab/cleanup.rake'
- 'scripts/security-harness'
- 'sidekiq_cluster/cli.rb'
- 'sidekiq_cluster/sidekiq_cluster.rb'
- 'spec/frontend/fixtures/merge_requests.rb'
- 'spec/helpers/releases_helper_spec.rb'
- 'spec/lib/gitlab/avatar_cache_spec.rb'
- 'spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb'
- 'spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb'
- 'spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects_spec.rb'
- 'spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects_spec.rb'
- 'spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb'
- 'spec/lib/gitlab/git/tree_spec.rb'
- 'spec/lib/gitlab/pagination/keyset/iterator_spec.rb'
- 'spec/lib/gitlab/shard_health_cache_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb'
- 'spec/metrics_server/metrics_server_spec.rb'
- 'spec/models/ci/build_trace_chunk_spec.rb'
- 'spec/models/namespace/package_setting_spec.rb'
- 'spec/models/namespace_spec.rb'
- 'spec/requests/api/alert_management_alerts_spec.rb'
- 'spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb'
- 'spec/services/alert_management/metric_images/upload_service_spec.rb'
- 'spec/services/suggestions/apply_service_spec.rb'
- 'spec/support/helpers/snowplow_helpers.rb'
- 'spec/support/helpers/wait_for_requests.rb'
- 'spec/support/shared_examples/models/boards/listable_shared_examples.rb'
- 'spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb'
- 'spec/support/shared_examples/models/packages/debian/distribution_key_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb'
- 'spec/uploaders/packages/composer/cache_uploader_spec.rb'
- 'tooling/danger/product_intelligence.rb'
- 'tooling/lib/tooling/helm3_client.rb'
- 'tooling/lib/tooling/kubernetes_client.rb'
- 'tooling/rspec_flaky/listener.rb'

View File

@ -0,0 +1,115 @@
---
RSpec/RepeatedExampleGroupDescription:
# Offense count: 263
# Temporarily disabled due to too many offenses
Enabled: false
Exclude:
- 'ee/spec/finders/merge_trains_finder_spec.rb'
- 'ee/spec/graphql/resolvers/vulnerabilities_grade_resolver_spec.rb'
- 'ee/spec/graphql/resolvers/vulnerability_severities_count_resolver_spec.rb'
- 'ee/spec/helpers/ee/auth_helper_spec.rb'
- 'ee/spec/lib/gitlab/auth/ldap/person_spec.rb'
- 'ee/spec/lib/gitlab/usage/metrics/instrumentations/approval_project_rules_with_user_metric_spec.rb'
- 'ee/spec/models/approval_merge_request_rule_spec.rb'
- 'ee/spec/models/ci/build_spec.rb'
- 'ee/spec/models/dast/profile_spec.rb'
- 'ee/spec/models/ee/namespace_spec.rb'
- 'ee/spec/models/geo/deleted_project_spec.rb'
- 'ee/spec/models/geo_node_spec.rb'
- 'ee/spec/models/integrations/github_spec.rb'
- 'ee/spec/models/merge_requests/external_status_check_spec.rb'
- 'ee/spec/models/project_spec.rb'
- 'ee/spec/models/release_highlight_spec.rb'
- 'ee/spec/models/security/scan_spec.rb'
- 'ee/spec/models/software_license_spec.rb'
- 'ee/spec/policies/app_sec/fuzzing/coverage/corpus_policy_spec.rb'
- 'ee/spec/policies/group_policy_spec.rb'
- 'ee/spec/policies/project_policy_spec.rb'
- 'ee/spec/requests/api/graphql/iteration_spec.rb'
- 'ee/spec/requests/api/graphql/mutations/iterations/create_spec.rb'
- 'ee/spec/requests/api/graphql/vulnerabilities/sort_spec.rb'
- 'ee/spec/requests/groups/security/credentials_controller_spec.rb'
- 'ee/spec/services/app_sec/dast/profiles/create_associations_service_spec.rb'
- 'ee/spec/services/app_sec/dast/site_validations/find_or_create_service_spec.rb'
- 'ee/spec/services/audit_event_service_spec.rb'
- 'ee/spec/services/groups/sync_service_spec.rb'
- 'ee/spec/services/todo_service_spec.rb'
- 'ee/spec/support/shared_examples/services/scoped_label_shared_examples.rb'
- 'ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb'
- 'spec/controllers/profiles/notifications_controller_spec.rb'
- 'spec/controllers/projects/issues_controller_spec.rb'
- 'spec/controllers/projects/merge_requests/drafts_controller_spec.rb'
- 'spec/controllers/projects/pages_domains_controller_spec.rb'
- 'spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb'
- 'spec/features/merge_request/user_sees_merge_widget_spec.rb'
- 'spec/features/projects/jobs_spec.rb'
- 'spec/features/projects/new_project_spec.rb'
- 'spec/features/security/project/private_access_spec.rb'
- 'spec/finders/ci/pipelines_for_merge_request_finder_spec.rb'
- 'spec/frontend/fixtures/runner.rb'
- 'spec/frontend/fixtures/startup_css.rb'
- 'spec/helpers/admin/user_actions_helper_spec.rb'
- 'spec/helpers/dropdowns_helper_spec.rb'
- 'spec/helpers/gitlab_routing_helper_spec.rb'
- 'spec/helpers/namespaces_helper_spec.rb'
- 'spec/initializers/omniauth_spec.rb'
- 'spec/lib/banzai/pipeline/gfm_pipeline_spec.rb'
- 'spec/lib/gitlab/alert_management/payload/base_spec.rb'
- 'spec/lib/gitlab/auth/atlassian/auth_hash_spec.rb'
- 'spec/lib/gitlab/auth/blocked_user_tracker_spec.rb'
- 'spec/lib/gitlab/auth/ldap/dn_spec.rb'
- 'spec/lib/gitlab/ci/config/edge_stages_injector_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/jobs_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/needs_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/policy_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/processable_spec.rb'
- 'spec/lib/gitlab/ci/config/entry/release_spec.rb'
- 'spec/lib/gitlab/ci/config_spec.rb'
- 'spec/lib/gitlab/ci/parsers/security/common_spec.rb'
- 'spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
- 'spec/lib/gitlab/ci/yaml_processor_spec.rb'
- 'spec/lib/gitlab/data_builder/push_spec.rb'
- 'spec/lib/gitlab/database_importers/common_metrics/importer_spec.rb'
- 'spec/lib/gitlab/git/diff_spec.rb'
- 'spec/lib/gitlab/git/push_spec.rb'
- 'spec/lib/gitlab/git/repository_spec.rb'
- 'spec/lib/gitlab/import_export/project/sample/relation_factory_spec.rb'
- 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
- 'spec/lib/gitlab/kubernetes/rollout_status_spec.rb'
- 'spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb'
- 'spec/lib/gitlab/sanitizers/exif_spec.rb'
- 'spec/lib/gitlab/template/finders/global_template_finder_spec.rb'
- 'spec/lib/gitlab/usage_data_spec.rb'
- 'spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb'
- 'spec/models/ci/build_trace_chunk_spec.rb'
- 'spec/models/ci/runner_spec.rb'
- 'spec/models/concerns/ci/has_ref_spec.rb'
- 'spec/models/concerns/issuable_spec.rb'
- 'spec/models/integrations/chat_message/pipeline_message_spec.rb'
- 'spec/models/merge_request_assignee_spec.rb'
- 'spec/models/merge_request_reviewer_spec.rb'
- 'spec/models/merge_request_spec.rb'
- 'spec/models/personal_access_token_spec.rb'
- 'spec/models/project_spec.rb'
- 'spec/models/ssh_host_key_spec.rb'
- 'spec/requests/api/files_spec.rb'
- 'spec/requests/api/graphql/project/release_spec.rb'
- 'spec/requests/api/group_clusters_spec.rb'
- 'spec/requests/api/merge_requests_spec.rb'
- 'spec/requests/api/notification_settings_spec.rb'
- 'spec/requests/api/project_clusters_spec.rb'
- 'spec/requests/api/users_spec.rb'
- 'spec/routing/project_routing_spec.rb'
- 'spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb'
- 'spec/services/ci/register_job_service_spec.rb'
- 'spec/services/import/github_service_spec.rb'
- 'spec/services/merge_requests/refresh_service_spec.rb'
- 'spec/services/metrics/dashboard/gitlab_alert_embed_service_spec.rb'
- 'spec/services/resource_access_tokens/create_service_spec.rb'
- 'spec/services/verify_pages_domain_service_spec.rb'
- 'spec/support/cycle_analytics_helpers/test_generation.rb'
- 'spec/support/shared_examples/models/application_setting_shared_examples.rb'
- 'spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb'
- 'spec/support/shared_examples/serializers/diff_file_entity_shared_examples.rb'
- 'spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb'
- 'spec/support_specs/database/prevent_cross_joins_spec.rb'

View File

@ -857,6 +857,14 @@ const Api = {
});
},
tag(id, tagName) {
const url = Api.buildUrl(this.tagPath)
.replace(':id', encodeURIComponent(id))
.replace(':tag_name', encodeURIComponent(tagName));
return axios.get(url);
},
freezePeriods(id) {
const url = Api.buildUrl(this.freezePeriodsPath).replace(':id', encodeURIComponent(id));

View File

@ -0,0 +1,12 @@
import axios from '../lib/utils/axios_utils';
import { buildApiUrl } from './api_utils';
const TAG_PATH = '/api/:version/projects/:id/repository/tags/:tag_name';
export function getTag(id, tagName) {
const url = buildApiUrl(TAG_PATH)
.replace(':id', encodeURIComponent(id))
.replace(':tag_name', encodeURIComponent(tagName));
return axios.get(url);
}

View File

@ -216,6 +216,15 @@ export default {
return data[keys[0]];
},
getDrawerHeaderHeight() {
const wrapperEl = document.querySelector('.content-wrapper');
if (wrapperEl) {
return `${wrapperEl.offsetTop}px`;
}
return '';
},
},
MSG_CANCEL,
INDEX_ROUTE_NAME,
@ -224,7 +233,12 @@ export default {
<template>
<mounting-portal v-if="!loading" mount-to="#js-crm-form-portal" append>
<gl-drawer class="gl-drawer-responsive gl-absolute" :open="drawerOpen" @close="close(false)">
<gl-drawer
:header-height="getDrawerHeaderHeight()"
class="gl-drawer-responsive"
:open="drawerOpen"
@close="close(false)"
>
<template #title>
<h3>{{ title }}</h3>
</template>

View File

@ -2,10 +2,15 @@
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { mapGetters } from 'vuex';
import { __ } from '~/locale';
import { IssuableType, WorkspaceType } from '~/issues/constants';
import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue';
export default {
WorkspaceType,
IssuableType,
components: {
GlIcon,
ConfidentialityBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -26,11 +31,6 @@ export default {
visible: this.isLocked,
dataTestId: 'locked',
},
{
iconName: 'eye-slash',
visible: this.isConfidential,
dataTestId: 'confidential',
},
{
iconName: 'spam',
visible: this.hidden,
@ -45,6 +45,12 @@ export default {
<template>
<div class="gl-display-inline-block">
<confidentiality-badge
v-if="isConfidential"
data-testid="confidential"
:workspace-type="$options.WorkspaceType.project"
:issuable-type="$options.IssuableType.Issue"
/>
<template v-for="meta in warningIconsMeta">
<div
v-if="meta.visible"

View File

@ -2,10 +2,16 @@
import { GlIcon, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui';
import Visibility from 'visibilityjs';
import createFlash from '~/flash';
import { IssuableStatus, IssuableStatusText, IssuableType } from '~/issues/constants';
import {
IssuableStatus,
IssuableStatusText,
WorkspaceType,
IssuableType,
} from '~/issues/constants';
import Poll from '~/lib/utils/poll';
import { visitUrl } from '~/lib/utils/url_utility';
import { __, sprintf } from '~/locale';
import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue';
import { ISSUE_TYPE_PATH, INCIDENT_TYPE_PATH, INCIDENT_TYPE, POLLING_DELAY } from '../constants';
import eventHub from '../event_hub';
import getIssueStateQuery from '../queries/get_issue_state.query.graphql';
@ -18,6 +24,7 @@ import PinnedLinks from './pinned_links.vue';
import titleComponent from './title.vue';
export default {
WorkspaceType,
components: {
GlIcon,
GlIntersectionObserver,
@ -25,6 +32,7 @@ export default {
editedComponent,
formComponent,
PinnedLinks,
ConfidentialityBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -156,7 +164,7 @@ export default {
issuableType: {
type: String,
required: false,
default: 'issue',
default: IssuableType.Issue,
},
canAttachFile: {
type: Boolean,
@ -519,9 +527,12 @@ export default {
<span v-if="isLocked" data-testid="locked" class="issuable-warning-icon">
<gl-icon name="lock" :aria-label="__('Locked')" />
</span>
<span v-if="isConfidential" data-testid="confidential" class="issuable-warning-icon">
<gl-icon name="eye-slash" :aria-label="__('Confidential')" />
</span>
<confidentiality-badge
v-if="isConfidential"
data-testid="confidential"
:workspace-type="$options.WorkspaceType.project"
:issuable-type="issuableType"
/>
<span
v-if="isHidden"
v-gl-tooltip

View File

@ -1,5 +1,5 @@
<script>
import { GlButton, GlFormInput, GlFormGroup, GlSprintf } from '@gitlab/ui';
import { GlButton, GlFormCheckbox, GlFormInput, GlFormGroup, GlLink, GlSprintf } from '@gitlab/ui';
import { mapState, mapActions, mapGetters } from 'vuex';
import { isSameOriginUrl, getParameterByName } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
@ -12,9 +12,11 @@ import TagField from './tag_field.vue';
export default {
name: 'ReleaseEditNewApp',
components: {
GlFormCheckbox,
GlFormInput,
GlFormGroup,
GlButton,
GlLink,
GlSprintf,
MarkdownField,
AssetLinksForm,
@ -28,6 +30,7 @@ export default {
'fetchError',
'markdownDocsPath',
'markdownPreviewPath',
'editReleaseDocsPath',
'releasesPagePath',
'release',
'newMilestonePath',
@ -35,8 +38,9 @@ export default {
'projectId',
'groupId',
'groupMilestonesAvailable',
'tagNotes',
]),
...mapGetters('editNew', ['isValid', 'isExistingRelease']),
...mapGetters('editNew', ['isValid', 'isExistingRelease', 'formattedReleaseNotes']),
showForm() {
return Boolean(!this.isFetchingRelease && !this.fetchError && this.release);
},
@ -64,6 +68,14 @@ export default {
this.updateReleaseMilestones(milestones);
},
},
includeTagNotes: {
get() {
return this.$store.state.editNew.includeTagNotes;
},
set(includeTagNotes) {
this.updateIncludeTagNotes(includeTagNotes);
},
},
cancelPath() {
const backUrl = getParameterByName(BACK_URL_PARAM);
@ -105,6 +117,7 @@ export default {
'updateReleaseTitle',
'updateReleaseNotes',
'updateReleaseMilestones',
'updateIncludeTagNotes',
]),
submitForm() {
if (!this.isFormSubmissionDisabled) {
@ -161,7 +174,7 @@ export default {
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
:add-spacing-classes="false"
:textarea-value="releaseNotes"
:textarea-value="formattedReleaseNotes"
class="gl-mt-3 gl-mb-3"
>
<template #textarea>
@ -178,6 +191,25 @@ export default {
</markdown-field>
</div>
</gl-form-group>
<gl-form-group v-if="!isExistingRelease">
<gl-form-checkbox v-model="includeTagNotes">
{{ s__('Release|Include message from the annotated tag.') }}
<template #help>
<gl-sprintf
:message="
s__(
'Release|You can edit the content later by editing the release. %{linkStart}How do I edit a release?%{linkEnd}',
)
"
>
<template #link="{ content }">
<gl-link :href="editReleaseDocsPath">{{ content }}</gl-link>
</template>
</gl-sprintf>
</template>
</gl-form-checkbox>
</gl-form-group>
<asset-links-form />

View File

@ -62,7 +62,7 @@ export default {
},
},
methods: {
...mapActions('editNew', ['updateReleaseTagName', 'updateCreateFrom']),
...mapActions('editNew', ['updateReleaseTagName', 'updateCreateFrom', 'fetchTagNotes']),
markInputAsDirty() {
this.isInputDirty = true;
},
@ -125,6 +125,7 @@ export default {
:translations="$options.translations.tagName"
:enabled-ref-types="$options.tagNameEnabledRefTypes"
:state="!showTagNameValidationError"
@input="fetchTagNotes"
@hide.once="markInputAsDirty"
>
<template #footer="{ isLoading, matches, query }">

View File

@ -1,3 +1,4 @@
import { getTag } from '~/rest_api';
import createFlash from '~/flash';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
@ -7,6 +8,7 @@ import deleteReleaseAssetLinkMutation from '~/releases/graphql/mutations/delete_
import updateReleaseMutation from '~/releases/graphql/mutations/update_release.mutation.graphql';
import oneReleaseForEditingQuery from '~/releases/graphql/queries/one_release_for_editing.query.graphql';
import { gqClient, convertOneReleaseGraphQLResponse } from '~/releases/util';
import * as types from './mutation_types';
export const initializeRelease = ({ commit, dispatch, getters }) => {
@ -224,3 +226,23 @@ export const updateRelease = async ({ commit, dispatch, state, getters }) => {
});
}
};
export const fetchTagNotes = ({ commit, state }, tagName) => {
commit(types.REQUEST_TAG_NOTES);
return getTag(state.projectId, tagName)
.then(({ data }) => {
commit(types.RECEIVE_TAG_NOTES_SUCCESS, data);
})
.catch((error) => {
createFlash({
message: s__('Release|Something went wrong while getting the tag notes.'),
});
commit(types.RECEIVE_TAG_NOTES_ERROR, error);
});
};
export const updateIncludeTagNotes = ({ commit }, includeTagNotes) => {
commit(types.UPDATE_INCLUDE_TAG_NOTES, includeTagNotes);
};

View File

@ -1,4 +1,5 @@
import { isEmpty } from 'lodash';
import { s__ } from '~/locale';
import { hasContent } from '~/lib/utils/text_utility';
import { getDuplicateItemsFromArray } from '~/lib/utils/array_utility';
@ -117,7 +118,7 @@ export const isValid = (_state, getters) => {
};
/** Returns all the variables for a `releaseUpdate` GraphQL mutation */
export const releaseUpdateMutatationVariables = (state) => {
export const releaseUpdateMutatationVariables = (state, getters) => {
const name = state.release.name?.trim().length > 0 ? state.release.name.trim() : null;
// Milestones may be either a list of milestone objects OR just a list
@ -129,7 +130,9 @@ export const releaseUpdateMutatationVariables = (state) => {
projectPath: state.projectPath,
tagName: state.release.tagName,
name,
description: state.release.description,
description: state.includeTagNotes
? getters.formattedReleaseNotes
: state.release.description,
milestones,
},
};
@ -151,3 +154,8 @@ export const releaseCreateMutatationVariables = (state, getters) => {
},
};
};
export const formattedReleaseNotes = ({ includeTagNotes, release: { description }, tagNotes }) =>
includeTagNotes && tagNotes
? `${description}\n\n### ${s__('Releases|Tag message')}\n\n${tagNotes}\n`
: description;

View File

@ -20,3 +20,9 @@ export const UPDATE_ASSET_LINK_URL = 'UPDATE_ASSET_LINK_URL';
export const UPDATE_ASSET_LINK_NAME = 'UPDATE_ASSET_LINK_NAME';
export const UPDATE_ASSET_LINK_TYPE = 'UPDATE_ASSET_LINK_TYPE';
export const REMOVE_ASSET_LINK = 'REMOVE_ASSET_LINK';
export const REQUEST_TAG_NOTES = 'REQUEST_TAG_NOTES';
export const RECEIVE_TAG_NOTES_SUCCESS = 'RECEIVE_TAG_NOTES_SUCCESS';
export const RECEIVE_TAG_NOTES_ERROR = 'RECEIVE_TAG_NOTES_ERROR';
export const UPDATE_INCLUDE_TAG_NOTES = 'UPDATE_INCLUDE_TAG_NOTES';

View File

@ -95,4 +95,21 @@ export default {
[types.REMOVE_ASSET_LINK](state, linkIdToRemove) {
state.release.assets.links = state.release.assets.links.filter((l) => l.id !== linkIdToRemove);
},
[types.REQUEST_TAG_NOTES](state) {
state.isFetchingTagNotes = true;
},
[types.RECEIVE_TAG_NOTES_SUCCESS](state, data) {
state.fetchError = undefined;
state.isFetchingTagNotes = false;
state.tagNotes = data.message;
},
[types.RECEIVE_TAG_NOTES_ERROR](state, error) {
state.fetchError = error;
state.isFetchingTagNotes = false;
state.tagNotes = '';
},
[types.UPDATE_INCLUDE_TAG_NOTES](state, includeTagNotes) {
state.includeTagNotes = includeTagNotes;
},
};

View File

@ -9,6 +9,7 @@ export default ({
manageMilestonesPath,
newMilestonePath,
releasesPagePath,
editReleaseDocsPath,
tagName = null,
defaultBranch = null,
@ -23,6 +24,7 @@ export default ({
manageMilestonesPath,
newMilestonePath,
releasesPagePath,
editReleaseDocsPath,
/**
* The name of the tag associated with the release, provided by the backend.
@ -48,4 +50,7 @@ export default ({
isUpdatingRelease: false,
updateError: null,
tagNotes: '',
includeTagNotes: false,
});

View File

@ -4,6 +4,7 @@ export * from './api/user_api';
export * from './api/markdown_api';
export * from './api/bulk_imports_api';
export * from './api/namespaces_api';
export * from './api/tags_api';
// Note: It's not possible to spy on methods imported from this file in
// Jest tests.

View File

@ -55,7 +55,7 @@ export const DAST_DESCRIPTION = s__(
);
export const DAST_HELP_PATH = helpPagePath('user/application_security/dast/index');
export const DAST_CONFIG_HELP_PATH = helpPagePath('user/application_security/dast/index', {
anchor: 'enable-dast',
anchor: 'enable-automatic-dast-run',
});
export const DAST_BADGE_TEXT = __('Available on-demand');
export const DAST_BADGE_TOOLTIP = __(
@ -126,7 +126,7 @@ export const COVERAGE_FUZZING_HELP_PATH = helpPagePath(
);
export const COVERAGE_FUZZING_CONFIG_HELP_PATH = helpPagePath(
'user/application_security/coverage_fuzzing/index',
{ anchor: 'configuration' },
{ anchor: 'enable-coverage-guided-fuzz-testing' },
);
export const CORPUS_MANAGEMENT_NAME = __('Corpus Management');

View File

@ -1,10 +1,13 @@
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import { GlIcon, GlAlert, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
import { IssuableType, WorkspaceType } from '~/issues/constants';
import { confidentialityInfoText } from '~/vue_shared/constants';
export default {
components: {
GlIcon,
GlAlert,
},
directives: {
GlTooltip: GlTooltipDirective,
@ -20,12 +23,11 @@ export default {
},
},
computed: {
confidentialText() {
return this.confidential
? sprintf(__('This %{issuableType} is confidential'), {
issuableType: this.issuableType,
})
: __('Not confidential');
confidentialBodyText() {
return confidentialityInfoText(
this.issuableType === IssuableType.Epic ? WorkspaceType.group : WorkspaceType.project,
this.issuableType,
);
},
confidentialIcon() {
return this.confidential ? 'eye-slash' : 'eye';
@ -59,6 +61,17 @@ export default {
class="sidebar-item-icon inline hide-collapsed"
:class="{ 'is-active': confidential }"
/>
<span class="hide-collapsed" data-testid="confidential-text">{{ confidentialText }}</span>
<span class="hide-collapsed" data-testid="confidential-text">
{{ tooltipLabel }}
<gl-alert
v-if="confidential"
:show-icon="false"
:dismissible="false"
variant="warning"
class="gl-mt-3"
>
{{ confidentialBodyText }}
</gl-alert>
</span>
</div>
</template>

View File

@ -8,7 +8,7 @@ import { confidentialityQueries } from '~/sidebar/constants';
export default {
i18n: {
confidentialityOnWarning: __(
'You are going to turn on confidentiality. Only team members with %{strongStart}at least Reporter access%{strongEnd} will be able to see and leave comments on the %{issuableType}.',
'You are going to turn on confidentiality. Only %{context} members with %{strongStart}at least Reporter role%{strongEnd} can view or be notified about this %{issuableType}.',
),
confidentialityOffWarning: __(
'You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}.',
@ -53,6 +53,9 @@ export default {
? this.$options.i18n.confidentialityOffWarning
: this.$options.i18n.confidentialityOnWarning;
},
context() {
return this.issuableType === IssuableType.Issue ? __('project') : __('group');
},
workspacePath() {
return this.issuableType === IssuableType.Issue
? {
@ -119,6 +122,7 @@ export default {
<template #strong="{ content }">
<strong>{{ content }}</strong>
</template>
<template #context>{{ context }}</template>
<template #issuableType>{{ issuableType }}</template>
</gl-sprintf>
</p>

View File

@ -0,0 +1,39 @@
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { confidentialityInfoText } from '../constants';
export default {
components: {
GlBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
workspaceType: {
type: String,
required: true,
},
issuableType: {
type: String,
required: true,
},
},
computed: {
confidentialTooltip() {
return confidentialityInfoText(this.workspaceType, this.issuableType);
},
},
};
</script>
<template>
<gl-badge
v-gl-tooltip.bottom
:title="confidentialTooltip"
icon="eye-slash"
variant="warning"
class="gl-display-inline gl-mr-2"
>{{ __('Confidential') }}</gl-badge
>
</template>

View File

@ -1,5 +1,5 @@
<script>
import { GlIcon } from '@gitlab/ui';
import { GlIcon, GlLoadingIcon } from '@gitlab/ui';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import Mousetrap from 'mousetrap';
import VirtualList from 'vue-virtual-scroll-list';
@ -9,13 +9,13 @@ import Item from './item.vue';
export const MAX_FILE_FINDER_RESULTS = 40;
export const FILE_FINDER_ROW_HEIGHT = 55;
export const FILE_FINDER_EMPTY_ROW_HEIGHT = 33;
const originalStopCallback = Mousetrap.prototype.stopCallback;
export default {
components: {
GlIcon,
GlLoadingIcon,
Item,
VirtualList,
},
@ -71,7 +71,7 @@ export default {
return this.filteredBlobsLength ? Math.min(this.filteredBlobsLength, 5) : 1;
},
listHeight() {
return this.filteredBlobsLength ? FILE_FINDER_ROW_HEIGHT : FILE_FINDER_EMPTY_ROW_HEIGHT;
return FILE_FINDER_ROW_HEIGHT;
},
showClearInputButton() {
return this.searchText.trim() !== '';
@ -265,9 +265,9 @@ export default {
</li>
</template>
<li v-else class="dropdown-menu-empty-item">
<div class="gl-mr-3 gl-ml-3 gl-mt-3 gl-mb-3">
<div class="gl-mr-3 gl-ml-3 gl-mt-5 gl-mb-3">
<template v-if="loading">
{{ __('Loading...') }}
<gl-loading-icon />
</template>
<template v-else>
{{ __('No files found.') }}

View File

@ -1,4 +1,5 @@
import { __ } from '~/locale';
import { __, sprintf } from '~/locale';
import { IssuableType, WorkspaceType } from '~/issues/constants';
const INTERVALS = {
minute: 'minute',
@ -66,3 +67,14 @@ export const getTimeWindow = (timeWindowName) =>
export const AVATAR_SHAPE_OPTION_CIRCLE = 'circle';
export const AVATAR_SHAPE_OPTION_RECT = 'rect';
export const confidentialityInfoText = (workspaceType, issuableType) =>
sprintf(
__(
'Only %{workspaceType} members with at least Reporter role can view or be notified about this %{issuableType}.',
),
{
workspaceType: workspaceType === WorkspaceType.project ? __('project') : __('group'),
issuableType: issuableType === IssuableType.Issue ? __('issue') : __('epic'),
},
);

View File

@ -12,7 +12,7 @@ module ReleasesHelper
image_path(IMAGE_PATH)
end
def help_page(anchor: nil)
def releases_help_page_path(anchor: nil)
help_page_path(DOCUMENTATION_PATH, anchor: anchor)
end
@ -21,7 +21,7 @@ module ReleasesHelper
project_id: @project.id,
project_path: @project.full_path,
illustration_path: illustration,
documentation_path: help_page
documentation_path: releases_help_page_path
}.tap do |data|
if can?(current_user, :create_release, @project)
data[:new_release_path] = new_project_release_path(@project)
@ -78,9 +78,10 @@ module ReleasesHelper
project_path: @project.full_path,
markdown_preview_path: preview_markdown_path(@project),
markdown_docs_path: help_page_path('user/markdown'),
release_assets_docs_path: help_page(anchor: 'release-assets'),
release_assets_docs_path: releases_help_page_path(anchor: 'release-assets'),
manage_milestones_path: project_milestones_path(@project),
new_milestone_path: new_project_milestone_path(@project)
new_milestone_path: new_project_milestone_path(@project),
edit_release_docs_path: releases_help_page_path(anchor: 'edit-a-release')
}
end
end

View File

@ -60,7 +60,7 @@ module DevOpsReport
description: 'created per active user',
feature: 'environments',
blog: 'https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/',
docs: help_page_path('ci/environments')
docs: help_page_path('ci/environments/index')
),
Card.new(
metric: metric,

View File

@ -68,7 +68,7 @@ module Projects
end
def latest_pipeline_path
return help_page_path('ci/pipelines') unless latest_default_branch_pipeline
return help_page_path('ci/pipelines/index') unless latest_default_branch_pipeline
project_pipeline_path(self, latest_default_branch_pipeline)
end

View File

@ -143,7 +143,7 @@ class MergeRequestWidgetEntity < Grape::Entity
end
expose :security_reports_docs_path do |merge_request|
help_page_path('user/application_security/index.md', anchor: 'viewing-security-scan-information-in-merge-requests')
help_page_path('user/application_security/index.md', anchor: 'view-security-scan-information-in-merge-requests')
end
expose :enabled_reports do |merge_request|

View File

@ -34,7 +34,7 @@
= render 'groups/settings/ip_restriction_registration_features_cta', f: f
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
= render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group
- if @group.licensed_feature_available?(:group_wikis) && Feature.enabled?(:group_wiki_settings_toggle, @group, default_enabled: :yaml)
- if @group.licensed_feature_available?(:group_wikis)
= render_if_exists 'groups/settings/wiki', f: f, group: @group
= render 'groups/settings/lfs', f: f
= render 'groups/settings/project_creation_level', f: f, group: @group

View File

@ -1,8 +0,0 @@
---
name: push_rules_supersede_code_owners
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44126
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/262019
type: development
group: group::code review
default_enabled: true
milestone: '13.5'

View File

@ -1,8 +1,8 @@
---
name: group_wiki_settings_toggle
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82298
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/358387
milestone: '14.10'
name: slack_app_use_v2_flow
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85726
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/360680
milestone: '15.0'
type: development
group: group::editor
group: group::integrations
default_enabled: false

View File

@ -91,6 +91,7 @@ To create a release in the Releases page:
- [Title](#title).
- [Milestones](#associate-milestones-with-a-release).
- [Release notes](#release-notes-description).
- Whether or not to include the [Tag message](../../../topics/git/tags.md).
- [Asset links](#links).
1. Select **Create release**.
@ -439,8 +440,11 @@ Every release has a description. You can add any text you like, but we recommend
including a changelog to describe the content of your release. This helps users
quickly scan the differences between each release you publish.
[Git's tagging messages](https://git-scm.com/book/en/v2/Git-Basics-Tagging) and
Release note descriptions are unrelated. Description supports [Markdown](../../markdown.md).
[Git's tagging messages](https://git-scm.com/book/en/v2/Git-Basics-Tagging) can
be included in Release note descriptions by selecting **Include tag message in
the release notes**.
Description supports [Markdown](../../markdown.md).
### Release assets

View File

@ -61,6 +61,27 @@ available, you have to:
All files in the wiki are available in this Git repository.
## Configure group wiki visibility
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/208412) in GitLab 15.0.
Wikis are enabled by default in GitLab. Group [administrators](../../permissions.md)
can enable or disable a group wiki through the group settings.
To open group settings:
```markdown
1. On the top bar, select **Menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
```
In the wiki section you may select one from the following options:
- Enabled: everyone who can access the group can access the wiki.
- Private: only group members can access the wiki.
- Disabled: the wiki will be entirely disabled and it won't be accessible nor downloadable.
## Related topics
- [Wiki settings for administrators](../../../administration/wikis/index.md)

View File

@ -275,7 +275,7 @@ can enable or disable a project wiki by following the instructions in
Administrators for self-managed GitLab installs can
[configure additional wiki settings](../../../administration/wikis/index.md).
You can't disable [group wikis](group.md) from the GitLab user interface.
You can disable group wikis from the [group settings](group.md#configure-group-wiki-visibility)
## Link an external wiki

View File

@ -26472,6 +26472,9 @@ msgstr ""
msgid "One or more of your personal access tokens will expire in %{days_to_expire} days or less:"
msgstr ""
msgid "Only %{workspaceType} members with at least Reporter role can view or be notified about this %{issuableType}."
msgstr ""
msgid "Only 'Reporter' roles and above on tiers Premium and above can see Value Stream Analytics."
msgstr ""
@ -26496,6 +26499,9 @@ msgstr ""
msgid "Only effective when remote storage is enabled. Set to 0 for no size limit."
msgstr ""
msgid "Only group members with at least Reporter role can view or be notified about this epic."
msgstr ""
msgid "Only include features new to your current subscription tier."
msgstr ""
@ -31308,15 +31314,27 @@ msgstr ""
msgid "Releases|New Release"
msgstr ""
msgid "Releases|Tag message"
msgstr ""
msgid "Release|Include message from the annotated tag."
msgstr ""
msgid "Release|Something went wrong while creating a new release."
msgstr ""
msgid "Release|Something went wrong while getting the release details."
msgstr ""
msgid "Release|Something went wrong while getting the tag notes."
msgstr ""
msgid "Release|Something went wrong while saving the release details."
msgstr ""
msgid "Release|You can edit the content later by editing the release. %{linkStart}How do I edit a release?%{linkEnd}"
msgstr ""
msgid "Reload page"
msgstr ""
@ -38431,9 +38449,6 @@ msgstr ""
msgid "This %{issuableDisplayName} is locked. Only project members can comment."
msgstr ""
msgid "This %{issuableType} is confidential"
msgstr ""
msgid "This %{issuable} is locked. Only %{strong_open}project members%{strong_close} can comment."
msgstr ""
@ -43011,7 +43026,7 @@ msgstr ""
msgid "You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}."
msgstr ""
msgid "You are going to turn on confidentiality. Only team members with %{strongStart}at least Reporter access%{strongEnd} will be able to see and leave comments on the %{issuableType}."
msgid "You are going to turn on confidentiality. Only %{context} members with %{strongStart}at least Reporter role%{strongEnd} can view or be notified about this %{issuableType}."
msgstr ""
msgid "You are not allowed to %{action} a user"

View File

@ -167,7 +167,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
it 'does not update Devise trackable attributes' do
expect { gitlab_sign_in(user, password: user.password) }
.not_to change { User.ghost.reload.sign_in_count }
.not_to change { user.reload.sign_in_count }
end
end

View File

@ -0,0 +1,37 @@
import MockAdapter from 'axios-mock-adapter';
import * as tagsApi from '~/api/tags_api';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
describe('~/api/tags_api.js', () => {
let mock;
let originalGon;
const projectId = 1;
beforeEach(() => {
mock = new MockAdapter(axios);
originalGon = window.gon;
window.gon = { api_version: 'v7' };
});
afterEach(() => {
mock.restore();
window.gon = originalGon;
});
describe('getTag', () => {
it('fetches a tag of a given tag name of a particular project', () => {
const tagName = 'tag-name';
const expectedUrl = `/api/v7/projects/${projectId}/repository/tags/${tagName}`;
mock.onGet(expectedUrl).reply(httpStatus.OK, {
name: tagName,
});
return tagsApi.getTag(projectId, tagName).then(({ data }) => {
expect(data.name).toBe(tagName);
});
});
});
});

View File

@ -66,7 +66,15 @@ describe('IssuableHeaderWarnings', () => {
});
it(`${renderTestMessage(confidentialStatus)} the confidential icon`, () => {
expect(findConfidentialIcon().exists()).toBe(confidentialStatus);
const confidentialEl = findConfidentialIcon();
expect(confidentialEl.exists()).toBe(confidentialStatus);
if (confidentialStatus && !hiddenStatus) {
expect(confidentialEl.props()).toMatchObject({
workspaceType: 'project',
issuableType: 'issue',
});
}
});
it(`${renderTestMessage(confidentialStatus)} the hidden icon`, () => {

View File

@ -487,7 +487,14 @@ describe('Issuable output', () => {
await nextTick();
expect(findConfidentialBadge().exists()).toBe(isConfidential);
const confidentialEl = findConfidentialBadge();
expect(confidentialEl.exists()).toBe(isConfidential);
if (isConfidential) {
expect(confidentialEl.props()).toMatchObject({
workspaceType: 'project',
issuableType: 'issue',
});
}
});
it.each`

View File

@ -4,6 +4,7 @@ import MockAdapter from 'axios-mock-adapter';
import { merge } from 'lodash';
import Vuex from 'vuex';
import { nextTick } from 'vue';
import { GlFormCheckbox } from '@gitlab/ui';
import originalRelease from 'test_fixtures/api/releases/release.json';
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
@ -11,6 +12,7 @@ import * as commonUtils from '~/lib/utils/common_utils';
import ReleaseEditNewApp from '~/releases/components/app_edit_new.vue';
import AssetLinksForm from '~/releases/components/asset_links_form.vue';
import { BACK_URL_PARAM } from '~/releases/constants';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
const originalMilestones = originalRelease.milestones;
const releasesPagePath = 'path/to/releases/page';
@ -47,6 +49,7 @@ describe('Release edit/new component', () => {
links: [],
},
}),
formattedReleaseNotes: () => 'these notes are formatted',
};
const store = new Vuex.Store(
@ -129,6 +132,11 @@ describe('Release edit/new component', () => {
expect(wrapper.find('#release-notes').element.value).toBe(release.description);
});
it('sets the preview text to be the formatted release notes', () => {
const notes = getters.formattedReleaseNotes();
expect(wrapper.findComponent(MarkdownField).props('textareaValue')).toBe(notes);
});
it('renders the "Save changes" button as type="submit"', () => {
expect(findSubmitButton().attributes('type')).toBe('submit');
});
@ -195,6 +203,10 @@ describe('Release edit/new component', () => {
it('renders the submit button with the text "Create release"', () => {
expect(findSubmitButton().text()).toBe('Create release');
});
it('renders a checkbox to include release notes', () => {
expect(wrapper.find(GlFormCheckbox).exists()).toBe(true);
});
});
describe('when editing an existing release', () => {

View File

@ -1,5 +1,7 @@
import { GlDropdownItem } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import Vue, { nextTick } from 'vue';
import { __ } from '~/locale';
import TagFieldNew from '~/releases/components/tag_field_new.vue';
@ -14,6 +16,7 @@ const NONEXISTENT_TAG_NAME = 'nonexistent-tag';
describe('releases/components/tag_field_new', () => {
let store;
let wrapper;
let mock;
let RefSelectorStub;
const createComponent = (
@ -65,11 +68,14 @@ describe('releases/components/tag_field_new', () => {
links: [],
},
};
mock = new MockAdapter(axios);
gon.api_version = 'v4';
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
mock.restore();
});
const findTagNameFormGroup = () => wrapper.find('[data-testid="tag-name-field"]');
@ -114,9 +120,14 @@ describe('releases/components/tag_field_new', () => {
expect(store.state.editNew.release.tagName).toBe(updatedTagName);
});
it('shows the "Create from" field', () => {
it('hides the "Create from" field', () => {
expect(findCreateFromFormGroup().exists()).toBe(false);
});
it('fetches the release notes for the tag', () => {
const expectedUrl = `/api/v4/projects/1234/repository/tags/${updatedTagName}`;
expect(mock.history.get).toContainEqual(expect.objectContaining({ url: expectedUrl }));
});
});
});

View File

@ -1,8 +1,10 @@
import { cloneDeep } from 'lodash';
import originalOneReleaseForEditingQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release_for_editing.query.graphql.json';
import testAction from 'helpers/vuex_action_helper';
import { getTag } from '~/api/tags_api';
import createFlash from '~/flash';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import { ASSET_LINK_TYPE } from '~/releases/constants';
import createReleaseAssetLinkMutation from '~/releases/graphql/mutations/create_release_link.mutation.graphql';
import deleteReleaseAssetLinkMutation from '~/releases/graphql/mutations/delete_release_link.mutation.graphql';
@ -12,6 +14,8 @@ import * as types from '~/releases/stores/modules/edit_new/mutation_types';
import createState from '~/releases/stores/modules/edit_new/state';
import { gqClient, convertOneReleaseGraphQLResponse } from '~/releases/util';
jest.mock('~/api/tags_api');
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({
@ -567,4 +571,46 @@ describe('Release edit/new actions', () => {
});
});
});
describe('fetchTagNotes', () => {
const tagName = 'v8.0.0';
it('saves the tag notes on succes', async () => {
const tag = { message: 'this is a tag' };
getTag.mockResolvedValue({ data: tag });
await testAction(
actions.fetchTagNotes,
tagName,
state,
[
{ type: types.REQUEST_TAG_NOTES },
{ type: types.RECEIVE_TAG_NOTES_SUCCESS, payload: tag },
],
[],
);
expect(getTag).toHaveBeenCalledWith(state.projectId, tagName);
});
it('creates a flash on error', async () => {
error = new Error();
getTag.mockRejectedValue(error);
await testAction(
actions.fetchTagNotes,
tagName,
state,
[
{ type: types.REQUEST_TAG_NOTES },
{ type: types.RECEIVE_TAG_NOTES_ERROR, payload: error },
],
[],
);
expect(createFlash).toHaveBeenCalledWith({
message: s__('Release|Something went wrong while getting the tag notes.'),
});
expect(getTag).toHaveBeenCalledWith(state.projectId, tagName);
});
});
});

View File

@ -1,3 +1,4 @@
import { s__ } from '~/locale';
import * as getters from '~/releases/stores/modules/edit_new/getters';
describe('Release edit/new getters', () => {
@ -369,4 +370,25 @@ describe('Release edit/new getters', () => {
expect(actualVariables).toEqual(expectedVariables);
});
});
describe('formattedReleaseNotes', () => {
it.each`
description | includeTagNotes | tagNotes | included
${'release notes'} | ${true} | ${'tag notes'} | ${true}
${'release notes'} | ${true} | ${''} | ${false}
${'release notes'} | ${false} | ${'tag notes'} | ${false}
`(
'should include tag notes=$included when includeTagNotes=$includeTagNotes and tagNotes=$tagNotes',
({ description, includeTagNotes, tagNotes, included }) => {
const state = { release: { description }, includeTagNotes, tagNotes };
const text = `### ${s__('Releases|Tag message')}\n\n${tagNotes}\n`;
if (included) {
expect(getters.formattedReleaseNotes(state)).toContain(text);
} else {
expect(getters.formattedReleaseNotes(state)).not.toContain(text);
}
},
);
});
});

View File

@ -237,4 +237,40 @@ describe('Release edit/new mutations', () => {
expect(state.release.assets.links).not.toContainEqual(linkToRemove);
});
});
describe(`${types.REQUEST_TAG_NOTES}`, () => {
it('sets isFetchingTagNotes to true', () => {
state.isFetchingTagNotes = false;
mutations[types.REQUEST_TAG_NOTES](state);
expect(state.isFetchingTagNotes).toBe(true);
});
});
describe(`${types.RECEIVE_TAG_NOTES_SUCCESS}`, () => {
it('sets the tag notes in the state', () => {
state.isFetchingTagNotes = true;
const message = 'tag notes';
mutations[types.RECEIVE_TAG_NOTES_SUCCESS](state, { message });
expect(state.tagNotes).toBe(message);
expect(state.isFetchingTagNotes).toBe(false);
});
});
describe(`${types.RECEIVE_TAG_NOTES_ERROR}`, () => {
it('sets tag notes to empty', () => {
const message = 'there was an error';
state.isFetchingTagNotes = true;
state.tagNotes = 'tag notes';
mutations[types.RECEIVE_TAG_NOTES_ERROR](state, { message });
expect(state.tagNotes).toBe('');
expect(state.isFetchingTagNotes).toBe(false);
});
});
describe(`${types.UPDATE_INCLUDE_TAG_NOTES}`, () => {
it('sets whether or not to include the tag notes', () => {
state.includeTagNotes = false;
mutations[types.UPDATE_INCLUDE_TAG_NOTES](state, true);
expect(state.includeTagNotes).toBe(true);
});
});
});

View File

@ -1,4 +1,4 @@
import { GlIcon } from '@gitlab/ui';
import { GlIcon, GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SidebarConfidentialityContent from '~/sidebar/components/confidential/sidebar_confidentiality_content.vue';
@ -60,12 +60,24 @@ describe('Sidebar Confidentiality Content', () => {
it('displays a correct confidential text for issue', () => {
createComponent({ confidential: true });
expect(findText().text()).toBe('This issue is confidential');
const alertEl = findText().findComponent(GlAlert);
expect(alertEl.props()).toMatchObject({
showIcon: false,
dismissible: false,
variant: 'warning',
});
expect(alertEl.text()).toBe(
'Only project members with at least Reporter role can view or be notified about this issue.',
);
});
it('displays a correct confidential text for epic', () => {
createComponent({ confidential: true, issuableType: 'epic' });
expect(findText().text()).toBe('This epic is confidential');
expect(findText().findComponent(GlAlert).text()).toBe(
'Only group members with at least Reporter role can view or be notified about this epic.',
);
});
});
});

View File

@ -89,7 +89,7 @@ describe('Sidebar Confidentiality Form', () => {
it('renders a message about making an issue confidential', () => {
expect(findWarningMessage().text()).toBe(
'You are going to turn on confidentiality. Only team members with at least Reporter access will be able to see and leave comments on the issue.',
'You are going to turn on confidentiality. Only project members with at least Reporter role can view or be notified about this issue.',
);
});

View File

@ -0,0 +1,52 @@
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { WorkspaceType, IssuableType } from '~/issues/constants';
import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue';
const createComponent = ({
workspaceType = WorkspaceType.project,
issuableType = IssuableType.Issue,
} = {}) =>
shallowMount(ConfidentialityBadge, {
propsData: {
workspaceType,
issuableType,
},
});
describe('ConfidentialityBadge', () => {
let wrapper;
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it.each`
workspaceType | issuableType | expectedTooltip
${WorkspaceType.project} | ${IssuableType.Issue} | ${'Only project members with at least Reporter role can view or be notified about this issue.'}
${WorkspaceType.group} | ${IssuableType.Epic} | ${'Only group members with at least Reporter role can view or be notified about this epic.'}
`(
'should render gl-badge with correct tooltip when workspaceType is $workspaceType and issuableType is $issuableType',
({ workspaceType, issuableType, expectedTooltip }) => {
wrapper = createComponent({
workspaceType,
issuableType,
});
const badgeEl = wrapper.findComponent(GlBadge);
expect(badgeEl.props()).toMatchObject({
icon: 'eye-slash',
variant: 'warning',
});
expect(badgeEl.attributes('title')).toBe(expectedTooltip);
expect(badgeEl.text()).toBe('Confidential');
},
);
});

View File

@ -105,18 +105,6 @@ describe('File finder item spec', () => {
});
});
describe('listHeight', () => {
it('returns 55 when entries exist', () => {
expect(vm.listHeight).toBe(55);
});
it('returns 33 when entries dont exist', () => {
vm.searchText = 'testing 123';
expect(vm.listHeight).toBe(33);
});
});
describe('filteredBlobsLength', () => {
it('returns length of filtered blobs', () => {
vm.searchText = 'index';
@ -253,11 +241,9 @@ describe('File finder item spec', () => {
describe('without entries', () => {
it('renders loading text when loading', () => {
createComponent({
loading: true,
});
createComponent({ loading: true });
expect(vm.$el.textContent).toContain('Loading...');
expect(vm.$el.querySelector('.gl-spinner')).not.toBe(null);
});
it('renders no files text', () => {

View File

@ -9,9 +9,9 @@ RSpec.describe ReleasesHelper do
end
end
describe '#help_page' do
describe '#releases_help_page_path' do
it 'returns the correct link to the help page' do
expect(helper.help_page).to include('user/project/releases/index')
expect(helper.releases_help_page_path).to include('user/project/releases/index')
end
end
@ -63,7 +63,8 @@ RSpec.describe ReleasesHelper do
releases_page_path
release_assets_docs_path
manage_milestones_path
new_milestone_path)
new_milestone_path
edit_release_docs_path)
expect(helper.data_for_edit_release_page.keys).to match_array(keys)
end
@ -81,7 +82,8 @@ RSpec.describe ReleasesHelper do
release_assets_docs_path
manage_milestones_path
new_milestone_path
default_branch)
default_branch
edit_release_docs_path)
expect(helper.data_for_new_release_page.keys).to match_array(keys)
end

View File

@ -263,7 +263,7 @@ RSpec.describe Projects::Security::ConfigurationPresenter do
end
it 'includes a link to CI pipeline docs' do
expect(html_data[:latest_pipeline_path]).to eq(help_page_path('ci/pipelines'))
expect(html_data[:latest_pipeline_path]).to eq(help_page_path('ci/pipelines/index'))
end
context 'when gathering feature data' do

View File

@ -108,7 +108,11 @@ RSpec.shared_examples 'issue boards sidebar' do
wait_for_requests
expect(page).to have_content('This issue is confidential')
expect(page).to have_content(
_('Only project members with at least' \
' Reporter role can view or be' \
' notified about this issue.')
)
end
end
end