From c79523e3952dad31a9ab8ccbb9e38466fec3aec3 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 11 Aug 2022 15:11:19 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../deprecate_track_redis_hll_event.yml | 29 ++++ GITALY_SERVER_VERSION | 2 +- .../components/add_issuable_form.vue | 7 +- .../admin_runners/admin_runners_app.vue | 15 +- .../components/runner_filtered_search_bar.vue | 8 +- .../runner/components/runner_jobs.vue | 11 +- .../runner/components/runner_pagination.vue | 67 +++----- .../runner/components/runner_projects.vue | 13 +- app/assets/javascripts/runner/constants.js | 1 - .../group_runner_connection.fragment.graphql | 1 - .../group_runners/group_runners_app.vue | 15 +- .../javascripts/runner/runner_search_utils.js | 69 ++++---- .../work_item_links/work_item_links_form.vue | 1 + .../stylesheets/framework/highlight.scss | 3 +- .../stylesheets/pages/merge_requests.scss | 4 +- app/models/ci/build_metadata.rb | 1 + app/models/concerns/ci/artifactable.rb | 1 + app/models/concerns/ci/metadatable.rb | 10 ++ .../projects/alerting/notify_service.rb | 2 +- .../prometheus/alerts/notify_service.rb | 2 +- .../build_metadata_id_tokens.json | 22 +++ ...rd_failure_for_mirrors_without_license.yml | 8 - config/metrics/aggregates/code_review.yml | 12 ++ ...0124_add_id_token_to_ci_builds_metadata.rb | 9 ++ ...715152108_backfill_project_import_level.rb | 22 +++ db/schema_migrations/20220715152108 | 1 + db/schema_migrations/20220808190124 | 1 + db/structure.sql | 3 +- doc/api/groups.md | 45 ++++++ doc/api/merge_requests.md | 60 +++++++ .../documentation/styleguide/index.md | 42 +---- doc/development/service_ping/implement.md | 7 +- doc/user/group/saml_sso/group_sync.md | 6 + doc/user/packages/container_registry/index.md | 6 + lib/api/entities/merge_request_reviewer.rb | 12 ++ lib/api/merge_requests.rb | 11 ++ .../backfill_project_import_level.rb | 35 +++++ .../ci/build/artifacts/adapters/zip_stream.rb | 61 ++++++++ lib/gitlab/event_store.rb | 1 + .../form_builders/gitlab_ui_form_builder.rb | 2 +- .../import_export/base/relation_factory.rb | 12 +- .../base/relation_object_saver.rb | 2 +- .../import_export/project/import_export.yml | 6 +- .../known_events/code_review_events.yml | 25 +++ .../merge_request_widget_extension_counter.rb | 2 +- locale/gitlab.pot | 27 ++++ .../gitlab/deprecate_track_redis_hll_event.rb | 33 ++++ spec/factories/ci/job_artifacts.rb | 22 +++ .../corrupted_project_export.tar.gz | Bin 3846 -> 4603 bytes .../adapters/zip_stream/100_files.zip | Bin 0 -> 15902 bytes .../zip_stream/200_mb_decompressed.zip | Bin 0 -> 203718 bytes .../adapters/zip_stream/multiple_files.zip | Bin 0 -> 332 bytes .../adapters/zip_stream/single_file.zip | Bin 0 -> 177 bytes .../adapters/zip_stream/with_directory.zip | Bin 0 -> 520 bytes .../artifacts/adapters/zip_stream/zipbomb.zip | Bin 0 -> 1042247 bytes .../gitlab/import_export/complex/project.json | 25 ++- .../tree/project/merge_requests.ndjson | 4 +- .../components/devops_score_spec.js | 2 +- .../components/signup_checkbox_spec.js | 2 +- .../components/signup_form_spec.js | 4 +- .../statistics_panel/components/app_spec.js | 2 +- .../users/components/actions/actions_spec.js | 2 +- .../admin/users/components/app_spec.js | 2 +- .../modals/delete_user_modal_spec.js | 2 +- .../users/components/user_actions_spec.js | 4 +- .../users/components/user_avatar_spec.js | 6 +- .../users/components/users_table_spec.js | 8 +- spec/frontend/admin/users/index_spec.js | 4 +- .../pipelines/graph/linked_pipeline_spec.js | 2 +- .../admin_runners/admin_runners_app_spec.js | 13 +- .../runner_filtered_search_bar_spec.js | 6 +- .../components/runner_pagination_spec.js | 148 +++++++----------- .../group_runners/group_runners_app_spec.js | 13 +- spec/frontend/runner/mock_data.js | 34 ++-- .../runner/runner_search_utils_spec.js | 40 ++--- .../backfill_project_import_level_spec.rb | 123 +++++++++++++++ .../artifacts/adapters/zip_stream_spec.rb | 86 ++++++++++ spec/lib/gitlab/import_export/all_models.yml | 1 + .../base/relation_object_saver_spec.rb | 6 +- .../project/tree_restorer_spec.rb | 5 + .../import_export/project/tree_saver_spec.rb | 9 +- .../import_export/safe_model_attributes.yml | 2 + .../backfill_project_import_level_spec.rb | 29 ++++ spec/models/ci/bridge_spec.rb | 2 + spec/models/ci/build_metadata_spec.rb | 13 +- spec/models/ci/build_spec.rb | 2 + spec/models/concerns/ci/artifactable_spec.rb | 24 ++- spec/requests/api/merge_requests_spec.rb | 39 +++++ .../deprecate_track_redis_hll_event_spec.rb | 19 +++ .../projects/alerting/notify_service_spec.rb | 4 +- .../prometheus/alerts/notify_service_spec.rb | 6 +- .../ci/metadata_id_tokens_shared_examples.rb | 44 ++++++ .../invalidate_domain_cache_worker_spec.rb | 12 ++ 93 files changed, 1168 insertions(+), 338 deletions(-) create mode 100644 .rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml create mode 100644 app/validators/json_schemas/build_metadata_id_tokens.json delete mode 100644 config/feature_flags/development/hard_failure_for_mirrors_without_license.yml create mode 100644 db/migrate/20220808190124_add_id_token_to_ci_builds_metadata.rb create mode 100644 db/post_migrate/20220715152108_backfill_project_import_level.rb create mode 100644 db/schema_migrations/20220715152108 create mode 100644 db/schema_migrations/20220808190124 create mode 100644 lib/api/entities/merge_request_reviewer.rb create mode 100644 lib/gitlab/background_migration/backfill_project_import_level.rb create mode 100644 lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb create mode 100644 rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb create mode 100644 spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/100_files.zip create mode 100644 spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zip create mode 100644 spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip create mode 100644 spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip create mode 100644 spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zip create mode 100644 spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/zipbomb.zip create mode 100644 spec/lib/gitlab/background_migration/backfill_project_import_level_spec.rb create mode 100644 spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb create mode 100644 spec/migrations/backfill_project_import_level_spec.rb create mode 100644 spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb create mode 100644 spec/support/shared_examples/models/ci/metadata_id_tokens_shared_examples.rb diff --git a/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml b/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml new file mode 100644 index 00000000000..06651a0f60a --- /dev/null +++ b/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml @@ -0,0 +1,29 @@ +--- +Gitlab/DeprecateTrackRedisHLLEvent: + Exclude: + - 'app/controllers/admin/dev_ops_report_controller.rb' + - 'app/controllers/admin/usage_trends_controller.rb' + - 'app/controllers/concerns/snippets_actions.rb' + - 'app/controllers/concerns/wiki_actions.rb' + - 'app/controllers/projects/blob_controller.rb' + - 'app/controllers/projects/cycle_analytics_controller.rb' + - 'app/controllers/projects/graphs_controller.rb' + - 'app/controllers/projects/pipelines_controller.rb' + - 'ee/app/controllers/admin/audit_logs_controller.rb' + - 'ee/app/controllers/admin/credentials_controller.rb' + - 'ee/app/controllers/ee/admin/dev_ops_report_controller.rb' + - 'ee/app/controllers/groups/analytics/ci_cd_analytics_controller.rb' + - 'ee/app/controllers/groups/analytics/devops_adoption_controller.rb' + - 'ee/app/controllers/groups/analytics/productivity_analytics_controller.rb' + - 'ee/app/controllers/groups/audit_events_controller.rb' + - 'ee/app/controllers/groups/contribution_analytics_controller.rb' + - 'ee/app/controllers/groups/epic_boards_controller.rb' + - 'ee/app/controllers/groups/insights_controller.rb' + - 'ee/app/controllers/groups/issues_analytics_controller.rb' + - 'ee/app/controllers/groups/security/compliance_dashboards_controller.rb' + - 'ee/app/controllers/projects/analytics/code_reviews_controller.rb' + - 'ee/app/controllers/projects/analytics/issues_analytics_controller.rb' + - 'ee/app/controllers/projects/analytics/merge_request_analytics_controller.rb' + - 'ee/app/controllers/projects/insights_controller.rb' + - 'ee/app/controllers/projects/integrations/jira/issues_controller.rb' + - 'spec/controllers/concerns/redis_tracking_spec.rb' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index bd24ccee0e6..9a936a6ed12 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -f3a6e61b3ca421866ee029c3f29c23fcea08a783 +f20b8259797f3a1f3b59142c9789af97fcb0f69f diff --git a/app/assets/javascripts/related_issues/components/add_issuable_form.vue b/app/assets/javascripts/related_issues/components/add_issuable_form.vue index d765033d00b..102f1228355 100644 --- a/app/assets/javascripts/related_issues/components/add_issuable_form.vue +++ b/app/assets/javascripts/related_issues/components/add_issuable_form.vue @@ -208,7 +208,7 @@ export default {

{{ addRelatedErrorMessage }}

-
+
{{ __('Add') }} - + {{ __('Cancel') }}
diff --git a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue index f6b7a8b46d7..cd5ce3e2976 100644 --- a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue +++ b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue @@ -152,6 +152,9 @@ export default { isChecked, }); }, + onPaginationInput(value) { + this.search.pagination = value; + }, }, filteredSearchNamespace: ADMIN_FILTERED_SEARCH_NAMESPACE, INSTANCE_TYPE, @@ -217,11 +220,13 @@ export default { /> - + +
diff --git a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue index bff5ec9b238..5a9ab21a457 100644 --- a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue +++ b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue @@ -64,19 +64,19 @@ export default { }, methods: { onFilter(filters) { - // Apply new filters, from page 1 + // Apply new filters, resetting pagination this.$emit('input', { ...this.value, filters, - pagination: { page: 1 }, + pagination: {}, }); }, onSort(sort) { - // Apply new sort, from page 1 + // Apply new sort, resetting pagination this.$emit('input', { ...this.value, sort, - pagination: { page: 1 }, + pagination: {}, }); }, }, diff --git a/app/assets/javascripts/runner/components/runner_jobs.vue b/app/assets/javascripts/runner/components/runner_jobs.vue index 57afdc4b9be..9003eba3636 100644 --- a/app/assets/javascripts/runner/components/runner_jobs.vue +++ b/app/assets/javascripts/runner/components/runner_jobs.vue @@ -27,9 +27,7 @@ export default { items: [], pageInfo: {}, }, - pagination: { - page: 1, - }, + pagination: {}, }; }, apollo: { @@ -62,6 +60,11 @@ export default { return this.$apollo.queries.jobs.loading; }, }, + methods: { + onPaginationInput(value) { + this.pagination = value; + }, + }, I18N_NO_JOBS_FOUND, }; @@ -74,6 +77,6 @@ export default {

{{ $options.I18N_NO_JOBS_FOUND }}

- + diff --git a/app/assets/javascripts/runner/components/runner_pagination.vue b/app/assets/javascripts/runner/components/runner_pagination.vue index cfc21d1407b..a5bf3074dd1 100644 --- a/app/assets/javascripts/runner/components/runner_pagination.vue +++ b/app/assets/javascripts/runner/components/runner_pagination.vue @@ -1,18 +1,12 @@ diff --git a/app/assets/javascripts/runner/components/runner_projects.vue b/app/assets/javascripts/runner/components/runner_projects.vue index 0a435f06792..2c1d2fc2b10 100644 --- a/app/assets/javascripts/runner/components/runner_projects.vue +++ b/app/assets/javascripts/runner/components/runner_projects.vue @@ -35,9 +35,7 @@ export default { pageInfo: {}, count: 0, }, - pagination: { - page: 1, - }, + pagination: {}, }; }, apollo: { @@ -82,6 +80,9 @@ export default { isOwner(projectId) { return projectId === this.projects.ownerProjectId; }, + onPaginationInput(value) { + this.pagination = value; + }, }, I18N_NONE, }; @@ -111,6 +112,10 @@ export default { {{ $options.I18N_NONE }} - + diff --git a/app/assets/javascripts/runner/constants.js b/app/assets/javascripts/runner/constants.js index 64541729701..57cc9a645fa 100644 --- a/app/assets/javascripts/runner/constants.js +++ b/app/assets/javascripts/runner/constants.js @@ -102,7 +102,6 @@ export const PARAM_KEY_TAG = 'tag'; export const PARAM_KEY_SEARCH = 'search'; export const PARAM_KEY_SORT = 'sort'; -export const PARAM_KEY_PAGE = 'page'; export const PARAM_KEY_AFTER = 'after'; export const PARAM_KEY_BEFORE = 'before'; diff --git a/app/assets/javascripts/runner/graphql/list/group_runner_connection.fragment.graphql b/app/assets/javascripts/runner/graphql/list/group_runner_connection.fragment.graphql index 66975bf2c36..baef16a4b41 100644 --- a/app/assets/javascripts/runner/graphql/list/group_runner_connection.fragment.graphql +++ b/app/assets/javascripts/runner/graphql/list/group_runner_connection.fragment.graphql @@ -11,7 +11,6 @@ fragment GroupRunnerConnection on CiRunnerConnection { } } pageInfo { - __typename ...PageInfo } } diff --git a/app/assets/javascripts/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/runner/group_runners/group_runners_app.vue index 76b51864876..a82411a2120 100644 --- a/app/assets/javascripts/runner/group_runners/group_runners_app.vue +++ b/app/assets/javascripts/runner/group_runners/group_runners_app.vue @@ -167,6 +167,9 @@ export default { reportToSentry(error) { captureException({ error, component: this.$options.name }); }, + onPaginationInput(value) { + this.search.pagination = value; + }, }, TABS_RUNNER_TYPES: [GROUP_TYPE, PROJECT_TYPE], GROUP_TYPE, @@ -226,11 +229,13 @@ export default { /> - + + diff --git a/app/assets/javascripts/runner/runner_search_utils.js b/app/assets/javascripts/runner/runner_search_utils.js index e01878f355a..dc582ccbac1 100644 --- a/app/assets/javascripts/runner/runner_search_utils.js +++ b/app/assets/javascripts/runner/runner_search_utils.js @@ -1,3 +1,4 @@ +import { isEmpty } from 'lodash'; import { queryToObject, setUrlParams } from '~/lib/utils/url_utility'; import { filterToQueryObject, @@ -13,7 +14,6 @@ import { PARAM_KEY_TAG, PARAM_KEY_SEARCH, PARAM_KEY_SORT, - PARAM_KEY_PAGE, PARAM_KEY_AFTER, PARAM_KEY_BEFORE, DEFAULT_SORT, @@ -41,7 +41,7 @@ import { getPaginationVariables } from './utils'; * sort: 'CREATED_DESC', * * // Pagination information - * pagination: { page: 1 }, + * pagination: { "after": "..." }, * }; * ``` * @@ -66,25 +66,16 @@ export const searchValidator = ({ runnerType, filters, sort }) => { }; const getPaginationFromParams = (params) => { - const page = parseInt(params[PARAM_KEY_PAGE], 10); - const after = params[PARAM_KEY_AFTER]; - const before = params[PARAM_KEY_BEFORE]; - - if (page && (before || after)) { - return { - page, - before, - after, - }; - } return { - page: 1, + after: params[PARAM_KEY_AFTER], + before: params[PARAM_KEY_BEFORE], }; }; // Outdated URL parameters const STATUS_ACTIVE = 'ACTIVE'; const STATUS_PAUSED = 'PAUSED'; +const PARAM_KEY_PAGE = 'page'; /** * Replaces params into a URL @@ -97,6 +88,21 @@ const updateUrlParams = (url, params = {}) => { return setUrlParams(params, url, false, true, true); }; +const outdatedStatusParams = (status) => { + if (status === STATUS_ACTIVE) { + return { + [PARAM_KEY_PAUSED]: ['false'], + [PARAM_KEY_STATUS]: [], // Important! clear PARAM_KEY_STATUS to avoid a redirection loop! + }; + } else if (status === STATUS_PAUSED) { + return { + [PARAM_KEY_PAUSED]: ['true'], + [PARAM_KEY_STATUS]: [], // Important! clear PARAM_KEY_STATUS to avoid a redirection loop! + }; + } + return {}; +}; + /** * Returns an updated URL for old (or deprecated) admin runner URLs. * @@ -108,25 +114,22 @@ const updateUrlParams = (url, params = {}) => { export const updateOutdatedUrl = (url = window.location.href) => { const urlObj = new URL(url); const query = urlObj.search; - const params = queryToObject(query, { gatherArrays: true }); - const status = params[PARAM_KEY_STATUS]?.[0] || null; + // Remove `page` completely, not needed for keyset pagination + const pageParams = PARAM_KEY_PAGE in params ? { [PARAM_KEY_PAGE]: null } : {}; - switch (status) { - case STATUS_ACTIVE: - return updateUrlParams(url, { - [PARAM_KEY_PAUSED]: ['false'], - [PARAM_KEY_STATUS]: [], // Important! clear PARAM_KEY_STATUS to avoid a redirection loop! - }); - case STATUS_PAUSED: - return updateUrlParams(url, { - [PARAM_KEY_PAUSED]: ['true'], - [PARAM_KEY_STATUS]: [], // Important! clear PARAM_KEY_STATUS to avoid a redirection loop! - }); - default: - return null; + const status = params[PARAM_KEY_STATUS]?.[0]; + const redirectParams = { + // Replace paused status (active, paused) with a paused flag + ...outdatedStatusParams(status), + ...pageParams, + }; + + if (!isEmpty(redirectParams)) { + return updateUrlParams(url, redirectParams); } + return null; }; /** @@ -182,13 +185,11 @@ export const fromSearchToUrl = ( } const isDefaultSort = sort !== DEFAULT_SORT; - const isFirstPage = pagination?.page === 1; const otherParams = { // Sorting & Pagination [PARAM_KEY_SORT]: isDefaultSort ? sort : null, - [PARAM_KEY_PAGE]: isFirstPage ? null : pagination.page, - [PARAM_KEY_BEFORE]: isFirstPage ? null : pagination.before, - [PARAM_KEY_AFTER]: isFirstPage ? null : pagination.after, + [PARAM_KEY_BEFORE]: pagination?.before || null, + [PARAM_KEY_AFTER]: pagination?.after || null, }; return setUrlParams({ ...filterParams, ...otherParams }, url, false, true, true); @@ -247,6 +248,6 @@ export const fromSearchToVariables = ({ */ export const isSearchFiltered = ({ runnerType = null, filters = [], pagination = {} } = {}) => { return Boolean( - runnerType !== null || filters?.length !== 0 || (pagination && pagination?.page !== 1), + runnerType !== null || filters?.length !== 0 || pagination?.before || pagination?.after, ); }; diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue index 707baf81d29..7b9b66a472a 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue @@ -215,6 +215,7 @@ export default { type="submit" :disabled="search.length === 0" data-testid="add-child-button" + class="gl-mr-2" > {{ $options.i18n.createChildOptionLabel }} diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss index 1c43212f501..4c5d8628696 100644 --- a/app/assets/stylesheets/framework/highlight.scss +++ b/app/assets/stylesheets/framework/highlight.scss @@ -31,7 +31,8 @@ width: 100%; padding-left: 10px; padding-right: 10px; - white-space: pre; + white-space: break-spaces; + word-break: break-word; &:empty::before { content: '\200b'; diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 96fe6caeea2..b016d0a1068 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -342,10 +342,10 @@ $comparison-empty-state-height: 62px; .mr-compare { .diff-file .file-title-flex-parent { - top: calc(#{$header-height} + #{$mr-tabs-height} + 36px); + top: calc(#{$header-height} + #{$mr-tabs-height}); .with-performance-bar & { - top: calc(#{$performance-bar-height} + #{$header-height} + #{$mr-tabs-height} + 36px); + top: calc(#{$performance-bar-height} + #{$header-height} + #{$mr-tabs-height}); } } } diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb index 4ee661d89f4..5fc21ba3f28 100644 --- a/app/models/ci/build_metadata.rb +++ b/app/models/ci/build_metadata.rb @@ -19,6 +19,7 @@ module Ci before_create :set_build_project validates :build, presence: true + validates :id_tokens, json_schema: { filename: 'build_metadata_id_tokens' } validates :secrets, json_schema: { filename: 'build_metadata_secrets' } serialize :config_options, Serializers::SymbolizedJson # rubocop:disable Cop/ActiveRecordSerialize diff --git a/app/models/concerns/ci/artifactable.rb b/app/models/concerns/ci/artifactable.rb index 2e3afb66193..ee8e98ec1bf 100644 --- a/app/models/concerns/ci/artifactable.rb +++ b/app/models/concerns/ci/artifactable.rb @@ -11,6 +11,7 @@ module Ci NotSupportedAdapterError = Class.new(StandardError) FILE_FORMAT_ADAPTERS = { gzip: Gitlab::Ci::Build::Artifacts::Adapters::GzipStream, + zip: Gitlab::Ci::Build::Artifacts::Adapters::ZipStream, raw: Gitlab::Ci::Build::Artifacts::Adapters::RawStream }.freeze diff --git a/app/models/concerns/ci/metadatable.rb b/app/models/concerns/ci/metadatable.rb index aa9669ee208..8c3a05c23f0 100644 --- a/app/models/concerns/ci/metadatable.rb +++ b/app/models/concerns/ci/metadatable.rb @@ -20,6 +20,8 @@ module Ci delegate :interruptible, to: :metadata, prefix: false, allow_nil: true delegate :environment_auto_stop_in, to: :metadata, prefix: false, allow_nil: true delegate :set_cancel_gracefully, to: :metadata, prefix: false, allow_nil: false + delegate :id_tokens, to: :metadata, allow_nil: true + before_create :ensure_metadata end @@ -77,6 +79,14 @@ module Ci ensure_metadata.interruptible = value end + def id_tokens? + !!metadata&.id_tokens? + end + + def id_tokens=(value) + ensure_metadata.id_tokens = value + end + private def read_metadata_attribute(legacy_key, metadata_key, default_value = nil) diff --git a/app/services/projects/alerting/notify_service.rb b/app/services/projects/alerting/notify_service.rb index e5d40b60747..c21a61bcb52 100644 --- a/app/services/projects/alerting/notify_service.rb +++ b/app/services/projects/alerting/notify_service.rb @@ -32,7 +32,7 @@ module Projects attr_reader :project, :payload, :integration def valid_payload_size? - Gitlab::Utils::DeepSize.new(payload).valid? + Gitlab::Utils::DeepSize.new(payload.to_h).valid? end override :alert_source diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb index bc517ee3d6f..6265a74fad2 100644 --- a/app/services/projects/prometheus/alerts/notify_service.rb +++ b/app/services/projects/prometheus/alerts/notify_service.rb @@ -56,7 +56,7 @@ module Projects attr_reader :project, :payload def valid_payload_size? - Gitlab::Utils::DeepSize.new(payload).valid? + Gitlab::Utils::DeepSize.new(payload.to_h).valid? end def max_alerts_exceeded? diff --git a/app/validators/json_schemas/build_metadata_id_tokens.json b/app/validators/json_schemas/build_metadata_id_tokens.json new file mode 100644 index 00000000000..7f39c7274f3 --- /dev/null +++ b/app/validators/json_schemas/build_metadata_id_tokens.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "CI builds metadata ID tokens", + "type": "object", + "patternProperties": { + ".*": { + "type": "object", + "patternProperties": { + "^id_token$": { + "type": "object", + "required": ["aud"], + "properties": { + "aud": { "type": "string" }, + "field": { "type": "string" } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } +} diff --git a/config/feature_flags/development/hard_failure_for_mirrors_without_license.yml b/config/feature_flags/development/hard_failure_for_mirrors_without_license.yml deleted file mode 100644 index f138c8ea497..00000000000 --- a/config/feature_flags/development/hard_failure_for_mirrors_without_license.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: hard_failure_for_mirrors_without_license -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92422 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367851 -milestone: '15.2' -type: development -group: group::source code -default_enabled: false diff --git a/config/metrics/aggregates/code_review.yml b/config/metrics/aggregates/code_review.yml index 2159f1fa0b4..a9be0680bfb 100644 --- a/config/metrics/aggregates/code_review.yml +++ b/config/metrics/aggregates/code_review.yml @@ -104,6 +104,12 @@ - 'i_code_review_merge_request_widget_terraform_expand_success' - 'i_code_review_merge_request_widget_terraform_expand_warning' - 'i_code_review_merge_request_widget_terraform_expand_failed' + - 'i_code_review_merge_request_widget_metrics_view' + - 'i_code_review_merge_request_widget_metrics_full_report_clicked' + - 'i_code_review_merge_request_widget_metrics_expand' + - 'i_code_review_merge_request_widget_metrics_expand_success' + - 'i_code_review_merge_request_widget_metrics_expand_warning' + - 'i_code_review_merge_request_widget_metrics_expand_failed' - name: code_review_category_monthly_active_users operator: OR source: redis @@ -196,6 +202,12 @@ - 'i_code_review_merge_request_widget_terraform_expand_success' - 'i_code_review_merge_request_widget_terraform_expand_warning' - 'i_code_review_merge_request_widget_terraform_expand_failed' + - 'i_code_review_merge_request_widget_metrics_view' + - 'i_code_review_merge_request_widget_metrics_full_report_clicked' + - 'i_code_review_merge_request_widget_metrics_expand' + - 'i_code_review_merge_request_widget_metrics_expand_success' + - 'i_code_review_merge_request_widget_metrics_expand_warning' + - 'i_code_review_merge_request_widget_metrics_expand_failed' - name: code_review_extension_category_monthly_active_users operator: OR source: redis diff --git a/db/migrate/20220808190124_add_id_token_to_ci_builds_metadata.rb b/db/migrate/20220808190124_add_id_token_to_ci_builds_metadata.rb new file mode 100644 index 00000000000..00d27d7c516 --- /dev/null +++ b/db/migrate/20220808190124_add_id_token_to_ci_builds_metadata.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddIdTokenToCiBuildsMetadata < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def change + add_column :ci_builds_metadata, :id_tokens, :jsonb, null: false, default: {} + end +end diff --git a/db/post_migrate/20220715152108_backfill_project_import_level.rb b/db/post_migrate/20220715152108_backfill_project_import_level.rb new file mode 100644 index 00000000000..65a0dc0a58a --- /dev/null +++ b/db/post_migrate/20220715152108_backfill_project_import_level.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class BackfillProjectImportLevel < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + restrict_gitlab_migration gitlab_schema: :gitlab_main + + MIGRATION = 'BackfillProjectImportLevel' + INTERVAL = 120.seconds + + def up + queue_batched_background_migration( + MIGRATION, + :namespaces, + :id, + job_interval: INTERVAL + ) + end + + def down + delete_batched_background_migration(MIGRATION, :namespaces, :id, []) + end +end diff --git a/db/schema_migrations/20220715152108 b/db/schema_migrations/20220715152108 new file mode 100644 index 00000000000..23d61b45334 --- /dev/null +++ b/db/schema_migrations/20220715152108 @@ -0,0 +1 @@ +76f4adebfb71dcd51f861097ba441ae5ee3f62eeb2060f147730d4e6c6006402 \ No newline at end of file diff --git a/db/schema_migrations/20220808190124 b/db/schema_migrations/20220808190124 new file mode 100644 index 00000000000..99b7173cbb6 --- /dev/null +++ b/db/schema_migrations/20220808190124 @@ -0,0 +1 @@ +ab8dfd7549b2b61a5cf9d5b46935ec534ea77ec2025fdb58d03f654d81c8f6ee \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index b401213af76..4eeba13e90f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -12602,7 +12602,8 @@ CREATE TABLE ci_builds_metadata ( secrets jsonb DEFAULT '{}'::jsonb NOT NULL, build_id bigint NOT NULL, id bigint NOT NULL, - runtime_runner_features jsonb DEFAULT '{}'::jsonb NOT NULL + runtime_runner_features jsonb DEFAULT '{}'::jsonb NOT NULL, + id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL ); CREATE SEQUENCE ci_builds_metadata_id_seq diff --git a/doc/api/groups.md b/doc/api/groups.md index 702b05f4668..a841bad90c2 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -1418,6 +1418,51 @@ DELETE /groups/:id/ldap_group_links NOTE: To delete the LDAP group link, provide either a `cn` or a `filter`, but not both. +## SAML Group Links **(PREMIUM)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/290367) in GitLab 15.3. + +List, add, and delete SAML group links. + +### List SAML group links + +Lists SAML group links. + +```plaintext +GET /groups/:id/saml_group_links +``` + +| Attribute | Type | Required | Description | +| --------- | -------------- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) | + +### Add SAML group link + +Adds a SAML group link for a group. + +```plaintext +POST /groups/:id/saml_group_links +``` + +| Attribute | Type | Required | Description | +| --------- | -------------- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) | +| `saml_group_name` | string | yes | The name of a SAML group | +| `access_level` | string | yes | Minimum [access level](members.md#valid-access-levels) for members of the SAML group | + +### Delete SAML group link + +Deletes a SAML group link for the group. + +```plaintext +DELETE /groups/:id/saml_group_links/:saml_group_name +``` + +| Attribute | Type | Required | Description | +| --------- | -------------- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) | +| `saml_group_name` | string | yes | The name of an SAML group | + ## Namespaces in groups By default, groups only get 20 namespaces at a time because the API results are paginated. diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 1d563673a20..a491756e5f0 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -825,6 +825,66 @@ Parameters: ] ``` +## Get single MR reviewers + +Get a list of merge request reviewers. + +```plaintext +GET /projects/:id/merge_requests/:merge_request_iid/reviewers +``` + +Parameters: + +| Attribute | Type | Required | Description | +|---------------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------| +| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. | +| `merge_request_iid` | integer | yes | The internal ID of the merge request. | + +```json +[ + { + "user": { + "id": 1, + "name": "John Doe1", + "username": "user1", + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon", + "web_url": "http://localhost/user1" + }, + "updated_state_by": { + "id": 1, + "name": "John Doe1", + "username": "user1", + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon", + "web_url": "http://localhost/user1" + }, + "state": "unreviewed", + "created_at": "2022-07-27T17:03:27.684Z" + }, + { + "user": { + "id": 2, + "name": "John Doe2", + "username": "user2", + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon", + "web_url": "http://localhost/user2" + }, + "updated_state_by": { + "id": 1, + "name": "John Doe1", + "username": "user1", + "state": "active", + "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon", + "web_url": "http://localhost/user1" + }, + "state": "reviewed", + "created_at": "2022-07-27T17:03:27.684Z" + } +] +``` + ## Get single MR commits Get a list of merge request commits. diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md index 6149e187567..a434e3d9fb0 100644 --- a/doc/development/documentation/styleguide/index.md +++ b/doc/development/documentation/styleguide/index.md @@ -791,45 +791,15 @@ section of GitLab. ### Links to external documentation -When describing interactions with external software, it's often helpful to -include links to external documentation. When possible, make sure that you're -linking to an [**authoritative** source](#authoritative-sources). For example, -if you're describing a feature in Microsoft's Active Directory, include a link -to official Microsoft documentation. +When possible, avoid links to external documentation. These links can easily become outdated, and are difficult to maintain. -### Authoritative sources +- [They lead to link rot](https://en.wikipedia.org/wiki/Link_rot). +- [They create issues with maintenance](https://gitlab.com/gitlab-org/gitlab/-/issues/368300). -When citing external information, use sources that are written by the people who -created the item or product in question. These sources are the most likely to be -accurate and remain up to date. +Sometimes links are required. They might clarify troubleshooting steps or help prevent duplication of content. +Sometimes they are more precise and will be maintained more actively. -Examples of authoritative sources include: - -- Specifications, such as a [Request for Comments](https://www.ietf.org/standards/rfcs/) - document from the Internet Engineering Task Force. -- Official documentation for a product. For example, if you're setting up an - interface with the Google OAuth 2 authorization server, include a link to - Google's documentation. -- Official documentation for a project. For example, if you're citing NodeJS - functionality, refer directly to [NodeJS documentation](https://nodejs.org/en/docs/). -- Books from an authoritative publisher. - -Examples of sources to avoid include: - -- Personal blog posts. -- Wikipedia. -- Non-trustworthy articles. -- Discussions on forums such as Stack Overflow. -- Documentation from a company that describes another company's product. - -While many of these sources to avoid can help you learn skills and or features, -they can become obsolete quickly. Nobody is obliged to maintain any of these -sites. Therefore, we should avoid using them as reference literature. - -NOTE: -Non-authoritative sources are acceptable only if there is no equivalent -authoritative source. Even then, focus on non-authoritative sources that are -extensively cited or peer-reviewed. +For each external link you add, weigh the customer benefit with the maintenance difficulties. ### Links requiring permissions diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md index 3fa92f87d1b..7e9d35d9c98 100644 --- a/doc/development/service_ping/implement.md +++ b/doc/development/service_ping/implement.md @@ -371,14 +371,15 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd/) and [P - In the controller using the `RedisTracking` module and the following format: ```ruby - track_redis_hll_event(*controller_actions, name:, if: nil, &block) + track_event(*controller_actions, name:, conditions: nil, destinations: [:redis_hll], &block) ``` Arguments: - `controller_actions`: the controller actions to track. - `name`: the event name. - - `if`: optional custom conditions. Uses the same format as Rails callbacks. + - `conditions`: optional custom conditions. Uses the same format as Rails callbacks. + - `destinations`: optional list of destinations. Currently supports `:redis_hll` and `:snowplow`. Default: [:redis_hll]. - `&block`: optional block that computes and returns the `custom_id` that we want to track. This overrides the `visitor_id`. Example: @@ -389,7 +390,7 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd/) and [P include RedisTracking skip_before_action :authenticate_user!, only: :show - track_redis_hll_event :index, :show, name: 'users_visiting_projects' + track_event :index, :show, name: 'users_visiting_projects' def index render html: 'index' diff --git a/doc/user/group/saml_sso/group_sync.md b/doc/user/group/saml_sso/group_sync.md index c3328331d1c..8bc316f9396 100644 --- a/doc/user/group/saml_sso/group_sync.md +++ b/doc/user/group/saml_sso/group_sync.md @@ -161,3 +161,9 @@ graph TB GitLabGroupD --> |Member|GitLabUserC GitLabGroupD --> |Member|GitLabUserD ``` + +### Use the API + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/290367) in GitLab 15.3. + +You can use the GitLab API to [list, add, and delete](../../../api/groups.md#saml-group-links) SAML group links. diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md index 606e2cb135d..a203de2ed2c 100644 --- a/doc/user/packages/container_registry/index.md +++ b/doc/user/packages/container_registry/index.md @@ -555,6 +555,12 @@ this setting. However, disabling the Container Registry disables all Container R ## Troubleshooting the GitLab Container Registry +## Migrating OCI container images to GitLab Container Registry + +Migrating built container images to the GitLab registry is not a current feature. However, an [epic](https://gitlab.com/groups/gitlab-org/-/epics/5210) is open to track the work on this feature. + +Some third-party tools can help migrate container images, for example, [skopeo](https://github.com/containers/skopeo), which can [copy container images](https://github.com/containers/skopeo#copying-images) between various storage mechanisms. You can use skopeo to copy from container registries, container storage backends, local directories, and local OCI-layout directories to the GitLab Container Registry. + ### Docker connection error A Docker connection error can occur when there are special characters in either the group, diff --git a/lib/api/entities/merge_request_reviewer.rb b/lib/api/entities/merge_request_reviewer.rb new file mode 100644 index 00000000000..3bf2ccc36aa --- /dev/null +++ b/lib/api/entities/merge_request_reviewer.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module API + module Entities + class MergeRequestReviewer < Grape::Entity + expose :reviewer, as: :user, using: Entities::UserBasic + expose :updated_state_by, using: Entities::UserBasic + expose :state + expose :created_at + end + end +end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 58ae9e3dc8b..6bd13c48ab1 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -289,6 +289,17 @@ module API present paginate(participants), with: Entities::UserBasic end + desc 'Get the reviewers of a merge request' do + success Entities::MergeRequestReviewer + end + get ':id/merge_requests/:merge_request_iid/reviewers', feature_category: :code_review, urgency: :low do + merge_request = find_merge_request_with_access(params[:merge_request_iid]) + + reviewers = ::Kaminari.paginate_array(merge_request.merge_request_reviewers) + + present paginate(reviewers), with: Entities::MergeRequestReviewer + end + desc 'Get the commits of a merge request' do success Entities::Commit end diff --git a/lib/gitlab/background_migration/backfill_project_import_level.rb b/lib/gitlab/background_migration/backfill_project_import_level.rb new file mode 100644 index 00000000000..06706b729ea --- /dev/null +++ b/lib/gitlab/background_migration/backfill_project_import_level.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true +# rubocop:disable Style/Documentation +module Gitlab + module BackgroundMigration + class BackfillProjectImportLevel < BatchedMigrationJob + LEVEL = { + Gitlab::Access::NO_ACCESS => [0], + Gitlab::Access::DEVELOPER => [2], + Gitlab::Access::MAINTAINER => [1], + Gitlab::Access::OWNER => [nil] + }.freeze + + def perform + each_sub_batch(operation_name: :update_import_level) do |sub_batch| + update_import_level(sub_batch) + end + end + + private + + def update_import_level(relation) + LEVEL.each do |import_level, creation_level| + namespace_ids = relation + .where(type: 'Group', project_creation_level: creation_level) + + NamespaceSetting.where( + namespace_id: namespace_ids + ).update_all(project_import_level: import_level) + end + end + end + end +end + +# rubocop:enable Style/Documentation diff --git a/lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb b/lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb new file mode 100644 index 00000000000..690a47097c6 --- /dev/null +++ b/lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Build + module Artifacts + module Adapters + class ZipStream + MAX_DECOMPRESSED_SIZE = 100.megabytes + MAX_FILES_PROCESSED = 50 + + attr_reader :stream + + InvalidStreamError = Class.new(StandardError) + + def initialize(stream) + raise InvalidStreamError, "Stream is required" unless stream + + @stream = stream + @files_processed = 0 + end + + def each_blob + Zip::InputStream.open(stream) do |zio| + while entry = zio.get_next_entry + break if at_files_processed_limit? + next unless should_process?(entry) + + @files_processed += 1 + + yield entry.get_input_stream.read + end + end + end + + private + + def should_process?(entry) + file?(entry) && !too_large?(entry) + end + + def file?(entry) + # Check the file name as a workaround for incorrect + # file type detection when using InputStream + # https://github.com/rubyzip/rubyzip/issues/533 + entry.file? && !entry.name.end_with?('/') + end + + def too_large?(entry) + entry.size > MAX_DECOMPRESSED_SIZE + end + + def at_files_processed_limit? + @files_processed >= MAX_FILES_PROCESSED + end + end + end + end + end + end +end diff --git a/lib/gitlab/event_store.rb b/lib/gitlab/event_store.rb index bf31884f1c2..696d675a9e8 100644 --- a/lib/gitlab/event_store.rb +++ b/lib/gitlab/event_store.rb @@ -43,6 +43,7 @@ module Gitlab store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Projects::ProjectPathChangedEvent store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Projects::ProjectArchivedEvent store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Projects::ProjectTransferedEvent + store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Groups::GroupTransferedEvent store.subscribe ::MergeRequests::CreateApprovalEventWorker, to: ::MergeRequests::ApprovedEvent store.subscribe ::MergeRequests::CreateApprovalNoteWorker, to: ::MergeRequests::ApprovedEvent diff --git a/lib/gitlab/form_builders/gitlab_ui_form_builder.rb b/lib/gitlab/form_builders/gitlab_ui_form_builder.rb index f1ad8edd125..ea98f6b2eec 100644 --- a/lib/gitlab/form_builders/gitlab_ui_form_builder.rb +++ b/lib/gitlab/form_builders/gitlab_ui_form_builder.rb @@ -8,7 +8,7 @@ module Gitlab @template.render Pajamas::ButtonComponent.new( variant: :confirm, type: :submit, - button_options: options.except!(:pajamas_button) + button_options: options.except(:pajamas_button) ) do value end diff --git a/lib/gitlab/import_export/base/relation_factory.rb b/lib/gitlab/import_export/base/relation_factory.rb index 53dd6f8cd55..deef40d5abf 100644 --- a/lib/gitlab/import_export/base/relation_factory.rb +++ b/lib/gitlab/import_export/base/relation_factory.rb @@ -126,12 +126,16 @@ module Gitlab end end + # When an assignee did not exist in the members mapper, the importer is + # assigned. We only need to assign each user once. def remove_duplicate_assignees - return unless @relation_hash['issue_assignees'] + if @relation_hash['issue_assignees'] + @relation_hash['issue_assignees'].uniq!(&:user_id) + end - # When an assignee did not exist in the members mapper, the importer is - # assigned. We only need to assign each user once. - @relation_hash['issue_assignees'].uniq!(&:user_id) + if @relation_hash['merge_request_assignees'] + @relation_hash['merge_request_assignees'].uniq!(&:user_id) + end end def generate_imported_object diff --git a/lib/gitlab/import_export/base/relation_object_saver.rb b/lib/gitlab/import_export/base/relation_object_saver.rb index d0fae2cbb95..ea989487ebd 100644 --- a/lib/gitlab/import_export/base/relation_object_saver.rb +++ b/lib/gitlab/import_export/base/relation_object_saver.rb @@ -15,7 +15,7 @@ module Gitlab include Gitlab::Utils::StrongMemoize BATCH_SIZE = 100 - MIN_RECORDS_SIZE = 5 + MIN_RECORDS_SIZE = 1 # @param relation_object [Object] Object of a project/group, e.g. an issue # @param relation_key [String] Name of the object association to group/project, e.g. :issues diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index 4ead49e9c38..b912eccf5fe 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -55,6 +55,7 @@ tree: - merge_requests: - :metrics - :award_emoji + - :merge_request_assignees - notes: - :author - :award_emoji @@ -329,7 +330,6 @@ included_attributes: - :source_branch - :source_project_id - :author_id - - :assignee_id - :title - :created_at - :updated_at @@ -588,6 +588,10 @@ included_attributes: - :author_id issue_assignees: - :user_id + merge_request_assignees: + - :user_id + - :created_at + - :state sentry_issue: - :sentry_issue_identifier zoom_meetings: diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml index bdf6b48357f..d4e4e3d2482 100644 --- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml +++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml @@ -400,3 +400,28 @@ redis_slot: code_review category: code_review aggregation: weekly +## Metrics +- name: i_code_review_merge_request_widget_metrics_view + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_merge_request_widget_metrics_full_report_clicked + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_merge_request_widget_metrics_expand + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_merge_request_widget_metrics_expand_success + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_merge_request_widget_metrics_expand_warning + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_merge_request_widget_metrics_expand_failed + redis_slot: code_review + category: code_review + aggregation: weekly diff --git a/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb b/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb index 0d7dd1ab085..5eb48864cfe 100644 --- a/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb +++ b/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb @@ -5,7 +5,7 @@ module Gitlab class MergeRequestWidgetExtensionCounter < BaseCounter KNOWN_EVENTS = %w[view full_report_clicked expand expand_success expand_warning expand_failed].freeze PREFIX = 'i_code_review_merge_request_widget' - WIDGETS = %w[accessibility code_quality terraform test_summary].freeze + WIDGETS = %w[accessibility code_quality terraform test_summary metrics].freeze class << self private diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 55ddf413be5..cf163371623 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -31741,18 +31741,36 @@ msgstr "" msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?" msgstr "" +msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}." +msgstr "" + msgid "ProtectedEnvironment|Allowed to deploy" msgstr "" +msgid "ProtectedEnvironment|Allowed to deploy to %{project} / %{environment}" +msgstr "" + msgid "ProtectedEnvironment|Environment" msgstr "" +msgid "ProtectedEnvironment|Environments protected upstream" +msgstr "" + +msgid "ProtectedEnvironment|Failed to load details for this group." +msgstr "" + +msgid "ProtectedEnvironment|No environments in this project are projected." +msgstr "" + msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments." msgstr "" msgid "ProtectedEnvironment|Only specified users can execute deployments in a protected environment." msgstr "" +msgid "ProtectedEnvironment|Parent group" +msgstr "" + msgid "ProtectedEnvironment|Protect" msgstr "" @@ -34931,6 +34949,9 @@ msgstr "" msgid "SecurityOrchestration|Failed to load cluster agents." msgstr "" +msgid "SecurityOrchestration|Failed to load images." +msgstr "" + msgid "SecurityOrchestration|Failed to load vulnerability scanners." msgstr "" @@ -35276,6 +35297,9 @@ msgstr "" msgid "SecurityReports|Hide dismissed" msgstr "" +msgid "SecurityReports|Image" +msgstr "" + msgid "SecurityReports|Issue Created" msgstr "" @@ -45780,6 +45804,9 @@ msgstr "" msgid "ciReport|All clusters" msgstr "" +msgid "ciReport|All images" +msgstr "" + msgid "ciReport|All projects" msgstr "" diff --git a/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb b/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb new file mode 100644 index 00000000000..3e30f3aa4d0 --- /dev/null +++ b/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'rack/utils' + +module RuboCop + module Cop + module Gitlab + # This cop prevents from using deprecated `track_redis_hll_event` method. + # + # @example + # + # # bad + # track_redis_hll_event :show, name: 'p_analytics_valuestream' + # + # # good + # track_event :show, name: 'g_analytics_valuestream', destinations: [:redis_hll] + class DeprecateTrackRedisHLLEvent < RuboCop::Cop::Cop + MSG = '`track_redis_hll_event` is deprecated. Use `track_event` helper instead. ' \ + 'See https://docs.gitlab.com/ee/development/service_ping/implement.html#add-new-events' + + def_node_matcher :track_redis_hll_event_used?, <<~PATTERN + (send _ :track_redis_hll_event ...) + PATTERN + + def on_send(node) + return unless track_redis_hll_event_used?(node) + + add_offense(node, location: :selector) + end + end + end + end +end diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb index cdbcdced5f4..114ad3a5847 100644 --- a/spec/factories/ci/job_artifacts.rb +++ b/spec/factories/ci/job_artifacts.rb @@ -102,6 +102,28 @@ FactoryBot.define do end end + trait :zip_with_single_file do + file_type { :archive } + file_format { :zip } + + after(:build) do |artifact, evaluator| + artifact.file = fixture_file_upload( + Rails.root.join('spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip'), + 'application/zip') + end + end + + trait :zip_with_multiple_files do + file_type { :archive } + file_format { :zip } + + after(:build) do |artifact, evaluator| + artifact.file = fixture_file_upload( + Rails.root.join('spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip'), + 'application/zip') + end + end + trait :junit do file_type { :junit } file_format { :gzip } diff --git a/spec/fixtures/gitlab/import_export/corrupted_project_export.tar.gz b/spec/fixtures/gitlab/import_export/corrupted_project_export.tar.gz index e99136e96b7454b46c5e284a0ce5105902f49ca3..d6632c5121a2de1c81a7fc07c92373c0841a2fac 100644 GIT binary patch literal 4603 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 literal 3846 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 AhX4Qo literal 0 HcmV?d00001 diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zip new file mode 100644 index 0000000000000000000000000000000000000000..8c56cce641aca320933ec3f4cba8b0eb0b8bc025 GIT binary patch literal 203718 zcmeI*KZ_z~0LSrXGPB{bcld|94FN;&B`jCj2+D!YHWo^_O59sZx#gA%*XbelY4Q$| zA}_!`>9*J+ou)Z#4|0ugUSpW}_$u9=M zAHUC^JPqH6=a-kG>GkMl^6K^U_RZw(ZgMjmd=v89#;5C_j_oP}_@$v=O- zKVBN<&ENk0`0elK@!Uax009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXFq%1K1 zdyyZ*fDoGHy4N2%2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+0D;s6#??v@ zTY&jt)aa%@#`6;(K!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk!)3cwi!DId z&87tp0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyKp=U6i~Y89Y60%cReg~B z90LdtAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5;&%_7fY4A3msy*>^C1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZAbEjtwNk_uV15`iy2-CGfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oPAdTea8%gxzdf;2=PN009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjY)7r5AOJEs=lzFgG@$009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5ZEm8EXDxMa^36WAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0tAv57*{JrYyswnQKOsu8UqLrAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5;&WxG|2EkM}KrUecH1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zAbEj{{kC&z0q)CHeUSVd0|*cxK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB=Eb zBF|zB&@9)zJ`Mr|2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkJxd4X}YQp6Tu zei${n$*(bh009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5LmWbwb%lL-E3Om zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0tAv5xY%zyrxxJ8T-683&oO`i z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U*evoa#sJN7-Rt8ZK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1d$F7&W@duQ7lC0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UShicW*aC#zY+B$TK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1d8Aey?C0&M literal 0 HcmV?d00001 diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip new file mode 100644 index 0000000000000000000000000000000000000000..09ac4e5df515561215fb0902b08d52b46014c263 GIT binary patch literal 332 zcmWIWW@h1H00A|B=Fn@Em8bZDY!GH+kYPy6%t_TX3=QFAU_R0FDhY&3E4UdLS-vtd zFtCUKRe@9}7%C*^=ar=9m2d^Xjne`eclD5;B8G8BVB^jMjko3&?O8@8bK_WYgr+#MRRF@H^MAT$03{L4>JqsRG?WPr(&4J$_BEL2?%EZ>3JXy F0|3$RLB9Y1 literal 0 HcmV?d00001 diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip new file mode 100644 index 0000000000000000000000000000000000000000..81768a9f2b3e91b1fa9e4150db5bb39d0a7dabf4 GIT binary patch literal 177 zcmWIWW@h1H00A|B=Fn@Em8bZDY!GH+kYPy6%t_TX3=QFAU_R0FDhY&3E4UdLS-vtd zFtCUKRe@9}7%C*^=ar=9m2d@kGcw6B<1$bJsDXh2sFh(!BZvhvo)uy|n$ZE?tZX1v Nj6moEr2Ro01^^2gA%_3} literal 0 HcmV?d00001 diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zip new file mode 100644 index 0000000000000000000000000000000000000000..6de321ea86a0cf7de6b745733033d5d875736aa5 GIT binary patch literal 520 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 bui~kH{`5RvH+=QW&*HiK<4?T))3yHvcrDkK literal 0 HcmV?d00001 diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json index 12dbabf833b..dba079e381d 100644 --- a/spec/fixtures/lib/gitlab/import_export/complex/project.json +++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json @@ -3149,6 +3149,28 @@ "created_at": "2020-01-07T11:21:21.235Z", "updated_at": "2020-01-07T11:21:21.235Z" } + ], + "merge_request_assignees": [ + { + "user_id": 1, + "created_at": "2020-01-07T11:21:21.235Z", + "state": "unreviewed" + }, + { + "user_id": 15, + "created_at": "2020-01-08T11:21:21.235Z", + "state": "reviewed" + }, + { + "user_id": 16, + "created_at": "2020-01-09T11:21:21.235Z", + "state": "attention_requested" + }, + { + "user_id": 6, + "created_at": "2020-01-10T11:21:21.235Z", + "state": "unreviewed" + } ] }, { @@ -3416,7 +3438,8 @@ "action": 1, "author_id": 1 } - ] + ], + "merge_request_assignees": [] }, { "id": 15, diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson index 16e45509a1b..2c9784669db 100644 --- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson +++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson @@ -1,5 +1,5 @@ -{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n
  • 16ea4e20...074a2a32 - 2 commits from branch master
  • ca223a02 - readme: fix typos
\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"

something else entirely

","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}]} -{"id":26,"target_branch":"master","source_branch":"feature","source_project_id":4,"author_id":1,"assignee_id":null,"title":"MR2","created_at":"2016-06-14T15:02:36.418Z","updated_at":"2016-06-14T15:02:57.013Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":8,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":679,"note":"Qui rerum totam nisi est.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.848Z","updated_at":"2016-06-14T15:02:56.848Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":680,"note":"Pariatur magni corrupti consequatur debitis minima error beatae voluptatem.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.871Z","updated_at":"2016-06-14T15:02:56.871Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":681,"note":"Qui quis ut modi eos rerum ratione.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.895Z","updated_at":"2016-06-14T15:02:56.895Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":682,"note":"Illum quidem expedita mollitia fugit.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.918Z","updated_at":"2016-06-14T15:02:56.918Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":683,"note":"Consectetur voluptate sit sint possimus veritatis quod.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.942Z","updated_at":"2016-06-14T15:02:56.942Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":684,"note":"Natus libero quibusdam rem assumenda deleniti accusamus sed earum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.966Z","updated_at":"2016-06-14T15:02:56.966Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":685,"note":"Tenetur autem nihil rerum odit.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.989Z","updated_at":"2016-06-14T15:02:56.989Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":686,"note":"Quia maiores et odio sed.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:57.012Z","updated_at":"2016-06-14T15:02:57.012Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":26,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":26,"sha":"0b4bc9a49b562e85de7cc9e834518ea6828729b9","relative_order":0,"message":"Feature added\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:26:01.000+01:00","committed_date":"2014-02-27T09:26:01.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":26,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,5 @@\n+class Feature\n+ def foo\n+ puts 'bar'\n+ end\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":26,"created_at":"2016-06-14T15:02:36.421Z","updated_at":"2016-06-14T15:02:36.474Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"1"},"events":[{"id":222,"target_type":"MergeRequest","target_id":26,"project_id":36,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1},{"id":186,"target_type":"MergeRequest","target_id":26,"project_id":5,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1}]} +{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n
  • 16ea4e20...074a2a32 - 2 commits from branch master
  • ca223a02 - readme: fix typos
\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"

something else entirely

","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}],"merge_request_assignees":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"attention_requested"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}]} +{"id":26,"target_branch":"master","source_branch":"feature","source_project_id":4,"author_id":1,"assignee_id":null,"title":"MR2","created_at":"2016-06-14T15:02:36.418Z","updated_at":"2016-06-14T15:02:57.013Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":8,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":679,"note":"Qui rerum totam nisi est.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.848Z","updated_at":"2016-06-14T15:02:56.848Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":680,"note":"Pariatur magni corrupti consequatur debitis minima error beatae voluptatem.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.871Z","updated_at":"2016-06-14T15:02:56.871Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":681,"note":"Qui quis ut modi eos rerum ratione.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.895Z","updated_at":"2016-06-14T15:02:56.895Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":682,"note":"Illum quidem expedita mollitia fugit.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.918Z","updated_at":"2016-06-14T15:02:56.918Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":683,"note":"Consectetur voluptate sit sint possimus veritatis quod.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.942Z","updated_at":"2016-06-14T15:02:56.942Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":684,"note":"Natus libero quibusdam rem assumenda deleniti accusamus sed earum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.966Z","updated_at":"2016-06-14T15:02:56.966Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":685,"note":"Tenetur autem nihil rerum odit.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.989Z","updated_at":"2016-06-14T15:02:56.989Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":686,"note":"Quia maiores et odio sed.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:57.012Z","updated_at":"2016-06-14T15:02:57.012Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":26,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":26,"sha":"0b4bc9a49b562e85de7cc9e834518ea6828729b9","relative_order":0,"message":"Feature added\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:26:01.000+01:00","committed_date":"2014-02-27T09:26:01.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":26,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,5 @@\n+class Feature\n+ def foo\n+ puts 'bar'\n+ end\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":26,"created_at":"2016-06-14T15:02:36.421Z","updated_at":"2016-06-14T15:02:36.474Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"1"},"events":[{"id":222,"target_type":"MergeRequest","target_id":26,"project_id":36,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1},{"id":186,"target_type":"MergeRequest","target_id":26,"project_id":5,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1}],"merge_request_assignees":[]} {"id":15,"target_branch":"test-7","source_branch":"test-1","source_project_id":5,"author_id":22,"assignee_id":16,"title":"Qui accusantium et inventore facilis doloribus occaecati officiis.","created_at":"2016-06-14T15:02:25.168Z","updated_at":"2016-06-14T15:02:59.521Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":7,"description":"Et commodi deserunt aspernatur vero rerum. Ut non dolorum alias in odit est libero. Voluptatibus eos in et vitae repudiandae facilis ex mollitia.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":777,"note":"Pariatur voluptas placeat aspernatur culpa suscipit soluta.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.348Z","updated_at":"2016-06-14T15:02:59.348Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":778,"note":"Alias et iure mollitia suscipit molestiae voluptatum nostrum asperiores.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.372Z","updated_at":"2016-06-14T15:02:59.372Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":779,"note":"Laudantium qui eum qui sunt.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.395Z","updated_at":"2016-06-14T15:02:59.395Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":780,"note":"Quas rem est iusto ut delectus fugiat recusandae mollitia.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.418Z","updated_at":"2016-06-14T15:02:59.418Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":781,"note":"Repellendus ab et qui nesciunt.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.444Z","updated_at":"2016-06-14T15:02:59.444Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":782,"note":"Non possimus voluptatum odio qui ut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.469Z","updated_at":"2016-06-14T15:02:59.469Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":783,"note":"Dolores repellendus eum ducimus quam ab dolorem quia.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.494Z","updated_at":"2016-06-14T15:02:59.494Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":784,"note":"Facilis dolorem aut corrupti id ratione occaecati.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.520Z","updated_at":"2016-06-14T15:02:59.520Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":15,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":15,"relative_order":0,"sha":"94b8d581c48d894b86661718582fecbc5e3ed2eb","message":"fixes #10\n","authored_date":"2016-01-19T13:22:56.000+01:00","committed_date":"2016-01-19T13:22:56.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}}],"merge_request_diff_files":[{"merge_request_diff_id":15,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":15,"created_at":"2016-06-14T15:02:25.171Z","updated_at":"2016-06-14T15:02:25.230Z","base_commit_sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","real_size":"1"},"events":[{"id":223,"target_type":"MergeRequest","target_id":15,"project_id":36,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":1},{"id":175,"target_type":"MergeRequest","target_id":15,"project_id":5,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":22}]} {"id":14,"target_branch":"fix","source_branch":"test-3","source_project_id":5,"author_id":20,"assignee_id":20,"title":"In voluptas aut sequi voluptatem ullam vel corporis illum consequatur.","created_at":"2016-06-14T15:02:24.760Z","updated_at":"2016-06-14T15:02:59.749Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":6,"description":"Dicta magnam non voluptates nam dignissimos nostrum deserunt. Dolorum et suscipit iure quae doloremque. Necessitatibus saepe aut labore sed.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":785,"note":"Atque cupiditate necessitatibus deserunt minus natus odit.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.559Z","updated_at":"2016-06-14T15:02:59.559Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":786,"note":"Non dolorem provident mollitia nesciunt optio ex eveniet.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.587Z","updated_at":"2016-06-14T15:02:59.587Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":787,"note":"Similique officia nemo quasi commodi accusantium quae qui.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.621Z","updated_at":"2016-06-14T15:02:59.621Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":788,"note":"Et est et alias ad dolor qui.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.650Z","updated_at":"2016-06-14T15:02:59.650Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":789,"note":"Numquam temporibus ratione voluptatibus aliquid.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.675Z","updated_at":"2016-06-14T15:02:59.675Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":790,"note":"Ut ex aliquam consectetur perferendis est hic aut quia.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.703Z","updated_at":"2016-06-14T15:02:59.703Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":791,"note":"Esse eos quam quaerat aut ut asperiores officiis.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.726Z","updated_at":"2016-06-14T15:02:59.726Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":792,"note":"Sint facilis accusantium iure blanditiis.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.748Z","updated_at":"2016-06-14T15:02:59.748Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":14,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":14,"relative_order":0,"sha":"ddd4ff416a931589c695eb4f5b23f844426f6928","message":"fixes #10\n","authored_date":"2016-01-19T14:14:43.000+01:00","committed_date":"2016-01-19T14:14:43.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":14,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","committed_date":"2015-12-07T12:52:12.000+01:00","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","committed_date":"2015-12-07T11:54:28.000+01:00","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":14,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","committed_date":"2015-11-13T16:27:12.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","committed_date":"2015-11-13T08:50:17.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","committed_date":"2015-11-13T08:39:43.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","committed_date":"2015-11-13T07:21:40.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","committed_date":"2015-11-13T06:01:27.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","committed_date":"2015-11-13T06:00:16.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","committed_date":"2015-11-13T05:23:14.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","committed_date":"2015-11-13T05:08:45.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","committed_date":"2015-11-13T05:08:04.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","committed_date":"2015-08-25T17:53:12.000+02:00","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}},{"merge_request_diff_id":14,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","committed_date":"2015-01-10T22:23:29.000+01:00","commit_author":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"},"committer":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","committed_date":"2015-01-10T21:28:18.000+01:00","commit_author":{"name":"marmis85","email":"marmis85@gmail.com"},"committer":{"name":"marmis85","email":"marmis85@gmail.com"}},{"merge_request_diff_id":14,"relative_order":15,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":16,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":17,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":18,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":19,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets \n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":14,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":3,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":5,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":8,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":9,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":10,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":11,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":12,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":13,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":14,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":14,"created_at":"2016-06-14T15:02:24.770Z","updated_at":"2016-06-14T15:02:25.007Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"15"},"events":[{"id":224,"target_type":"MergeRequest","target_id":14,"project_id":36,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":1},{"id":174,"target_type":"MergeRequest","target_id":14,"project_id":5,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":20}]} {"id":13,"target_branch":"improve/awesome","source_branch":"test-8","source_project_id":5,"author_id":16,"assignee_id":25,"title":"Voluptates consequatur eius nemo amet libero animi illum delectus tempore.","created_at":"2016-06-14T15:02:24.415Z","updated_at":"2016-06-14T15:02:59.958Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":5,"description":"Est eaque quasi qui qui. Similique voluptatem impedit iusto ratione reprehenderit. Itaque est illum ut nulla aut.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":793,"note":"In illum maxime aperiam nulla est aspernatur.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.782Z","updated_at":"2016-06-14T15:02:59.782Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[{"merge_request_diff_id":14,"id":529,"target_type":"Note","target_id":793,"project_id":4,"created_at":"2016-07-07T14:35:12.128Z","updated_at":"2016-07-07T14:35:12.128Z","action":6,"author_id":1}]},{"id":794,"note":"Enim quia perferendis cum distinctio tenetur optio voluptas veniam.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.807Z","updated_at":"2016-06-14T15:02:59.807Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":795,"note":"Dolor ad quia quis pariatur ducimus.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.831Z","updated_at":"2016-06-14T15:02:59.831Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":796,"note":"Et a odio voluptate aut.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.854Z","updated_at":"2016-06-14T15:02:59.854Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":797,"note":"Quis nihil temporibus voluptatum modi minima a ut.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.879Z","updated_at":"2016-06-14T15:02:59.879Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":798,"note":"Ut alias consequatur in nostrum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.904Z","updated_at":"2016-06-14T15:02:59.904Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":799,"note":"Voluptatibus aperiam assumenda et neque sint libero.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.926Z","updated_at":"2016-06-14T15:02:59.926Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":800,"note":"Veritatis voluptatem dolor dolores magni quo ut ipsa fuga.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.956Z","updated_at":"2016-06-14T15:02:59.956Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":13,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":13,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":13,"relative_order":0,"sha":"0bfedc29d30280c7e8564e19f654584b459e5868","message":"fixes #10\n","authored_date":"2016-01-19T15:25:23.000+01:00","committed_date":"2016-01-19T15:25:23.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":13,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","committed_date":"2015-12-07T12:52:12.000+01:00","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":13,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","committed_date":"2015-12-07T11:54:28.000+01:00","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":13,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","committed_date":"2015-11-13T16:27:12.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","committed_date":"2015-11-13T08:50:17.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","committed_date":"2015-11-13T08:39:43.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","committed_date":"2015-11-13T07:21:40.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","committed_date":"2015-11-13T06:01:27.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":13,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","committed_date":"2015-11-13T06:00:16.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":13,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","committed_date":"2015-11-13T05:23:14.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":13,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","committed_date":"2015-11-13T05:08:45.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":13,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","committed_date":"2015-11-13T05:08:04.000+01:00","commit_author":{"name":"윤민식","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민식","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":13,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","committed_date":"2015-08-25T17:53:12.000+02:00","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}},{"merge_request_diff_id":13,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","committed_date":"2015-01-10T22:23:29.000+01:00","commit_author":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"},"committer":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"}},{"merge_request_diff_id":13,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","committed_date":"2015-01-10T21:28:18.000+01:00","commit_author":{"name":"marmis85","email":"marmis85@gmail.com"},"committer":{"name":"marmis85","email":"marmis85@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":13,"relative_order":0,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":1,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":2,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+\n+\n+ \n+ wm\n+ Created with Sketch.\n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+ \n+\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":3,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":5,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":13,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":13,"created_at":"2016-06-14T15:02:24.420Z","updated_at":"2016-06-14T15:02:24.561Z","base_commit_sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","real_size":"7"},"events":[{"id":225,"target_type":"MergeRequest","target_id":13,"project_id":36,"created_at":"2016-06-14T15:02:24.636Z","updated_at":"2016-06-14T15:02:24.636Z","action":1,"author_id":16},{"id":173,"target_type":"MergeRequest","target_id":13,"project_id":5,"created_at":"2016-06-14T15:02:24.636Z","updated_at":"2016-06-14T15:02:24.636Z","action":1,"author_id":16}]} diff --git a/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js b/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js index d6c5c5f963a..534af2a3033 100644 --- a/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js +++ b/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js @@ -129,7 +129,7 @@ describe('DevopsScore', () => { }); it('displays the correct badge', () => { - const badge = findUsageCol().find(GlBadge); + const badge = findUsageCol().findComponent(GlBadge); expect(badge.exists()).toBe(true); expect(badge.props('variant')).toBe('muted'); diff --git a/spec/frontend/admin/signup_restrictions/components/signup_checkbox_spec.js b/spec/frontend/admin/signup_restrictions/components/signup_checkbox_spec.js index ae9b6f57ee0..eecc21e206b 100644 --- a/spec/frontend/admin/signup_restrictions/components/signup_checkbox_spec.js +++ b/spec/frontend/admin/signup_restrictions/components/signup_checkbox_spec.js @@ -24,7 +24,7 @@ describe('Signup Form', () => { const findByTestId = (id) => wrapper.find(`[data-testid="${id}"]`); const findHiddenInput = () => findByTestId('input'); - const findCheckbox = () => wrapper.find(GlFormCheckbox); + const findCheckbox = () => wrapper.findComponent(GlFormCheckbox); const findCheckboxLabel = () => findByTestId('label'); const findHelpText = () => findByTestId('helpText'); diff --git a/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js b/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js index 31a0c2b07e4..411126d0c89 100644 --- a/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js +++ b/spec/frontend/admin/signup_restrictions/components/signup_form_spec.js @@ -28,7 +28,7 @@ describe('Signup Form', () => { const findForm = () => wrapper.findByTestId('form'); const findInputCsrf = () => findForm().find('[name="authenticity_token"]'); - const findFormSubmitButton = () => findForm().find(GlButton); + const findFormSubmitButton = () => findForm().findComponent(GlButton); const findDenyListRawRadio = () => queryByLabelText('Enter denylist manually'); const findDenyListFileRadio = () => queryByLabelText('Upload denylist file'); @@ -36,7 +36,7 @@ describe('Signup Form', () => { const findDenyListRawInputGroup = () => wrapper.findByTestId('domain-denylist-raw-input-group'); const findDenyListFileInputGroup = () => wrapper.findByTestId('domain-denylist-file-input-group'); const findUserCapInput = () => wrapper.findByTestId('user-cap-input'); - const findModal = () => wrapper.find(GlModal); + const findModal = () => wrapper.findComponent(GlModal); afterEach(() => { wrapper.destroy(); diff --git a/spec/frontend/admin/statistics_panel/components/app_spec.js b/spec/frontend/admin/statistics_panel/components/app_spec.js index bac542e72fb..190f0eb94a0 100644 --- a/spec/frontend/admin/statistics_panel/components/app_spec.js +++ b/spec/frontend/admin/statistics_panel/components/app_spec.js @@ -41,7 +41,7 @@ describe('Admin statistics app', () => { store.dispatch('requestStatistics'); createComponent(); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); }); diff --git a/spec/frontend/admin/users/components/actions/actions_spec.js b/spec/frontend/admin/users/components/actions/actions_spec.js index b758c15a91a..4967753b91c 100644 --- a/spec/frontend/admin/users/components/actions/actions_spec.js +++ b/spec/frontend/admin/users/components/actions/actions_spec.js @@ -12,7 +12,7 @@ import { paths } from '../../mock_data'; describe('Action components', () => { let wrapper; - const findDropdownItem = () => wrapper.find(GlDropdownItem); + const findDropdownItem = () => wrapper.findComponent(GlDropdownItem); const initComponent = ({ component, props } = {}) => { wrapper = shallowMount(component, { diff --git a/spec/frontend/admin/users/components/app_spec.js b/spec/frontend/admin/users/components/app_spec.js index 65b13e3a40d..913732aae42 100644 --- a/spec/frontend/admin/users/components/app_spec.js +++ b/spec/frontend/admin/users/components/app_spec.js @@ -28,7 +28,7 @@ describe('AdminUsersApp component', () => { }); it('renders the admin users table with props', () => { - expect(wrapper.find(AdminUsersTable).props()).toEqual({ + expect(wrapper.findComponent(AdminUsersTable).props()).toEqual({ users, paths, }); diff --git a/spec/frontend/admin/users/components/modals/delete_user_modal_spec.js b/spec/frontend/admin/users/components/modals/delete_user_modal_spec.js index 09a345ac826..a5007e18f5f 100644 --- a/spec/frontend/admin/users/components/modals/delete_user_modal_spec.js +++ b/spec/frontend/admin/users/components/modals/delete_user_modal_spec.js @@ -17,7 +17,7 @@ describe('Delete user modal', () => { const findButton = (variant, category) => wrapper - .findAll(GlButton) + .findAllComponents(GlButton) .filter((w) => w.attributes('variant') === variant && w.attributes('category') === category) .at(0); const findForm = () => wrapper.find('form'); diff --git a/spec/frontend/admin/users/components/user_actions_spec.js b/spec/frontend/admin/users/components/user_actions_spec.js index e04c43ae3f2..ffc05e744c8 100644 --- a/spec/frontend/admin/users/components/user_actions_spec.js +++ b/spec/frontend/admin/users/components/user_actions_spec.js @@ -83,7 +83,7 @@ describe('AdminUserActions component', () => { }); it.each(CONFIRMATION_ACTIONS)('renders an action component item for "%s"', (action) => { - const component = wrapper.find(Actions[capitalizeFirstCharacter(action)]); + const component = wrapper.findComponent(Actions[capitalizeFirstCharacter(action)]); expect(component.props('username')).toBe(user.name); expect(component.props('path')).toBe(userPaths[action]); @@ -119,7 +119,7 @@ describe('AdminUserActions component', () => { }); it.each(DELETE_ACTIONS)('renders a delete action component item for "%s"', (action) => { - const component = wrapper.find(Actions[capitalizeFirstCharacter(action)]); + const component = wrapper.findComponent(Actions[capitalizeFirstCharacter(action)]); expect(component.props('username')).toBe(user.name); expect(component.props('paths')).toEqual(userPaths); diff --git a/spec/frontend/admin/users/components/user_avatar_spec.js b/spec/frontend/admin/users/components/user_avatar_spec.js index 8bbfb89bec1..94fac875fbe 100644 --- a/spec/frontend/admin/users/components/user_avatar_spec.js +++ b/spec/frontend/admin/users/components/user_avatar_spec.js @@ -12,10 +12,10 @@ describe('AdminUserAvatar component', () => { const user = users[0]; const adminUserPath = paths.adminUser; - const findNote = () => wrapper.find(GlIcon); - const findAvatar = () => wrapper.find(GlAvatarLabeled); + const findNote = () => wrapper.findComponent(GlIcon); + const findAvatar = () => wrapper.findComponent(GlAvatarLabeled); const findUserLink = () => wrapper.find('.js-user-link'); - const findAllBadges = () => wrapper.findAll(GlBadge); + const findAllBadges = () => wrapper.findAllComponents(GlBadge); const findTooltip = () => getBinding(findNote().element, 'gl-tooltip'); const initComponent = (props = {}) => { diff --git a/spec/frontend/admin/users/components/users_table_spec.js b/spec/frontend/admin/users/components/users_table_spec.js index ad1c45495b5..fe07f0fce00 100644 --- a/spec/frontend/admin/users/components/users_table_spec.js +++ b/spec/frontend/admin/users/components/users_table_spec.js @@ -30,10 +30,10 @@ describe('AdminUsersTable component', () => { const fetchGroupCountsResponse = createFetchGroupCount([{ id: user.id, groupCount: 5 }]); const findUserGroupCount = (id) => wrapper.findByTestId(`user-group-count-${id}`); - const findUserGroupCountLoader = (id) => findUserGroupCount(id).find(GlSkeletonLoader); + const findUserGroupCountLoader = (id) => findUserGroupCount(id).findComponent(GlSkeletonLoader); const getCellByLabel = (trIdx, label) => { return wrapper - .find(GlTable) + .findComponent(GlTable) .find('tbody') .findAll('tr') .at(trIdx) @@ -72,7 +72,7 @@ describe('AdminUsersTable component', () => { }); it('renders the user actions', () => { - expect(wrapper.find(AdminUserActions).exists()).toBe(true); + expect(wrapper.findComponent(AdminUserActions).exists()).toBe(true); }); it.each` @@ -81,7 +81,7 @@ describe('AdminUsersTable component', () => { ${AdminUserDate} | ${'Created on'} ${AdminUserDate} | ${'Last activity'} `('renders the component for column $label', ({ component, label }) => { - expect(getCellByLabel(0, label).find(component).exists()).toBe(true); + expect(getCellByLabel(0, label).findComponent(component).exists()).toBe(true); }); }); diff --git a/spec/frontend/admin/users/index_spec.js b/spec/frontend/admin/users/index_spec.js index 961fa96acdd..b51858d5129 100644 --- a/spec/frontend/admin/users/index_spec.js +++ b/spec/frontend/admin/users/index_spec.js @@ -8,7 +8,7 @@ describe('initAdminUsersApp', () => { let wrapper; let el; - const findApp = () => wrapper.find(AdminUsersApp); + const findApp = () => wrapper.findComponent(AdminUsersApp); beforeEach(() => { el = document.createElement('div'); @@ -36,7 +36,7 @@ describe('initAdminUserActions', () => { let wrapper; let el; - const findUserActions = () => wrapper.find(UserActions); + const findUserActions = () => wrapper.findComponent(UserActions); beforeEach(() => { el = document.createElement('div'); diff --git a/spec/frontend/pipelines/graph/linked_pipeline_spec.js b/spec/frontend/pipelines/graph/linked_pipeline_spec.js index cdeaa0db61d..7d1e4774a24 100644 --- a/spec/frontend/pipelines/graph/linked_pipeline_spec.js +++ b/spec/frontend/pipelines/graph/linked_pipeline_spec.js @@ -426,7 +426,7 @@ describe('Linked pipeline', () => { jest.spyOn(wrapper.vm, '$emit'); findButton().trigger('click'); - expect(wrapper.emitted().pipelineClicked).toBeTruthy(); + expect(wrapper.emitted().pipelineClicked).toHaveLength(1); }); it(`should emit ${BV_HIDE_TOOLTIP} to close the tooltip`, () => { diff --git a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js index 263204915ba..531c60c9c3e 100644 --- a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js +++ b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js @@ -84,7 +84,7 @@ describe('AdminRunnersApp', () => { const findRunnerList = () => wrapper.findComponent(RunnerList); const findRunnerListEmptyState = () => wrapper.findComponent(RunnerListEmptyState); const findRunnerPagination = () => extendedWrapper(wrapper.findComponent(RunnerPagination)); - const findRunnerPaginationNext = () => findRunnerPagination().findByLabelText('Go to next page'); + const findRunnerPaginationNext = () => findRunnerPagination().findByText(s__('Pagination|Next')); const findRunnerFilteredSearchBar = () => wrapper.findComponent(RunnerFilteredSearchBar); const createComponent = ({ @@ -279,7 +279,7 @@ describe('AdminRunnersApp', () => { { type: PARAM_KEY_PAUSED, value: { data: 'true', operator: '=' } }, ], sort: 'CREATED_DESC', - pagination: { page: 1 }, + pagination: {}, }); }); @@ -340,6 +340,7 @@ describe('AdminRunnersApp', () => { it('when runners have not loaded, shows a loading state', () => { createComponent(); expect(findRunnerList().props('loading')).toBe(true); + expect(findRunnerPagination().attributes('disabled')).toBe('true'); }); describe('when bulk delete is enabled', () => { @@ -434,19 +435,25 @@ describe('AdminRunnersApp', () => { }); describe('Pagination', () => { + const { pageInfo } = allRunnersDataPaginated.data.runners; + beforeEach(async () => { mockRunnersHandler.mockResolvedValue(allRunnersDataPaginated); await createComponent({ mountFn: mountExtended }); }); + it('passes the page info', () => { + expect(findRunnerPagination().props('pageInfo')).toEqualGraphqlFixture(pageInfo); + }); + it('navigates to the next page', async () => { await findRunnerPaginationNext().trigger('click'); expect(mockRunnersHandler).toHaveBeenLastCalledWith({ sort: CREATED_DESC, first: RUNNER_PAGE_SIZE, - after: allRunnersDataPaginated.data.runners.pageInfo.endCursor, + after: pageInfo.endCursor, }); }); }); diff --git a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js b/spec/frontend/runner/components/runner_filtered_search_bar_spec.js index 83fb1764c6d..e35bec3aa38 100644 --- a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js +++ b/spec/frontend/runner/components/runner_filtered_search_bar_spec.js @@ -143,7 +143,7 @@ describe('RunnerList', () => { runnerType: INSTANCE_TYPE, filters: mockFilters, sort: mockOtherSort, - pagination: { page: 1 }, + pagination: {}, }); }); }); @@ -156,7 +156,7 @@ describe('RunnerList', () => { runnerType: null, filters: mockFilters, sort: mockDefaultSort, - pagination: { page: 1 }, + pagination: {}, }); }); @@ -167,7 +167,7 @@ describe('RunnerList', () => { runnerType: null, filters: [], sort: mockOtherSort, - pagination: { page: 1 }, + pagination: {}, }); }); }); diff --git a/spec/frontend/runner/components/runner_pagination_spec.js b/spec/frontend/runner/components/runner_pagination_spec.js index e144b52ceb3..499cc59250d 100644 --- a/spec/frontend/runner/components/runner_pagination_spec.js +++ b/spec/frontend/runner/components/runner_pagination_spec.js @@ -1,5 +1,5 @@ -import { GlPagination } from '@gitlab/ui'; -import { mount } from '@vue/test-utils'; +import { GlKeysetPagination } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; import RunnerPagination from '~/runner/components/runner_pagination.vue'; const mockStartCursor = 'START_CURSOR'; @@ -8,21 +8,11 @@ const mockEndCursor = 'END_CURSOR'; describe('RunnerPagination', () => { let wrapper; - const findPagination = () => wrapper.findComponent(GlPagination); + const findPagination = () => wrapper.findComponent(GlKeysetPagination); - const createComponent = ({ page = 1, hasPreviousPage = false, hasNextPage = true } = {}) => { - wrapper = mount(RunnerPagination, { - propsData: { - value: { - page, - }, - pageInfo: { - hasPreviousPage, - hasNextPage, - startCursor: mockStartCursor, - endCursor: mockEndCursor, - }, - }, + const createComponent = (propsData = {}) => { + wrapper = shallowMount(RunnerPagination, { + propsData, }); }; @@ -30,114 +20,96 @@ describe('RunnerPagination', () => { wrapper.destroy(); }); - describe('When on the first page', () => { - beforeEach(() => { - createComponent({ - page: 1, - hasPreviousPage: false, - hasNextPage: true, - }); - }); - - it('Contains the current page information', () => { - expect(findPagination().props('value')).toBe(1); - expect(findPagination().props('prevPage')).toBe(null); - expect(findPagination().props('nextPage')).toBe(2); - }); - - it('Goes to the second page', () => { - findPagination().vm.$emit('input', 2); - - expect(wrapper.emitted('input')[0]).toEqual([ - { - after: mockEndCursor, - page: 2, - }, - ]); - }); - }); - describe('When in between pages', () => { + const mockPageInfo = { + startCursor: mockStartCursor, + endCursor: mockEndCursor, + hasPreviousPage: true, + hasNextPage: true, + }; + beforeEach(() => { createComponent({ - page: 2, - hasPreviousPage: true, - hasNextPage: true, + pageInfo: mockPageInfo, }); }); it('Contains the current page information', () => { - expect(findPagination().props('value')).toBe(2); - expect(findPagination().props('prevPage')).toBe(1); - expect(findPagination().props('nextPage')).toBe(3); + expect(findPagination().props()).toMatchObject(mockPageInfo); }); - it('Shows the next and previous pages', () => { - const links = findPagination().findAll('a'); + it('Goes to the prev page', () => { + findPagination().vm.$emit('prev'); - expect(links).toHaveLength(2); - expect(links.at(0).text()).toBe('Previous'); - expect(links.at(1).text()).toBe('Next'); + expect(wrapper.emitted('input')[0]).toEqual([ + { + before: mockStartCursor, + }, + ]); }); - it('Goes to the last page', () => { - findPagination().vm.$emit('input', 3); + it('Goes to the next page', () => { + findPagination().vm.$emit('next'); expect(wrapper.emitted('input')[0]).toEqual([ { after: mockEndCursor, - page: 3, - }, - ]); - }); - - it('Goes to the first page', () => { - findPagination().vm.$emit('input', 1); - - expect(wrapper.emitted('input')[0]).toEqual([ - { - page: 1, }, ]); }); }); - describe('When in the last page', () => { + describe.each` + page | hasPreviousPage | hasNextPage + ${'first'} | ${false} | ${true} + ${'last'} | ${true} | ${false} + `('When on the $page page', ({ page, hasPreviousPage, hasNextPage }) => { + const mockPageInfo = { + startCursor: mockStartCursor, + endCursor: mockEndCursor, + hasPreviousPage, + hasNextPage, + }; + beforeEach(() => { createComponent({ - page: 3, - hasPreviousPage: true, - hasNextPage: false, + pageInfo: mockPageInfo, }); }); - it('Contains the current page', () => { - expect(findPagination().props('value')).toBe(3); - expect(findPagination().props('prevPage')).toBe(2); - expect(findPagination().props('nextPage')).toBe(null); + it(`Contains the ${page} page information`, () => { + expect(findPagination().props()).toMatchObject(mockPageInfo); }); }); - describe('When only one page', () => { + describe('When no other pages', () => { beforeEach(() => { createComponent({ - page: 1, - hasPreviousPage: false, - hasNextPage: false, + pageInfo: { + hasPreviousPage: false, + hasNextPage: false, + }, }); }); - it('does not display pagination', () => { - expect(wrapper.html()).toBe(''); + it('is not shown', () => { + expect(findPagination().exists()).toBe(false); + }); + }); + + describe('When adding more attributes', () => { + beforeEach(() => { + createComponent({ + pageInfo: { + hasPreviousPage: true, + hasNextPage: false, + }, + disabled: true, + }); }); - it('Contains the current page', () => { - expect(findPagination().props('value')).toBe(1); - }); - - it('Shows no more page buttons', () => { - expect(findPagination().props('prevPage')).toBe(null); - expect(findPagination().props('nextPage')).toBe(null); + it('attributes are passed', () => { + expect(findPagination().props('disabled')).toBe(true); }); }); }); diff --git a/spec/frontend/runner/group_runners/group_runners_app_spec.js b/spec/frontend/runner/group_runners/group_runners_app_spec.js index 9c85e5de96c..a288d088b5b 100644 --- a/spec/frontend/runner/group_runners/group_runners_app_spec.js +++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js @@ -82,7 +82,7 @@ describe('GroupRunnersApp', () => { const findRunnerListEmptyState = () => wrapper.findComponent(RunnerListEmptyState); const findRunnerRow = (id) => extendedWrapper(wrapper.findByTestId(`runner-row-${id}`)); const findRunnerPagination = () => extendedWrapper(wrapper.findComponent(RunnerPagination)); - const findRunnerPaginationNext = () => findRunnerPagination().findByLabelText('Go to next page'); + const findRunnerPaginationNext = () => findRunnerPagination().findByText(s__('Pagination|Next')); const findRunnerFilteredSearchBar = () => wrapper.findComponent(RunnerFilteredSearchBar); const createComponent = ({ props = {}, mountFn = shallowMountExtended, ...options } = {}) => { @@ -263,7 +263,7 @@ describe('GroupRunnersApp', () => { runnerType: INSTANCE_TYPE, filters: [{ type: 'status', value: { data: STATUS_ONLINE, operator: '=' } }], sort: 'CREATED_DESC', - pagination: { page: 1 }, + pagination: {}, }); }); @@ -326,6 +326,7 @@ describe('GroupRunnersApp', () => { it('when runners have not loaded, shows a loading state', () => { createComponent(); expect(findRunnerList().props('loading')).toBe(true); + expect(findRunnerPagination().attributes('disabled')).toBe('true'); }); describe('when no runners are found', () => { @@ -372,12 +373,18 @@ describe('GroupRunnersApp', () => { }); describe('Pagination', () => { + const { pageInfo } = groupRunnersDataPaginated.data.group.runners; + beforeEach(async () => { mockGroupRunnersHandler.mockResolvedValue(groupRunnersDataPaginated); await createComponent({ mountFn: mountExtended }); }); + it('passes the page info', () => { + expect(findRunnerPagination().props('pageInfo')).toEqualGraphqlFixture(pageInfo); + }); + it('navigates to the next page', async () => { await findRunnerPaginationNext().trigger('click'); @@ -385,7 +392,7 @@ describe('GroupRunnersApp', () => { groupFullPath: mockGroupFullPath, sort: CREATED_DESC, first: RUNNER_PAGE_SIZE, - after: groupRunnersDataPaginated.data.group.runners.pageInfo.endCursor, + after: pageInfo.endCursor, }); }); }); diff --git a/spec/frontend/runner/mock_data.js b/spec/frontend/runner/mock_data.js index e73d888bd9f..555ec40184f 100644 --- a/spec/frontend/runner/mock_data.js +++ b/spec/frontend/runner/mock_data.js @@ -34,7 +34,7 @@ export const mockSearchExamples = [ { name: 'a default query', urlQuery: '', - search: { runnerType: null, filters: [], pagination: { page: 1 }, sort: 'CREATED_DESC' }, + search: { runnerType: null, filters: [], pagination: {}, sort: 'CREATED_DESC' }, graphqlVariables: { sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, isDefault: true, }, @@ -44,7 +44,7 @@ export const mockSearchExamples = [ search: { runnerType: null, filters: [{ type: 'status', value: { data: 'ACTIVE', operator: '=' } }], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_DESC', }, graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, @@ -60,7 +60,7 @@ export const mockSearchExamples = [ value: { data: 'something' }, }, ], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_DESC', }, graphqlVariables: { search: 'something', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, @@ -80,7 +80,7 @@ export const mockSearchExamples = [ value: { data: 'else' }, }, ], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_DESC', }, graphqlVariables: { search: 'something else', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, @@ -91,7 +91,7 @@ export const mockSearchExamples = [ search: { runnerType: 'INSTANCE_TYPE', filters: [], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_DESC', }, graphqlVariables: { type: 'INSTANCE_TYPE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, @@ -105,7 +105,7 @@ export const mockSearchExamples = [ { type: 'status', value: { data: 'ACTIVE', operator: '=' } }, { type: 'status', value: { data: 'PAUSED', operator: '=' } }, ], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_DESC', }, graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, @@ -116,7 +116,7 @@ export const mockSearchExamples = [ search: { runnerType: 'INSTANCE_TYPE', filters: [{ type: 'status', value: { data: 'ACTIVE', operator: '=' } }], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_ASC', }, graphqlVariables: { @@ -132,7 +132,7 @@ export const mockSearchExamples = [ search: { runnerType: null, filters: [{ type: 'tag', value: { data: 'tag-1', operator: '=' } }], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_DESC', }, graphqlVariables: { @@ -150,7 +150,7 @@ export const mockSearchExamples = [ { type: 'tag', value: { data: 'tag-1', operator: '=' } }, { type: 'tag', value: { data: 'tag-2', operator: '=' } }, ], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_DESC', }, graphqlVariables: { @@ -161,22 +161,22 @@ export const mockSearchExamples = [ }, { name: 'the next page', - urlQuery: '?page=2&after=AFTER_CURSOR', + urlQuery: '?after=AFTER_CURSOR', search: { runnerType: null, filters: [], - pagination: { page: 2, after: 'AFTER_CURSOR' }, + pagination: { after: 'AFTER_CURSOR' }, sort: 'CREATED_DESC', }, graphqlVariables: { sort: 'CREATED_DESC', after: 'AFTER_CURSOR', first: RUNNER_PAGE_SIZE }, }, { name: 'the previous page', - urlQuery: '?page=2&before=BEFORE_CURSOR', + urlQuery: '?before=BEFORE_CURSOR', search: { runnerType: null, filters: [], - pagination: { page: 2, before: 'BEFORE_CURSOR' }, + pagination: { before: 'BEFORE_CURSOR' }, sort: 'CREATED_DESC', }, graphqlVariables: { sort: 'CREATED_DESC', before: 'BEFORE_CURSOR', last: RUNNER_PAGE_SIZE }, @@ -184,7 +184,7 @@ export const mockSearchExamples = [ { name: 'the next page filtered by a status, an instance type, tags and a non default sort', urlQuery: - '?status[]=ACTIVE&runner_type[]=INSTANCE_TYPE&tag[]=tag-1&tag[]=tag-2&sort=CREATED_ASC&page=2&after=AFTER_CURSOR', + '?status[]=ACTIVE&runner_type[]=INSTANCE_TYPE&tag[]=tag-1&tag[]=tag-2&sort=CREATED_ASC&after=AFTER_CURSOR', search: { runnerType: 'INSTANCE_TYPE', filters: [ @@ -192,7 +192,7 @@ export const mockSearchExamples = [ { type: 'tag', value: { data: 'tag-1', operator: '=' } }, { type: 'tag', value: { data: 'tag-2', operator: '=' } }, ], - pagination: { page: 2, after: 'AFTER_CURSOR' }, + pagination: { after: 'AFTER_CURSOR' }, sort: 'CREATED_ASC', }, graphqlVariables: { @@ -210,7 +210,7 @@ export const mockSearchExamples = [ search: { runnerType: null, filters: [{ type: 'paused', value: { data: 'true', operator: '=' } }], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_DESC', }, graphqlVariables: { paused: true, sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, @@ -221,7 +221,7 @@ export const mockSearchExamples = [ search: { runnerType: null, filters: [{ type: 'paused', value: { data: 'false', operator: '=' } }], - pagination: { page: 1 }, + pagination: {}, sort: 'CREATED_DESC', }, graphqlVariables: { paused: false, sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE }, diff --git a/spec/frontend/runner/runner_search_utils_spec.js b/spec/frontend/runner/runner_search_utils_spec.js index 6f954143ab1..e1f90482b34 100644 --- a/spec/frontend/runner/runner_search_utils_spec.js +++ b/spec/frontend/runner/runner_search_utils_spec.js @@ -24,11 +24,14 @@ describe('search_params.js', () => { }); it.each` - query | updatedQuery - ${'status[]=ACTIVE'} | ${'paused[]=false'} - ${'status[]=ACTIVE&a=b'} | ${'a=b&paused[]=false'} - ${'status[]=ACTIVE'} | ${'paused[]=false'} - ${'status[]=PAUSED'} | ${'paused[]=true'} + query | updatedQuery + ${'status[]=ACTIVE'} | ${'paused[]=false'} + ${'status[]=ACTIVE&a=b'} | ${'a=b&paused[]=false'} + ${'status[]=ACTIVE'} | ${'paused[]=false'} + ${'status[]=PAUSED'} | ${'paused[]=true'} + ${'page=2&after=AFTER'} | ${'after=AFTER'} + ${'page=2&before=BEFORE'} | ${'before=BEFORE'} + ${'status[]=PAUSED&page=2&after=AFTER'} | ${'after=AFTER&paused[]=true'} `('updates "$query" to "$updatedQuery"', ({ query, updatedQuery }) => { const mockUrl = 'http://test.host/admin/runners?'; @@ -49,24 +52,6 @@ describe('search_params.js', () => { { type: 'filtered-search-term', value: { data: 'text' } }, ]); }); - - it('When a page cannot be parsed as a number, it defaults to `1`', () => { - expect(fromUrlQueryToSearch('?page=NONSENSE&after=AFTER_CURSOR').pagination).toEqual({ - page: 1, - }); - }); - - it('When a page is less than 1, it defaults to `1`', () => { - expect(fromUrlQueryToSearch('?page=0&after=AFTER_CURSOR').pagination).toEqual({ - page: 1, - }); - }); - - it('When a page with no cursor is given, it defaults to `1`', () => { - expect(fromUrlQueryToSearch('?page=2').pagination).toEqual({ - page: 1, - }); - }); }); describe('fromSearchToUrl', () => { @@ -143,8 +128,11 @@ describe('search_params.js', () => { }); }); - it('given a missing pagination, evaluates as not filtered', () => { - expect(isSearchFiltered({ pagination: null })).toBe(false); - }); + it.each([null, undefined, {}])( + 'given a missing pagination, evaluates as not filtered', + (mockPagination) => { + expect(isSearchFiltered({ pagination: mockPagination })).toBe(false); + }, + ); }); }); diff --git a/spec/lib/gitlab/background_migration/backfill_project_import_level_spec.rb b/spec/lib/gitlab/background_migration/backfill_project_import_level_spec.rb new file mode 100644 index 00000000000..ae296483166 --- /dev/null +++ b/spec/lib/gitlab/background_migration/backfill_project_import_level_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +# rubocop:disable Layout/HashAlignment +RSpec.describe Gitlab::BackgroundMigration::BackfillProjectImportLevel do + let(:migration) do + described_class.new( + start_id: table(:namespaces).minimum(:id), + end_id: table(:namespaces).maximum(:id), + batch_table: :namespaces, + batch_column: :id, + sub_batch_size: 2, + pause_ms: 0, + connection: ApplicationRecord.connection + ) + end + # rubocop:enable Layout/HashAlignment + + let(:namespaces_table) { table(:namespaces) } + let(:namespace_settings_table) { table(:namespace_settings) } + + let!(:user_namespace) do + namespaces_table.create!( + name: 'user_namespace', + path: 'user_namespace', + type: 'User', + project_creation_level: 100 + ) + end + + let!(:group_namespace_nil) do + namespaces_table.create!( + name: 'group_namespace_nil', + path: 'group_namespace_nil', + type: 'Group', + project_creation_level: nil + ) + end + + let!(:group_namespace_0) do + namespaces_table.create!( + name: 'group_namespace_0', + path: 'group_namespace_0', + type: 'Group', + project_creation_level: 0 + ) + end + + let!(:group_namespace_1) do + namespaces_table.create!( + name: 'group_namespace_1', + path: 'group_namespace_1', + type: 'Group', + project_creation_level: 1 + ) + end + + let!(:group_namespace_2) do + namespaces_table.create!( + name: 'group_namespace_2', + path: 'group_namespace_2', + type: 'Group', + project_creation_level: 2 + ) + end + + let!(:group_namespace_9999) do + namespaces_table.create!( + name: 'group_namespace_9999', + path: 'group_namespace_9999', + type: 'Group', + project_creation_level: 9999 + ) + end + + subject(:perform_migration) { migration.perform } + + before do + namespace_settings_table.create!(namespace_id: user_namespace.id) + namespace_settings_table.create!(namespace_id: group_namespace_nil.id) + namespace_settings_table.create!(namespace_id: group_namespace_0.id) + namespace_settings_table.create!(namespace_id: group_namespace_1.id) + namespace_settings_table.create!(namespace_id: group_namespace_2.id) + namespace_settings_table.create!(namespace_id: group_namespace_9999.id) + end + + describe 'Groups' do + using RSpec::Parameterized::TableSyntax + + where(:namespace_id, :prev_level, :new_level) do + lazy { group_namespace_0.id } | ::Gitlab::Access::OWNER | ::Gitlab::Access::NO_ACCESS + lazy { group_namespace_1.id } | ::Gitlab::Access::OWNER | ::Gitlab::Access::MAINTAINER + lazy { group_namespace_2.id } | ::Gitlab::Access::OWNER | ::Gitlab::Access::DEVELOPER + end + + with_them do + it 'backfills the correct project_import_level of Group namespaces' do + expect { perform_migration } + .to change { namespace_settings_table.find_by(namespace_id: namespace_id).project_import_level } + .from(prev_level).to(new_level) + end + end + + it 'does not update `User` namespaces or values outside range' do + expect { perform_migration } + .not_to change { namespace_settings_table.find_by(namespace_id: user_namespace.id).project_import_level } + + expect { perform_migration } + .not_to change { namespace_settings_table.find_by(namespace_id: group_namespace_9999.id).project_import_level } + end + + it 'maintains default import_level if creation_level is nil' do + project_import_level = namespace_settings_table.find_by(namespace_id: group_namespace_nil.id).project_import_level + + expect { perform_migration } + .not_to change { project_import_level } + + expect(project_import_level).to eq(::Gitlab::Access::OWNER) + end + end +end diff --git a/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb b/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb new file mode 100644 index 00000000000..2c236ba3726 --- /dev/null +++ b/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Build::Artifacts::Adapters::ZipStream do + let(:file_name) { 'single_file.zip' } + let(:fixture_path) { "lib/gitlab/ci/build/artifacts/adapters/zip_stream/#{file_name}" } + let(:stream) { File.open(expand_fixture_path(fixture_path), 'rb') } + + describe '#initialize' do + it 'initializes when stream is passed' do + expect { described_class.new(stream) }.not_to raise_error + end + + context 'when stream is not passed' do + let(:stream) { nil } + + it 'raises an error' do + expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError) + end + end + end + + describe '#each_blob' do + let(:adapter) { described_class.new(stream) } + + context 'when stream is a zip file' do + it 'iterates file content when zip file contains one file' do + expect { |b| adapter.each_blob(&b) } + .to yield_with_args("file 1 content\n") + end + + context 'when zip file contains multiple files' do + let(:file_name) { 'multiple_files.zip' } + + it 'iterates content of all files' do + expect { |b| adapter.each_blob(&b) } + .to yield_successive_args("file 1 content\n", "file 2 content\n") + end + end + + context 'when zip file includes files in a directory' do + let(:file_name) { 'with_directory.zip' } + + it 'iterates contents from files only' do + expect { |b| adapter.each_blob(&b) } + .to yield_successive_args("file 1 content\n", "file 2 content\n") + end + end + + context 'when zip contains a file which decompresses beyond the size limit' do + let(:file_name) { '200_mb_decompressed.zip' } + + it 'does not read the file' do + expect { |b| adapter.each_blob(&b) }.not_to yield_control + end + end + + context 'when the zip contains too many files' do + let(:file_name) { '100_files.zip' } + + it 'stops processing when the limit is reached' do + expect { |b| adapter.each_blob(&b) } + .to yield_control.exactly(described_class::MAX_FILES_PROCESSED).times + end + end + + context 'when stream is a zipbomb' do + let(:file_name) { 'zipbomb.zip' } + + it 'does not read the file' do + expect { |b| adapter.each_blob(&b) }.not_to yield_control + end + end + end + + context 'when stream is not a zip file' do + let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') } + + it 'does not yield any data' do + expect { |b| adapter.each_blob(&b) }.not_to yield_control + expect { adapter.each_blob { |b| b } }.not_to raise_error + end + end + end +end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 4ffde842819..0d03f30a6d2 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -653,6 +653,7 @@ search_data: merge_request_assignees: - merge_request - assignee +- updated_state_by lfs_file_locks: - user project_badges: diff --git a/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb index 7c84b9604a6..9f1b15aa049 100644 --- a/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb +++ b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb @@ -58,8 +58,8 @@ RSpec.describe Gitlab::ImportExport::Base::RelationObjectSaver do end context 'when subrelation collection count is small' do - let(:notes) { build_list(:note, 2, project: project, importing: true) } - let(:relation_object) { build(:issue, project: project, notes: notes) } + let(:note) { build(:note, project: project, importing: true) } + let(:relation_object) { build(:issue, project: project, notes: [note]) } let(:relation_definition) { { 'notes' => {} } } it 'saves subrelation as part of the relation object itself' do @@ -68,7 +68,7 @@ RSpec.describe Gitlab::ImportExport::Base::RelationObjectSaver do saver.execute issue = project.issues.last - expect(issue.notes.count).to eq(2) + expect(issue.notes.count).to eq(1) end end diff --git a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb index a5e8a9cb58a..723f7e441f9 100644 --- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb @@ -254,6 +254,11 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do end end + it 'has multiple merge request assignees' do + expect(MergeRequest.find_by(title: 'MR1').assignees).to contain_exactly(@user, *@existing_members) + expect(MergeRequest.find_by(title: 'MR2').assignees).to be_empty + end + it 'has labels associated to label links, associated to issues' do expect(Label.first.label_links.first.target).not_to be_nil end diff --git a/spec/lib/gitlab/import_export/project/tree_saver_spec.rb b/spec/lib/gitlab/import_export/project/tree_saver_spec.rb index a248a04a5a0..edf645c403d 100644 --- a/spec/lib/gitlab/import_export/project/tree_saver_spec.rb +++ b/spec/lib/gitlab/import_export/project/tree_saver_spec.rb @@ -103,6 +103,13 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do it 'has merge request resource label events' do expect(subject.first['resource_label_events']).not_to be_empty end + + it 'has merge request assignees' do + reviewer = subject.first['merge_request_assignees'].first + + expect(reviewer).not_to be_nil + expect(reviewer['user_id']).to eq(user.id) + end end context 'with snippets' do @@ -468,7 +475,7 @@ RSpec.describe Gitlab::ImportExport::Project::TreeSaver do create(:label_link, label: group_label, target: issue) create(:label_priority, label: group_label, priority: 1) milestone = create(:milestone, project: project) - merge_request = create(:merge_request, source_project: project, milestone: milestone) + merge_request = create(:merge_request, source_project: project, milestone: milestone, assignees: [user]) ci_build = create(:ci_build, project: project, when: nil) ci_build.pipeline.update!(project: project) diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 3ebed1096e3..1078d4587f7 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -741,6 +741,8 @@ MergeRequestAssignee: - id - user_id - merge_request_id +- created_at +- state ProjectMetricsSetting: - project_id - external_dashboard_url diff --git a/spec/migrations/backfill_project_import_level_spec.rb b/spec/migrations/backfill_project_import_level_spec.rb new file mode 100644 index 00000000000..c24ddac0730 --- /dev/null +++ b/spec/migrations/backfill_project_import_level_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe BackfillProjectImportLevel do + let_it_be(:batched_migration) { described_class::MIGRATION } + + describe '#up' do + it 'schedules background jobs for each batch of namespaces' do + migrate! + + expect(batched_migration).to have_scheduled_batched_migration( + table_name: :namespaces, + column_name: :id, + interval: described_class::INTERVAL + ) + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(batched_migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index 7e71f53b015..40c2d62c465 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -25,6 +25,8 @@ RSpec.describe Ci::Bridge do expect(bridge).to have_many(:sourced_pipelines) end + it_behaves_like 'has ID tokens', :ci_bridge + it 'has one downstream pipeline' do expect(bridge).to have_one(:sourced_pipeline) expect(bridge).to have_one(:downstream_pipeline) diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb index 5e30f9160cd..e904463a5ca 100644 --- a/spec/models/ci/build_metadata_spec.rb +++ b/spec/models/ci/build_metadata_spec.rb @@ -105,6 +105,13 @@ RSpec.describe Ci::BuildMetadata do } } } + metadata.id_tokens = { + TEST_JWT_TOKEN: { + id_token: { + aud: 'https://gitlab.test' + } + } + } expect(metadata).to be_valid end @@ -113,10 +120,14 @@ RSpec.describe Ci::BuildMetadata do context 'when data is invalid' do it 'returns errors' do metadata.secrets = { DATABASE_PASSWORD: { vault: {} } } + metadata.id_tokens = { TEST_JWT_TOKEN: { id_token: { aud: nil } } } aggregate_failures do expect(metadata).to be_invalid - expect(metadata.errors.full_messages).to eq(["Secrets must be a valid json schema"]) + expect(metadata.errors.full_messages).to contain_exactly( + 'Secrets must be a valid json schema', + 'Id tokens must be a valid json schema' + ) end end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 546cfd80fcf..c8a399ecff1 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -82,6 +82,8 @@ RSpec.describe Ci::Build do end end + it_behaves_like 'has ID tokens', :ci_build + describe '.manual_actions' do let!(:manual_but_created) { create(:ci_build, :manual, status: :created, pipeline: pipeline) } let!(:manual_but_succeeded) { create(:ci_build, :manual, status: :success, pipeline: pipeline) } diff --git a/spec/models/concerns/ci/artifactable_spec.rb b/spec/models/concerns/ci/artifactable_spec.rb index 6af244a5a0f..64691165e21 100644 --- a/spec/models/concerns/ci/artifactable_spec.rb +++ b/spec/models/concerns/ci/artifactable_spec.rb @@ -46,8 +46,30 @@ RSpec.describe Ci::Artifactable do end end + context 'when file format is zip' do + context 'when artifact contains one file' do + let(:artifact) { build(:ci_job_artifact, :zip_with_single_file) } + + it 'iterates blob once' do + expect { |b| artifact.each_blob(&b) }.to yield_control.once + end + end + + context 'when artifact contains two files' do + let(:artifact) { build(:ci_job_artifact, :zip_with_multiple_files) } + + it 'iterates blob two times' do + expect { |b| artifact.each_blob(&b) }.to yield_control.exactly(2).times + end + end + end + context 'when there are no adapters for the file format' do - let(:artifact) { build(:ci_job_artifact, :junit, file_format: :zip) } + let(:artifact) { build(:ci_job_artifact, :junit) } + + before do + allow(artifact).to receive(:file_format).and_return(:unknown) + end it 'raises an error' do expect { |b| artifact.each_blob(&b) }.to raise_error(described_class::NotSupportedAdapterError) diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 1747f0e019c..2a03ae89389 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -1512,6 +1512,45 @@ RSpec.describe API::MergeRequests do end end + describe 'GET /projects/:id/merge_requests/:merge_request_iid/reviewers' do + it 'returns reviewers' do + reviewer = create(:user) + merge_request.merge_request_reviewers.create!(reviewer: reviewer) + + get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/reviewers", user) + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.size).to eq(merge_request.merge_request_reviewers.size) + + expect(json_response.last['user']['id']).to eq(reviewer.id) + expect(json_response.last['user']['name']).to eq(reviewer.name) + expect(json_response.last['user']['username']).to eq(reviewer.username) + expect(json_response.last['state']).to eq('unreviewed') + expect(json_response.last['updated_state_by']).to be_nil + expect(json_response.last['created_at']).to be_present + end + + it 'returns a 404 when iid does not exist' do + get api("/projects/#{project.id}/merge_requests/#{non_existing_record_iid}/reviewers", user) + + expect(response).to have_gitlab_http_status(:not_found) + end + + it 'returns a 404 when id is used instead of iid' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/reviewers", user) + + expect(response).to have_gitlab_http_status(:not_found) + end + + context 'when merge request author has only guest access' do + it_behaves_like 'rejects user from accessing merge request info' do + let(:url) { "/projects/#{project.id}/merge_requests/#{merge_request.iid}/reviewers" } + end + end + end + describe 'GET /projects/:id/merge_requests/:merge_request_iid/commits' do include_context 'with merge requests' diff --git a/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb b/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb new file mode 100644 index 00000000000..453f0c36c14 --- /dev/null +++ b/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' +require_relative '../../../../rubocop/cop/gitlab/deprecate_track_redis_hll_event' + +RSpec.describe RuboCop::Cop::Gitlab::DeprecateTrackRedisHLLEvent do + subject(:cop) { described_class.new } + + it 'does not flag the use of track_event' do + expect_no_offenses('track_event :show, name: "p_analytics_insights"') + end + + it 'flags the use of track_redis_hll_event' do + expect_offense(<<~SOURCE) + track_redis_hll_event :show, name: 'p_analytics_valuestream' + ^^^^^^^^^^^^^^^^^^^^^ `track_redis_hll_event` is deprecated[...] + SOURCE + end +end diff --git a/spec/services/projects/alerting/notify_service_spec.rb b/spec/services/projects/alerting/notify_service_spec.rb index fbf147b567f..aa2ef39bf98 100644 --- a/spec/services/projects/alerting/notify_service_spec.rb +++ b/spec/services/projects/alerting/notify_service_spec.rb @@ -119,10 +119,10 @@ RSpec.describe Projects::Alerting::NotifyService do end context 'with overlong payload' do - let(:deep_size_object) { instance_double(Gitlab::Utils::DeepSize, valid?: false) } + let(:payload_raw) { { 'the-payload-is-too-big' => true } } before do - allow(Gitlab::Utils::DeepSize).to receive(:new).and_return(deep_size_object) + stub_const('::Gitlab::Utils::DeepSize::DEFAULT_MAX_DEPTH', 0) end it_behaves_like 'alerts service responds with an error and takes no actions', :bad_request diff --git a/spec/services/projects/prometheus/alerts/notify_service_spec.rb b/spec/services/projects/prometheus/alerts/notify_service_spec.rb index 6f760e6dbfa..4fb46a05af5 100644 --- a/spec/services/projects/prometheus/alerts/notify_service_spec.rb +++ b/spec/services/projects/prometheus/alerts/notify_service_spec.rb @@ -313,11 +313,11 @@ RSpec.describe Projects::Prometheus::Alerts::NotifyService do end context 'when the payload is too big' do - let(:payload) { { 'the-payload-is-too-big' => true } } - let(:deep_size_object) { instance_double(Gitlab::Utils::DeepSize, valid?: false) } + let(:payload_raw) { { 'the-payload-is-too-big' => true } } + let(:payload) { ActionController::Parameters.new(payload_raw).permit! } before do - allow(Gitlab::Utils::DeepSize).to receive(:new).and_return(deep_size_object) + stub_const('::Gitlab::Utils::DeepSize::DEFAULT_MAX_DEPTH', 0) end it_behaves_like 'alerts service responds with an error and takes no actions', :bad_request diff --git a/spec/support/shared_examples/models/ci/metadata_id_tokens_shared_examples.rb b/spec/support/shared_examples/models/ci/metadata_id_tokens_shared_examples.rb new file mode 100644 index 00000000000..0c71ebe7a4d --- /dev/null +++ b/spec/support/shared_examples/models/ci/metadata_id_tokens_shared_examples.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +RSpec.shared_examples_for 'has ID tokens' do |ci_type| + subject(:ci) { FactoryBot.build(ci_type) } + + describe 'delegations' do + it { is_expected.to delegate_method(:id_tokens).to(:metadata).allow_nil } + end + + describe '#id_tokens?' do + subject { ci.id_tokens? } + + context 'without metadata' do + let(:ci) { FactoryBot.build(ci_type) } + + it { is_expected.to be_falsy } + end + + context 'with metadata' do + let(:ci) { FactoryBot.build(ci_type, metadata: FactoryBot.build(:ci_build_metadata, id_tokens: id_tokens)) } + + context 'when ID tokens exist' do + let(:id_tokens) { { TEST_JOB_JWT: { id_token: { aud: 'developers ' } } } } + + it { is_expected.to be_truthy } + end + + context 'when ID tokens do not exist' do + let(:id_tokens) { {} } + + it { is_expected.to be_falsy } + end + end + end + + describe '#id_tokens=' do + it 'assigns the ID tokens to the CI job' do + id_tokens = [{ 'JOB_ID_TOKEN' => { 'id_token' => { 'aud' => 'https://gitlab.test ' } } }] + ci.id_tokens = id_tokens + + expect(ci.id_tokens).to match_array(id_tokens) + end + end +end diff --git a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb b/spec/workers/pages/invalidate_domain_cache_worker_spec.rb index 49dcd18d64e..df1b91ff0e5 100644 --- a/spec/workers/pages/invalidate_domain_cache_worker_spec.rb +++ b/spec/workers/pages/invalidate_domain_cache_worker_spec.rb @@ -92,6 +92,18 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do { type: :namespace, id: 5 } ] + it_behaves_like 'clears caches with', + event_class: Groups::GroupTransferedEvent, + event_data: { + group_id: 1, + old_root_namespace_id: 3, + new_root_namespace_id: 5 + }, + caches: [ + { type: :namespace, id: 3 }, + { type: :namespace, id: 5 } + ] + context 'when namespace based cache keys are duplicated' do # de-dups namespace cache keys it_behaves_like 'clears caches with',