Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-05-05 09:12:42 +00:00
parent 7c0c3a7dc9
commit 2a134be97d
66 changed files with 512 additions and 1068 deletions

View File

@ -2834,8 +2834,6 @@ Layout/LineLength:
- 'lib/gitlab/github_import.rb'
- 'lib/gitlab/github_import/importer/pull_request_importer.rb'
- 'lib/gitlab/github_import/parallel_scheduling.rb'
- 'lib/gitlab/gitlab_import/client.rb'
- 'lib/gitlab/gitlab_import/importer.rb'
- 'lib/gitlab/gl_repository.rb'
- 'lib/gitlab/global_id/deprecations.rb'
- 'lib/gitlab/golang.rb'

View File

@ -218,7 +218,6 @@ Lint/RedundantCopDisableDirective:
- 'lib/gitlab/git/patches/collection.rb'
- 'lib/gitlab/github_import/markdown_text.rb'
- 'lib/gitlab/github_import/user_finder.rb'
- 'lib/gitlab/gitlab_import/importer.rb'
- 'lib/gitlab/graphql/pagination/keyset/connection.rb'
- 'lib/gitlab/health_checks/metric.rb'
- 'lib/gitlab/health_checks/probes/status.rb'

View File

@ -148,14 +148,11 @@ Rails/Pluck:
- 'spec/controllers/projects/feature_flags_controller_spec.rb'
- 'spec/controllers/projects/issues_controller_spec.rb'
- 'spec/controllers/projects/jobs_controller_spec.rb'
- 'spec/controllers/projects/merge_requests/conflicts_controller_spec.rb'
- 'spec/controllers/projects/merge_requests/diffs_controller_spec.rb'
- 'spec/controllers/projects/pipelines/tests_controller_spec.rb'
- 'spec/controllers/projects/releases_controller_spec.rb'
- 'spec/controllers/projects/starrers_controller_spec.rb'
- 'spec/db/schema_spec.rb'
- 'spec/features/issues/csv_spec.rb'
- 'spec/features/merge_request/user_sees_versions_spec.rb'
- 'spec/finders/license_template_finder_spec.rb'
- 'spec/graphql/resolvers/ci/test_suite_resolver_spec.rb'
- 'spec/graphql/resolvers/concerns/looks_ahead_spec.rb'

View File

@ -3811,9 +3811,6 @@ RSpec/MissingFeatureCategory:
- 'spec/lib/gitlab/github_import/single_endpoint_notes_importing_spec.rb'
- 'spec/lib/gitlab/github_import/user_finder_spec.rb'
- 'spec/lib/gitlab/github_import_spec.rb'
- 'spec/lib/gitlab/gitlab_import/client_spec.rb'
- 'spec/lib/gitlab/gitlab_import/importer_spec.rb'
- 'spec/lib/gitlab/gitlab_import/project_creator_spec.rb'
- 'spec/lib/gitlab/gl_repository/identifier_spec.rb'
- 'spec/lib/gitlab/gl_repository/repo_type_spec.rb'
- 'spec/lib/gitlab/gl_repository_spec.rb'

View File

@ -528,8 +528,6 @@ Style/GuardClause:
- 'lib/gitlab/github_import/importer/pull_request_importer.rb'
- 'lib/gitlab/github_import/importer/pull_request_review_importer.rb'
- 'lib/gitlab/github_import/object_counter.rb'
- 'lib/gitlab/gitlab_import/client.rb'
- 'lib/gitlab/gitlab_import/importer.rb'
- 'lib/gitlab/graphql/query_analyzers/ast/recursion_analyzer.rb'
- 'lib/gitlab/i18n/po_linter.rb'
- 'lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb'

View File

@ -850,8 +850,6 @@ Style/IfUnlessModifier:
- 'lib/gitlab/github_import/client.rb'
- 'lib/gitlab/github_import/importer/pull_request_review_importer.rb'
- 'lib/gitlab/github_import/representation/issue.rb'
- 'lib/gitlab/gitlab_import/client.rb'
- 'lib/gitlab/gitlab_import/importer.rb'
- 'lib/gitlab/golang.rb'
- 'lib/gitlab/graphql/pagination/keyset/connection.rb'
- 'lib/gitlab/graphql/queries.rb'

View File

@ -457,7 +457,7 @@ group :test do
gem 'capybara', '~> 3.39'
gem 'capybara-screenshot', '~> 1.0.26'
gem 'selenium-webdriver', '~> 3.142', '>= 3.142.7'
gem 'selenium-webdriver', '~> 4.9'
gem 'graphlyte', '~> 1.0.0'
@ -510,7 +510,7 @@ gem 'spamcheck', '~> 1.3.0'
gem 'gitaly', '~> 15.9.0-rc3'
# KAS GRPC protocol definitions
gem 'kas-grpc', '~> 0.0.2'
gem 'kas-grpc', '~> 0.1.0'
gem 'grpc', '~> 1.42.0'

View File

@ -76,7 +76,6 @@
{"name":"charlock_holmes","version":"0.7.7","platform":"ruby","checksum":"1790eca3f661ffa6bbf5866c53c7191e4b8472626fc4997ff9dbe7c425e2cb43"},
{"name":"chef-config","version":"16.10.17","platform":"ruby","checksum":"1f4961e4d6aa4df374f739c6f62ae1d2be03dcff1bd93e56d9c963b8a156747c"},
{"name":"chef-utils","version":"16.10.17","platform":"ruby","checksum":"a74253da6aab8ff92c955549536bdecbc4d1ce8032c8201576f2a8ef4e8ed7b3"},
{"name":"childprocess","version":"3.0.0","platform":"ruby","checksum":"4579a87cdc962de252eebf1482a4185fad383ae7dbe29a746ba2be8e261280c5"},
{"name":"chunky_png","version":"1.3.5","platform":"ruby","checksum":"b6ab1011b2e79bcc973c92deee4110d071d5cd59ed950efcd0aba49a5f57c06d"},
{"name":"circuitbox","version":"2.0.0.pre5","platform":"ruby","checksum":"033d4820a9b688f0539630b81ae0d707ce8e6ccd34756de31a79063b190ffffc"},
{"name":"citrus","version":"3.0.2","platform":"ruby","checksum":"4ec2412fc389ad186735f4baee1460f7900a8e130ffe3f216b30d4f9c684f650"},
@ -322,7 +321,7 @@
{"name":"kaminari-actionview","version":"1.2.2","platform":"ruby","checksum":"1330f6fc8b59a4a4ef6a549ff8a224797289ebf7a3a503e8c1652535287cc909"},
{"name":"kaminari-activerecord","version":"1.2.2","platform":"ruby","checksum":"0dd3a67bab356a356f36b3b7236bcb81cef313095365befe8e98057dd2472430"},
{"name":"kaminari-core","version":"1.2.2","platform":"ruby","checksum":"3bd26fec7370645af40ca73b9426a448d09b8a8ba7afa9ba3c3e0d39cdbb83ff"},
{"name":"kas-grpc","version":"0.0.2","platform":"ruby","checksum":"111ff7515952e939f491297ba4c69a218b72d9d0ef8e5bff80a5df6a56df9a16"},
{"name":"kas-grpc","version":"0.1.0","platform":"ruby","checksum":"b219c79b7bddf1f3ac6a78119e25b0b52c0c29608b3a80c75929c47f32a50dda"},
{"name":"knapsack","version":"1.21.1","platform":"ruby","checksum":"82f70422adebcacec1b514f6ebff65265fc85d836e3c320718a160d8ac41cf14"},
{"name":"kramdown","version":"2.3.2","platform":"ruby","checksum":"cb4530c2e9d16481591df2c9336723683c354e5416a5dd3e447fa48215a6a71c"},
{"name":"kramdown-parser-gfm","version":"1.1.0","platform":"ruby","checksum":"fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729"},
@ -562,7 +561,7 @@
{"name":"sawyer","version":"0.9.2","platform":"ruby","checksum":"fa3a72d62a4525517b18857ddb78926aab3424de0129be6772a8e2ba240e7aca"},
{"name":"sd_notify","version":"0.1.1","platform":"ruby","checksum":"cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131"},
{"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"},
{"name":"selenium-webdriver","version":"3.142.7","platform":"ruby","checksum":"dea0993e0e4fdb364f0453144814c0e6099a411d17396807c6cac666d0ddac29"},
{"name":"selenium-webdriver","version":"4.9.0","platform":"ruby","checksum":"0f5fc4118ab231e5ef1895b1e14a4366eb9d73d60a8e42b0d84f69cdfdd8b6cf"},
{"name":"semver_dialects","version":"1.2.1","platform":"ruby","checksum":"60a1f67659f79c51a667e8858ec9b089c1e4ce4f6d2a0f0b4ac101916946eb23"},
{"name":"sentry-rails","version":"5.8.0","platform":"ruby","checksum":"c11b2d909de2c2bfda793c45f64180fd784d54c46886338b683ee3f8efa7731b"},
{"name":"sentry-raven","version":"3.1.2","platform":"ruby","checksum":"103d3b122958810d34898ce2e705bcf549ddb9d855a70ce9a3970ee2484f364a"},
@ -680,6 +679,7 @@
{"name":"webfinger","version":"1.2.0","platform":"ruby","checksum":"7814ef1c85da47514f65c6e5ca14205fa9ce41ea2a70785e0c872842162852a2"},
{"name":"webmock","version":"3.18.1","platform":"ruby","checksum":"54c955df4ae4bec6181dd266eeec632a1808288c633f9551d81bafb53921d2d7"},
{"name":"webrick","version":"1.8.1","platform":"ruby","checksum":"19411ec6912911fd3df13559110127ea2badd0c035f7762873f58afc803e158f"},
{"name":"websocket","version":"1.2.9","platform":"ruby","checksum":"884b12dee993217795bb5f58acc89c0121c88bdc99df4d1636c0505dca352b36"},
{"name":"websocket-driver","version":"0.7.5","platform":"java","checksum":"fffa83aa188e9ac90e32a385832ec9d26acdf019538e1c7d703f2c8a323b39c8"},
{"name":"websocket-driver","version":"0.7.5","platform":"ruby","checksum":"a280c3f44dcbb0323d58bc78dc49350c05d589ab7d13267fcff08d9d5ae76b28"},
{"name":"websocket-extensions","version":"0.1.5","platform":"ruby","checksum":"1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241"},

View File

@ -303,7 +303,6 @@ GEM
mixlib-shellout (>= 2.0, < 4.0)
tomlrb (~> 1.2)
chef-utils (16.10.17)
childprocess (3.0.0)
chunky_png (1.3.5)
circuitbox (2.0.0.pre5)
citrus (3.0.2)
@ -871,7 +870,7 @@ GEM
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
kas-grpc (0.0.2)
kas-grpc (0.1.0)
grpc (~> 1.0)
knapsack (1.21.1)
rake
@ -1404,9 +1403,10 @@ GEM
seed-fu (2.3.7)
activerecord (>= 3.1)
activesupport (>= 3.1)
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
selenium-webdriver (4.9.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
semver_dialects (1.2.1)
pastel (~> 0.8.0)
thor (~> 1.2.0)
@ -1638,6 +1638,7 @@ GEM
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.8.1)
websocket (1.2.9)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
@ -1809,7 +1810,7 @@ DEPENDENCIES
json_schemer (~> 0.2.18)
jwt (~> 2.5)
kaminari (~> 1.2.2)
kas-grpc (~> 0.0.2)
kas-grpc (~> 0.1.0)
knapsack (~> 1.21.1)
kramdown (~> 2.3.1)
kubeclient (~> 4.11.0)
@ -1918,7 +1919,7 @@ DEPENDENCIES
sassc-rails (~> 2.1.0)
sd_notify (~> 0.1.0)
seed-fu (~> 2.3.7)
selenium-webdriver (~> 3.142, >= 3.142.7)
selenium-webdriver (~> 4.9)
semver_dialects (~> 1.2.1)
sentry-rails (~> 5.8.0)
sentry-raven (~> 3.1)

View File

@ -2,7 +2,7 @@
import { GlTableLite } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { durationTimeFormatted } from '~/lib/utils/datetime_utility';
import { formatTime } from '~/lib/utils/datetime_utility';
import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
import RunnerTags from '~/ci/runner/components/runner_tags.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
@ -50,11 +50,11 @@ export default {
},
duration(job) {
const { duration } = job;
return duration ? durationTimeFormatted(duration) : '';
return duration ? formatTime(duration * 1000) : '';
},
queued(job) {
const { queuedDuration } = job;
return queuedDuration ? durationTimeFormatted(queuedDuration) : '';
return queuedDuration ? formatTime(queuedDuration * 1000) : '';
},
},
fields: [

View File

@ -1,6 +1,6 @@
<script>
import { GlIcon } from '@gitlab/ui';
import { durationTimeFormatted } from '~/lib/utils/datetime_utility';
import { formatTime } from '~/lib/utils/datetime_utility';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import timeagoMixin from '~/vue_shared/mixins/timeago';
@ -25,7 +25,7 @@ export default {
return this.job?.duration;
},
durationFormatted() {
return durationTimeFormatted(this.duration);
return formatTime(this.duration * 1000);
},
},
};

View File

@ -162,16 +162,24 @@ export const formatDate = (datetime, format = 'mmm d, yyyy h:MMtt Z', utc = fals
* @returns {string}
*/
export const formatTime = (milliseconds) => {
const remainingSeconds = Math.floor(milliseconds / 1000) % 60;
const remainingMinutes = Math.floor(milliseconds / 1000 / 60) % 60;
const remainingHours = Math.floor(milliseconds / 1000 / 60 / 60);
const seconds = Math.round(milliseconds / 1000);
const absSeconds = Math.abs(seconds);
const remainingSeconds = Math.floor(absSeconds) % 60;
const remainingMinutes = Math.floor(absSeconds / 60) % 60;
const hours = Math.floor(absSeconds / 60 / 60);
let formattedTime = '';
if (remainingHours < 10) formattedTime += '0';
formattedTime += `${remainingHours}:`;
if (hours < 10) formattedTime += '0';
formattedTime += `${hours}:`;
if (remainingMinutes < 10) formattedTime += '0';
formattedTime += `${remainingMinutes}:`;
if (remainingSeconds < 10) formattedTime += '0';
formattedTime += remainingSeconds;
if (seconds < 0) {
return `-${formattedTime}`;
}
return formattedTime;
};
@ -203,7 +211,7 @@ export const stringifyTime = (timeObject, fullNameFormat = false) => {
const isNonZero = Boolean(unitValue);
if (fullNameFormat && isNonZero) {
// Remove traling 's' if unit value is singular
// Remove trailing 's' if unit value is singular
const formattedUnitName = unitValue > 1 ? unitName : unitName.replace(/s$/, '');
return `${memo} ${unitValue} ${formattedUnitName}`;
}
@ -387,27 +395,6 @@ export const formatTimeAsSummary = ({ seconds, hours, days, minutes, weeks, mont
return '-';
};
export const durationTimeFormatted = (duration) => {
const date = new Date(Math.abs(duration) * 1000);
const days = date.getUTCDate() - 1;
let hh = 24 * days + date.getUTCHours();
let mm = date.getUTCMinutes();
let ss = date.getSeconds();
if (hh < 10) {
hh = `0${hh}`;
}
if (mm < 10) {
mm = `0${mm}`;
}
if (ss < 10) {
ss = `0${ss}`;
}
return `${duration < 0 ? '-' : ''}${hh}:${mm}:${ss}`;
};
/**
* Converts a numeric utc offset in seconds to +/- hours
* ie -32400 => -9 hours

View File

@ -1,6 +1,6 @@
<script>
import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { durationTimeFormatted } from '~/lib/utils/datetime_utility';
import { formatTime } from '~/lib/utils/datetime_utility';
import timeagoMixin from '~/vue_shared/mixins/timeago';
export default {
@ -20,7 +20,7 @@ export default {
return this.pipeline?.details?.duration;
},
durationFormatted() {
return durationTimeFormatted(this.duration);
return formatTime(this.duration * 1000);
},
finishedTime() {
return this.pipeline?.details?.finished_at;

View File

@ -60,7 +60,6 @@ class ApplicationController < ActionController::Base
helper_method :can?
helper_method :import_sources_enabled?, :github_import_enabled?,
:gitea_import_enabled?, :github_import_configured?,
:gitlab_import_enabled?, :gitlab_import_configured?,
:bitbucket_import_enabled?, :bitbucket_import_configured?,
:bitbucket_server_import_enabled?, :fogbugz_import_enabled?,
:git_import_enabled?, :gitlab_project_import_enabled?,
@ -448,14 +447,6 @@ class ApplicationController < ActionController::Base
Gitlab::Auth::OAuth::Provider.enabled?(:github)
end
def gitlab_import_enabled?
request.host != 'gitlab.com' && Gitlab::CurrentSettings.import_sources.include?('gitlab')
end
def gitlab_import_configured?
Gitlab::Auth::OAuth::Provider.enabled?(:gitlab)
end
def bitbucket_import_enabled?
Gitlab::CurrentSettings.import_sources.include?('bitbucket')
end

View File

@ -1,92 +0,0 @@
# frozen_string_literal: true
class Import::GitlabController < Import::BaseController
extend ::Gitlab::Utils::Override
MAX_PROJECT_PAGES = 15
PER_PAGE_PROJECTS = 100
before_action :verify_gitlab_import_enabled
before_action :gitlab_auth, except: :callback
rescue_from OAuth2::Error, with: :gitlab_unauthorized
def callback
session[:gitlab_access_token] = client.get_token(params[:code], callback_import_gitlab_url(namespace_id: params[:namespace_id]))
redirect_to status_import_gitlab_url(namespace_id: params[:namespace_id])
end
# We need to re-expose controller's internal method 'status' as action.
# rubocop:disable Lint/UselessMethodDefinition
def status
super
end
# rubocop:enable Lint/UselessMethodDefinition
def create
repo = client.project(params[:repo_id].to_i)
target_namespace = find_or_create_namespace(repo['namespace']['path'], client.user['username'])
if current_user.can?(:create_projects, target_namespace)
project = Gitlab::GitlabImport::ProjectCreator.new(repo, target_namespace, current_user, access_params).execute
if project.persisted?
render json: ProjectSerializer.new.represent(project, serializer: :import)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
else
render json: { errors: _('This namespace has already been taken! Please choose another one.') }, status: :unprocessable_entity
end
end
protected
override :importable_repos
def importable_repos
client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS).to_a
end
override :incompatible_repos
def incompatible_repos
[]
end
override :provider_name
def provider_name
:gitlab
end
override :provider_url
def provider_url
'https://gitlab.com'
end
private
def client
@client ||= Gitlab::GitlabImport::Client.new(session[:gitlab_access_token])
end
def verify_gitlab_import_enabled
render_404 unless gitlab_import_enabled?
end
def gitlab_auth
if session[:gitlab_access_token].blank?
go_to_gitlab_for_permissions
end
end
def go_to_gitlab_for_permissions
redirect_to client.authorize_url(callback_import_gitlab_url(namespace_id: params[:namespace_id]))
end
def gitlab_unauthorized
go_to_gitlab_for_permissions
end
def access_params
{ gitlab_access_token: session[:gitlab_access_token] }
end
end

View File

@ -802,7 +802,7 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord
end
def remove_old_import_sources
self.import_sources -= ['phabricator'] if self.import_sources
self.import_sources -= %w[phabricator gitlab] if self.import_sources
end
Recursion = Class.new(RuntimeError)

View File

@ -21,11 +21,6 @@
= render Pajamas::ButtonComponent.new(href: '#', icon: 'tanuki', button_options: { class: 'btn_import_gitlab_project js-import-project-btn', data: { href: new_import_gitlab_project_path, platform: 'gitlab_export', **tracking_attrs_data(track_label, 'click_button', 'gitlab_export') } }) do
= _('GitLab export')
- if gitlab_import_enabled?
%div
= render Pajamas::ButtonComponent.new(href: status_import_gitlab_path(namespace_id: namespace_id), icon: 'tanuki', button_options: { class: "import_gitlab js-import-project-btn #{'js-how-to-import-link' unless gitlab_import_configured?}", data: { modal_title: _("Import projects from GitLab.com"), modal_message: import_from_gitlab_message, platform: 'gitlab_com', **tracking_attrs_data(track_label, 'click_button', 'gitlab_com') } }) do
GitLab.com
- if github_import_enabled?
%div
= render Pajamas::ButtonComponent.new(href: new_import_github_path(namespace_id: namespace_id), icon: 'github', button_options: { class: 'js-import-github js-import-project-btn', data: { platform: 'github', **tracking_attrs_data(track_label, 'click_button', 'github') } }) do

View File

@ -59,9 +59,13 @@ order of precedence for configuration is described below, where `$NAME` and
`$FALLBACK_NAME` are the upper-cased instance names from the table, and `$name`
and `$fallback_name` are the lower-cased versions:
1. The configuration file pointed to by the `GITLAB_REDIS_$NAME_CONFIG_FILE`
environment variable.
1. The configuration file `redis.$name.yml`.
1. **If a fallback instance is available**, the configuration file
`redis.$fallback_name.yml`.
1. The configuration file pointed to by the `GITLAB_REDIS_CONFIG_FILE`
environment variable.
1. The configuration file `resque.yml`.
An example configuration file for Redis is in this directory under the name

View File

@ -6,7 +6,9 @@ product_section: dev
product_stage: manage
product_group: import
value_type: number
status: active
status: removed
removed_by: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118891"
milestone_removed: "16.0"
time_frame: 28d
data_source: database
instrumentation_class: CountImportedProjectsMetric

View File

@ -6,7 +6,9 @@ product_section: dev
product_stage: manage
product_group: import
value_type: number
status: active
status: removed
removed_by: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118891"
milestone_removed: "16.0"
time_frame: all
data_source: database
instrumentation_class: CountImportedProjectsMetric

View File

@ -34,12 +34,6 @@ namespace :import do
get :realtime_changes
end
resource :gitlab, only: [:create], controller: :gitlab do
get :status
get :callback
get :realtime_changes
end
resource :bitbucket, only: [:create], controller: :bitbucket do
get :status
get :callback

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
SEE_DB_DOC = "See the [database dictionary documentation](https://docs.gitlab.com/ee/development/database/database_dictionary.html)."
PARTITIONING_COMMENT = <<~SUGGEST_COMMENT
When adding new CI tables, consider [partitioning](https://docs.gitlab.com/ee/development/cicd/cicd_tables.html) the table
from the start if it references any of the larger CI tables: `ci_pipelines`, `ci_stages`, `ci_builds`, `p_ci_builds_metadata`, `ci_job_artifacts`, `ci_pipeline_variables`.
SUGGEST_COMMENT
def check_database_dictionary_yaml(database_dictionary)
return unless database_dictionary.ci_schema?
# `p_` prefix is used by the partitioned tables, so we can assume that the table is already partitioned
return if database_dictionary.table_name.to_s.start_with?('p_')
mr_line = database_dictionary.raw.lines.find_index { |line| line.start_with?('table_name:') }
return unless mr_line
markdown(PARTITIONING_COMMENT, file: database_dictionary.path, line: mr_line.succ)
rescue Psych::Exception
# YAML could not be parsed, fail the build.
fail "#{helper.html_link(database_ditionary.path)} isn't valid YAML! #{SEE_DB_DOC}" # rubocop:disable Style/SignalException
rescue StandardError => e
warn "There was a problem trying to check the database dictionary file. Exception: #{e.class.name} - #{e.message}"
end
def added_database_dictionary_files
database_dictionary.database_dictionary_files(change_type: :added)
end
added_database_dictionary_files.each do |database_dictionary|
check_database_dictionary_yaml(database_dictionary)
end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
require_relative '../../tooling/danger/database_dictionary'
module Danger
class DatabaseDictionary < Plugin
# Put the helper code somewhere it can be tested
include Tooling::Danger::DatabaseDictionary
end
end

View File

@ -404,7 +404,7 @@ listed in the descriptions of the relevant settings.
| `housekeeping_incremental_repack_period` | integer | no | Deprecated. Number of Git pushes after which an incremental `git repack` is run. Use `housekeeping_optimize_repository_period` instead. For more information, see [Housekeeping fields](#housekeeping-fields).|
| `housekeeping_optimize_repository_period`| integer | no | Number of Git pushes after which an incremental `git repack` is run. |
| `html_emails_enabled` | boolean | no | Enable HTML emails. |
| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `gitlab`, `fogbugz`, `git`, `gitlab_project`, `gitea`, and `manifest`. |
| `import_sources` | array of strings | no | Sources to allow project import from, possible values: `github`, `bitbucket`, `bitbucket_server`, `fogbugz`, `git`, `gitlab_project`, `gitea`, and `manifest`. |
| `in_product_marketing_emails_enabled` | boolean | no | Enable [in-product marketing emails](../user/profile/notifications.md#global-notification-settings). Enabled by default. |
| `invisible_captcha_enabled` | boolean | no | Enable Invisible CAPTCHA spam detection during sign-up. Disabled by default. |
| `issues_create_limit` | integer | no | Max number of issue creation requests per minute per user. Disabled by default.|

View File

@ -903,7 +903,7 @@ The `scripts/frontend/download_fixtures.sh` script is meant to download and extr
```shell
# Checks if a frontend fixture package exists in the gitlab-org/gitlab
# package registry by looking at the commits on a local branch.
#
#
# The package is downloaded and extracted if it exists
$ scripts/frontend/download_fixtures.sh
@ -1753,11 +1753,8 @@ Inside the terminal, where capybara is running, you can also execute `next` whic
### Updating ChromeDriver
On MacOS, if you get a ChromeDriver error, make sure to update it by running
```shell
brew upgrade chromedriver
```
Starting from `Selenium` 4.6, ChromeDriver can be automatically managed by `Selenium Manager` which comes with the `selenium-webdriver` gem.
You are no longer required to manually keeping chromedriver in sync.
---

View File

@ -13,7 +13,7 @@ The Jira issue integration connects one or more GitLab projects to a Jira instan
Prerequisites:
- Your GitLab installation must not use a [relative URL](https://docs.gitlab.com/omnibus/settings/configuration.html#configure-a-relative-url-for-gitlab).
- **For Jira Server**, you must have a Jira username and password.
- **For Jira Server**, you must have a [Jira username and password](jira_server_configuration.md).
- **For Jira Cloud**, you must have a [Jira Cloud API token](#create-a-jira-cloud-api-token) and
the email address you used to create the token.

View File

@ -6,14 +6,20 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Jira Server credentials **(FREE)**
To [integrate Jira with GitLab](index.md), you must
create a Jira user account for your Jira projects to access projects in GitLab.
To [integrate Jira with GitLab](index.md), you can:
- Recommended. Create a separate Jira user account for your Jira projects to access projects in GitLab.
This Jira user account must have write access to your Jira projects. To create the
credentials, you must:
1. [Create a Jira Server user](#create-a-jira-server-user).
1. [Create a Jira Server group](#create-a-jira-server-group) for the user to belong to.
1. [Create a permission scheme for your group](#create-a-permission-scheme-for-your-group).
1. [Create a Jira Server user](#create-a-jira-server-user).
1. [Create a Jira Server group](#create-a-jira-server-group) for the user to belong to.
1. [Create a permission scheme for your group](#create-a-permission-scheme-for-your-group).
- Use an existing Jira user account provided the user belongs to a Jira group that
has been granted the **Administer Projects** [permission scheme](#create-a-permission-scheme-for-your-group).
After you select a Jira user account for the integration, [configure GitLab](configure.md) to use the account.
## Create a Jira Server user
@ -22,15 +28,12 @@ This process creates a user named `gitlab`:
1. Sign in to your Jira instance as a Jira administrator.
1. On the top bar, in the upper-right corner, select the gear icon, then
select **User Management**.
1. Create a new user account (`gitlab`) with write access to
1. [Create a new user account manually](https://confluence.atlassian.com/adminjiraserver/create-edit-or-remove-a-user-938847025.html#Create,edit,orremoveauser-CreateusersmanuallyinJira) with write access to
projects in Jira.
- **Email address**: Jira requires a valid email address, and sends a verification
email, which is required to set up the password.
- **Username**: Jira creates the username by using the email prefix. You can change
this username later.
- **Password**: You must create a password, because the GitLab integration doesn't
support SSO, such as SAML. To create the password, go to the user profile, look up
the username, and set a password.
- **Email address**: You should use a valid email address.
- **Username**: Set the username to `gitlab`.
- **Password**: You must set a password because the Jira issue integration does not
support SSO such as SAML.
1. Select **Create user**.
After you create the user, create a group for it.
@ -67,7 +70,7 @@ Next, create a permission scheme for your group.
## Create a permission scheme for your group
After creating the group in Jira, grant permissions to the group by creating a permission scheme:
After you [create the group in Jira](#create-a-jira-server-group), grant permissions to the group by creating a permission scheme:
1. Sign in to your Jira instance as a Jira administrator.
1. On the top bar, in the upper-right corner, select the gear icon, then

View File

@ -4,32 +4,13 @@ group: Import and Integrate
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Import a project from GitLab.com to your self-managed GitLab instance (deprecated) **(FREE)**
# Import a project from GitLab.com to your self-managed GitLab instance (removed) **(FREE)**
WARNING:
The GitLab.com importer was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108502) in GitLab 15.8
and will be removed in GitLab 16.0. To import GitLab projects from GitLab.com to a self-managed GitLab instance use
and removed in GitLab 16.0. To import GitLab projects from GitLab.com to a self-managed GitLab instance use
[migrating groups and projects by direct transfer](../../group/import/index.md#migrate-groups-by-direct-transfer-recommended).
You can import your existing GitLab.com projects to your GitLab instance.
Prerequisite:
- GitLab.com integration must be enabled on your GitLab instance.
[Read more about GitLab.com integration for self-managed GitLab instances](../../../integration/gitlab.md).
To import a GitLab.com project to your self-managed GitLab instance:
1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
1. On the right of the page, select **New project**.
1. Select **Import project**.
1. Select **GitLab.com**.
1. Give GitLab.com permission to access your projects.
1. Select **Import**.
The importer imports your repository and issues.
When the importer is done, a new GitLab project is created with your imported data.
## Related topics
- [Automate group and project import](index.md#automate-group-and-project-import)

View File

@ -97,7 +97,7 @@ module API
end
optional :html_emails_enabled, type: Boolean, desc: 'By default GitLab sends emails in HTML and plain text formats so mail clients can choose what format to use. Disable this option if you only want to send emails in plain text format.'
optional :import_sources, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce,
values: %w[github bitbucket bitbucket_server gitlab fogbugz git gitlab_project gitea manifest],
values: %w[github bitbucket bitbucket_server fogbugz git gitlab_project gitea manifest],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
optional :in_product_marketing_emails_enabled, type: Boolean, desc: 'By default, in-product marketing emails are enabled. To disable these emails, disable this option.'
optional :invisible_captcha_enabled, type: Boolean, desc: 'Enable Invisible Captcha spam detection during signup.'

View File

@ -1,89 +0,0 @@
# frozen_string_literal: true
module Gitlab
module GitlabImport
class Client
attr_reader :client, :api
PER_PAGE = 100
def initialize(access_token)
@client = ::OAuth2::Client.new(
config.app_id,
config.app_secret,
gitlab_options
)
if access_token
@api = OAuth2::AccessToken.from_hash(@client, access_token: access_token)
end
end
def authorize_url(redirect_uri)
client.auth_code.authorize_url({
redirect_uri: redirect_uri,
scope: "api"
})
end
def get_token(code, redirect_uri)
client.auth_code.get_token(code, redirect_uri: redirect_uri).token
end
def user
api.get("/api/v4/user").parsed
end
def issues(project_identifier, **kwargs)
lazy_page_iterator(**kwargs) do |page, per_page|
api.get("/api/v4/projects/#{project_identifier}/issues?per_page=#{per_page}&page=#{page}").parsed
end
end
def issue_comments(project_identifier, issue_id, **kwargs)
lazy_page_iterator(**kwargs) do |page, per_page|
api.get("/api/v4/projects/#{project_identifier}/issues/#{issue_id}/notes?per_page=#{per_page}&page=#{page}").parsed
end
end
def project(id)
api.get("/api/v4/projects/#{id}").parsed
end
def projects(**kwargs)
lazy_page_iterator(**kwargs) do |page, per_page|
api.get("/api/v4/projects?per_page=#{per_page}&page=#{page}&simple=true&membership=true").parsed
end
end
private
def lazy_page_iterator(starting_page: 1, page_limit: nil, per_page: PER_PAGE)
Enumerator.new do |y|
page = starting_page
page_limit = (starting_page - 1) + page_limit if page_limit
loop do
items = yield(page, per_page)
items.each do |item|
y << item
end
break if items.empty? || items.size < per_page || (page_limit && page >= page_limit)
page += 1
end
end
end
def config
Gitlab::Auth::OAuth::Provider.config_for('gitlab')
end
def gitlab_options
OmniAuth::Strategies::GitLab.default_options[:client_options].to_h.symbolize_keys
end
end
end
end

View File

@ -1,65 +0,0 @@
# frozen_string_literal: true
module Gitlab
module GitlabImport
class Importer
attr_reader :project, :client
def initialize(project)
@project = project
import_data = project.import_data
if import_data && import_data.credentials && import_data.credentials[:password]
@client = Client.new(import_data.credentials[:password])
@formatter = Gitlab::ImportFormatter.new
else
raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}"
end
end
def execute
ActiveRecord::Base.no_touching do
project_identifier = CGI.escape(project.import_source)
# Issues && Comments
issues = client.issues(project_identifier)
issues.each do |issue|
body = [@formatter.author_line(issue["author"]["name"])]
body << issue["description"]
comments = client.issue_comments(project_identifier, issue["iid"])
if comments.any?
body << @formatter.comments_header
end
comments.each do |comment|
body << @formatter.comment(comment["author"]["name"], comment["created_at"], comment["body"])
end
project.issues.create!(
iid: issue["iid"],
description: body.join,
title: issue["title"],
state: issue["state"],
updated_at: issue["updated_at"],
author_id: gitlab_user_id(project, issue["author"]["id"]),
confidential: issue["confidential"]
)
end
end
true
end
private
# rubocop: disable CodeReuse/ActiveRecord
def gitlab_user_id(project, gitlab_id)
user_id = User.by_provider_and_extern_uid(:gitlab, gitlab_id).select(:id).first&.id
user_id || project.creator_id
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end

View File

@ -1,30 +0,0 @@
# frozen_string_literal: true
module Gitlab
module GitlabImport
class ProjectCreator
attr_reader :repo, :namespace, :current_user, :session_data
def initialize(repo, namespace, current_user, session_data)
@repo = repo
@namespace = namespace
@current_user = current_user
@session_data = session_data
end
def execute
::Projects::CreateService.new(
current_user,
name: repo["name"],
path: repo["path"],
description: repo["description"],
namespace_id: namespace.id,
visibility_level: Gitlab::VisibilityLevel.level_value(repo["visibility"]),
import_type: "gitlab",
import_source: repo["path_with_namespace"],
import_url: repo["http_url_to_repo"].sub("://", "://oauth2:#{@session_data[:gitlab_access_token]}@")
).execute
end
end
end
end

View File

@ -13,7 +13,6 @@ module Gitlab
ImportSource.new('github', 'GitHub', Gitlab::GithubImport::ParallelImporter),
ImportSource.new('bitbucket', 'Bitbucket Cloud', Gitlab::BitbucketImport::Importer),
ImportSource.new('bitbucket_server', 'Bitbucket Server', Gitlab::BitbucketServerImport::Importer),
ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer),
ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer),
ImportSource.new('git', 'Repository by URL', nil),
ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer),

View File

@ -8,7 +8,8 @@ module Gitlab
STUB_CLASSES = {
agent_tracker: Gitlab::Agent::AgentTracker::Rpc::AgentTracker::Stub,
configuration_project: Gitlab::Agent::ConfigurationProject::Rpc::ConfigurationProject::Stub
configuration_project: Gitlab::Agent::ConfigurationProject::Rpc::ConfigurationProject::Stub,
notifications: Gitlab::Agent::Notifications::Rpc::Notifications::Stub
}.freeze
ConfigurationError = Class.new(StandardError)
@ -39,6 +40,18 @@ module Gitlab
.to_a
end
def send_git_push_event(project:)
request = Gitlab::Agent::Notifications::Rpc::GitPushEventRequest.new(
project: Gitlab::Agent::Notifications::Rpc::Project.new(
id: project.id,
full_path: project.full_path
)
)
stub_for(:notifications)
.git_push_event(request, metadata: metadata)
end
private
def stub_for(service)

View File

@ -55,11 +55,15 @@ module Gitlab
def config_file_name
[
# Instance specific config sources:
ENV["GITLAB_REDIS_#{store_name.underscore.upcase}_CONFIG_FILE"],
config_file_path("redis.#{store_name.underscore}.yml"),
# The current Redis instance may have been split off from another one
# (e.g. TraceChunks was split off from SharedState).
config_fallback&.config_file_name
config_fallback&.config_file_name,
# Global config sources:
ENV['GITLAB_REDIS_CONFIG_FILE']
].compact.first
end

View File

@ -661,7 +661,6 @@ module Gitlab
time_frame = metric_time_period(time_period)
counters = {
gitlab_project: add_metric('CountImportedProjectsMetric', time_frame: time_frame, options: { import_type: 'gitlab_project' }),
gitlab: add_metric('CountImportedProjectsMetric', time_frame: time_frame, options: { import_type: 'gitlab' }),
github: add_metric('CountImportedProjectsMetric', time_frame: time_frame, options: { import_type: 'github' }),
bitbucket: add_metric('CountImportedProjectsMetric', time_frame: time_frame, options: { import_type: 'bitbucket' }),
bitbucket_server: add_metric('CountImportedProjectsMetric', time_frame: time_frame, options: { import_type: 'bitbucket_server' }),

View File

@ -11140,6 +11140,12 @@ msgstr ""
msgid "Completed in %{duration_seconds} seconds (%{relative_time})"
msgstr ""
msgid "Compliance Report|Export as CSV"
msgstr ""
msgid "Compliance Report|Export frameworks as CSV. You will be emailed after export is processed."
msgstr ""
msgid "Compliance Report|Frameworks"
msgstr ""
@ -45997,9 +46003,6 @@ msgstr ""
msgid "This merge request was merged. To apply this suggestion, edit this file directly."
msgstr ""
msgid "This namespace has already been taken! Please choose another one."
msgstr ""
msgid "This only applies to repository indexing operations."
msgstr ""
@ -50871,6 +50874,9 @@ msgstr ""
msgid "Workspaces|GitLab Workspaces is a powerful collaborative platform that provides a comprehensive set of tools for software development teams to manage their entire development lifecycle."
msgstr ""
msgid "Workspaces|Hours before automatic termination"
msgstr ""
msgid "Workspaces|New workspace"
msgstr ""
@ -53078,6 +53084,9 @@ msgstr ""
msgid "help"
msgstr ""
msgid "hours"
msgstr ""
msgid "http:"
msgstr ""

View File

@ -7,13 +7,12 @@ RSpec.describe 'mail_room.yml', feature_category: :service_desk do
let(:mailroom_config_path) { 'config/mail_room.yml' }
let(:gitlab_config_path) { 'config/mail_room.yml' }
let(:queues_config_source_path) { 'config/redis.queues.yml' }
let(:queues_config_path) { 'config/redis.queues.yml' }
let(:queue_config_tmp_path) { 'config/redis.queues.yml.tmp' }
let(:configuration) do
vars = {
'MAIL_ROOM_GITLAB_CONFIG_FILE' => absolute_path(gitlab_config_path)
'MAIL_ROOM_GITLAB_CONFIG_FILE' => absolute_path(gitlab_config_path),
'GITLAB_REDIS_QUEUES_CONFIG_FILE' => absolute_path(queues_config_path)
}
cmd = "puts ERB.new(File.read(#{absolute_path(mailroom_config_path).inspect})).result"
@ -26,16 +25,8 @@ RSpec.describe 'mail_room.yml', feature_category: :service_desk do
YAML.safe_load(output, permitted_classes: [Symbol])
end
around do |example|
# put aside config/redis.queues.yml if it exists
FileUtils.mv(queues_config_path, queue_config_tmp_path) if File.file?(queues_config_path)
# set config/redis.queues.yml
FileUtils.cp(queues_config_source_path, queues_config_path) if queues_config_source_path != queues_config_path
example.run
FileUtils.mv(queue_config_tmp_path, queues_config_path) if File.file?(queue_config_tmp_path)
before do
stub_env('GITLAB_REDIS_QUEUES_CONFIG_FILE', absolute_path(queues_config_path))
end
context 'when incoming email is disabled' do
@ -48,7 +39,7 @@ RSpec.describe 'mail_room.yml', feature_category: :service_desk do
context 'when both incoming email and service desk email are enabled' do
let(:gitlab_config_path) { 'spec/fixtures/config/mail_room_enabled.yml' }
let(:queues_config_source_path) { 'spec/fixtures/config/redis_new_format_host.yml' }
let(:queues_config_path) { 'spec/fixtures/config/redis_new_format_host.yml' }
let(:gitlab_redis_queues) { Gitlab::Redis::Queues.new(Rails.env) }
it 'contains the intended configuration' do
@ -78,7 +69,7 @@ RSpec.describe 'mail_room.yml', feature_category: :service_desk do
context 'when both incoming email and service desk email are enabled for Microsoft Graph' do
let(:gitlab_config_path) { 'spec/fixtures/config/mail_room_enabled_ms_graph.yml' }
let(:queues_config_source_path) { 'spec/fixtures/config/redis_new_format_host.yml' }
let(:queues_config_path) { 'spec/fixtures/config/redis_new_format_host.yml' }
let(:gitlab_redis_queues) { Gitlab::Redis::Queues.new(Rails.env) }
it 'contains the intended configuration' do

View File

@ -1,313 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Import::GitlabController, feature_category: :importers do
include ImportSpecHelper
let(:user) { create(:user) }
let(:token) { "asdasd12345" }
let(:access_params) { { gitlab_access_token: token } }
def assign_session_token
session[:gitlab_access_token] = token
end
before do
sign_in(user)
allow(controller).to receive(:gitlab_import_enabled?).and_return(true)
end
describe "GET callback" do
it "updates access token" do
allow_next_instance_of(Gitlab::GitlabImport::Client) do |instance|
allow(instance).to receive(:get_token).and_return(token)
end
stub_omniauth_provider('gitlab')
get :callback
expect(session[:gitlab_access_token]).to eq(token)
expect(controller).to redirect_to(status_import_gitlab_url)
end
it "importable_repos should return an array" do
allow_next_instance_of(Gitlab::GitlabImport::Client) do |instance|
allow(instance).to receive(:projects).and_return([{ "id": 1 }].to_enum)
end
expect(controller.send(:importable_repos)).to be_an_instance_of(Array)
end
it "passes namespace_id query param to status if provided" do
namespace_id = 30
allow_next_instance_of(Gitlab::GitlabImport::Client) do |instance|
allow(instance).to receive(:get_token).and_return(token)
end
get :callback, params: { namespace_id: namespace_id }
expect(controller).to redirect_to(status_import_gitlab_url(namespace_id: namespace_id))
end
end
describe "GET status" do
let(:repo_fake) { Struct.new(:id, :path, :path_with_namespace, :web_url, keyword_init: true) }
let(:repo) { repo_fake.new(id: 1, path: 'vim', path_with_namespace: 'asd/vim', web_url: 'https://gitlab.com/asd/vim') }
context 'when session contains access token' do
before do
assign_session_token
end
it_behaves_like 'import controller status' do
let(:repo_id) { repo.id }
let(:import_source) { repo.path_with_namespace }
let(:provider_name) { 'gitlab' }
let(:client_repos_field) { :projects }
end
end
it 'redirects to auth if session does not contain access token' do
remote_gitlab_url = 'https://test.host/auth/gitlab'
allow(Gitlab::GitlabImport::Client)
.to receive(:new)
.and_return(double(authorize_url: remote_gitlab_url))
get :status
expect(response).to redirect_to(remote_gitlab_url)
end
end
describe "POST create" do
let(:project) { create(:project) }
let(:gitlab_username) { user.username }
let(:gitlab_user) do
{ username: gitlab_username }.with_indifferent_access
end
let(:gitlab_repo) do
{
path: 'vim',
path_with_namespace: "#{gitlab_username}/vim",
owner: { name: gitlab_username },
namespace: { path: gitlab_username }
}.with_indifferent_access
end
before do
stub_client(user: gitlab_user, project: gitlab_repo)
assign_session_token
end
it 'returns 200 response when the project is imported successfully' do
allow(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
.and_return(double(execute: project))
post :create, format: :json
expect(response).to have_gitlab_http_status(:ok)
end
it 'returns 422 response when the project could not be imported' do
allow(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
.and_return(double(execute: build(:project)))
post :create, format: :json
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
context "when the repository owner is the GitLab.com user" do
context "when the GitLab.com user and GitLab server user's usernames match" do
it "takes the current user's namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
.and_return(double(execute: project))
post :create, format: :json
end
end
context "when the GitLab.com user and GitLab server user's usernames don't match" do
let(:gitlab_username) { "someone_else" }
it "takes the current user's namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
.and_return(double(execute: project))
post :create, format: :json
end
end
end
context "when the repository owner is not the GitLab.com user" do
let(:other_username) { "someone_else" }
before do
gitlab_repo["namespace"]["path"] = other_username
assign_session_token
end
context "when a namespace with the GitLab.com user's username already exists" do
let!(:existing_namespace) { create(:group, name: other_username) }
context "when the namespace is owned by the GitLab server user" do
before do
existing_namespace.add_owner(user)
end
it "takes the existing namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, existing_namespace, user, access_params)
.and_return(double(execute: project))
post :create, format: :json
end
end
context "when the namespace is not owned by the GitLab server user" do
it "doesn't create a project" do
expect(Gitlab::GitlabImport::ProjectCreator)
.not_to receive(:new)
post :create, format: :json
end
end
end
context "when a namespace with the GitLab.com user's username doesn't exist" do
context "when current user can create namespaces" do
it "creates the namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).and_return(double(execute: project))
expect { post :create, format: :json }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, an_instance_of(Group), user, access_params)
.and_return(double(execute: project))
post :create, format: :json
end
end
context "when current user can't create namespaces" do
before do
user.update_attribute(:can_create_group, false)
end
it "doesn't create the namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).and_return(double(execute: project))
expect { post :create, format: :json }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
.and_return(double(execute: project))
post :create, format: :json
end
end
end
context 'user has chosen an existing nested namespace for the project' do
let(:parent_namespace) { create(:group, name: 'foo') }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
before do
parent_namespace.add_owner(user)
nested_namespace.add_owner(user)
end
it 'takes the selected namespace and name' do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, nested_namespace, user, access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: nested_namespace.full_path }, format: :json
end
end
context 'user has chosen a non-existent nested namespaces for the project' do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: 'foo/bar' }, format: :json
end
it 'creates the namespaces' do
allow(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
.and_return(double(execute: project))
expect { post :create, params: { target_namespace: 'foo/bar' }, format: :json }
.to change { Namespace.count }.by(2)
end
it 'new namespace has the right parent' do
allow(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: 'foo/bar' }, format: :json
expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo')
end
end
context 'user has chosen existent and non-existent nested namespaces and name for the project' do
let(:test_name) { 'test_name' }
let!(:parent_namespace) { create(:group, name: 'foo') }
before do
parent_namespace.add_owner(user)
end
it 'takes the selected namespace and name' do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
.and_return(double(execute: project))
post :create, params: { target_namespace: 'foo/foobar/bar' }, format: :json
end
it 'creates the namespaces' do
allow(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
.and_return(double(execute: project))
expect { post :create, params: { target_namespace: 'foo/foobar/bar' }, format: :json }
.to change { Namespace.count }.by(2)
end
end
context 'when user can not create projects in the chosen namespace' do
it 'returns 422 response' do
other_namespace = create(:group, name: 'other_namespace')
post :create, params: { target_namespace: other_namespace.name }, format: :json
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
end
it_behaves_like 'project import rate limiter'
end
end
end

View File

@ -85,7 +85,7 @@ RSpec.describe Projects::MergeRequests::ConflictsController do
end
it 'includes each file that has conflicts' do
filenames = json_response['files'].map { |file| file['new_path'] }
filenames = json_response['files'].pluck('new_path')
expect(filenames).to contain_exactly('files/ruby/popen.rb', 'files/ruby/regex.rb')
end
@ -114,7 +114,7 @@ RSpec.describe Projects::MergeRequests::ConflictsController do
it 'has unique section IDs across files' do
section_ids = json_response['files'].flat_map do |file|
file['sections'].map { |section| section['id'] }.compact
file['sections'].pluck('id').compact
end
expect(section_ids.uniq).to eq(section_ids)

View File

@ -341,7 +341,7 @@ RSpec.describe Projects::MergeRequests::DiffsController, feature_category: :code
it 'only renders the diffs for the path given' do
diff_for_path(old_path: existing_path, new_path: existing_path)
paths = json_response['diff_files'].map { |file| file['new_path'] }
paths = json_response['diff_files'].pluck('new_path')
expect(paths).to include(existing_path)
end

View File

@ -232,7 +232,7 @@ RSpec.describe 'Merge request > User sees versions', :js, feature_category: :cod
end
it 'only shows diffs from the commit' do
diff_commit_ids = find_all('.diff-file [data-commit-id]').map { |diff| diff['data-commit-id'] }
diff_commit_ids = find_all('.diff-file [data-commit-id]').pluck('data-commit-id')
expect(diff_commit_ids).not_to be_empty
expect(diff_commit_ids).to all(eq(params[:commit_id]))

View File

@ -79,7 +79,7 @@ RSpec.describe 'Set up Mattermost slash commands', :js, feature_category: :integ
select_element = find('#mattermost_team_id')
expect(select_element['disabled']).to be_falsey
expect(select_element['disabled']).to eq('false')
expect(select_element.all('option').count).to eq(3)
end

View File

@ -85,7 +85,6 @@ RSpec.describe 'New project', :js, feature_category: :projects do
expect(page).to have_link('GitHub')
expect(page).to have_link('Bitbucket')
expect(page).to have_link('GitLab.com')
expect(page).to have_button('Repository by URL')
expect(page).to have_link('GitLab export')
end
@ -136,7 +135,6 @@ RSpec.describe 'New project', :js, feature_category: :projects do
'github': :new_import_github_path,
'bitbucket': :status_import_bitbucket_path,
'bitbucket server': :status_import_bitbucket_server_path,
'gitlab.com': :status_import_gitlab_path,
'fogbugz': :new_import_fogbugz_path,
'gitea': :new_import_gitea_path,
'manifest': :new_import_manifest_path
@ -572,25 +570,6 @@ RSpec.describe 'New project', :js, feature_category: :projects do
end
end
context 'from GitLab.com', :js do
let(:target_link) { 'GitLab.com' }
let(:provider) { :gitlab }
context 'as a user' do
let(:user) { create(:user) }
let(:oauth_config_instructions) { 'To enable importing projects from GitLab.com, ask your GitLab administrator to configure OAuth integration' }
it_behaves_like 'has instructions to enable OAuth'
end
context 'as an admin', :do_not_mock_admin_mode_setting do
let(:user) { create(:admin) }
let(:oauth_config_instructions) { 'To enable importing projects from GitLab.com, as administrator you need to configure OAuth integration' }
it_behaves_like 'has instructions to enable OAuth'
end
end
describe 'sidebar' do
let_it_be(:user) { create(:user) }
let_it_be(:parent_group) { create(:group) }

View File

@ -134,23 +134,6 @@ describe('formatTimeAsSummary', () => {
});
});
describe('durationTimeFormatted', () => {
it.each`
duration | expectedOutput
${0} | ${'00:00:00'}
${12} | ${'00:00:12'}
${60} | ${'00:01:00'}
${60 + 27} | ${'00:01:27'}
${120 + 21} | ${'00:02:21'}
${4 * 60 * 60 + 25 * 60 + 37} | ${'04:25:37'}
${35 * 60 * 60 + 3 * 60 + 7} | ${'35:03:07'}
${-60} | ${'-00:01:00'}
${-(35 * 60 * 60 + 3 * 60 + 7)} | ${'-35:03:07'}
`('returns $expectedOutput when provided $duration', ({ duration, expectedOutput }) => {
expect(utils.durationTimeFormatted(duration)).toBe(expectedOutput);
});
});
describe('formatUtcOffset', () => {
it.each`
offset | expected

View File

@ -276,19 +276,35 @@ describe('getTimeframeWindowFrom', () => {
});
describe('formatTime', () => {
const expectedTimestamps = [
[0, '00:00:00'],
[1000, '00:00:01'],
[42000, '00:00:42'],
[121000, '00:02:01'],
[10921000, '03:02:01'],
[108000000, '30:00:00'],
];
it.each`
milliseconds | expected
${0} | ${'00:00:00'}
${1} | ${'00:00:00'}
${499} | ${'00:00:00'}
${500} | ${'00:00:01'}
${1000} | ${'00:00:01'}
${42 * 1000} | ${'00:00:42'}
${60 * 1000} | ${'00:01:00'}
${(60 + 1) * 1000} | ${'00:01:01'}
${(3 * 60 * 60 + 2 * 60 + 1) * 1000} | ${'03:02:01'}
${(11 * 60 * 60 + 59 * 60 + 59) * 1000} | ${'11:59:59'}
${30 * 60 * 60 * 1000} | ${'30:00:00'}
${(35 * 60 * 60 + 3 * 60 + 7) * 1000} | ${'35:03:07'}
${240 * 60 * 60 * 1000} | ${'240:00:00'}
${1000 * 60 * 60 * 1000} | ${'1000:00:00'}
`(`formats $milliseconds ms as $expected`, ({ milliseconds, expected }) => {
expect(datetimeUtility.formatTime(milliseconds)).toBe(expected);
});
expectedTimestamps.forEach(([milliseconds, expectedTimestamp]) => {
it(`formats ${milliseconds}ms as ${expectedTimestamp}`, () => {
expect(datetimeUtility.formatTime(milliseconds)).toBe(expectedTimestamp);
});
it.each`
milliseconds | expected
${-1} | ${'00:00:00'}
${-499} | ${'00:00:00'}
${-1000} | ${'-00:00:01'}
${-60 * 1000} | ${'-00:01:00'}
${-(35 * 60 * 60 + 3 * 60 + 7) * 1000} | ${'-35:03:07'}
`(`when negative, formats $milliseconds ms as $expected`, ({ milliseconds, expected }) => {
expect(datetimeUtility.formatTime(milliseconds)).toBe(expected);
});
});

View File

@ -1,111 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::GitlabImport::Client do
include ImportSpecHelper
let(:token) { '123456' }
let(:client) { described_class.new(token) }
before do
stub_omniauth_provider('gitlab')
end
it 'all OAuth2 client options are symbols' do
expect(client.client.options.keys).to all(be_kind_of(Symbol))
end
it 'uses membership and simple flags' do
stub_request('/api/v4/projects?membership=true&page=1&per_page=100&simple=true')
expect_next_instance_of(OAuth2::Response) do |instance|
expect(instance).to receive(:parsed).and_return([])
end
expect(client.projects.to_a).to eq []
end
shared_examples 'pagination params' do
before do
allow_next_instance_of(OAuth2::Response) do |instance|
allow(instance).to receive(:parsed).and_return([])
end
end
it 'allows page_limit param' do
allow_next_instance_of(OAuth2::Response) do |instance|
allow(instance).to receive(:parsed).and_return(element_list)
end
expect(client).to receive(:lazy_page_iterator).with(hash_including(page_limit: 2)).and_call_original
client.send(method, *args, page_limit: 2, per_page: 1).to_a
end
it 'allows per_page param' do
expect(client).to receive(:lazy_page_iterator).with(hash_including(per_page: 2)).and_call_original
client.send(method, *args, per_page: 2).to_a
end
it 'allows starting_page param' do
expect(client).to receive(:lazy_page_iterator).with(hash_including(starting_page: 3)).and_call_original
client.send(method, *args, starting_page: 3).to_a
end
end
describe '#projects' do
subject(:method) { :projects }
let(:args) { [] }
let(:element_list) { build_list(:project, 2) }
before do
stub_request('/api/v4/projects?membership=true&page=1&per_page=1&simple=true')
stub_request('/api/v4/projects?membership=true&page=2&per_page=1&simple=true')
stub_request('/api/v4/projects?membership=true&page=1&per_page=2&simple=true')
stub_request('/api/v4/projects?membership=true&page=3&per_page=100&simple=true')
end
it_behaves_like 'pagination params'
end
describe '#issues' do
subject(:method) { :issues }
let(:args) { [1] }
let(:element_list) { build_list(:issue, 2) }
before do
stub_request('/api/v4/projects/1/issues?page=1&per_page=1')
stub_request('/api/v4/projects/1/issues?page=2&per_page=1')
stub_request('/api/v4/projects/1/issues?page=1&per_page=2')
stub_request('/api/v4/projects/1/issues?page=3&per_page=100')
end
it_behaves_like 'pagination params'
end
describe '#issue_comments' do
subject(:method) { :issue_comments }
let(:args) { [1, 1] }
let(:element_list) { build_list(:note_on_issue, 2) }
before do
stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=1')
stub_request('/api/v4/projects/1/issues/1/notes?page=2&per_page=1')
stub_request('/api/v4/projects/1/issues/1/notes?page=1&per_page=2')
stub_request('/api/v4/projects/1/issues/1/notes?page=3&per_page=100')
end
it_behaves_like 'pagination params'
end
def stub_request(path)
WebMock.stub_request(:get, "https://gitlab.com#{path}")
.to_return(status: 200)
end
end

View File

@ -1,57 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::GitlabImport::Importer do
include ImportSpecHelper
describe '#execute' do
before do
stub_omniauth_provider('gitlab')
stub_request('issues', [
{
'id' => 2579857,
'iid' => 3,
'title' => 'Issue',
'description' => 'Lorem ipsum',
'state' => 'opened',
'confidential' => true,
'author' => {
'id' => 283999,
'name' => 'John Doe'
}
}
].to_json)
stub_request('issues/3/notes', [].to_json)
end
it 'persists issues' do
project = create(:project, import_source: 'asd/vim')
project.build_import_data(credentials: { password: 'password' })
subject = described_class.new(project)
subject.execute
expected_attributes = {
iid: 3,
title: 'Issue',
description: "*Created by: John Doe*\n\nLorem ipsum",
state: 'opened',
confidential: true,
author_id: project.creator_id
}
expect(project.issues.first).to have_attributes(expected_attributes)
end
def stub_request(path, body)
url = "https://gitlab.com/api/v4/projects/asd%2Fvim/#{path}?page=1&per_page=100"
WebMock.stub_request(:get, url)
.to_return(
headers: { 'Content-Type' => 'application/json' },
body: body
)
end
end
end

View File

@ -1,37 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::GitlabImport::ProjectCreator do
let(:user) { create(:user) }
let(:repo) do
{
name: 'vim',
path: 'vim',
visibility_level: Gitlab::VisibilityLevel::PRIVATE,
path_with_namespace: 'asd/vim',
http_url_to_repo: "https://gitlab.com/asd/vim.git",
owner: { name: "john" }
}.with_indifferent_access
end
let(:namespace) { create(:group) }
let(:token) { "asdffg" }
let(:access_params) { { gitlab_access_token: token } }
before do
namespace.add_owner(user)
end
it 'creates project' do
allow_next_instance_of(Project) do |project|
allow(project).to receive(:add_import_job)
end
project_creator = described_class.new(repo, namespace, user, access_params)
project = project_creator.execute
expect(project.import_url).to eq("https://oauth2:asdffg@gitlab.com/asd/vim.git")
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::ImportSources do
RSpec.describe Gitlab::ImportSources, feature_category: :importers do
describe '.options' do
it 'returns a hash' do
expected =
@ -10,7 +10,6 @@ RSpec.describe Gitlab::ImportSources do
'GitHub' => 'github',
'Bitbucket Cloud' => 'bitbucket',
'Bitbucket Server' => 'bitbucket_server',
'GitLab.com' => 'gitlab',
'FogBugz' => 'fogbugz',
'Repository by URL' => 'git',
'GitLab export' => 'gitlab_project',
@ -29,7 +28,6 @@ RSpec.describe Gitlab::ImportSources do
github
bitbucket
bitbucket_server
gitlab
fogbugz
git
gitlab_project
@ -48,7 +46,6 @@ RSpec.describe Gitlab::ImportSources do
github
bitbucket
bitbucket_server
gitlab
fogbugz
gitlab_project
gitea
@ -63,7 +60,6 @@ RSpec.describe Gitlab::ImportSources do
'github' => Gitlab::GithubImport::ParallelImporter,
'bitbucket' => Gitlab::BitbucketImport::Importer,
'bitbucket_server' => Gitlab::BitbucketServerImport::Importer,
'gitlab' => Gitlab::GitlabImport::Importer,
'fogbugz' => Gitlab::FogbugzImport::Importer,
'git' => nil,
'gitlab_project' => Gitlab::ImportExport::Importer,
@ -83,7 +79,6 @@ RSpec.describe Gitlab::ImportSources do
'github' => 'GitHub',
'bitbucket' => 'Bitbucket Cloud',
'bitbucket_server' => 'Bitbucket Server',
'gitlab' => 'GitLab.com',
'fogbugz' => 'FogBugz',
'git' => 'Repository by URL',
'gitlab_project' => 'GitLab export',

View File

@ -109,6 +109,35 @@ RSpec.describe Gitlab::Kas::Client do
it { expect(subject).to eq(agent_configurations) }
end
describe '#send_git_push_event' do
let(:stub) { instance_double(Gitlab::Agent::Notifications::Rpc::Notifications::Stub) }
let(:request) { instance_double(Gitlab::Agent::Notifications::Rpc::GitPushEventRequest) }
let(:project_param) { instance_double(Gitlab::Agent::Notifications::Rpc::Project) }
let(:response) { double(Gitlab::Agent::Notifications::Rpc::GitPushEventResponse) }
subject { described_class.new.send_git_push_event(project: project) }
before do
expect(Gitlab::Agent::Notifications::Rpc::Notifications::Stub).to receive(:new)
.with('example.kas.internal', :this_channel_is_insecure, timeout: described_class::TIMEOUT)
.and_return(stub)
expect(Gitlab::Agent::Notifications::Rpc::Project).to receive(:new)
.with(id: project.id, full_path: project.full_path)
.and_return(project_param)
expect(Gitlab::Agent::Notifications::Rpc::GitPushEventRequest).to receive(:new)
.with(project: project_param)
.and_return(request)
expect(stub).to receive(:git_push_event)
.with(request, metadata: { 'authorization' => 'bearer test-token' })
.and_return(response)
end
it { expect(subject).to eq(response) }
end
describe 'with grpcs' do
let(:stub) { instance_double(Gitlab::Agent::ConfigurationProject::Rpc::ConfigurationProject::Stub) }
let(:credentials) { instance_double(GRPC::Core::ChannelCredentials) }

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Redis::Cache do
let(:instance_specific_config_file) { "config/redis.cache.yml" }
let(:environment_config_file_name) { "GITLAB_REDIS_CACHE_CONFIG_FILE" }
include_examples "redis_shared_examples"

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Redis::Queues do
let(:instance_specific_config_file) { "config/redis.queues.yml" }
let(:environment_config_file_name) { "GITLAB_REDIS_QUEUES_CONFIG_FILE" }
include_examples "redis_shared_examples"

View File

@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Redis::SharedState do
let(:instance_specific_config_file) { "config/redis.shared_state.yml" }
let(:environment_config_file_name) { "GITLAB_REDIS_SHARED_STATE_CONFIG_FILE" }
include_examples "redis_shared_examples"
end

View File

@ -7,6 +7,7 @@ RSpec.describe Gitlab::Redis::SidekiqStatus do
# to move away from `Sidekiq.redis` for sidekiq status data. Thus, we use the
# same store configuration as the former.
let(:instance_specific_config_file) { "config/redis.shared_state.yml" }
let(:environment_config_file_name) { "GITLAB_REDIS_SHARED_STATE_CONFIG_FILE" }
include_examples "redis_shared_examples"

View File

@ -263,7 +263,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures, feature_category: :servic
for_defined_days_back do
user = create(:user)
%w(gitlab_project gitlab github bitbucket bitbucket_server gitea git manifest fogbugz).each do |type|
%w(gitlab_project github bitbucket bitbucket_server gitea git manifest fogbugz).each do |type|
create(:project, import_type: type, creator_id: user.id)
end
@ -292,11 +292,10 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures, feature_category: :servic
git: 2,
gitea: 2,
github: 2,
gitlab: 2,
gitlab_migration: 2,
gitlab_project: 2,
manifest: 2,
total: 18
total: 16
},
issue_imports: {
jira: 2,
@ -320,11 +319,10 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures, feature_category: :servic
git: 1,
gitea: 1,
github: 1,
gitlab: 1,
gitlab_migration: 1,
gitlab_project: 1,
manifest: 1,
total: 9
total: 8
},
issue_imports: {
jira: 1,

View File

@ -334,7 +334,7 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do
it 'removes phabricator as an import source' do
subject.validate
expect(subject.import_sources).to eq(%w[github bitbucket gitlab git gitlab_project gitea manifest])
expect(subject.import_sources).to eq(%w[github bitbucket git gitlab_project gitea manifest])
end
end

View File

@ -62,7 +62,7 @@ end
# realtime_changes_import_github GET /import/github/realtime_changes(.:format) import/github#jobs
# import_github POST /import/github(.:format) import/github#create
# new_import_github GET /import/github/new(.:format) import/github#new
RSpec.describe Import::GithubController, 'routing' do
RSpec.describe Import::GithubController, 'routing', feature_category: :importers do
it_behaves_like 'importer routing' do
let(:provider) { 'github' }
let(:is_realtime) { true }
@ -86,7 +86,7 @@ end
# realtime_changes_import_gitea GET /import/gitea/realtime_changes(.:format) import/gitea#jobs
# import_gitea POST /import/gitea(.:format) import/gitea#create
# new_import_gitea GET /import/gitea/new(.:format) import/gitea#new
RSpec.describe Import::GiteaController, 'routing' do
RSpec.describe Import::GiteaController, 'routing', feature_category: :importers do
it_behaves_like 'importer routing' do
let(:except_actions) { [:callback] }
let(:provider) { 'gitea' }
@ -98,23 +98,11 @@ RSpec.describe Import::GiteaController, 'routing' do
end
end
# status_import_gitlab GET /import/gitlab/status(.:format) import/gitlab#status
# callback_import_gitlab GET /import/gitlab/callback(.:format) import/gitlab#callback
# realtime_changes_import_gitlab GET /import/gitlab/realtime_changes(.:format) import/gitlab#realtime_changes
# import_gitlab POST /import/gitlab(.:format) import/gitlab#create
RSpec.describe Import::GitlabController, 'routing' do
it_behaves_like 'importer routing' do
let(:except_actions) { [:new] }
let(:provider) { 'gitlab' }
let(:is_realtime) { true }
end
end
# status_import_bitbucket GET /import/bitbucket/status(.:format) import/bitbucket#status
# callback_import_bitbucket GET /import/bitbucket/callback(.:format) import/bitbucket#callback
# realtime_changes_import_bitbucket GET /import/bitbucket/realtime_changes(.:format) import/bitbucket#realtime_changes
# import_bitbucket POST /import/bitbucket(.:format) import/bitbucket#create
RSpec.describe Import::BitbucketController, 'routing' do
RSpec.describe Import::BitbucketController, 'routing', feature_category: :importers do
it_behaves_like 'importer routing' do
let(:except_actions) { [:new] }
let(:provider) { 'bitbucket' }
@ -127,7 +115,7 @@ end
# realtime_changes_import_bitbucket_server GET /import/bitbucket_server/realtime_changes(.:format) import/bitbucket_server#realtime_changes
# new_import_bitbucket_server GET /import/bitbucket_server/new(.:format) import/bitbucket_server#new
# import_bitbucket_server POST /import/bitbucket_server(.:format) import/bitbucket_server#create
RSpec.describe Import::BitbucketServerController, 'routing' do
RSpec.describe Import::BitbucketServerController, 'routing', feature_category: :importers do
it_behaves_like 'importer routing' do
let(:provider) { 'bitbucket_server' }
let(:is_realtime) { true }
@ -141,7 +129,7 @@ end
# create_user_map_import_fogbugz POST /import/fogbugz/user_map(.:format) import/fogbugz#create_user_map
# import_fogbugz POST /import/fogbugz(.:format) import/fogbugz#create
# new_import_fogbugz GET /import/fogbugz/new(.:format) import/fogbugz#new
RSpec.describe Import::FogbugzController, 'routing' do
RSpec.describe Import::FogbugzController, 'routing', feature_category: :importers do
it_behaves_like 'importer routing' do
let(:except_actions) { [:callback] }
let(:provider) { 'fogbugz' }
@ -164,7 +152,7 @@ end
# import_gitlab_project POST /import/gitlab_project(.:format) import/gitlab_projects#create
# POST /import/gitlab_project(.:format) import/gitlab_projects#create
# new_import_gitlab_project GET /import/gitlab_project/new(.:format) import/gitlab_projects#new
RSpec.describe Import::GitlabProjectsController, 'routing' do
RSpec.describe Import::GitlabProjectsController, 'routing', feature_category: :importers do
it 'to #create' do
expect(post('/import/gitlab_project')).to route_to('import/gitlab_projects#create')
end
@ -175,7 +163,7 @@ RSpec.describe Import::GitlabProjectsController, 'routing' do
end
# status_import_github_group GET /import/github_group/status(.:format) import/github_groups#status
RSpec.describe Import::GithubGroupsController, 'routing' do
RSpec.describe Import::GithubGroupsController, 'routing', feature_category: :importers do
it 'to #status' do
expect(get('/import/github_group/status')).to route_to('import/github_groups#status')
end

View File

@ -24,7 +24,15 @@ JS_CONSOLE_FILTER = Regexp.union(
'Download the Vue Devtools extension',
'Download the Apollo DevTools',
"Unrecognized feature: 'interest-cohort'",
'Does this page need fixes or improvements?'
'Does this page need fixes or improvements?',
# Needed after https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60933
# which opts out gitlab from FloC by default
# see https://web.dev/floc/ for more info on FloC
"Origin trial controlled feature not enabled: 'interest-cohort'",
# ERR_CONNECTION error could happen due to automated test session disabling browser network request
'net::ERR_CONNECTION'
]
)
@ -34,6 +42,9 @@ SCREENSHOT_FILENAME_LENGTH = ENV['CI'] || ENV['CI_SERVER'] ? 255 : 99
@blackhole_tcp_server = nil
chrome_options = Selenium::WebDriver::Chrome::Options.chrome
chromedriver_path = File.dirname(Selenium::WebDriver::SeleniumManager.driver_path(chrome_options))
# Run Workhorse on the given host and port, proxying to Puma on a UNIX socket,
# for a closer-to-production experience
Capybara.register_server :puma_via_workhorse do |app, port, host, **options|
@ -42,21 +53,14 @@ Capybara.register_server :puma_via_workhorse do |app, port, host, **options|
file.close! # We just want the filename
TestEnv.with_workhorse(host, port, socket_path) do
# In cases of multiple installations of chromedriver, prioritize the version installed by SeleniumManager
ENV['PATH'] = "#{chromedriver_path}:#{ENV['PATH']}" # rubocop:disable RSpec/EnvAssignment
Capybara.servers[:puma].call(app, nil, socket_path, **options)
end
end
Capybara.register_driver :chrome do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
# This enables access to logs with `page.driver.manage.get_log(:browser)`
loggingPrefs: {
browser: "ALL",
client: "ALL",
driver: "ALL",
server: "ALL"
}
)
options = Selenium::WebDriver::Chrome::Options.new
# Force the browser's scale factor to prevent inconsistencies on high-res devices
@ -82,9 +86,6 @@ Capybara.register_driver :chrome do |app|
options.add_preference("download.prompt_for_download", false)
end
# Chrome 75 defaults to W3C mode which doesn't allow console log access
options.add_option(:w3c, false)
# Set up a proxy server to block all external traffic.
@blackhole_tcp_server = TCPServer.new(0)
Thread.new do
@ -99,18 +100,11 @@ Capybara.register_driver :chrome do |app|
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
desired_capabilities: capabilities,
options: options
)
end
Capybara.register_driver :firefox do |app|
capabilities = Selenium::WebDriver::Remote::Capabilities.firefox(
log: {
level: :trace
}
)
options = Selenium::WebDriver::Firefox::Options.new(log_level: :trace)
options.add_argument("--window-size=#{CAPYBARA_WINDOW_SIZE.join(',')}")
@ -121,7 +115,6 @@ Capybara.register_driver :firefox do |app|
Capybara::Selenium::Driver.new(
app,
browser: :firefox,
desired_capabilities: capabilities,
options: options
)
end
@ -213,20 +206,11 @@ RSpec.configure do |config|
# fixed. If we raised the `JSException` the fixed test would be marked as
# failed again.
if example.exception && !example.exception.is_a?(RSpec::Core::Pending::PendingExampleFixedError)
begin
console = page.driver.browser.manage.logs.get(:browser)&.reject { |log| log.message =~ JS_CONSOLE_FILTER }
console = page.driver.browser.logs.get(:browser)&.reject { |log| log.message =~ JS_CONSOLE_FILTER }
if console.present?
message = "Unexpected browser console output:\n" + console.map(&:message).join("\n")
raise JSConsoleError, message
end
rescue Selenium::WebDriver::Error::WebDriverError => error
if error.message =~ %r{unknown command: session/[0-9a-zA-Z]+(?:/se)?/log}
message = "Unable to access Chrome javascript console logs. You may be using an outdated version of ChromeDriver."
raise JSConsoleError, message
else
raise error
end
if console.present?
message = "Unexpected browser console output:\n" + console.map(&:message).join("\n")
raise JSConsoleError, message
end
end

View File

@ -6468,9 +6468,6 @@
- './spec/lib/gitlab/github_import_spec.rb'
- './spec/lib/gitlab/github_import/user_finder_spec.rb'
- './spec/lib/gitlab/git/keep_around_spec.rb'
- './spec/lib/gitlab/gitlab_import/client_spec.rb'
- './spec/lib/gitlab/gitlab_import/importer_spec.rb'
- './spec/lib/gitlab/gitlab_import/project_creator_spec.rb'
- './spec/lib/gitlab/git/lfs_changes_spec.rb'
- './spec/lib/gitlab/git/lfs_pointer_file_spec.rb'
- './spec/lib/gitlab/git/merge_base_spec.rb'
@ -7002,6 +6999,7 @@
- './spec/lib/gitlab/sidekiq_config/worker_matcher_spec.rb'
- './spec/lib/gitlab/sidekiq_config/worker_router_spec.rb'
- './spec/lib/gitlab/sidekiq_config/worker_spec.rb'
- './spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb'
- './spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb'
- './spec/lib/gitlab/sidekiq_death_handler_spec.rb'
- './spec/lib/gitlab/sidekiq_logging/deduplication_logger_spec.rb'

View File

@ -22,7 +22,6 @@ RSpec.shared_examples 'edits content using the content editor' do
begin
refresh
rescue Selenium::WebDriver::Error::UnexpectedAlertOpenError
page.driver.browser.switch_to.alert.dismiss
end
expect(page).to have_text('Typing text in the content editor')

View File

@ -6,6 +6,7 @@ RSpec.shared_examples "redis_new_instance_shared_examples" do |name, fallback_cl
include TmpdirHelper
let(:instance_specific_config_file) { "config/redis.#{name}.yml" }
let(:environment_config_file_name) { "GITLAB_REDIS_#{name.upcase}_CONFIG_FILE" }
let(:fallback_config_file) { nil }
let(:rails_root) { mktmpdir }
@ -15,6 +16,32 @@ RSpec.shared_examples "redis_new_instance_shared_examples" do |name, fallback_cl
it_behaves_like "redis_shared_examples"
describe '.config_file_name' do
subject { described_class.config_file_name }
before do
# Undo top-level stub of config_file_name because we are testing that method now.
allow(described_class).to receive(:config_file_name).and_call_original
allow(described_class).to receive(:rails_root).and_return(rails_root)
FileUtils.mkdir_p(File.join(rails_root, 'config'))
end
context 'and there is a global env override' do
before do
stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
end
it { expect(subject).to eq('global override') }
context "and #{fallback_class.name.demodulize} has a different config file" do
let(:fallback_config_file) { 'fallback config file' }
it { expect(subject).to eq('fallback config file') }
end
end
end
describe '#fetch_config' do
subject { described_class.new('test').send(:fetch_config) }

View File

@ -39,6 +39,34 @@ RSpec.shared_examples "redis_shared_examples" do
context 'when there is no config file anywhere' do
it { expect(subject).to be_nil }
context 'and there is a global env override' do
before do
stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
end
it { expect(subject).to eq('global override') }
context 'and there is an instance specific config file' do
before do
FileUtils.touch(File.join(rails_root, instance_specific_config_file))
end
it { expect(subject).to eq("#{rails_root}/#{instance_specific_config_file}") }
it 'returns a path that exists' do
expect(File.file?(subject)).to eq(true)
end
context 'and there is a specific env override' do
before do
stub_env(environment_config_file_name, 'instance specific override')
end
it { expect(subject).to eq('instance specific override') }
end
end
end
end
end

View File

@ -0,0 +1,152 @@
# frozen_string_literal: true
require 'gitlab-dangerfiles'
require 'gitlab/dangerfiles/spec_helper'
require_relative '../../../tooling/danger/database_dictionary'
RSpec.describe Tooling::Danger::DatabaseDictionary, feature_category: :shared do
include_context "with dangerfile"
let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
subject(:database_dictionary) { fake_danger.new(helper: fake_helper) }
describe '#database_dictionary_files' do
let(:database_dictionary_files) do
[
'db/docs/ci_pipelines.yml',
'db/docs/projects.yml'
]
end
let(:other_files) do
[
'app/models/model.rb',
'app/assets/javascripts/file.js'
]
end
shared_examples 'an array of Found objects' do |change_type|
it 'returns an array of Found objects' do
expect(database_dictionary.database_dictionary_files(change_type: change_type))
.to contain_exactly(
an_instance_of(described_class::Found),
an_instance_of(described_class::Found)
)
expect(database_dictionary.database_dictionary_files(change_type: change_type).map(&:path))
.to eq(database_dictionary_files)
end
end
shared_examples 'an empty array' do |change_type|
it 'returns an array of Found objects' do
expect(database_dictionary.database_dictionary_files(change_type: change_type)).to be_empty
end
end
describe 'retrieves added database dictionary files' do
context 'with added added database dictionary files' do
let(:added_files) { database_dictionary_files }
include_examples 'an array of Found objects', :added
end
context 'without added added database dictionary files' do
let(:added_files) { other_files }
include_examples 'an empty array', :added
end
end
describe 'retrieves modified database dictionary files' do
context 'with modified modified database dictionary files' do
let(:modified_files) { database_dictionary_files }
include_examples 'an array of Found objects', :modified
end
context 'without modified modified database dictionary files' do
let(:modified_files) { other_files }
include_examples 'an empty array', :modified
end
end
describe 'retrieves deleted database dictionary files' do
context 'with deleted deleted database dictionary files' do
let(:deleted_files) { database_dictionary_files }
include_examples 'an array of Found objects', :deleted
end
context 'without deleted deleted database dictionary files' do
let(:deleted_files) { other_files }
include_examples 'an empty array', :deleted
end
end
end
describe described_class::Found do
let(:database_dictionary_path) { 'db/docs/ci_pipelines.yml' }
let(:gitlab_schema) { 'gitlab_ci' }
let(:yaml) do
{
'table_name' => 'ci_pipelines',
'classes' => ['Ci::Pipeline'],
'feature_categories' => ['continuous_integration'],
'description' => 'TODO',
'introduced_by_url' => 'https://gitlab.com/gitlab-org/gitlab/-/commit/c6ae290cea4b88ecaa9cfe0bc9d88e8fd32070c1',
'milestone' => '9.0',
'gitlab_schema' => gitlab_schema
}
end
let(:raw_yaml) { YAML.dump(yaml) }
subject(:found) { described_class.new(database_dictionary_path) }
before do
allow(File).to receive(:read).and_call_original
allow(File).to receive(:read).with(database_dictionary_path).and_return(raw_yaml)
end
described_class::ATTRIBUTES.each do |attribute|
describe "##{attribute}" do
it 'returns value from the YAML' do
expect(found.public_send(attribute)).to eq(yaml[attribute])
end
end
end
describe '#raw' do
it 'returns the raw YAML' do
expect(found.raw).to eq(raw_yaml)
end
end
describe '#ci_schema?' do
it { expect(found.ci_schema?).to be_truthy }
context 'with main schema' do
let(:gitlab_schema) { 'gitlab_main' }
it { expect(found.ci_schema?).to be_falsey }
end
end
describe '#main_schema?' do
it { expect(found.main_schema?).to be_falsey }
context 'with main schema' do
let(:gitlab_schema) { 'gitlab_main' }
it { expect(found.main_schema?).to be_truthy }
end
end
end
end

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
require 'yaml'
module Tooling
module Danger
module DatabaseDictionary
DICTIONARY_PATH_REGEXP = %r{db/docs/.*\.yml}
# `change_type` can be:
# - :added
# - :modified
# - :deleted
def database_dictionary_files(change_type:)
files = helper.public_send("#{change_type}_files") # rubocop:disable GitlabSecurity/PublicSend
files.filter_map { |path| Found.new(path) if path =~ DICTIONARY_PATH_REGEXP }
end
class Found
ATTRIBUTES = %w[
table_name classes feature_categories description introduced_by_url milestone gitlab_schema
].freeze
attr_reader :path
def initialize(path)
@path = path
end
ATTRIBUTES.each do |attribute|
define_method(attribute) do
yaml[attribute]
end
end
def raw
@raw ||= File.read(path)
end
def ci_schema?
gitlab_schema == 'gitlab_ci'
end
def main_schema?
gitlab_schema == 'gitlab_main'
end
private
def yaml
@yaml ||= YAML.safe_load(raw)
end
end
end
end
end

View File

@ -1 +1 @@
golang 1.19.7
golang 1.19.9