summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/application_controller.rb12
-rw-r--r--app/controllers/import/manifest_controller.rb93
-rw-r--r--app/helpers/namespaces_helper.rb11
-rw-r--r--app/services/groups/nested_create_service.rb12
-rw-r--r--app/views/import/_githubish_status.html.haml20
-rw-r--r--app/views/import/_project_status.html.haml11
-rw-r--r--app/views/import/manifest/_form.html.haml23
-rw-r--r--app/views/import/manifest/new.html.haml12
-rw-r--r--app/views/import/manifest/status.html.haml42
-rw-r--r--app/views/projects/_import_project_pane.html.haml47
-rw-r--r--app/views/projects/new.html.haml13
-rw-r--r--changelogs/unreleased/dz-manifest-import.yml5
-rw-r--r--config/routes/import.rb6
-rw-r--r--doc/api/settings.md2
-rw-r--r--doc/user/project/import/img/manifest_status.pngbin0 -> 34878 bytes
-rw-r--r--doc/user/project/import/img/manifest_upload.pngbin0 -> 12079 bytes
-rw-r--r--doc/user/project/import/index.md1
-rw-r--r--doc/user/project/import/manifest.md48
-rw-r--r--lib/api/settings.rb2
-rw-r--r--lib/gitlab/import_sources.rb3
-rw-r--r--lib/gitlab/manifest_import/manifest.rb81
-rw-r--r--lib/gitlab/manifest_import/project_creator.rb41
-rw-r--r--locale/gitlab.pot21
-rw-r--r--spec/features/import/manifest_import_spec.rb51
-rw-r--r--spec/features/projects/new_project_spec.rb27
-rw-r--r--spec/fixtures/aosp_manifest.xml685
-rw-r--r--spec/helpers/namespaces_helper_spec.rb10
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb10
-rw-r--r--spec/lib/gitlab/manifest_import/manifest_spec.rb46
-rw-r--r--spec/lib/gitlab/manifest_import/project_creator_spec.rb33
30 files changed, 1313 insertions, 55 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 21cc6dfdd16..f45fcd4d900 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -30,7 +30,13 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
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?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
+ 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?,
+ :google_code_import_enabled?, :fogbugz_import_enabled?,
+ :git_import_enabled?, :gitlab_project_import_enabled?,
+ :manifest_import_enabled?
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
@@ -351,6 +357,10 @@ class ApplicationController < ActionController::Base
Gitlab::CurrentSettings.import_sources.include?('gitlab_project')
end
+ def manifest_import_enabled?
+ Group.supports_nested_groups? && Gitlab::CurrentSettings.import_sources.include?('manifest')
+ end
+
# U2F (universal 2nd factor) devices need a unique identifier for the application
# to perform authentication.
# https://developers.yubico.com/U2F/App_ID.html
diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb
new file mode 100644
index 00000000000..e5a719fa0df
--- /dev/null
+++ b/app/controllers/import/manifest_controller.rb
@@ -0,0 +1,93 @@
+class Import::ManifestController < Import::BaseController
+ before_action :whitelist_query_limiting, only: [:create]
+ before_action :verify_import_enabled
+ before_action :ensure_import_vars, only: [:create, :status]
+
+ def new
+ end
+
+ def status
+ @already_added_projects = find_already_added_projects
+ already_added_import_urls = @already_added_projects.pluck(:import_url)
+
+ @pending_repositories = repositories.to_a.reject do |repository|
+ already_added_import_urls.include?(repository[:url])
+ end
+ end
+
+ def upload
+ group = Group.find(params[:group_id])
+
+ unless can?(current_user, :create_projects, group)
+ @errors = ["You don't have enough permissions to create projects in the selected group"]
+
+ render :new && return
+ end
+
+ manifest = Gitlab::ManifestImport::Manifest.new(params[:manifest].tempfile)
+
+ if manifest.valid?
+ session[:manifest_import_repositories] = manifest.projects
+ session[:manifest_import_group_id] = group.id
+
+ redirect_to status_import_manifest_path
+ else
+ @errors = manifest.errors
+
+ render :new
+ end
+ end
+
+ def jobs
+ render json: find_jobs
+ end
+
+ def create
+ repository = repositories.find do |project|
+ project[:id] == params[:repo_id].to_i
+ end
+
+ project = Gitlab::ManifestImport::ProjectCreator.new(repository, group, current_user).execute
+
+ if project.persisted?
+ render json: ProjectSerializer.new.represent(project)
+ else
+ render json: { errors: project_save_error(project) }, status: :unprocessable_entity
+ end
+ end
+
+ private
+
+ def ensure_import_vars
+ unless group && repositories.present?
+ redirect_to(new_import_manifest_path)
+ end
+ end
+
+ def group
+ @group ||= Group.find_by(id: session[:manifest_import_group_id])
+ end
+
+ def repositories
+ @repositories ||= session[:manifest_import_repositories]
+ end
+
+ def find_jobs
+ find_already_added_projects.to_json(only: [:id], methods: [:import_status])
+ end
+
+ def find_already_added_projects
+ group.all_projects
+ .where(import_type: 'manifest')
+ .where(creator_id: current_user)
+ .includes(:import_state)
+ end
+
+ def verify_import_enabled
+ render_404 unless manifest_import_enabled?
+ end
+
+ def whitelist_query_limiting
+ Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/48939')
+ end
+end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index 9be93fa69ae..9008db1b300 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -3,7 +3,7 @@ module NamespacesHelper
params.dig(:project, :namespace_id) || params[:namespace_id]
end
- def namespaces_options(selected = :current_user, display_path: false, extra_group: nil)
+ def namespaces_options(selected = :current_user, display_path: false, extra_group: nil, groups_only: false)
groups = current_user.manageable_groups
.joins(:route)
.includes(:route)
@@ -20,10 +20,13 @@ module NamespacesHelper
options = []
options << options_for_group(groups, display_path: display_path, type: 'group')
- options << options_for_group(users, display_path: display_path, type: 'user')
- if selected == :current_user && current_user.namespace
- selected = current_user.namespace.id
+ unless groups_only
+ options << options_for_group(users, display_path: display_path, type: 'user')
+
+ if selected == :current_user && current_user.namespace
+ selected = current_user.namespace.id
+ end
end
grouped_options_for_select(options, selected)
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
index 5c337a9faa5..c2dfbac5414 100644
--- a/app/services/groups/nested_create_service.rb
+++ b/app/services/groups/nested_create_service.rb
@@ -1,11 +1,12 @@
module Groups
class NestedCreateService < Groups::BaseService
- attr_reader :group_path
+ attr_reader :group_path, :visibility_level
def initialize(user, params)
@current_user, @params = user, params.dup
-
@group_path = @params.delete(:group_path)
+ @visibility_level = @params.delete(:visibility_level) ||
+ Gitlab::CurrentSettings.current_application_settings.default_group_visibility
end
def execute
@@ -36,11 +37,12 @@ module Groups
new_params = params.reverse_merge(
path: subgroup_name,
name: subgroup_name,
- parent: last_group
+ parent: last_group,
+ visibility_level: visibility_level
)
- new_params[:visibility_level] ||= Gitlab::CurrentSettings.current_application_settings.default_group_visibility
- last_group = namespace_or_group(partial_path) || Groups::CreateService.new(current_user, new_params).execute
+ last_group = namespace_or_group(partial_path) ||
+ Groups::CreateService.new(current_user, new_params).execute
end
last_group
diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml
index 5e7be5cd37b..f0d1e837317 100644
--- a/app/views/import/_githubish_status.html.haml
+++ b/app/views/import/_githubish_status.html.haml
@@ -21,23 +21,13 @@
%th= _('Status')
%tbody
- @already_added_projects.each do |project|
- %tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" }
+ %tr{ id: "project_#{project.id}", class: project_status_css_class(project.import_status) }
%td
= provider_project_link(provider, project.import_source)
%td
= link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status
- - if project.import_status == 'finished'
- %span
- %i.fa.fa-check
- = _('Done')
- - elsif project.import_status == 'started'
- %i.fa.fa-spinner.fa-spin
- = _('Started')
- - elsif project.import_status == 'failed'
- = _('Failed')
- - else
- = project.human_import_status_name
+ = render 'import/project_status', project: project
- @repos.each do |repo|
%tr{ id: "repo_#{repo.id}", data: { qa: { repo_path: repo.full_name } } }
@@ -61,6 +51,6 @@
= has_ci_cd_only_params? ? _('Connect') : _('Import')
= icon("spinner spin", class: "loading-icon")
-.js-importer-status{ data: { jobs_import_path: "#{url_for([:jobs, :import, provider])}",
- import_path: "#{url_for([:import, provider])}",
- ci_cd_only: "#{has_ci_cd_only_params?}" } }
+.js-importer-status{ data: { jobs_import_path: url_for([:jobs, :import, provider]),
+ import_path: url_for([:import, provider]),
+ ci_cd_only: has_ci_cd_only_params?.to_s } }
diff --git a/app/views/import/_project_status.html.haml b/app/views/import/_project_status.html.haml
new file mode 100644
index 00000000000..280bcbc1e63
--- /dev/null
+++ b/app/views/import/_project_status.html.haml
@@ -0,0 +1,11 @@
+- case project.import_status
+- when 'finished'
+ = icon('check')
+ = _('Done')
+- when 'started'
+ = icon("spinner spin")
+ = _('Started')
+- when 'failed'
+ = _('Failed')
+- else
+ = project.human_import_status_name
diff --git a/app/views/import/manifest/_form.html.haml b/app/views/import/manifest/_form.html.haml
new file mode 100644
index 00000000000..763beb5958f
--- /dev/null
+++ b/app/views/import/manifest/_form.html.haml
@@ -0,0 +1,23 @@
+= form_tag upload_import_manifest_path, multipart: true do
+ .form-group
+ = label_tag :group_id, nil, class: 'label-light' do
+ = _('Group')
+ .input-group
+ .input-group-prepend.has-tooltip{ title: root_url }
+ .input-group-text
+ = root_url
+ = select_tag :group_id, namespaces_options(nil, display_path: true, groups_only: true), { class: 'select2 js-select-namespace' }
+ .form-text.text-muted
+ = _('Choose the top-level group for your repository imports.')
+
+ .form-group
+ = label_tag :manifest, class: 'label-light' do
+ = _('Manifest')
+ = file_field_tag :manifest, class: 'form-control-file', required: true
+ .form-text.text-muted
+ = _('Import multiple repositories by uploading a manifest file.')
+ = link_to icon('question-circle'), help_page_path('user/project/import/manifest')
+
+ .append-bottom-10
+ = submit_tag _('List available repositories'), class: 'btn btn-success'
+ = link_to _('Cancel'), new_project_path, class: 'btn btn-cancel'
diff --git a/app/views/import/manifest/new.html.haml b/app/views/import/manifest/new.html.haml
new file mode 100644
index 00000000000..056e4922b9e
--- /dev/null
+++ b/app/views/import/manifest/new.html.haml
@@ -0,0 +1,12 @@
+- page_title "Manifest file import"
+- header_title "Projects", root_path
+
+%h3.page-title
+ = _('Manifest file import')
+
+- if @errors.present?
+ .alert.alert-danger
+ - @errors.each do |error|
+ = error
+
+= render 'form'
diff --git a/app/views/import/manifest/status.html.haml b/app/views/import/manifest/status.html.haml
new file mode 100644
index 00000000000..5b2e1005398
--- /dev/null
+++ b/app/views/import/manifest/status.html.haml
@@ -0,0 +1,42 @@
+- page_title "Manifest import"
+- header_title "Projects", root_path
+- provider = 'manifest'
+
+%h3.page-title
+ = _('Manifest file import')
+
+%p
+ = button_tag class: "btn btn-import btn-success js-import-all" do
+ = import_all_githubish_repositories_button_label
+ = icon("spinner spin", class: "loading-icon")
+
+.table-responsive
+ %table.table.import-jobs
+ %thead
+ %tr
+ %th= _('Repository URL')
+ %th= _('To GitLab')
+ %th= _('Status')
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{ id: "project_#{project.id}", class: project_status_css_class(project.import_status) }
+ %td
+ = project.import_url
+ %td
+ = link_to_project project
+ %td.job-status
+ = render 'import/project_status', project: project
+
+ - @pending_repositories.each do |repository|
+ %tr{ id: "repo_#{repository[:id]}" }
+ %td
+ = repository[:url]
+ %td.import-target
+ = import_project_target(@group.full_path, repository[:path])
+ %td.import-actions.job-status
+ = button_tag class: "btn btn-import js-add-to-import" do
+ = _('Import')
+ = icon("spinner spin", class: "loading-icon")
+
+.js-importer-status{ data: { jobs_import_path: url_for([:jobs, :import, provider]),
+ import_path: url_for([:import, provider]) } }
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 8f535b9d789..3da6db08580 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -1,49 +1,62 @@
- active_tab = local_assigns.fetch(:active_tab, 'blank')
-- f = local_assigns.fetch(:f)
.project-import
.form-group.import-btn-container.clearfix
- = f.label :visibility_level, class: 'label-light' do #the label here seems wrong
+ %h5
Import project from
.import-buttons
- if gitlab_project_import_enabled?
.import_gitlab_project.has-tooltip{ data: { container: 'body' } }
= link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
= icon('gitlab', text: 'GitLab export')
- %div
- - if github_import_enabled?
+
+ - if github_import_enabled?
+ %div
= link_to new_import_github_path, class: 'btn js-import-github' do
= icon('github', text: 'GitHub')
- %div
- - if bitbucket_import_enabled?
+
+ - if bitbucket_import_enabled?
+ %div
= link_to status_import_bitbucket_path, class: "btn import_bitbucket #{'how_to_import_link' unless bitbucket_import_configured?}" do
= icon('bitbucket', text: 'Bitbucket')
- unless bitbucket_import_configured?
= render 'bitbucket_import_modal'
- %div
- - if gitlab_import_enabled?
+
+ - if gitlab_import_enabled?
+ %div
= link_to status_import_gitlab_path, class: "btn import_gitlab #{'how_to_import_link' unless gitlab_import_configured?}" do
= icon('gitlab', text: 'GitLab.com')
- unless gitlab_import_configured?
= render 'gitlab_import_modal'
- %div
- - if google_code_import_enabled?
+
+ - if google_code_import_enabled?
+ %div
= link_to new_import_google_code_path, class: 'btn import_google_code' do
= icon('google', text: 'Google Code')
- %div
- - if fogbugz_import_enabled?
+
+ - if fogbugz_import_enabled?
+ %div
= link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
= icon('bug', text: 'Fogbugz')
- %div
- - if gitea_import_enabled?
+
+ - if gitea_import_enabled?
+ %div
= link_to new_import_gitea_path, class: 'btn import_gitea' do
= custom_icon('go_logo')
Gitea
- %div
- - if git_import_enabled?
+
+ - if git_import_enabled?
+ %div
%button.btn.js-toggle-button.js-import-git-toggle-button{ type: "button", data: { toggle_open_class: 'active' } }
= icon('git', text: 'Repo by URL')
+
+ - if manifest_import_enabled?
+ %div
+ = link_to new_import_manifest_path, class: 'btn import_manifest' do
+ = icon('file-text-o', text: 'Manifest file')
+
.js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
- %hr
+ = form_for @project, html: { class: 'new_project' } do |f|
+ %hr
= render "shared/import_form", f: f
= render 'new_project_fields', f: f, project_name_id: "import-url-name"
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 5bb1bfb7059..6c363345e38 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -55,13 +55,12 @@
= render 'project_templates', f: f
.tab-pane.import-project-pane.js-toggle-container{ id: 'import-project-pane', class: active_when(active_tab == 'import'), role: 'tabpanel' }
- = form_for @project, html: { class: 'new_project' } do |f|
- - if import_sources_enabled?
- = render 'import_project_pane', f: f, active_tab: active_tab
- - else
- .nothing-here-block
- %h4 No import options available
- %p Contact an administrator to enable options for importing your project.
+ - if import_sources_enabled?
+ = render 'import_project_pane', active_tab: active_tab
+ - else
+ .nothing-here-block
+ %h4 No import options available
+ %p Contact an administrator to enable options for importing your project.
.save-project-loader.d-none
.center
diff --git a/changelogs/unreleased/dz-manifest-import.yml b/changelogs/unreleased/dz-manifest-import.yml
new file mode 100644
index 00000000000..b0d29b0869f
--- /dev/null
+++ b/changelogs/unreleased/dz-manifest-import.yml
@@ -0,0 +1,5 @@
+---
+title: Add ability to import multiple repositories by uploading a manifest file
+merge_request: 20304
+author:
+type: added
diff --git a/config/routes/import.rb b/config/routes/import.rb
index c378253bf15..efd0260ff60 100644
--- a/config/routes/import.rb
+++ b/config/routes/import.rb
@@ -45,4 +45,10 @@ namespace :import do
resource :gitlab_project, only: [:create, :new] do
post :create
end
+
+ resource :manifest, only: [:create, :new], controller: :manifest do
+ get :status
+ get :jobs
+ post :upload
+ end
end
diff --git a/doc/api/settings.md b/doc/api/settings.md
index e6b207d8746..b6f2101fc7b 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -105,7 +105,7 @@ PUT /application/settings
| `housekeeping_gc_period` | integer | no | Number of Git pushes after which 'git gc' is run. |
| `housekeeping_incremental_repack_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 gitlab google_code fogbugz git gitlab_project |
+| `import_sources` | Array of strings | no | Sources to allow project import from, possible values: "github bitbucket gitlab google_code fogbugz git gitlab_project manifest |
| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
| `max_artifacts_size` | integer | no | Maximum artifacts size in MB |
diff --git a/doc/user/project/import/img/manifest_status.png b/doc/user/project/import/img/manifest_status.png
new file mode 100644
index 00000000000..b706116a2ac
--- /dev/null
+++ b/doc/user/project/import/img/manifest_status.png
Binary files differ
diff --git a/doc/user/project/import/img/manifest_upload.png b/doc/user/project/import/img/manifest_upload.png
new file mode 100644
index 00000000000..d6bf4b157dd
--- /dev/null
+++ b/doc/user/project/import/img/manifest_upload.png
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index 72cc58546b7..b55435e5b4f 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -11,6 +11,7 @@
1. [From SVN](svn.md)
1. [From TFS](tfs.md)
1. [From repo by URL](repo_by_url.md)
+1. [By uploading a manifest file](manifest.md)
In addition to the specific migration documentation above, you can import any
Git repository via HTTP from the New Project page. Be aware that if the
diff --git a/doc/user/project/import/manifest.md b/doc/user/project/import/manifest.md
new file mode 100644
index 00000000000..812ecf05faf
--- /dev/null
+++ b/doc/user/project/import/manifest.md
@@ -0,0 +1,48 @@
+# Import multiple repositories by uploading a manifest file
+
+GitLab allows you to import all the required git repositories
+based a manifest file like the one used by the Android repository.
+
+
+>**Note:**
+This feature requires [subgroups](../../group/subgroups/index.md) to be supported by your database.
+
+You can do it by following next steps:
+
+1. From your GitLab dashboard click **New project**
+1. Switch to the **Import project** tab
+1. Click on the **Manifest file** button
+1. Provide GitLab with a manifest xml file
+1. Select a group you want to import to (you need to create a group first if you don't have one)
+1. Click **List available repositories**
+1. You will be redirected to the import status page with projects list based on manifest file
+1. Check the list and click 'Import all repositories' to start import.
+
+![Manifest upload](img/manifest_upload.png)
+
+![Manifest status](img/manifest_status.png)
+
+### Manifest format
+
+A manifest must be an XML file. There must be one `remote` tag with `review` attribute
+that contains a URL to a git server. Each `project` tag must have `name` and `path` attribute.
+GitLab will build URL to the repository by combining URL from `remote` tag with a project name.
+A path attribute will be used to represent project path in GitLab system.
+
+Below is a valid example of manifest file.
+
+```xml
+<manifest>
+ <remote review="https://android-review.googlesource.com/" />
+
+ <project path="build/make" name="platform/build" />
+ <project path="build/blueprint" name="platform/build/blueprint" />
+</manifest>
+```
+
+As result next projects will be created:
+
+| GitLab | Import URL |
+|---|---|
+| https://gitlab/YOUR_GROUP/build/make | https://android-review.googlesource.com/platform/build |
+| https://gitlab/YOUR_GROUP/build/blueprint | https://android-review.googlesource.com/platform/build/blueprint |
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index 02ef89f997f..1ca7d23203b 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -25,7 +25,7 @@ module API
optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility'
optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility'
optional :restricted_visibility_levels, type: Array[String], desc: 'Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users.'
- optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
+ optional :import_sources, type: Array[String], values: %w[github bitbucket gitlab google_code fogbugz git gitlab_project manifest],
desc: 'Enabled sources for code import during project creation. OmniAuth must be configured for GitHub, Bitbucket, and GitLab.com'
optional :disabled_oauth_sign_in_sources, type: Array[String], desc: 'Disable certain OAuth sign-in sources'
optional :enabled_git_access_protocol, type: String, values: %w[ssh http nil], desc: 'Allow only the selected protocols to be used for Git access.'
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 60d5fa4d29a..af9b880ef9e 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -16,7 +16,8 @@ module Gitlab
ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer),
ImportSource.new('git', 'Repo by URL', nil),
ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer),
- ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer)
+ ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer),
+ ImportSource.new('manifest', 'Manifest file', nil)
].freeze
class << self
diff --git a/lib/gitlab/manifest_import/manifest.rb b/lib/gitlab/manifest_import/manifest.rb
new file mode 100644
index 00000000000..4d6034fb956
--- /dev/null
+++ b/lib/gitlab/manifest_import/manifest.rb
@@ -0,0 +1,81 @@
+# Class to parse manifest file and build a list of repositories for import
+#
+# <manifest>
+# <remote review="https://android-review.googlesource.com/" />
+# <project path="platform-common" name="platform" />
+# <project path="platform/art" name="platform/art" />
+# <project path="platform/device" name="platform/device" />
+# </manifest>
+#
+# 1. Project path must be uniq and can't be part of other project path.
+# For example, you can't have projects with 'foo' and 'foo/bar' paths.
+# 2. Remote must be present with review attribute so GitLab knows
+# where to fetch source code
+module Gitlab
+ module ManifestImport
+ class Manifest
+ attr_reader :parsed_xml, :errors
+
+ def initialize(file)
+ @parsed_xml = Nokogiri::XML(file) { |config| config.strict }
+ @errors = []
+ rescue Nokogiri::XML::SyntaxError
+ @errors = ['The uploaded file is not a valid XML file.']
+ end
+
+ def projects
+ raw_projects.each_with_index.map do |project, i|
+ {
+ id: i,
+ name: project['name'],
+ path: project['path'],
+ url: repository_url(project['name'])
+ }
+ end
+ end
+
+ def valid?
+ return false if @errors.any?
+
+ unless validate_remote
+ @errors << 'Make sure a <remote> tag is present and is valid.'
+ end
+
+ unless validate_projects
+ @errors << 'Make sure every <project> tag has name and path attributes.'
+ end
+
+ @errors.empty?
+ end
+
+ private
+
+ def validate_remote
+ remote.present? && URI.parse(remote).host
+ rescue URI::Error
+ false
+ end
+
+ def validate_projects
+ raw_projects.all? do |project|
+ project['name'] && project['path']
+ end
+ end
+
+ def repository_url(name)
+ URI.join(remote, name).to_s
+ end
+
+ def remote
+ return @remote if defined?(@remote)
+
+ remote_tag = parsed_xml.css('manifest > remote').first
+ @remote = remote_tag['review'] if remote_tag
+ end
+
+ def raw_projects
+ @raw_projects ||= parsed_xml.css('manifest > project')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/manifest_import/project_creator.rb b/lib/gitlab/manifest_import/project_creator.rb
new file mode 100644
index 00000000000..b5967c93735
--- /dev/null
+++ b/lib/gitlab/manifest_import/project_creator.rb
@@ -0,0 +1,41 @@
+module Gitlab
+ module ManifestImport
+ class ProjectCreator
+ attr_reader :repository, :destination, :current_user
+
+ def initialize(repository, destination, current_user)
+ @repository = repository
+ @destination = destination
+ @current_user = current_user
+ end
+
+ def execute
+ group_full_path, _, project_path = repository[:path].rpartition('/')
+ group_full_path = File.join(destination.full_path, group_full_path) if destination
+ group = create_group_with_parents(group_full_path)
+
+ params = {
+ import_url: repository[:url],
+ import_type: 'manifest',
+ namespace_id: group.id,
+ path: project_path,
+ name: project_path,
+ visibility_level: destination.visibility_level
+ }
+
+ Projects::CreateService.new(current_user, params).execute
+ end
+
+ private
+
+ def create_group_with_parents(full_path)
+ params = {
+ group_path: full_path,
+ visibility_level: destination.visibility_level
+ }
+
+ Groups::NestedCreateService.new(current_user, params).execute
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1792302bb0e..eb3433b3ba2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -981,6 +981,9 @@ msgstr ""
msgid "Choose file..."
msgstr ""
+msgid "Choose the top-level group for your repository imports."
+msgstr ""
+
msgid "Choose which repositories you want to import."
msgstr ""
@@ -2447,6 +2450,9 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "Group"
+msgstr ""
+
msgid "Group CI/CD settings"
msgstr ""
@@ -2650,6 +2656,9 @@ msgstr ""
msgid "Import in progress"
msgstr ""
+msgid "Import multiple repositories by uploading a manifest file."
+msgstr ""
+
msgid "Import repositories from GitHub"
msgstr ""
@@ -2859,6 +2868,9 @@ msgstr ""
msgid "List"
msgstr ""
+msgid "List available repositories"
+msgstr ""
+
msgid "List your GitHub repositories"
msgstr ""
@@ -2898,6 +2910,12 @@ msgstr ""
msgid "Manage project labels"
msgstr ""
+msgid "Manifest"
+msgstr ""
+
+msgid "Manifest file import"
+msgstr ""
+
msgid "Mar"
msgstr ""
@@ -3920,6 +3938,9 @@ msgstr ""
msgid "Repository Settings"
msgstr ""
+msgid "Repository URL"
+msgstr ""
+
msgid "Repository maintenance"
msgstr ""
diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb
new file mode 100644
index 00000000000..e381d073804
--- /dev/null
+++ b/spec/features/import/manifest_import_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+describe 'Import multiple repositories by uploading a manifest file', :js, :postgresql do
+ include Select2Helper
+
+ let(:user) { create(:admin) }
+ let(:group) { create(:group) }
+
+ before do
+ sign_in(user)
+
+ group.add_owner(user)
+ end
+
+ it 'parses manifest file and list repositories' do
+ visit new_import_manifest_path
+
+ attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
+ click_on 'List available repositories'
+
+ expect(page).to have_button('Import all repositories')
+ expect(page).to have_content('https://android-review.googlesource.com/platform/build/blueprint')
+ end
+
+ it 'imports succesfully imports a project' do
+ visit new_import_manifest_path
+
+ attach_file('manifest', Rails.root.join('spec/fixtures/aosp_manifest.xml'))
+ click_on 'List available repositories'
+
+ page.within(first_row) do
+ click_on 'Import'
+
+ expect(page).to have_content 'Done'
+ expect(page).to have_content("#{group.full_path}/build/make")
+ end
+ end
+
+ it 'renders an error if invalid file was provided' do
+ visit new_import_manifest_path
+
+ attach_file('manifest', Rails.root.join('spec/fixtures/banana_sample.gif'))
+ click_on 'List available repositories'
+
+ expect(page).to have_content 'The uploaded file is not a valid XML file.'
+ end
+
+ def first_row
+ page.all('table.import-jobs tbody tr')[0]
+ end
+end
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index df8528e79dd..bbe08ff83ff 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -25,6 +25,22 @@ describe 'New project' do
expect(page).to have_link('GitLab export')
end
+ describe 'manifest import option' do
+ before do
+ visit new_project_path
+
+ find('#import-project-tab').click
+ end
+
+ context 'when using postgres', :postgresql do
+ it { expect(page).to have_link('Manifest file') }
+ end
+
+ context 'when using mysql', :mysql do
+ it { expect(page).not_to have_link('Manifest file') }
+ end
+ end
+
context 'Visibility level selector', :js do
Gitlab::VisibilityLevel.options.each do |key, level|
it "sets selector to #{key}" do
@@ -201,5 +217,16 @@ describe 'New project' do
expect(current_path).to eq new_import_google_code_path
end
end
+
+ context 'from manifest file', :postgresql do
+ before do
+ first('.import_manifest').click
+ end
+
+ it 'shows import instructions' do
+ expect(page).to have_content('Manifest file import')
+ expect(current_path).to eq new_import_manifest_path
+ end
+ end
end
end
diff --git a/spec/fixtures/aosp_manifest.xml b/spec/fixtures/aosp_manifest.xml
new file mode 100644
index 00000000000..cfd0094b735
--- /dev/null
+++ b/spec/fixtures/aosp_manifest.xml
@@ -0,0 +1,685 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest>
+
+ <remote name="aosp"
+ fetch=".."
+ review="https://android-review.googlesource.com/" />
+ <default revision="master"
+ remote="aosp"
+ sync-j="4" />
+
+ <project path="build/make" name="platform/build" groups="pdk" >
+ <copyfile src="core/root.mk" dest="Makefile" />
+ <linkfile src="CleanSpec.mk" dest="build/CleanSpec.mk" />
+ <linkfile src="buildspec.mk.default" dest="build/buildspec.mk.default" />
+ <linkfile src="core" dest="build/core" />
+ <linkfile src="envsetup.sh" dest="build/envsetup.sh" />
+ <linkfile src="target" dest="build/target" />
+ <linkfile src="tools" dest="build/tools" />
+ </project>
+ <project path="build/blueprint" name="platform/build/blueprint" groups="pdk,tradefed" />
+ <project path="build/kati" name="platform/build/kati" groups="pdk,tradefed" />
+ <project path="build/soong" name="platform/build/soong" groups="pdk,tradefed" >
+ <linkfile src="root.bp" dest="Android.bp" />
+ <linkfile src="bootstrap.bash" dest="bootstrap.bash" />
+ </project>
+ <project path="art" name="platform/art" groups="pdk" />
+ <project path="bionic" name="platform/bionic" groups="pdk" />
+ <project path="bootable/recovery" name="platform/bootable/recovery" groups="pdk" />
+ <project path="compatibility/cdd" name="platform/compatibility/cdd" groups="pdk" />
+ <project path="cts" name="platform/cts" groups="cts,pdk-cw-fs,pdk-fs" />
+ <project path="dalvik" name="platform/dalvik" groups="pdk-cw-fs,pdk-fs" />
+ <project path="developers/build" name="platform/developers/build" groups="developers" />
+ <project path="developers/demos" name="platform/developers/demos" groups="developers" />
+ <project path="developers/samples/android" name="platform/developers/samples/android" groups="developers" />
+ <project path="development" name="platform/development" groups="developers,pdk-cw-fs,pdk-fs" />
+ <project path="device/asus/fugu" name="device/asus/fugu" groups="device,fugu,broadcom_pdk" />
+ <project path="device/asus/fugu-kernel" name="device/asus/fugu-kernel" groups="device,fugu,broadcom_pdk" clone-depth="1" />
+ <project path="device/common" name="device/common" groups="pdk-cw-fs,pdk" />
+ <project path="device/generic/arm64" name="device/generic/arm64" groups="pdk" />
+ <project path="device/generic/armv7-a-neon" name="device/generic/armv7-a-neon" groups="pdk" />
+ <project path="device/generic/car" name="device/generic/car" groups="pdk" />
+ <project path="device/generic/common" name="device/generic/common" groups="pdk" />
+ <project path="device/generic/goldfish" name="device/generic/goldfish" groups="pdk" />
+ <project path="device/generic/goldfish-opengl" name="device/generic/goldfish-opengl" groups="pdk" />
+ <project path="device/generic/mini-emulator-arm64" name="device/generic/mini-emulator-arm64" groups="pdk" />
+ <project path="device/generic/mini-emulator-armv7-a-neon" name="device/generic/mini-emulator-armv7-a-neon" groups="pdk" />
+ <project path="device/generic/mini-emulator-x86" name="device/generic/mini-emulator-x86" groups="pdk" />
+ <project path="device/generic/mini-emulator-x86_64" name="device/generic/mini-emulator-x86_64" groups="pdk" />
+ <project path="device/generic/qemu" name="device/generic/qemu" groups="pdk" />
+ <project path="device/generic/uml" name="device/generic/uml" groups="device,pdk" />
+ <project path="device/generic/x86" name="device/generic/x86" groups="pdk" />
+ <project path="device/generic/x86_64" name="device/generic/x86_64" groups="pdk" />
+ <project path="device/google/accessory/arduino" name="device/google/accessory/arduino" groups="device,pdk" />
+ <project path="device/google/accessory/demokit" name="device/google/accessory/demokit" groups="device,pdk" />
+ <project path="device/google/atv" name="device/google/atv" groups="device,broadcom_pdk,generic_fs,pdk" />
+ <project path="device/google/contexthub" name="device/google/contexthub" groups="device,marlin,pdk" />
+ <project path="device/google/cuttlefish" name="device/google/cuttlefish" groups="device" />
+ <project path="device/google/cuttlefish_common" name="device/google/cuttlefish_common" groups="device" />
+ <project path="device/google/cuttlefish_kernel" name="device/google/cuttlefish_kernel" groups="device" clone-depth="1" />
+ <project path="device/google/dragon" name="device/google/dragon" groups="device,dragon" />
+ <project path="device/google/dragon-kernel" name="device/google/dragon-kernel" groups="device,dragon" clone-depth="1" />
+ <project path="device/google/marlin" name="device/google/marlin" groups="device,marlin,pdk" />
+ <project path="device/google/marlin-kernel" name="device/google/marlin-kernel" groups="device,marlin,pdk" clone-depth="1" />
+ <project path="device/google/muskie" name="device/google/muskie" groups="device,muskie" />
+ <project path="device/google/taimen" name="device/google/taimen" groups="device,taimen" />
+ <project path="device/google/vrservices" name="device/google/vrservices" groups="pdk" clone-depth="1" />
+ <project path="device/google/wahoo" name="device/google/wahoo" groups="device,wahoo" />
+ <project path="device/google/wahoo-kernel" name="device/google/wahoo-kernel" groups="device,wahoo" clone-depth="1" />
+ <project path="device/huawei/angler" name="device/huawei/angler" groups="device,angler,broadcom_pdk" />
+ <project path="device/huawei/angler-kernel" name="device/huawei/angler-kernel" groups="device,angler,broadcom_pdk" clone-depth="1" />
+ <project path="device/lge/bullhead" name="device/lge/bullhead" groups="device,bullhead" />
+ <project path="device/lge/bullhead-kernel" name="device/lge/bullhead-kernel" groups="device,bullhead" clone-depth="1" />
+ <project path="device/linaro/bootloader/arm-trusted-firmware" name="device/linaro/bootloader/arm-trusted-firmware" />
+ <project path="device/linaro/bootloader/edk2" name="device/linaro/bootloader/edk2" />
+ <project path="device/linaro/bootloader/OpenPlatformPkg" name="device/linaro/bootloader/OpenPlatformPkg" />
+ <project path="device/linaro/hikey" name="device/linaro/hikey" groups="device,hikey,pdk" />
+ <project path="device/linaro/hikey-kernel" name="device/linaro/hikey-kernel" groups="device,hikey,pdk" clone-depth="1" />
+ <project path="device/sample" name="device/sample" groups="pdk" />
+ <project path="external/aac" name="platform/external/aac" groups="pdk" />
+ <project path="external/abi-compliance-checker" name="platform/external/abi-compliance-checker" groups="pdk" />
+ <project path="external/abi-dumper" name="platform/external/abi-dumper" groups="pdk" />
+ <project path="external/adt-infra" name="platform/external/adt-infra" groups="adt-infra,notdefault,pdk-fs" />
+ <project path="external/android-clat" name="platform/external/android-clat" groups="pdk" />
+ <project path="external/androidplot" name="platform/external/androidplot" groups="pdk" />
+ <project path="external/annotation-tools" name="platform/external/annotation-tools" groups="pdk" />
+ <project path="external/ant-glob" name="platform/external/ant-glob" groups="pdk" />
+ <project path="external/antlr" name="platform/external/antlr" groups="pdk" />
+ <project path="external/apache-commons-math" name="platform/external/apache-commons-math" groups="pdk" />
+ <project path="external/apache-harmony" name="platform/external/apache-harmony" groups="pdk" />
+ <project path="external/apache-http" name="platform/external/apache-http" groups="pdk" />
+ <project path="external/apache-xml" name="platform/external/apache-xml" groups="pdk" />
+ <project path="external/archive-patcher" name="platform/external/archive-patcher" groups="pdk" />
+ <project path="external/arm-neon-tests" name="platform/external/arm-neon-tests" groups="vendor" />
+ <project path="external/autotest" name="platform/external/autotest" groups="pdk-fs" />
+ <project path="external/avb" name="platform/external/avb" groups="pdk" />
+ <project path="external/bart" name="platform/external/bart" groups="pdk" />
+ <project path="external/blktrace" name="platform/external/blktrace" groups="pdk" />
+ <project path="external/boringssl" name="platform/external/boringssl" groups="pdk" />
+ <project path="external/bouncycastle" name="platform/external/bouncycastle" groups="pdk" />
+ <project path="external/brotli" name="platform/external/brotli" groups="pdk" />
+ <project path="external/bsdiff" name="platform/external/bsdiff" groups="pdk" />
+ <project path="external/bzip2" name="platform/external/bzip2" groups="pdk" />
+ <project path="external/caliper" name="platform/external/caliper" groups="pdk" />
+ <project path="external/cblas" name="platform/external/cblas" groups="pdk" />
+ <project path="external/chromium-libpac" name="platform/external/chromium-libpac" groups="pdk" />
+ <project path="external/chromium-trace" name="platform/external/chromium-trace" groups="pdk" />
+ <project path="external/chromium-webview" name="platform/external/chromium-webview" groups="pdk" clone-depth="1" />
+ <project path="external/clang" name="platform/external/clang" groups="pdk" />
+ <project path="external/cldr" name="platform/external/cldr" groups="pdk" />
+ <project path="external/cmockery" name="platform/external/cmockery" groups="pdk" />
+ <project path="external/cn-cbor" name="platform/external/cn-cbor" groups="pdk" />
+ <project path="external/compiler-rt" name="platform/external/compiler-rt" groups="pdk" />
+ <project path="external/conscrypt" name="platform/external/conscrypt" groups="pdk" />
+ <project path="external/crcalc" name="platform/external/crcalc" groups="pdk" />
+ <project path="external/cros/system_api" name="platform/external/cros/system_api" groups="pdk" />
+ <project path="external/curl" name="platform/external/curl" groups="pdk" />
+ <project path="external/dagger2" name="platform/external/dagger2" groups="pdk" />
+ <project path="external/deqp" name="platform/external/deqp" groups="pdk-fs" />
+ <project path="external/desugar" name="platform/external/desugar" groups="pdk" />
+ <project path="external/devlib" name="platform/external/devlib" groups="pdk" />
+ <project path="external/dexmaker" name="platform/external/dexmaker" groups="pdk" />
+ <project path="external/dhcpcd-6.8.2" name="platform/external/dhcpcd-6.8.2" groups="pdk" />
+ <project path="external/dlmalloc" name="platform/external/dlmalloc" groups="pdk" />
+ <project path="external/dng_sdk" name="platform/external/dng_sdk" groups="pdk" />
+ <project path="external/dnsmasq" name="platform/external/dnsmasq" groups="pdk" />
+ <project path="external/doclava" name="platform/external/doclava" groups="pdk" />
+ <project path="external/dokka" name="platform/external/dokka" groups="pdk" />
+ <project path="external/drm_hwcomposer" name="platform/external/drm_hwcomposer" groups="drm_hwcomposer,pdk-fs" />
+ <project path="external/droiddriver" name="platform/external/droiddriver" groups="pdk" />
+ <project path="external/drrickorang" name="platform/external/drrickorang" groups="pdk" />
+ <project path="external/dtc" name="platform/external/dtc" groups="pdk"/>
+ <project path="external/e2fsprogs" name="platform/external/e2fsprogs" groups="pdk" />
+ <project path="external/easymock" name="platform/external/easymock" groups="pdk" />
+ <project path="external/eigen" name="platform/external/eigen" groups="pdk" />
+ <project path="external/elfutils" name="platform/external/elfutils" groups="pdk" />
+ <project path="external/emma" name="platform/external/emma" groups="pdk" />
+ <project path="external/error_prone" name="platform/external/error_prone" groups="pdk" />
+ <project path="external/esd" name="platform/external/esd" groups="pdk" />
+ <project path="external/expat" name="platform/external/expat" groups="pdk" />
+ <project path="external/eyes-free" name="platform/external/eyes-free" groups="pdk" />
+ <project path="external/f2fs-tools" name="platform/external/f2fs-tools" groups="pdk" />
+ <project path="external/fdlibm" name="platform/external/fdlibm" groups="pdk" />
+ <project path="external/fec" name="platform/external/fec" groups="pdk" />
+ <project path="external/flac" name="platform/external/flac" groups="pdk" />
+ <project path="external/flatbuffers" name="platform/external/flatbuffers" groups="pdk" />
+ <project path="external/fonttools" name="platform/external/fonttools" groups="pdk" />
+ <project path="external/freetype" name="platform/external/freetype" groups="pdk" />
+ <project path="external/fsck_msdos" name="platform/external/fsck_msdos" groups="pdk" />
+ <project path="external/gemmlowp" name="platform/external/gemmlowp" groups="pdk" />
+ <project path="external/gflags" name="platform/external/gflags" groups="pdk" />
+ <project path="external/giflib" name="platform/external/giflib" groups="pdk,qcom_msm8x26" />
+ <project path="external/glide" name="platform/external/glide" groups="pdk" />
+ <project path="external/golang-protobuf" name="platform/external/golang-protobuf" groups="pdk" />
+ <project path="external/google-benchmark" name="platform/external/google-benchmark" groups="pdk" />
+ <project path="external/google-breakpad" name="platform/external/google-breakpad" groups="pdk-fs" />
+ <project path="external/google-fonts/carrois-gothic-sc" name="platform/external/google-fonts/carrois-gothic-sc" groups="pdk" />
+ <project path="external/google-fonts/coming-soon" name="platform/external/google-fonts/coming-soon" groups="pdk" />
+ <project path="external/google-fonts/cutive-mono" name="platform/external/google-fonts/cutive-mono" groups="pdk" />
+ <project path="external/google-fonts/dancing-script" name="platform/external/google-fonts/dancing-script" groups="pdk" />
+ <project path="external/google-styleguide" name="platform/external/google-styleguide" groups="pdk" />
+ <project path="external/google-tv-pairing-protocol" name="platform/external/google-tv-pairing-protocol" groups="pdk" />
+ <project path="external/googletest" name="platform/external/googletest" groups="pdk" />
+ <project path="external/gptfdisk" name="platform/external/gptfdisk" groups="pdk" />
+ <project path="external/guava" name="platform/external/guava" groups="pdk" />
+ <project path="external/guice" name="platform/external/guice" groups="pdk" />
+ <project path="external/hamcrest" name="platform/external/hamcrest" groups="pdk" />
+ <project path="external/harfbuzz_ng" name="platform/external/harfbuzz_ng" groups="pdk,qcom_msm8x26" />
+ <project path="external/hyphenation-patterns" name="platform/external/hyphenation-patterns" groups="pdk" />
+ <project path="external/icu" name="platform/external/icu" groups="pdk" />
+ <project path="external/ImageMagick" name="platform/external/ImageMagick" groups="pdk" />
+ <project path="external/ims" name="platform/external/ims" groups="pdk" />
+ <project path="external/iproute2" name="platform/external/iproute2" groups="pdk" />
+ <project path="external/ipsec-tools" name="platform/external/ipsec-tools" groups="pdk" />
+ <project path="external/iptables" name="platform/external/iptables" groups="pdk" />
+ <project path="external/iputils" name="platform/external/iputils" groups="pdk" />
+ <project path="external/iw" name="platform/external/iw" groups="pdk" />
+ <project path="external/jacoco" name="platform/external/jacoco" groups="pdk" />
+ <project path="external/jarjar" name="platform/external/jarjar" groups="pdk" />
+ <project path="external/javaparser" name="platform/external/javaparser" groups="pdk" />
+ <project path="external/javasqlite" name="platform/external/javasqlite" groups="pdk" />
+ <project path="external/javassist" name="platform/external/javassist" groups="pdk" />
+ <project path="external/jcommander" name="platform/external/jcommander" groups="pdk" />
+ <project path="external/jdiff" name="platform/external/jdiff" groups="pdk" />
+ <project path="external/jemalloc" name="platform/external/jemalloc" groups="pdk" />
+ <project path="external/jline" name="platform/external/jline" groups="pdk,tradefed,pdk-fs" />
+ <project path="external/jmdns" name="platform/external/jmdns" groups="pdk" />
+ <project path="external/jsilver" name="platform/external/jsilver" groups="pdk" />
+ <project path="external/jsmn" name="platform/external/jsmn" groups="pdk" />
+ <project path="external/jsoncpp" name="platform/external/jsoncpp" groups="pdk" />
+ <project path="external/jsr305" name="platform/external/jsr305" groups="pdk" />
+ <project path="external/jsr330" name="platform/external/jsr330" groups="pdk" />
+ <project path="external/junit" name="platform/external/junit" groups="pdk" />
+ <project path="external/junit-params" name="platform/external/junit-params" groups="pdk" />
+ <project path="external/kernel-headers" name="platform/external/kernel-headers" groups="pdk" />
+ <project path="external/kmod" name="platform/external/kmod" groups="pdk" />
+ <project path="external/kotlinc" name="platform/external/kotlinc" groups="pdk" />
+ <project path="external/ksoap2" name="platform/external/ksoap2" groups="pdk" />
+ <project path="external/libavc" name="platform/external/libavc" groups="pdk" />
+ <project path="external/libbackup" name="platform/external/libbackup" groups="pdk" />
+ <project path="external/libbrillo" name="platform/external/libbrillo" groups="pdk" />
+ <project path="external/libcap" name="platform/external/libcap" groups="pdk" />
+ <project path="external/libcap-ng" name="platform/external/libcap-ng" groups="pdk" />
+ <project path="external/libchrome" name="platform/external/libchrome" groups="pdk" />
+ <project path="external/libconstrainedcrypto" name="platform/external/libconstrainedcrypto" groups="pdk" />
+ <project path="external/libcups" name="platform/external/libcups" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/libcxx" name="platform/external/libcxx" groups="pdk" />
+ <project path="external/libcxxabi" name="platform/external/libcxxabi" groups="pdk" />
+ <project path="external/libdaemon" name="platform/external/libdaemon" groups="pdk" />
+ <project path="external/libdivsufsort" name="platform/external/libdivsufsort" groups="pdk" />
+ <project path="external/libdrm" name="platform/external/libdrm" groups="pdk" />
+ <project path="external/libedit" name="platform/external/libedit" groups="pdk" />
+ <project path="external/libese" name="platform/external/libese" groups="pdk" />
+ <project path="external/libevent" name="platform/external/libevent" groups="pdk" />
+ <project path="external/libexif" name="platform/external/libexif" groups="pdk" />
+ <project path="external/libgsm" name="platform/external/libgsm" groups="pdk" />
+ <project path="external/libhevc" name="platform/external/libhevc" groups="pdk" />
+ <project path="external/libjpeg-turbo" name="platform/external/libjpeg-turbo" groups="pdk" />
+ <project path="external/libldac" name="platform/external/libldac" groups="pdk" />
+ <project path="external/libmicrohttpd" name="platform/external/libmicrohttpd" groups="pdk" />
+ <project path="external/libmpeg2" name="platform/external/libmpeg2" groups="pdk" />
+ <project path="external/libmtp" name="platform/external/libmtp" groups="pdk" />
+ <project path="external/libnetfilter_conntrack" name="platform/external/libnetfilter_conntrack" groups="pdk" />
+ <project path="external/libnfnetlink" name="platform/external/libnfnetlink" groups="pdk" />
+ <project path="external/libnl" name="platform/external/libnl" groups="pdk" />
+ <project path="external/libogg" name="platform/external/libogg" groups="pdk" />
+ <project path="external/libopus" name="platform/external/libopus" groups="pdk" />
+ <project path="external/libpcap" name="platform/external/libpcap" groups="pdk" />
+ <project path="external/libphonenumber" name="platform/external/libphonenumber" groups="pdk" />
+ <project path="external/libpng" name="platform/external/libpng" groups="pdk" />
+ <project path="external/libtextclassifier" name="platform/external/libtextclassifier" groups="pdk" />
+ <project path="external/libunwind" name="platform/external/libunwind" groups="pdk" />
+ <project path="external/libunwind_llvm" name="platform/external/libunwind_llvm" groups="pdk" />
+ <project path="external/libusb" name="platform/external/libusb" groups="pdk" />
+ <project path="external/libusb-compat" name="platform/external/libusb-compat" groups="pdk" />
+ <project path="external/libvncserver" name="platform/external/libvncserver" groups="pdk" />
+ <project path="external/libvorbis" name="platform/external/libvorbis" groups="pdk" />
+ <project path="external/libvpx" name="platform/external/libvpx" groups="pdk" />
+ <project path="external/libvterm" name="platform/external/libvterm" groups="pdk" />
+ <project path="external/libxcam" name="platform/external/libxcam" groups="pdk" />
+ <project path="external/libxml2" name="platform/external/libxml2" groups="pdk,libxml2" />
+ <project path="external/libyuv" name="platform/external/libyuv" groups="pdk,libyuv" />
+ <project path="external/linux-kselftest" name="platform/external/linux-kselftest" groups="vts,pdk" />
+ <project path="external/lisa" name="platform/external/lisa" groups="pdk" />
+ <project path="external/llvm" name="platform/external/llvm" groups="pdk" />
+ <project path="external/lmfit" name="platform/external/lmfit" groups="pdk" />
+ <project path="external/ltp" name="platform/external/ltp" groups="vts,pdk" />
+ <project path="external/lz4" name="platform/external/lz4" groups="pdk" />
+ <project path="external/lzma" name="platform/external/lzma" groups="pdk" />
+ <project path="external/markdown" name="platform/external/markdown" groups="pdk" />
+ <project path="external/mdnsresponder" name="platform/external/mdnsresponder" groups="pdk" />
+ <project path="external/mesa3d" name="platform/external/mesa3d" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/Microsoft-GSL" name="platform/external/Microsoft-GSL" groups="pdk" />
+ <project path="external/minijail" name="platform/external/minijail" groups="pdk" />
+ <project path="external/mksh" name="platform/external/mksh" groups="pdk" />
+ <project path="external/mmc-utils" name="platform/external/mmc-utils" groups="pdk" />
+ <project path="external/mockftpserver" name="platform/external/mockftpserver" groups="pdk" />
+ <project path="external/mockito" name="platform/external/mockito" groups="pdk" />
+ <project path="external/mockwebserver" name="platform/external/mockwebserver" groups="pdk" />
+ <project path="external/modp_b64" name="platform/external/modp_b64" groups="pdk" />
+ <project path="external/mp4parser" name="platform/external/mp4parser" groups="pdk" />
+ <project path="external/mtpd" name="platform/external/mtpd" groups="pdk" />
+ <project path="external/nanohttpd" name="platform/external/nanohttpd" groups="pdk" />
+ <project path="external/nanopb-c" name="platform/external/nanopb-c" groups="pdk" />
+ <project path="external/naver-fonts" name="platform/external/naver-fonts" groups="pdk" />
+ <project path="external/neven" name="platform/external/neven" groups="pdk" />
+ <project path="external/nfacct" name="platform/external/nfacct" groups="pdk" />
+ <project path="external/nist-pkits" name="platform/external/nist-pkits" groups="pdk" />
+ <project path="external/nist-sip" name="platform/external/nist-sip" groups="pdk" />
+ <project path="external/noto-fonts" name="platform/external/noto-fonts" groups="pdk" />
+ <project path="external/oauth" name="platform/external/oauth" groups="pdk" />
+ <project path="external/objenesis" name="platform/external/objenesis" groups="pdk" />
+ <project path="external/oj-libjdwp" name="platform/external/oj-libjdwp" groups="pdk" />
+ <project path="external/okhttp" name="platform/external/okhttp" groups="pdk" />
+ <project path="external/one-true-awk" name="platform/external/one-true-awk" groups="pdk" />
+ <project path="external/opencv" name="platform/external/opencv" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/owasp/sanitizer" name="platform/external/owasp/sanitizer" groups="pdk" />
+ <project path="external/parameter-framework" name="platform/external/parameter-framework" groups="pdk" />
+ <project path="external/pcre" name="platform/external/pcre" groups="pdk" />
+ <project path="external/pdfium" name="platform/external/pdfium" groups="pdk" />
+ <project path="external/perf_data_converter" name="platform/external/perf_data_converter" groups="pdk" />
+ <project path="external/perfetto" name="platform/external/perfetto" groups="pdk" />
+ <project path="external/piex" name="platform/external/piex" groups="pdk" />
+ <project path="external/ply" name="platform/external/ply" groups="pdk" />
+ <project path="external/ppp" name="platform/external/ppp" groups="pdk" />
+ <project path="external/proguard" name="platform/external/proguard" groups="pdk" />
+ <project path="external/protobuf" name="platform/external/protobuf" groups="pdk" />
+ <project path="external/puffin" name="platform/external/puffin" groups="pdk" />
+ <project path="external/python/appdirs" name="platform/external/python/appdirs" groups="vts,pdk" />
+ <project path="external/python/cachetools" name="platform/external/python/cachetools" groups="vts,pdk" />
+ <project path="external/python/cpython2" name="platform/external/python/cpython2" groups="pdk" />
+ <project path="external/python/cpython3" name="platform/external/python/cpython3" groups="pdk" />
+ <project path="external/python/dateutil" name="platform/external/python/dateutil" groups="pdk" />
+ <project path="external/python/dill" name="platform/external/python/dill" groups="vts,pdk" />
+ <project path="external/python/enum" name="platform/external/python/enum" groups="vts,pdk" />
+ <project path="external/python/enum34" name="platform/external/python/enum34" groups="vts,pdk" />
+ <project path="external/python/future" name="platform/external/python/future" groups="vts,pdk" />
+ <project path="external/python/futures" name="platform/external/python/futures" groups="vts,pdk" />
+ <project path="external/python/gapic-google-cloud-pubsub-v1" name="platform/external/python/gapic-google-cloud-pubsub-v1" groups="vts,pdk" />
+ <project path="external/python/google-api-python-client" name="platform/external/python/google-api-python-client" groups="vts,pdk" />
+ <project path="external/python/google-auth" name="platform/external/python/google-auth" groups="vts,pdk" />
+ <project path="external/python/google-auth-httplib2" name="platform/external/python/google-auth-httplib2" groups="vts,pdk" />
+ <project path="external/python/google-cloud-core" name="platform/external/python/google-cloud-core" groups="vts,pdk" />
+ <project path="external/python/google-cloud-pubsub" name="platform/external/python/google-cloud-pubsub" groups="vts,pdk" />
+ <project path="external/python/google-gax" name="platform/external/python/google-gax" groups="vts,pdk" />
+ <project path="external/python/googleapis" name="platform/external/python/googleapis" groups="vts,pdk" />
+ <project path="external/python/grpc-google-iam-v1" name="platform/external/python/grpc-google-iam-v1" groups="vts,pdk" />
+ <project path="external/python/grpcio" name="platform/external/python/grpcio" groups="vts,pdk" />
+ <project path="external/python/httplib2" name="platform/external/python/httplib2" groups="vts,pdk" />
+ <project path="external/python/matplotlib" name="platform/external/python/matplotlib" groups="vts,pdk" />
+ <project path="external/python/numpy" name="platform/external/python/numpy" groups="vts,pdk" />
+ <project path="external/python/oauth2client" name="platform/external/python/oauth2client" groups="vts,pdk" />
+ <project path="external/python/olefile" name="platform/external/python/olefile" groups="vts,pdk" />
+ <project path="external/python/packaging" name="platform/external/python/packaging" groups="vts,pdk" />
+ <project path="external/python/parse" name="platform/external/python/parse" groups="vts,pdk" />
+ <project path="external/python/Pillow" name="platform/external/python/Pillow" groups="vts,pdk" />
+ <project path="external/python/ply" name="platform/external/python/ply" groups="vts,pdk" />
+ <project path="external/python/proto-google-cloud-pubsub-v1" name="platform/external/python/proto-google-cloud-pubsub-v1" groups="vts,pdk" />
+ <project path="external/python/protobuf" name="platform/external/python/protobuf" groups="vts,pdk" />
+ <project path="external/python/pyasn1" name="platform/external/python/pyasn1" groups="vts,pdk" />
+ <project path="external/python/pyasn1-modules" name="platform/external/python/pyasn1-modules" groups="vts,pdk" />
+ <project path="external/python/pyparsing" name="platform/external/python/pyparsing" groups="vts,pdk" />
+ <project path="external/python/requests" name="platform/external/python/requests" groups="vts,pdk" />
+ <project path="external/python/rsa" name="platform/external/python/rsa" groups="vts,pdk" />
+ <project path="external/python/scipy" name="platform/external/python/scipy" groups="vts,pdk" />
+ <project path="external/python/setuptools" name="platform/external/python/setuptools" groups="vts,pdk" />
+ <project path="external/python/six" name="platform/external/python/six" groups="vts,pdk" />
+ <project path="external/python/uritemplates" name="platform/external/python/uritemplates" groups="vts,pdk" />
+ <project path="external/replicaisland" name="platform/external/replicaisland" groups="pdk" />
+ <project path="external/rmi4utils" name="platform/external/rmi4utils" groups="pdk" />
+ <project path="external/robolectric" name="platform/external/robolectric" groups="pdk-cw-fs,pdk-fs" />
+ <project path="external/roboto-fonts" name="platform/external/roboto-fonts" groups="pdk" />
+ <project path="external/rootdev" name="platform/external/rootdev" groups="pdk" />
+ <project path="external/safe-iop" name="platform/external/safe-iop" groups="pdk" />
+ <project path="external/scapy" name="platform/external/scapy" groups="pdk-fs" />
+ <project path="external/scrypt" name="platform/external/scrypt" groups="pdk" />
+ <project path="external/seccomp-tests" name="platform/external/seccomp-tests" groups="pdk" />
+ <project path="external/selinux" name="platform/external/selinux" groups="pdk" />
+ <project path="external/sfntly" name="platform/external/sfntly" groups="pdk,qcom_msm8x26" />
+ <project path="external/shaderc/spirv-headers" name="platform/external/shaderc/spirv-headers" groups="pdk" />
+ <project path="external/shflags" name="platform/external/shflags" groups="pdk" />
+ <project path="external/skia" name="platform/external/skia" groups="pdk,qcom_msm8x26" />
+ <project path="external/sl4a" name="platform/external/sl4a" groups="pdk" />
+ <project path="external/slf4j" name="platform/external/slf4j" groups="pdk" />
+ <project path="external/smali" name="platform/external/smali" groups="pdk" />
+ <project path="external/snakeyaml" name="platform/external/snakeyaml" groups="pdk" />
+ <project path="external/sonic" name="platform/external/sonic" groups="pdk" />
+ <project path="external/sonivox" name="platform/external/sonivox" groups="pdk" />
+ <project path="external/speex" name="platform/external/speex" groups="pdk" />
+ <project path="external/spirv-llvm" name="platform/external/spirv-llvm" groups="pdk" />
+ <project path="external/sqlite" name="platform/external/sqlite" groups="pdk" />
+ <project path="external/squashfs-tools" name="platform/external/squashfs-tools" groups="pdk" />
+ <project path="external/strace" name="platform/external/strace" groups="pdk" />
+ <project path="external/stressapptest" name="platform/external/stressapptest" groups="pdk" />
+ <project path="external/subsampling-scale-image-view" name="platform/external/subsampling-scale-image-view" clone-depth="1" />
+ <project path="external/swiftshader" name="platform/external/swiftshader" groups="pdk" />
+ <project path="external/syslinux" name="platform/external/syslinux" groups="pdk" />
+ <project path="external/tagsoup" name="platform/external/tagsoup" groups="pdk" />
+ <project path="external/tcpdump" name="platform/external/tcpdump" groups="pdk" />
+ <project path="external/tensorflow" name="platform/external/tensorflow" groups="pdk" />
+ <project path="external/testng" name="platform/external/testng" groups="pdk" />
+ <project path="external/tinyalsa" name="platform/external/tinyalsa" groups="pdk" />
+ <project path="external/tinycompress" name="platform/external/tinycompress" groups="pdk" />
+ <project path="external/tinyxml" name="platform/external/tinyxml" groups="pdk" />
+ <project path="external/tinyxml2" name="platform/external/tinyxml2" groups="pdk" />
+ <project path="external/toolchain-utils" name="platform/external/toolchain-utils" />
+ <project path="external/toybox" name="platform/external/toybox" groups="pdk" />
+ <project path="external/tpm2" name="platform/external/tpm2" groups="pdk" />
+ <project path="external/trappy" name="platform/external/trappy" groups="pdk" />
+ <project path="external/tremolo" name="platform/external/tremolo" groups="pdk" />
+ <project path="external/turbine" name="platform/external/turbine" groups="pdk" />
+ <project path="external/unicode" name="platform/external/unicode" groups="pdk" />
+ <project path="external/universal-tween-engine" name="platform/external/universal-tween-engine" />
+ <project path="external/v4l2_codec2" name="platform/external/v4l2_codec2" groups="pdk" />
+ <project path="external/v8" name="platform/external/v8" groups="pdk" />
+ <project path="external/valgrind" name="platform/external/valgrind" groups="pdk" />
+ <project path="external/vboot_reference" name="platform/external/vboot_reference" groups="vboot,pdk-fs" />
+ <project path="external/vixl" name="platform/external/vixl" groups="pdk" />
+ <project path="external/vogar" name="platform/external/vogar" groups="pdk" />
+ <project path="external/volley" name="platform/external/volley" groups="pdk" />
+ <project path="external/vulkan-validation-layers" name="platform/external/vulkan-validation-layers" groups="pdk" />
+ <project path="external/walt" name="platform/external/walt" groups="pdk" />
+ <project path="external/webp" name="platform/external/webp" groups="pdk,qcom_msm8x26" />
+ <project path="external/webrtc" name="platform/external/webrtc" groups="pdk" />
+ <project path="external/webview_support_interfaces" name="platform/external/webview_support_interfaces" groups="pdk" />
+ <project path="external/wpa_supplicant_8" name="platform/external/wpa_supplicant_8" groups="pdk" />
+ <project path="external/wycheproof" name="platform/external/wycheproof" groups="pdk" />
+ <project path="external/x264" name="platform/external/x264" groups="pdk" />
+ <project path="external/xmlrpcpp" name="platform/external/xmlrpcpp" groups="pdk" />
+ <project path="external/xmp_toolkit" name="platform/external/xmp_toolkit" groups="pdk" />
+ <project path="external/xz-embedded" name="platform/external/xz-embedded" groups="pdk" />
+ <project path="external/zlib" name="platform/external/zlib" groups="pdk" />
+ <project path="external/zopfli" name="platform/external/zopfli" groups="pdk" />
+ <project path="external/zxing" name="platform/external/zxing" groups="pdk" />
+ <project path="frameworks/av" name="platform/frameworks/av" groups="pdk" />
+ <project path="frameworks/base" name="platform/frameworks/base" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/compile/libbcc" name="platform/frameworks/compile/libbcc" groups="pdk" />
+ <project path="frameworks/compile/mclinker" name="platform/frameworks/compile/mclinker" groups="pdk" />
+ <project path="frameworks/compile/slang" name="platform/frameworks/compile/slang" groups="pdk" />
+ <project path="frameworks/data-binding" name="platform/frameworks/data-binding" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/ex" name="platform/frameworks/ex" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/hardware/interfaces" name="platform/frameworks/hardware/interfaces" groups="pdk" />
+ <project path="frameworks/layoutlib" name="platform/frameworks/layoutlib" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/minikin" name="platform/frameworks/minikin" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/ml" name="platform/frameworks/ml" groups="pdk" />
+ <project path="frameworks/multidex" name="platform/frameworks/multidex" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/native" name="platform/frameworks/native" groups="pdk" />
+ <project path="frameworks/opt/bitmap" name="platform/frameworks/opt/bitmap" groups="pdk-fs" />
+ <project path="frameworks/opt/calendar" name="platform/frameworks/opt/calendar" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/car/services" name="platform/frameworks/opt/car/services" groups="pdk-fs" />
+ <project path="frameworks/opt/chips" name="platform/frameworks/opt/chips" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/colorpicker" name="platform/frameworks/opt/colorpicker" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/datetimepicker" name="platform/frameworks/opt/datetimepicker" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/inputmethodcommon" name="platform/frameworks/opt/inputmethodcommon" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/net/ethernet" name="platform/frameworks/opt/net/ethernet" groups="pdk-fs" />
+ <project path="frameworks/opt/net/ims" name="platform/frameworks/opt/net/ims" groups="frameworks_ims,pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/net/lowpan" name="platform/frameworks/opt/net/lowpan" groups="pdk-fs" />
+ <project path="frameworks/opt/net/voip" name="platform/frameworks/opt/net/voip" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/net/wifi" name="platform/frameworks/opt/net/wifi" groups="pdk" />
+ <project path="frameworks/opt/photoviewer" name="platform/frameworks/opt/photoviewer" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/setupwizard" name="platform/frameworks/opt/setupwizard" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/telephony" name="platform/frameworks/opt/telephony" groups="pdk" />
+ <project path="frameworks/opt/timezonepicker" name="platform/frameworks/opt/timezonepicker" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/opt/vcard" name="platform/frameworks/opt/vcard" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/rs" name="platform/frameworks/rs" groups="pdk" />
+ <project path="frameworks/support" name="platform/frameworks/support" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/webview" name="platform/frameworks/webview" groups="pdk-cw-fs,pdk-fs" />
+ <project path="frameworks/wilhelm" name="platform/frameworks/wilhelm" groups="pdk-cw-fs,pdk-fs" />
+ <project path="hardware/akm" name="platform/hardware/akm" groups="pdk" />
+ <project path="hardware/broadcom/libbt" name="platform/hardware/broadcom/libbt" groups="pdk" />
+ <project path="hardware/broadcom/wlan" name="platform/hardware/broadcom/wlan" groups="pdk,broadcom_wlan" />
+ <project path="hardware/google/apf" name="platform/hardware/google/apf" groups="pdk" />
+ <project path="hardware/google/easel" name="platform/hardware/google/easel" groups="pdk,easel" />
+ <project path="hardware/google/interfaces" name="platform/hardware/google/interfaces" groups="pdk" />
+ <project path="hardware/intel/audio_media" name="platform/hardware/intel/audio_media" groups="intel,pdk" />
+ <project path="hardware/intel/bootstub" name="platform/hardware/intel/bootstub" groups="intel,pdk" />
+ <project path="hardware/intel/common/libmix" name="platform/hardware/intel/common/libmix" groups="intel,pdk" />
+ <project path="hardware/intel/common/libstagefrighthw" name="platform/hardware/intel/common/libstagefrighthw" groups="intel,pdk" />
+ <project path="hardware/intel/common/libva" name="platform/hardware/intel/common/libva" groups="intel,pdk" />
+ <project path="hardware/intel/common/libwsbm" name="platform/hardware/intel/common/libwsbm" groups="intel,pdk" />
+ <project path="hardware/intel/common/omx-components" name="platform/hardware/intel/common/omx-components" groups="intel,pdk" />
+ <project path="hardware/intel/common/utils" name="platform/hardware/intel/common/utils" groups="intel,pdk" />
+ <project path="hardware/intel/common/wrs_omxil_core" name="platform/hardware/intel/common/wrs_omxil_core" groups="intel,pdk" />
+ <project path="hardware/intel/img/hwcomposer" name="platform/hardware/intel/img/hwcomposer" groups="intel,pdk" />
+ <project path="hardware/intel/img/psb_headers" name="platform/hardware/intel/img/psb_headers" groups="intel,pdk" />
+ <project path="hardware/intel/img/psb_video" name="platform/hardware/intel/img/psb_video" groups="intel,pdk" />
+ <project path="hardware/interfaces" name="platform/hardware/interfaces" groups="pdk" />
+ <project path="hardware/invensense" name="platform/hardware/invensense" groups="invensense,pdk" />
+ <project path="hardware/libhardware" name="platform/hardware/libhardware" groups="pdk" />
+ <project path="hardware/libhardware_legacy" name="platform/hardware/libhardware_legacy" groups="pdk" />
+ <project path="hardware/marvell/bt" name="platform/hardware/marvell/bt" groups="marvell_bt,pdk" />
+ <project path="hardware/nxp/nfc" name="platform/hardware/nxp/nfc" groups="pdk" />
+ <project path="hardware/nxp/secure_element" name="platform/hardware/nxp/secure_element" groups="pdk" />
+ <project path="hardware/qcom/audio" name="platform/hardware/qcom/audio" groups="qcom,qcom_audio,pdk" />
+ <project path="hardware/qcom/bootctrl" name="platform/hardware/qcom/bootctrl" groups="pdk" />
+ <project path="hardware/qcom/bt" name="platform/hardware/qcom/bt" groups="qcom,pdk" />
+ <project path="hardware/qcom/camera" name="platform/hardware/qcom/camera" groups="qcom_camera,pdk" />
+ <project path="hardware/qcom/data/ipacfg-mgr" name="platform/hardware/qcom/data/ipacfg-mgr" groups="qcom,pdk" />
+ <project path="hardware/qcom/display" name="platform/hardware/qcom/display" groups="pdk,qcom,qcom_display" />
+ <project path="hardware/qcom/gps" name="platform/hardware/qcom/gps" groups="qcom,qcom_gps,pdk" />
+ <project path="hardware/qcom/keymaster" name="platform/hardware/qcom/keymaster" groups="qcom,qcom_keymaster,pdk" />
+ <project path="hardware/qcom/media" name="platform/hardware/qcom/media" groups="qcom,pdk" />
+ <project path="hardware/qcom/msm8960" name="platform/hardware/qcom/msm8960" groups="qcom_msm8960,pdk" />
+ <project path="hardware/qcom/msm8994" name="platform/hardware/qcom/msm8994" groups="qcom_msm8994,pdk" />
+ <project path="hardware/qcom/msm8996" name="platform/hardware/qcom/msm8996" groups="qcom_msm8996,pdk" />
+ <project path="hardware/qcom/msm8998" name="platform/hardware/qcom/msm8998" groups="qcom_msm8998,pdk" />
+ <project path="hardware/qcom/msm8x09" name="platform/hardware/qcom/msm8x09" groups="qcom_msm8x09" />
+ <project path="hardware/qcom/msm8x26" name="platform/hardware/qcom/msm8x26" groups="qcom_msm8x26,pdk" />
+ <project path="hardware/qcom/msm8x27" name="platform/hardware/qcom/msm8x27" groups="qcom_msm8x27,pdk" />
+ <project path="hardware/qcom/msm8x84" name="platform/hardware/qcom/msm8x84" groups="qcom_msm8x84,pdk" />
+ <project path="hardware/qcom/neuralnetworks/hvxservice" name="platform/hardware/qcom/neuralnetworks/hvxservice" groups="wahoo" />
+ <project path="hardware/qcom/power" name="platform/hardware/qcom/power" groups="qcom,pdk" />
+ <project path="hardware/qcom/wlan" name="platform/hardware/qcom/wlan" groups="qcom_wlan,pdk" />
+ <project path="hardware/ril" name="platform/hardware/ril" groups="pdk" />
+ <project path="hardware/st/nfc" name="platform/hardware/st/nfc" groups="pdk" />
+ <project path="kernel/configs" name="kernel/configs" groups="vts,pdk" />
+ <project path="kernel/tests" name="kernel/tests" />
+ <project path="libcore" name="platform/libcore" groups="pdk" />
+ <project path="libnativehelper" name="platform/libnativehelper" groups="pdk" />
+ <project path="packages/apps/BasicSmsReceiver" name="platform/packages/apps/BasicSmsReceiver" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Bluetooth" name="platform/packages/apps/Bluetooth" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Browser2" name="platform/packages/apps/Browser2" groups="pdk-fs" />
+ <project path="packages/apps/Calendar" name="platform/packages/apps/Calendar" groups="pdk-fs" />
+ <project path="packages/apps/Camera2" name="platform/packages/apps/Camera2" groups="pdk-fs" />
+ <project path="packages/apps/Car/Dialer" name="platform/packages/apps/Car/Dialer" groups="pdk-fs" />
+ <project path="packages/apps/Car/Hvac" name="platform/packages/apps/Car/Hvac" groups="pdk-fs" />
+ <project path="packages/apps/Car/LatinIME" name="platform/packages/apps/Car/LatinIME" groups="pdk-fs" />
+ <project path="packages/apps/Car/Launcher" name="platform/packages/apps/Car/Launcher" groups="pdk-fs" />
+ <project path="packages/apps/Car/LensPicker" name="platform/packages/apps/Car/LensPicker" groups="pdk-fs" />
+ <project path="packages/apps/Car/libs" name="platform/packages/apps/Car/libs" groups="pdk-fs" />
+ <project path="packages/apps/Car/LocalMediaPlayer" name="platform/packages/apps/Car/LocalMediaPlayer" groups="pdk-fs" />
+ <project path="packages/apps/Car/Media" name="platform/packages/apps/Car/Media" groups="pdk-fs" />
+ <project path="packages/apps/Car/Messenger" name="platform/packages/apps/Car/Messenger" groups="pdk-fs" />
+ <project path="packages/apps/Car/Overview" name="platform/packages/apps/Car/Overview" groups="pdk-fs" />
+ <project path="packages/apps/Car/Radio" name="platform/packages/apps/Car/Radio" groups="pdk-fs" />
+ <project path="packages/apps/Car/Settings" name="platform/packages/apps/Car/Settings" groups="pdk-fs" />
+ <project path="packages/apps/Car/Stream" name="platform/packages/apps/Car/Stream" groups="pdk-fs" />
+ <project path="packages/apps/Car/SystemUpdater" name="platform/packages/apps/Car/SystemUpdater" groups="pdk-fs" />
+ <project path="packages/apps/CarrierConfig" name="platform/packages/apps/CarrierConfig" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/CellBroadcastReceiver" name="platform/packages/apps/CellBroadcastReceiver" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/CertInstaller" name="platform/packages/apps/CertInstaller" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Contacts" name="platform/packages/apps/Contacts" groups="pdk-fs" />
+ <project path="packages/apps/DeskClock" name="platform/packages/apps/DeskClock" groups="pdk-fs" />
+ <project path="packages/apps/DevCamera" name="platform/packages/apps/DevCamera" groups="pdk" />
+ <project path="packages/apps/Dialer" name="platform/packages/apps/Dialer" groups="pdk-fs" />
+ <project path="packages/apps/DocumentsUI" name="platform/packages/apps/DocumentsUI" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Email" name="platform/packages/apps/Email" groups="pdk-fs" />
+ <project path="packages/apps/EmergencyInfo" name="platform/packages/apps/EmergencyInfo" groups="pdk-fs" />
+ <project path="packages/apps/ExactCalculator" name="platform/packages/apps/ExactCalculator" groups="pdk-fs" />
+ <project path="packages/apps/Gallery" name="platform/packages/apps/Gallery" groups="pdk-fs" />
+ <project path="packages/apps/Gallery2" name="platform/packages/apps/Gallery2" groups="pdk-fs" />
+ <project path="packages/apps/HTMLViewer" name="platform/packages/apps/HTMLViewer" groups="pdk-fs" />
+ <project path="packages/apps/KeyChain" name="platform/packages/apps/KeyChain" groups="pdk-fs" />
+ <project path="packages/apps/Launcher2" name="platform/packages/apps/Launcher2" groups="pdk-fs" />
+ <project path="packages/apps/Launcher3" name="platform/packages/apps/Launcher3" groups="pdk-fs" />
+ <project path="packages/apps/LegacyCamera" name="platform/packages/apps/LegacyCamera" groups="pdk-fs" />
+ <project path="packages/apps/ManagedProvisioning" name="platform/packages/apps/ManagedProvisioning" groups="pdk-fs" />
+ <project path="packages/apps/Messaging" name="platform/packages/apps/Messaging" groups="pdk-fs" />
+ <project path="packages/apps/Music" name="platform/packages/apps/Music" groups="pdk-fs" />
+ <project path="packages/apps/MusicFX" name="platform/packages/apps/MusicFX" groups="pdk-fs" />
+ <project path="packages/apps/Nfc" name="platform/packages/apps/Nfc" groups="apps_nfc,pdk-fs" />
+ <project path="packages/apps/OneTimeInitializer" name="platform/packages/apps/OneTimeInitializer" groups="pdk-fs" />
+ <project path="packages/apps/PackageInstaller" name="platform/packages/apps/PackageInstaller" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/PhoneCommon" name="platform/packages/apps/PhoneCommon" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/apps/Protips" name="platform/packages/apps/Protips" groups="pdk-fs" />
+ <project path="packages/apps/Provision" name="platform/packages/apps/Provision" groups="pdk-fs" />
+ <project path="packages/apps/QuickSearchBox" name="platform/packages/apps/QuickSearchBox" groups="pdk-fs" />
+ <project path="packages/apps/SafetyRegulatoryInfo" name="platform/packages/apps/SafetyRegulatoryInfo" groups="pdk-fs" />
+ <project path="packages/apps/SecureElement" name="platform/packages/apps/SecureElement" groups="apps_se,pdk-fs" />
+ <project path="packages/apps/Settings" name="platform/packages/apps/Settings" groups="pdk-fs" />
+ <project path="packages/apps/SoundRecorder" name="platform/packages/apps/SoundRecorder" groups="pdk-fs" />
+ <project path="packages/apps/SpareParts" name="platform/packages/apps/SpareParts" groups="pdk-fs" />
+ <project path="packages/apps/Stk" name="platform/packages/apps/Stk" groups="apps_stk,pdk-fs" />
+ <project path="packages/apps/StorageManager" name="platform/packages/apps/StorageManager" groups="pdk-fs" />
+ <project path="packages/apps/Tag" name="platform/packages/apps/Tag" groups="pdk-fs" />
+ <project path="packages/apps/Terminal" name="platform/packages/apps/Terminal" groups="pdk-fs" />
+ <project path="packages/apps/Test/connectivity" name="platform/packages/apps/Test/connectivity" groups="pdk" />
+ <project path="packages/apps/TimeZoneData" name="platform/packages/apps/TimeZoneData" groups="pdk" />
+ <project path="packages/apps/TimeZoneUpdater" name="platform/packages/apps/TimeZoneUpdater" groups="pdk" />
+ <project path="packages/apps/Traceur" name="platform/packages/apps/Traceur" groups="pdk-fs" />
+ <project path="packages/apps/TvSettings" name="platform/packages/apps/TvSettings" groups="pdk-fs" />
+ <project path="packages/apps/TV" name="platform/packages/apps/TV" />
+ <project path="packages/apps/UnifiedEmail" name="platform/packages/apps/UnifiedEmail" groups="pdk-fs" />
+ <project path="packages/apps/WallpaperPicker" name="platform/packages/apps/WallpaperPicker" groups="pdk-fs" />
+ <project path="packages/experimental" name="platform/packages/experimental" />
+ <project path="packages/inputmethods/LatinIME" name="platform/packages/inputmethods/LatinIME" groups="pdk-fs" />
+ <project path="packages/inputmethods/OpenWnn" name="platform/packages/inputmethods/OpenWnn" groups="pdk-fs" />
+ <project path="packages/providers/ApplicationsProvider" name="platform/packages/providers/ApplicationsProvider" groups="pdk-fs" />
+ <project path="packages/providers/BlockedNumberProvider" name="platform/packages/providers/BlockedNumberProvider" groups="pdk-fs" />
+ <project path="packages/providers/BookmarkProvider" name="platform/packages/providers/BookmarkProvider" groups="pdk-fs" />
+ <project path="packages/providers/CalendarProvider" name="platform/packages/providers/CalendarProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/CallLogProvider" name="platform/packages/providers/CallLogProvider" groups="pdk-fs" />
+ <project path="packages/providers/ContactsProvider" name="platform/packages/providers/ContactsProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/DownloadProvider" name="platform/packages/providers/DownloadProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/MediaProvider" name="platform/packages/providers/MediaProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/PartnerBookmarksProvider" name="platform/packages/providers/PartnerBookmarksProvider" groups="pdk-fs" />
+ <project path="packages/providers/TelephonyProvider" name="platform/packages/providers/TelephonyProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/providers/TvProvider" name="platform/packages/providers/TvProvider" groups="pdk-fs" />
+ <project path="packages/providers/UserDictionaryProvider" name="platform/packages/providers/UserDictionaryProvider" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/screensavers/Basic" name="platform/packages/screensavers/Basic" groups="pdk-fs" />
+ <project path="packages/screensavers/PhotoTable" name="platform/packages/screensavers/PhotoTable" groups="pdk-fs" />
+ <project path="packages/screensavers/WebView" name="platform/packages/screensavers/WebView" groups="pdk-fs" />
+ <project path="packages/services/BuiltInPrintService" name="platform/packages/services/BuiltInPrintService" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/Car" name="platform/packages/services/Car" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/Mms" name="platform/packages/services/Mms" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/NetworkRecommendation" name="platform/packages/services/NetworkRecommendation" groups="pdk-fs" />
+ <project path="packages/services/Telecomm" name="platform/packages/services/Telecomm" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/services/Telephony" name="platform/packages/services/Telephony" groups="pdk-cw-fs,pdk-fs" />
+ <project path="packages/wallpapers/LivePicker" name="platform/packages/wallpapers/LivePicker" groups="pdk-fs" />
+ <project path="pdk" name="platform/pdk" groups="pdk" />
+ <project path="platform_testing" name="platform/platform_testing" groups="pdk-fs,pdk-cw-fs,cts" />
+ <project path="prebuilts/abi-dumps/ndk" name="platform/prebuilts/abi-dumps/ndk" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/abi-dumps/vndk" name="platform/prebuilts/abi-dumps/vndk" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/android-emulator" name="platform/prebuilts/android-emulator" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/build-tools" name="platform/prebuilts/build-tools" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/checkstyle" name="platform/prebuilts/checkstyle" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/clang-tools" name="platform/prebuilts/clang-tools" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/clang/host/darwin-x86" name="platform/prebuilts/clang/host/darwin-x86" groups="pdk,darwin" clone-depth="1" />
+ <project path="prebuilts/clang/host/linux-x86" name="platform/prebuilts/clang/host/linux-x86" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/deqp" name="platform/prebuilts/deqp" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/devtools" name="platform/prebuilts/devtools" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/aarch64/aarch64-linux-android-4.9" name="platform/prebuilts/gcc/darwin-x86/aarch64/aarch64-linux-android-4.9" groups="pdk,darwin,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9" name="platform/prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9" groups="pdk,darwin,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/host/i686-apple-darwin-4.2.1" name="platform/prebuilts/gcc/darwin-x86/host/i686-apple-darwin-4.2.1" groups="pdk,darwin" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/mips/mips64el-linux-android-4.9" name="platform/prebuilts/gcc/darwin-x86/mips/mips64el-linux-android-4.9" groups="pdk,darwin,mips" clone-depth="1" />
+ <project path="prebuilts/gcc/darwin-x86/x86/x86_64-linux-android-4.9" name="platform/prebuilts/gcc/darwin-x86/x86/x86_64-linux-android-4.9" groups="pdk,darwin,x86" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9" groups="pdk,linux,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9" groups="pdk,linux,arm" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8" groups="pdk,linux" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8" name="platform/prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8" groups="pdk-fs" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9" name="platform/prebuilts/gcc/linux-x86/mips/mips64el-linux-android-4.9" groups="pdk,linux,mips" clone-depth="1" />
+ <project path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9" groups="pdk,linux,x86" clone-depth="1" />
+ <project path="prebuilts/gdb/darwin-x86" name="platform/prebuilts/gdb/darwin-x86" groups="darwin" clone-depth="1" />
+ <project path="prebuilts/gdb/linux-x86" name="platform/prebuilts/gdb/linux-x86" groups="linux" clone-depth="1" />
+ <project path="prebuilts/go/darwin-x86" name="platform/prebuilts/go/darwin-x86" groups="darwin,pdk,tradefed" clone-depth="1" />
+ <project path="prebuilts/go/linux-x86" name="platform/prebuilts/go/linux-x86" groups="linux,pdk,tradefed" clone-depth="1" />
+ <project path="prebuilts/gradle-plugin" name="platform/prebuilts/gradle-plugin" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/jdk/jdk8" name="platform/prebuilts/jdk/jdk8" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/jdk/jdk9" name="platform/prebuilts/jdk/jdk9" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/libs/libedit" name="platform/prebuilts/libs/libedit" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/maven_repo/android" name="platform/prebuilts/maven_repo/android" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/maven_repo/bumptech" name="platform/prebuilts/maven_repo/bumptech" groups="pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/maven_repo/google-play-service-client-libraries-3p" name="platform/prebuilts/maven_repo/google-play-service-client-libraries-3p" clone-depth="1" />
+ <project path="prebuilts/misc" name="platform/prebuilts/misc" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/ndk" name="platform/prebuilts/ndk" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/python/darwin-x86/2.7.5" name="platform/prebuilts/python/darwin-x86/2.7.5" groups="darwin,pdk,pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/python/linux-x86/2.7.5" name="platform/prebuilts/python/linux-x86/2.7.5" groups="linux,pdk,pdk-cw-fs,pdk-fs" clone-depth="1" />
+ <project path="prebuilts/qemu-kernel" name="platform/prebuilts/qemu-kernel" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/r8" name="platform/prebuilts/r8" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/sdk" name="platform/prebuilts/sdk" groups="pdk" clone-depth="1" />
+ <project path="prebuilts/tools" name="platform/prebuilts/tools" groups="pdk,tools" clone-depth="1" />
+ <project path="sdk" name="platform/sdk" groups="pdk-cw-fs,pdk-fs" />
+ <project path="system/bt" name="platform/system/bt" groups="pdk" />
+ <project path="system/ca-certificates" name="platform/system/ca-certificates" groups="pdk" />
+ <project path="system/chre" name="platform/system/chre" groups="pdk" />
+ <project path="system/connectivity/wificond" name="platform/system/connectivity/wificond" groups="pdk" />
+ <project path="system/connectivity/wifilogd" name="platform/system/connectivity/wifilogd" groups="pdk" />
+ <project path="system/core" name="platform/system/core" groups="pdk" />
+ <project path="system/extras" name="platform/system/extras" groups="pdk" />
+ <project path="system/gatekeeper" name="platform/system/gatekeeper" groups="pdk" />
+ <project path="system/hardware/interfaces" name="platform/system/hardware/interfaces" groups="pdk" />
+ <project path="system/hwservicemanager" name="platform/system/hwservicemanager" groups="pdk" />
+ <project path="system/iot/attestation" name="platform/system/iot/attestation" groups="pdk" />
+ <project path="system/keymaster" name="platform/system/keymaster" groups="pdk" />
+ <project path="system/libfmq" name="platform/system/libfmq" groups="pdk" />
+ <project path="system/libhidl" name="platform/system/libhidl" groups="pdk" />
+ <project path="system/libhwbinder" name="platform/system/libhwbinder" groups="pdk" />
+ <project path="system/libufdt" name="platform/system/libufdt" groups="pdk" />
+ <project path="system/libvintf" name="platform/system/libvintf" groups="pdk" />
+ <project path="system/media" name="platform/system/media" groups="pdk" />
+ <project path="system/netd" name="platform/system/netd" groups="pdk" />
+ <project path="system/nfc" name="platform/system/nfc" groups="pdk" />
+ <project path="system/nvram" name="platform/system/nvram" groups="pdk" />
+ <project path="system/security" name="platform/system/security" groups="pdk" />
+ <project path="system/sepolicy" name="platform/system/sepolicy" groups="pdk" />
+ <project path="system/timezone" name="platform/system/timezone" groups="pdk" />
+ <project path="system/tools/aidl" name="platform/system/tools/aidl" groups="pdk-cw-fs,pdk-fs" />
+ <project path="system/tools/bpt" name="platform/system/tools/bpt" groups="pdk" />
+ <project path="system/tools/hidl" name="platform/system/tools/hidl" groups="pdk" />
+ <project path="system/update_engine" name="platform/system/update_engine" groups="pdk" />
+ <project path="system/vold" name="platform/system/vold" groups="pdk" />
+ <project path="test/framework" name="platform/test/framework" groups="vts,pdk" />
+ <project path="test/mlts/benchmark" name="platform/test/mlts/benchmark" groups="pdk" />
+ <project path="test/mlts/models" name="platform/test/mlts/models" groups="pdk" />
+ <project path="test/sts" name="platform/test/sts" groups="sts,pdk" />
+ <project path="test/vti/alert" name="platform/test/vti/alert" groups="vts,pdk" />
+ <project path="test/vti/dashboard" name="platform/test/vti/dashboard" groups="vts,pdk" />
+ <project path="test/vti/fuzz_test_serving" name="platform/test/vti/fuzz_test_serving" groups="vts,pdk" />
+ <project path="test/vti/test_serving" name="platform/test/vti/test_serving" groups="vts,pdk" />
+ <project path="test/vts" name="platform/test/vts" groups="vts,pdk" />
+ <project path="test/vts-testcase/fuzz" name="platform/test/vts-testcase/fuzz" groups="vts,pdk" />
+ <project path="test/vts-testcase/hal" name="platform/test/vts-testcase/hal" groups="vts,pdk" />
+ <project path="test/vts-testcase/hal-trace" name="platform/test/vts-testcase/hal-trace" groups="vts,pdk" />
+ <project path="test/vts-testcase/kernel" name="platform/test/vts-testcase/kernel" groups="vts,pdk" />
+ <project path="test/vts-testcase/nbu" name="platform/test/vts-testcase/nbu" groups="vts,pdk" />
+ <project path="test/vts-testcase/performance" name="platform/test/vts-testcase/performance" groups="vts,pdk" />
+ <project path="test/vts-testcase/security" name="platform/test/vts-testcase/security" groups="vts,pdk" />
+ <project path="test/vts-testcase/vndk" name="platform/test/vts-testcase/vndk" groups="vts,pdk" />
+ <project path="toolchain/benchmark" name="toolchain/benchmark" />
+ <project path="toolchain/binutils" name="toolchain/binutils" groups="pdk" />
+ <project path="toolchain/pgo-profiles" name="toolchain/pgo-profiles" groups="pdk" />
+ <project path="tools/acloud" name="platform/tools/acloud" groups="tools,vts,pdk,tradefed" />
+ <project path="tools/adt/idea" name="platform/tools/adt/idea" groups="notdefault,tools" />
+ <project path="tools/apksig" name="platform/tools/apksig" groups="pdk,tradefed" />
+ <project path="tools/apkzlib" name="platform/tools/apkzlib" groups="pdk,tradefed" />
+ <project path="tools/base" name="platform/tools/base" groups="notdefault,tools" />
+ <project path="tools/build" name="platform/tools/build" groups="notdefault,tools" />
+ <project path="tools/dexter" name="platform/tools/dexter" groups="tools,pdk-fs" />
+ <project path="tools/external/fat32lib" name="platform/tools/external/fat32lib" groups="tools" />
+ <project path="tools/external/gradle" name="platform/tools/external/gradle" groups="tools" clone-depth="1" />
+ <project path="tools/idea" name="platform/tools/idea" groups="notdefault,tools" />
+ <project path="tools/loganalysis" name="platform/tools/loganalysis" groups="nopresubmit,pdk,tradefed" />
+ <project path="tools/metalava" name="platform/tools/metalava" groups="tools" />
+ <project path="tools/motodev" name="platform/tools/motodev" groups="notdefault,motodev" />
+ <project path="tools/repohooks" name="platform/tools/repohooks" groups="adt-infra,cts,developers,motodev,pdk,tools,tradefed" />
+ <project path="tools/security" name="platform/tools/security" groups="pdk,tools" />
+ <project path="tools/studio/cloud" name="platform/tools/studio/cloud" groups="notdefault,tools" />
+ <project path="tools/swt" name="platform/tools/swt" groups="notdefault,tools" />
+ <project path="tools/test/connectivity" name="platform/tools/test/connectivity" groups="pdk" />
+ <project path="tools/test/graphicsbenchmark" name="platform/tools/test/graphicsbenchmark" groups="pdk" />
+ <project path="tools/tradefederation/core" name="platform/tools/tradefederation" groups="pdk,tradefed" />
+ <project path="tools/tradefederation/contrib" name="platform/tools/tradefederation/contrib" groups="pdk,tradefed" />
+
+ <repo-hooks in-project="platform/tools/repohooks" enabled-list="pre-upload" />
+
+</manifest>
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
index 460d3b6a7e4..343e140f5fb 100644
--- a/spec/helpers/namespaces_helper_spec.rb
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -28,6 +28,16 @@ describe NamespacesHelper do
expect(options).not_to include(admin_group.name)
expect(options).to include(user_group.name)
+ expect(options).to include(user.name)
+ end
+
+ it 'returns only groups if groups_only option is true' do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ options = helper.namespaces_options(nil, groups_only: true)
+
+ expect(options).not_to include(user.name)
+ expect(options).to include(user_group.name)
end
context 'when nested groups are available', :nested_groups do
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index 10341486512..25827423914 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -12,7 +12,8 @@ describe Gitlab::ImportSources do
'FogBugz' => 'fogbugz',
'Repo by URL' => 'git',
'GitLab export' => 'gitlab_project',
- 'Gitea' => 'gitea'
+ 'Gitea' => 'gitea',
+ 'Manifest file' => 'manifest'
}
expect(described_class.options).to eq(expected)
@@ -31,6 +32,7 @@ describe Gitlab::ImportSources do
git
gitlab_project
gitea
+ manifest
)
expect(described_class.values).to eq(expected)
@@ -63,7 +65,8 @@ describe Gitlab::ImportSources do
'fogbugz' => Gitlab::FogbugzImport::Importer,
'git' => nil,
'gitlab_project' => Gitlab::ImportExport::Importer,
- 'gitea' => Gitlab::LegacyGithubImport::Importer
+ 'gitea' => Gitlab::LegacyGithubImport::Importer,
+ 'manifest' => nil
}
import_sources.each do |name, klass|
@@ -82,7 +85,8 @@ describe Gitlab::ImportSources do
'fogbugz' => 'FogBugz',
'git' => 'Repo by URL',
'gitlab_project' => 'GitLab export',
- 'gitea' => 'Gitea'
+ 'gitea' => 'Gitea',
+ 'manifest' => 'Manifest file'
}
import_sources.each do |name, title|
diff --git a/spec/lib/gitlab/manifest_import/manifest_spec.rb b/spec/lib/gitlab/manifest_import/manifest_spec.rb
new file mode 100644
index 00000000000..ab305fb2316
--- /dev/null
+++ b/spec/lib/gitlab/manifest_import/manifest_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Gitlab::ManifestImport::Manifest, :postgresql do
+ let(:file) { File.open(Rails.root.join('spec/fixtures/aosp_manifest.xml')) }
+ let(:manifest) { described_class.new(file) }
+
+ describe '#valid?' do
+ context 'valid file' do
+ it { expect(manifest.valid?).to be true }
+ end
+
+ context 'missing or invalid attributes' do
+ let(:file) { Tempfile.new('foo') }
+
+ before do
+ content = <<~EOS
+ <manifest>
+ <remote review="invalid-url" />
+ <project name="platform/build"/>
+ </manifest>
+ EOS
+
+ file.write(content)
+ file.rewind
+ end
+
+ it { expect(manifest.valid?).to be false }
+
+ describe 'errors' do
+ before do
+ manifest.valid?
+ end
+
+ it { expect(manifest.errors).to include('Make sure a <remote> tag is present and is valid.') }
+ it { expect(manifest.errors).to include('Make sure every <project> tag has name and path attributes.') }
+ end
+ end
+ end
+
+ describe '#projects' do
+ it { expect(manifest.projects.size).to eq(660) }
+ it { expect(manifest.projects[0][:name]).to eq('platform/build') }
+ it { expect(manifest.projects[0][:path]).to eq('build/make') }
+ it { expect(manifest.projects[0][:url]).to eq('https://android-review.googlesource.com/platform/build') }
+ end
+end
diff --git a/spec/lib/gitlab/manifest_import/project_creator_spec.rb b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
new file mode 100644
index 00000000000..1d01d437535
--- /dev/null
+++ b/spec/lib/gitlab/manifest_import/project_creator_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe Gitlab::ManifestImport::ProjectCreator, :postgresql do
+ let(:group) { create(:group) }
+ let(:user) { create(:user) }
+ let(:repository) do
+ {
+ path: 'device/common',
+ url: 'https://android-review.googlesource.com/device/common'
+ }
+ end
+
+ before do
+ group.add_owner(user)
+ end
+
+ subject { described_class.new(repository, group, user) }
+
+ describe '#execute' do
+ it { expect(subject.execute).to be_a(Project) }
+ it { expect { subject.execute }.to change { Project.count }.by(1) }
+ it { expect { subject.execute }.to change { Group.count }.by(1) }
+
+ it 'creates project with valid full path and import url' do
+ subject.execute
+
+ project = Project.last
+
+ expect(project.full_path).to eq(File.join(group.path, 'device/common'))
+ expect(project.import_url).to eq('https://android-review.googlesource.com/device/common')
+ end
+ end
+end