Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7c0c3a7dc9
commit
2a134be97d
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
4
Gemfile
4
Gemfile
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
15
Gemfile.lock
15
Gemfile.lock
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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: [
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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.|
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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' }),
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +1 @@
|
|||
golang 1.19.7
|
||||
golang 1.19.9
|
||||
|
|
|
|||
Loading…
Reference in New Issue