Manifest plugin with update command Change-Id: Ifa618333dff466c2135ae10ac1ca9476799c8ded
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f9d776 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,8 @@ +/target +/.classpath +/.project +/.settings/org.maven.ide.eclipse.prefs +/.settings/org.eclipse.m2e.core.prefs +/.idea + +/manifest.iml
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..f15f85d --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@ +#Tue May 15 09:19:33 PDT 2012 +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding//src/test/resources=UTF-8
diff --git a/.settings/org.eclipse.core.runtime.prefs b/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000..8667cfd --- /dev/null +++ b/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,3 @@ +#Tue Sep 02 16:59:24 PDT 2008 +eclipse.preferences.version=1 +line.separator=\n
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..7e74f17 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,263 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=16 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=0 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=true +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=80 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=2 +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..d4218a5 --- /dev/null +++ b/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,61 @@ +#Wed Jul 29 11:31:38 PDT 2009 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_Google Format +formatter_settings_version=11 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=com.google;com;junit;net;org;java;javax; +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.staticondemandthreshold=99 +org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates/> +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=false +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=true +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=false +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=false +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..11069ed --- /dev/null +++ b/LICENSE
@@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.
diff --git a/deploy_and_test_manifest_plugin.sh b/deploy_and_test_manifest_plugin.sh new file mode 100755 index 0000000..85075f6 --- /dev/null +++ b/deploy_and_test_manifest_plugin.sh
@@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +source "$(dirname "$0")/test/help_deploy.sh" + +plugin=manifest +jar_glob="$(dirname "$0")/../../plugins/$plugin/target/$plugin*.jar" +matches_one_file "$jar_glob" || exit 1 +jar=$(echo $jar_glob) +plugin="$plugin"__"$USER" + +host=review-android-dev.quicinc.com +gerrit_dir=$(get_quic_gerrit_dir) + +# Main +deploy_plugin "$plugin" "$jar" "$host" "$gerrit_dir" && + "$(dirname "$0")/test/test_manifest_plugin.sh" \ + "$plugin" "$host" "$gerrit_dir" || exit $? + +echo "All test cases passed." +exit 0
diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..39a0ce4 --- /dev/null +++ b/pom.xml
@@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +Copyright (C) 2015 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>com.googlesource.gerrit.plugins.manifest</groupId> + <artifactId>manifest</artifactId> + <name>manifest</name> + <packaging>jar</packaging> + <version>2.13.2</version> + + <properties> + <Gerrit-ApiType>plugin</Gerrit-ApiType> + <Gerrit-ApiVersion>${project.version}</Gerrit-ApiVersion> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>2.4</version> + <configuration> + <archive> + <manifestEntries> + <Gerrit-SshModule>com.googlesource.gerrit.plugins.manifest.SshModule</Gerrit-SshModule> + + <Implementation-Vendor>QuIC Qualcomm Innovation Center</Implementation-Vendor> + <Implementation-URL>http://review-bait-quicinc.com/gerrit/</Implementation-URL> + + <Implementation-Title>Plugin ${project.artifactId}</Implementation-Title> + <Implementation-Version>${project.version}</Implementation-Version> + + <Gerrit-ApiType>${Gerrit-ApiType}</Gerrit-ApiType> + <Gerrit-ApiVersion>${Gerrit-ApiVersion}</Gerrit-ApiVersion> + </manifestEntries> + </archive> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.3.2</version> + <configuration> + <source>1.7</source> + <target>1.7</target> + <encoding>UTF-8</encoding> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <configuration> + <createDependencyReducedPom>false</createDependencyReducedPom> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>com.google.gerrit</groupId> + <artifactId>gerrit-${Gerrit-ApiType}-api</artifactId> + <version>${Gerrit-ApiVersion}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.jdom</groupId> + <artifactId>jdom2</artifactId> + <version>2.0.6</version> + </dependency> + + <dependency> + <groupId>jaxen</groupId> + <artifactId>jaxen</artifactId> + <version>1.1.1</version> + </dependency> + </dependencies> + + <repositories> + <repository> + <id>gerrit-api-repository</id> + <url>https://gerrit-api.commondatastorage.googleapis.com/release/</url> + </repository> + </repositories> +</project>
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/CustomOutputter.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/CustomOutputter.java new file mode 100644 index 0000000..53c9feb --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/CustomOutputter.java
@@ -0,0 +1,61 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +import org.jdom2.Attribute; +import org.jdom2.Document; +import org.jdom2.output.Format; +import org.jdom2.output.support.AbstractXMLOutputProcessor; +import org.jdom2.output.support.FormatStack; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; + +public class CustomOutputter extends AbstractXMLOutputProcessor { + private ArrayList<DTDAttribute> dtdAttributes; + + public CustomOutputter(ArrayList<DTDAttribute> dtdAttributes) { + this.dtdAttributes = dtdAttributes; + } + + @Override + public void process(Writer out, Format format, Document doc) throws IOException { + format.setLineSeparator("\n"); + super.process(out, format, doc); + } + + @Override + protected void printAttribute(java.io.Writer out, FormatStack fstack, + Attribute attribute) throws java.io.IOException { + // Do not print attributes that use default values + for (DTDAttribute dtdAttribute : dtdAttributes) { + if (attribute.getName().equals(dtdAttribute.getAttributeName()) && + attribute.getParent().getName().equals(dtdAttribute.getElementName()) && + attribute.getAttributeType().toString().equals(dtdAttribute.getType()) && + attribute.getValue().equals(dtdAttribute.getValue())) { + return; + } + } + + out.append(fstack.getLineSeparator()); + String indent = fstack.getIndent(); + out.append(fstack.getLevelIndent()); + out.append(indent); + // super.printAttribute() indents with an extra space, this will offset that + out.append(indent.substring(0, indent.length() - 1)); + super.printAttribute(out, fstack, attribute); + } +} \ No newline at end of file
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/DTDAttribute.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/DTDAttribute.java new file mode 100644 index 0000000..b6b1169 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/DTDAttribute.java
@@ -0,0 +1,52 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +public class DTDAttribute { + private String elementName; + private String attributeName; + private String type; + private String valueDefault; + private String value; + + public DTDAttribute(String eName, String aName, String type, + String valueDefault, String value) { + this.elementName = eName; + this.attributeName = aName; + this.type = type; + this.valueDefault = valueDefault; + this.value = value; + } + + public String getElementName(){ + return this.elementName; + } + + public String getAttributeName(){ + return this.attributeName; + } + + public String getType(){ + return this.type; + } + + public String getValueDefault(){ + return this.valueDefault; + } + + public String getValue(){ + return this.value; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/Git.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/Git.java new file mode 100644 index 0000000..50b43e6 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/Git.java
@@ -0,0 +1,48 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +import com.google.gerrit.reviewdb.client.Branch; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.server.git.GitRepositoryManager; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevWalk; + +import java.io.IOException; + +public class Git { + public static ObjectId getCommit(GitRepositoryManager repos, + Branch.NameKey branch) throws IOException { + Repository repo = repos.openRepository(branch.getParentKey()); + try { + return repo.getRef(branch.get()).getObjectId(); + } finally { + repo.close(); + } + } + + public static boolean mergedInto(GitRepositoryManager repos, String project, + ObjectId haystack, ObjectId needle) throws IOException { + Repository repo = repos.openRepository(Project.NameKey.parse(project)); + RevWalk walk = new RevWalk(repo); + try { + return walk.isMergedInto(walk.parseCommit(needle), + walk.parseCommit(haystack)); + } finally { + repo.close(); + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/GitFile.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/GitFile.java new file mode 100644 index 0000000..cd93776 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/GitFile.java
@@ -0,0 +1,105 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +import com.google.gerrit.reviewdb.client.Branch; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.VersionedMetaData; + +import com.google.gerrit.server.project.NoSuchProjectException; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; + +import java.io.IOException; + +/** + * A GitFile is a text file (UTF8) from a git repository + */ +public class GitFile extends VersionedMetaData { + public interface Factory { + GitFile create(@Assisted Branch.NameKey branch, @Assisted String file); + } + + private MetaDataUpdate.User metaDataUpdateFactory; + private GitRepositoryManager repos; + private Branch.NameKey branch; + private String file; + + public String text; + + @Inject + public GitFile(MetaDataUpdate.User metaDataUpdateFactory, + GitRepositoryManager repos, + @Assisted Branch.NameKey branch, + @Assisted String file) { + this.metaDataUpdateFactory = metaDataUpdateFactory; + this.repos = repos; + this.branch = branch; + this.file = file; + } + + public String read() throws ConfigInvalidException, IOException { + Repository repo = repos.openRepository(branch.getParentKey()); + try { + load(repo); + return text; + } finally { + repo.close(); + } + } + + public RevCommit write(String fileContent, String commitMessage) + throws ConfigInvalidException, IOException, NoSuchProjectException { + MetaDataUpdate md = metaDataUpdateFactory.create(branch.getParentKey()); + try { + load(md); + text = fileContent; + md.getCommitBuilder().setCommitter(metaDataUpdateFactory.getUserPersonIdent()); + md.setMessage(commitMessage); + return commit(md); + } finally { + md.close(); + } + } + + public void setBranch(Branch.NameKey branch) { + this.branch = branch; + } + + public void setFileName(String fileName) { + this.file = fileName; + } + + @Override + protected String getRefName() { + return branch.get(); + } + + @Override + protected void onLoad() throws IOException { + text = readUTF8(file); + } + + @Override + protected boolean onSave(CommitBuilder commit) throws IOException { + saveUTF8(file, text); + return true; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/ManifestOperation.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/ManifestOperation.java new file mode 100644 index 0000000..71a2cb0 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/ManifestOperation.java
@@ -0,0 +1,19 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +public interface ManifestOperation { + public void transform(ManifestUpdater manifestUpdater) throws Exception; +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/ManifestUpdater.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/ManifestUpdater.java new file mode 100644 index 0000000..c31de75 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/ManifestUpdater.java
@@ -0,0 +1,169 @@ +//Copyright (C) 2016 The Android Open Source Project +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +import com.google.gerrit.common.errors.PermissionDeniedException; +import com.google.gerrit.reviewdb.client.Branch; +import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.project.InvalidChangeOperationException; +import com.google.gerrit.server.project.NoSuchProjectException; +import com.google.gerrit.server.project.NoSuchRefException; +import com.google.gerrit.server.project.ProjectControl; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; + +import com.googlesource.gerrit.plugins.manifest.extensions.RepoManifest; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.jdom2.Attribute; +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.filter.Filters; +import org.jdom2.JDOMException; +import org.jdom2.xpath.XPathExpression; +import org.jdom2.xpath.XPathFactory; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.xml.parsers.ParserConfigurationException; + +public class ManifestUpdater { + public class Project { + private Element element; + + public Project(Element element) { + this.element = element; + } + + public String getAttribute(String attname) { + switch (attname) { + case RepoManifest.ATTR_PROJECT_NAME: + String name = element.getAttributeValue(RepoManifest.ATTR_PROJECT_NAME); + if (name == null) { + name = element.getAttributeValue(RepoManifest.ATTR_PROJECT_PATH); + } + return name; + case RepoManifest.ATTR_PROJECT_REVISION: + String revision = element.getAttributeValue(RepoManifest.ATTR_PROJECT_REVISION); + if (revision == null) { + revision = getDefault(RepoManifest.ATTR_PROJECT_REVISION); + } + return revision; + default: + return element.getAttributeValue(attname); + } + } + + public void setAttribute(String attname, String value) { + element.setAttribute(attname, value); + } + + private String getDefault(String attname) { + Document doc = manifestXml.getDocument(); + XPathFactory xFactory = XPathFactory.instance(); + String xPathStr = "/manifest/default/@" + attname; + + XPathExpression<Attribute> attribXPathExpr = xFactory.compile(xPathStr, + Filters.attribute()); + try { + return attribXPathExpr.evaluate(doc).get(0).getValue(); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + } + +// protected CreateChange.Factory ccFactory; + protected ManifestXml manifestXml; + protected GitFile manifestFile; + protected GitFile.Factory gitFileFactory; + protected ProjectControl.Factory pctlFactory; + protected GitRepositoryManager repos; + protected TempRef.Factory tmpRefFactory; + protected CurrentUser user; + + @Inject + public ManifestUpdater(CurrentUser user, ProjectControl.Factory pctlFactory, + GitFile.Factory gitFileFactory, GitRepositoryManager repos, + //CreateChange.Factory ccFactory, + TempRef.Factory tmpRefFactory) { +// this.ccFactory = ccFactory; + this.gitFileFactory = gitFileFactory; + this.pctlFactory = pctlFactory; + this.repos = repos; + this.tmpRefFactory = tmpRefFactory; + this.user = user; + } + + public void read(Branch.NameKey branch, String srcFile) + throws ConfigInvalidException, IOException, JDOMException, + NoSuchProjectException, NoSuchRefException, ParserConfigurationException, + PermissionDeniedException, SAXException { + ProjectControl pctl = pctlFactory.controlFor(branch.getParentKey()); + if (!pctl.controlForRef(branch).isVisible()) { + throw new NoSuchRefException(branch.getShortName()); + } + manifestFile = gitFileFactory.create(branch, srcFile); + manifestXml = new ManifestXml(manifestFile.read()); + } + + public List<Project> getProjects() { + Document doc = manifestXml.getDocument(); + XPathFactory xFactory = XPathFactory.instance(); + String xPathStr = "/manifest/project"; + XPathExpression<Element> prjXPathExpr = xFactory.compile(xPathStr, + Filters.element()); + + List<Project> projects = new ArrayList<>(); + for (Element element: prjXPathExpr.evaluate(doc)) { + projects.add(new Project(element)); + } + return projects; + } + + public List<Project> getProjects(String attname, String value) { + return match(getProjects(), attname, value); + } + + public RevCommit write(Branch.NameKey branch, String file, + String commitMessage) throws ConfigInvalidException, IOException, + NoSuchProjectException, PermissionDeniedException { + ProjectControl pctl = pctlFactory.controlFor(branch.getParentKey()); + if (!pctl.controlForRef(branch).canUpdate()) { + throw new PermissionDeniedException("Cannot write to " + branch); + } + manifestFile.setBranch(branch); + manifestFile.setFileName(file); + return manifestFile.write(manifestXml.getManifestText(), commitMessage); + } + + public static List<Project> match(Iterable<Project> projects, String attname, + String value) { + List<Project> retProjects = new ArrayList<>(); + for (Project project: projects) { + if (project.getAttribute(attname).equals(value)) { + retProjects.add(project); + } + } + return retProjects; + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/ManifestXml.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/ManifestXml.java new file mode 100644 index 0000000..a3c2c77 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/ManifestXml.java
@@ -0,0 +1,96 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +import org.jdom2.Document; +import org.jdom2.JDOMFactory; +import org.jdom2.input.SAXBuilder; +import org.jdom2.JDOMException; +import org.jdom2.input.sax.SAXHandler; +import org.jdom2.input.sax.SAXHandlerFactory; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.UUID; + + +/** Some xml parsing routines tuned for our manifests */ +public class ManifestXml { + private Document doc; + private ArrayList<DTDAttribute> dtdAttributes = new ArrayList<>(); + private String replacementText; + + public ManifestXml(String xml) throws IOException, + ParserConfigurationException, SAXException, JDOMException { + // Insert a unique identifier for entity definitions to prevent them from + // getting expanded during the parse + genReplacementText(xml); + xml = xml.replaceAll("&([^;]*);", replacementText + "$1;"); + + SAXBuilder builder = new SAXBuilder(); + builder.setSAXHandlerFactory(new SAXHandlerFactory() { + @Override + public SAXHandler createSAXHandler(JDOMFactory jdomFactory) { + return new SAXHandler(){ + @Override + public void attributeDecl(String eName, String aName, String type, + String valueDefault, String value){ + dtdAttributes.add(new DTDAttribute(eName, aName, type, valueDefault, + value)); + super.attributeDecl(eName, aName, type, valueDefault, value); + } + }; + } + }); + builder.setExpandEntities(false); + doc = builder.build(new InputSource(new StringReader(xml))); + } + + public Document getDocument() { + return doc; + } + + public String getManifestText() throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + printDocument(stream); + return stream.toString().replaceAll(replacementText, "&"); + } + + private void printDocument(OutputStream out) + throws IOException{ + XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat()); + outputter.setXMLOutputProcessor(new CustomOutputter(dtdAttributes)); + + outputter.output(doc, out); + } + + private void genReplacementText(String xml){ + String uuid = UUID.randomUUID().toString(); + while (xml.contains(uuid)) { + uuid = UUID.randomUUID().toString(); + } + replacementText = uuid; + } +} + +
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/RepoManifestImpl.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/RepoManifestImpl.java new file mode 100644 index 0000000..d850442 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/RepoManifestImpl.java
@@ -0,0 +1,123 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +import com.googlesource.gerrit.plugins.manifest.extensions.RepoManifest; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +public class RepoManifestImpl implements RepoManifest { + private Map<String, String> defaults = new + HashMap<String, String>(); + private Map<String, Map<String, String>> remotes = + new HashMap<String, Map<String, String>>(); + private Map<String, Map<String, String>> projects = + new HashMap<String, Map<String, String>>(); + + public RepoManifestImpl(String xml) throws IOException, + ParserConfigurationException, SAXException { + Document doc = newBuilder().parse(new InputSource(new StringReader(xml))); + NodeList tops = doc.getChildNodes(); + for (int i = 0 ; i < tops.getLength() ; i++) { + Node manifest = tops.item(i); + if (NODE_MANIFEST.equals(manifest.getNodeName())) { + NodeList subs = manifest.getChildNodes(); + for (int j = 0 ; j < subs.getLength() ; j++) { + Node node = subs.item(j); + String name = node.getNodeName(); + if (NODE_INCLUDE.equals(name) || NODE_REMOVE_PROJECT.equals(name)) { + throw new SAXException("unsupported element: " + name); + } + Map<String, String> atts = attributesToMap(node); + if (NODE_DEFAULT.equals(name)) { + defaults.putAll(atts); + } else if (NODE_REMOTE.equals(name)) { + remotes.put(atts.get(ATTR_REMOTE_NAME), atts); + } else if (NODE_PROJECT.equals(name)) { + if (atts.get(ATTR_PROJECT_PATH) == null) { + atts.put(ATTR_PROJECT_PATH, atts.get(ATTR_PROJECT_NAME)); + } + projects.put(atts.get(ATTR_PROJECT_PATH), atts); + } + } + + for (Map<String, String> project : projects.values()) { + populateDefault(project, ATTR_PROJECT_REMOTE); + populateDefault(project, ATTR_PROJECT_REVISION); + populateDefault(project, ATTR_PROJECT_DEST_BRANCH); + } + return; + } + } + throw new SAXException("Cannot Parse Manifest File"); + } + + @Override + public Map<String, String> getDefaults() { + return defaults; + } + + @Override + public Map<String, Map<String, String>> getRemotes() { + return remotes; + } + + @Override + public Map<String, Map<String, String>> getProjects() { + return projects; + } + + public void populateDefault(Map<String, String> atts, String att) { + if (!atts.containsKey(att)) { + atts.put(att, defaults.get(att)); + } + } + + public static Map<String, String> attributesToMap(Node node) { + Map<String, String> atts = new HashMap<String, String>(); + NamedNodeMap nmmap = node.getAttributes(); + if (nmmap != null) { + for (int i = 0 ; i < nmmap.getLength() ; i++) { + Attr att = (Attr) nmmap.item(i); + atts.put(att.getName(), att.getValue()); + } + } + return atts; + } + + private static DocumentBuilder newBuilder() + throws ParserConfigurationException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setValidating(false); + factory.setExpandEntityReferences(false); + factory.setIgnoringComments(true); + factory.setCoalescing(true); + return factory.newDocumentBuilder(); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/SshModule.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/SshModule.java new file mode 100644 index 0000000..090deac --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/SshModule.java
@@ -0,0 +1,29 @@ +// Copyright (C) 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +import com.google.gerrit.sshd.PluginCommandModule; + +import com.google.inject.assistedinject.FactoryModuleBuilder; + +public class SshModule extends PluginCommandModule { + @Override + protected void configureCommands() { + install(new FactoryModuleBuilder().build(GitFile.Factory.class)); + install(new FactoryModuleBuilder().build(TempRef.Factory.class)); + + command(UpdateCommand.class); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/TempRef.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/TempRef.java new file mode 100644 index 0000000..2948cd2 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/TempRef.java
@@ -0,0 +1,73 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +import com.google.gerrit.reviewdb.client.Branch; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; + +import java.io.IOException; +import java.util.UUID; + +public class TempRef implements AutoCloseable { + public interface Factory { + TempRef create(@Assisted Branch.NameKey branch); + } + + private GitRepositoryManager repoManager; + private Branch.NameKey tempRef; + + @Inject + public TempRef(GitRepositoryManager repoManager, + @Assisted Branch.NameKey branch) throws IOException { + this.repoManager = repoManager; + + Project.NameKey project = branch.getParentKey(); + tempRef = new Branch.NameKey(project, "refs/temp/" + + UUID.randomUUID().toString()); + + Repository repo = repoManager.openRepository(project); + try { + RefUpdate refUpdate = repo.updateRef(tempRef.get()); + refUpdate.setNewObjectId(repo.getRef(branch.get()).getObjectId()); + refUpdate.setForceUpdate(true); + refUpdate.update(); + } finally { + repo.close(); + } + } + + public Branch.NameKey getBranch() { + return tempRef; + } + + public void close() throws IOException { + Repository repo = repoManager.openRepository(tempRef.getParentKey()); + try { + RefUpdate refUpdate = repo.updateRef(tempRef.get()); + refUpdate.setNewObjectId(ObjectId.zeroId()); + refUpdate.setForceUpdate(true); + refUpdate.delete(); + } finally { + tempRef = null; + repo.close(); + } + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/TwoArgumentOptionHandler.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/TwoArgumentOptionHandler.java new file mode 100644 index 0000000..dde6614 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/TwoArgumentOptionHandler.java
@@ -0,0 +1,46 @@ +package com.googlesource.gerrit.plugins.manifest; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.OptionDef; +import org.kohsuke.args4j.spi.OptionHandler; +import org.kohsuke.args4j.spi.Parameters; +import org.kohsuke.args4j.spi.Setter; + +public abstract class TwoArgumentOptionHandler<T> extends OptionHandler<T> { + + public TwoArgumentOptionHandler(CmdLineParser parser, OptionDef option, + Setter<? super T> setter) { + super(parser, option, setter); + } + + @Override + public String getDefaultMetaVariable() { + return "ARG ARG"; + } + + @Override + public int parseArguments(Parameters params) throws CmdLineException { + if (params.size() < 2) { + throw new IllegalArgumentException("Expecting 2 arguments"); + } + + String firstArgument = params.getParameter(0); + String secondArgument = params.getParameter(1); + + T value = parse(firstArgument, secondArgument); + setter.addValue(value); + return 2; + } + + /** + * Parses a string to a real value of Type <T>. + * @param arg1 First string value to parse + * @param arg2 Second string value to parse + * @return the parsed value + * @throws CmdLineException + * if the parsing encounters a failure that should be reported to the user. + */ + protected abstract T parse(String arg1, String arg2) + throws CmdLineException; +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/UpdateCommand.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/UpdateCommand.java new file mode 100644 index 0000000..bed60fe --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/UpdateCommand.java
@@ -0,0 +1,214 @@ +//Copyright (C) 2015 The Android Open Source Project +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +//http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. + +package com.googlesource.gerrit.plugins.manifest; + +import com.googlesource.gerrit.plugins.manifest.extensions.RepoManifest; + +import com.google.gerrit.common.errors.PermissionDeniedException; +import com.google.gerrit.reviewdb.client.Branch; +import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.reviewdb.client.RefNames; +import com.google.gerrit.server.project.NoSuchProjectException; +import com.google.gerrit.server.project.NoSuchRefException; +import com.google.gerrit.server.project.ProjectControl; +import com.google.gerrit.sshd.BaseCommand; +import com.google.gerrit.sshd.CommandMetaData; +import com.google.inject.Inject; + +import org.apache.sshd.server.Environment; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.revwalk.RevCommit; +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.Option; +import org.kohsuke.args4j.OptionDef; +import org.kohsuke.args4j.spi.Setter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@CommandMetaData(name = "update", description = "Create/update a manifest") +public class UpdateCommand extends BaseCommand { + public static class RevisionOperation implements ManifestOperation { + public String projectName; + public String revision; + + public RevisionOperation(String projectName, String revision) { + this.projectName = projectName; + this.revision = revision; + } + + public void transform(ManifestUpdater manifestUpdater) throws Exception { + for (ManifestUpdater.Project project: manifestUpdater.getProjects( + RepoManifest.ATTR_PROJECT_NAME, projectName)) { + project.setAttribute(RepoManifest.ATTR_PROJECT_REVISION, revision); + } + } + } + + public static class ProjectRevisionOptionHandler + extends TwoArgumentOptionHandler<RevisionOperation> { + + public ProjectRevisionOptionHandler(CmdLineParser parser, OptionDef option, + Setter<? super RevisionOperation> setter) { + super(parser, option, setter); + } + + @Override + protected RevisionOperation parse(String project, String revision) + throws NumberFormatException, CmdLineException { + return new RevisionOperation(project, revision); + } + } + + @Option(name = "--src-project", aliases = "-sp", + usage = "source project containing the manifest") + private ProjectControl srcProjectControl; + + @Option(name = "--src-branch", aliases = "-sb", metaVar = "BRANCH", + usage = "source branch containing the manifest") + private String srcBranch; + + @Option(name = "--src-file", aliases = "-sf", metaVar = "FILE", + usage = "source file containing the manifest") + private String srcFile; + + @Option(name = "--dst-project", aliases = "-dp", + usage = "destination project to place the manifest in") + private ProjectControl dstProjectControl; + + @Option(name = "--dst-branch", aliases = "-db", metaVar = "BRANCH", + usage = "destination branch to place the manifest on") + private String dstBranch; + + @Option(name = "--dst-file", aliases = "-df", metaVar = "FILE", + usage = "destination file to place the manifest in") + private String dstFile; + + @Option(name = "--project-revision", aliases = "-pr", metaVar = "PROJECT REVISION", + handler = ProjectRevisionOptionHandler.class, + usage = "revision to update the project in the manifest to, Ex:" + + "\n\tkernel/msm refs/heads/msm-3.14") + public void parseOperation(RevisionOperation revisionOperation) { + operations.add(revisionOperation); + } + public List<ManifestOperation> operations = new ArrayList<>(); + + @Option(name = "--commit-message", aliases = "-m", metaVar = "COMMIT-MESSAGE", + usage = "commit message to use when updating the manifest") + private String commitMessage; + + @Inject + private ProjectControl.Factory projectControlFactory; + + @Inject + private ManifestUpdater manifestUpdater; + + public static final String DEFAULT_COMMIT_MESSAGE = "Update manifest\n"; + + private Branch.NameKey srcRef; + private Branch.NameKey dstRef; + + public void start(final Environment env) { + startThread(new CommandRunnable() { + public void run() throws IOException, UnloggedFailure { + parseCommandLine(); + processArgs(); + try { + updateManifest() ; + } catch (NoSuchRefException e) { + throw new UnloggedFailure(1, e.getMessage()); + } catch (NoSuchProjectException | RepositoryNotFoundException e) { + throw new UnloggedFailure(2, e.getMessage()); + } catch (PermissionDeniedException e) { + throw new UnloggedFailure(20, e.getMessage()); + } catch (Exception e) { + throw new UnloggedFailure(50, "Error processing manifest: " + e); + } + } + }); + } + + private void updateManifest() throws Exception { + manifestUpdater.read(srcRef, srcFile); + + for (ManifestOperation operation: operations) { + operation.transform(manifestUpdater); + } + + RevCommit commit = manifestUpdater.write(dstRef, dstFile, + getCommitMessage()); + out.write(("Commit: " + ObjectId.toString(commit.getId()) + "\n").getBytes(ENC)); + out.flush(); + } + + private String getCommitMessage() { + if (commitMessage == null) { + return DEFAULT_COMMIT_MESSAGE; + } else { + return commitMessage; + } + } + + private void processArgs() throws UnloggedFailure { + if (srcProjectControl == null && dstProjectControl == null) { + String project = "platform/manifest"; + try { + srcProjectControl = projectControlFactory.controlFor( + new Project.NameKey(project)); + } catch (NoSuchProjectException e) { + throw new UnloggedFailure("missing " + project); + } + dstProjectControl = srcProjectControl; + } else if (srcProjectControl == null) { + srcProjectControl = dstProjectControl; + } else if (dstProjectControl == null) { + dstProjectControl = srcProjectControl; + } + + if (srcBranch == null && dstBranch == null) { + srcBranch = "master"; + dstBranch = srcBranch; + } else if (srcBranch == null) { + srcBranch = dstBranch; + } else if (dstBranch == null) { + dstBranch = srcBranch; + } + srcBranch = RefNames.fullName(srcBranch); + dstBranch = RefNames.fullName(dstBranch); + + if (srcFile == null && dstFile == null) { + srcFile = "default.xml"; + dstFile = srcFile; + } else if (srcFile == null) { + srcFile = dstFile; + } else if (dstFile == null) { + dstFile = srcFile; + } + + srcRef = getBranch(srcProjectControl, srcBranch); + dstRef = getBranch(dstProjectControl, dstBranch); + } + + private Branch.NameKey getBranch(ProjectControl ctl, String branch) { + return new Branch.NameKey(ctl.getProject().getNameKey(), branch); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/manifest/extensions/RepoManifest.java b/src/main/java/com/googlesource/gerrit/plugins/manifest/extensions/RepoManifest.java new file mode 100644 index 0000000..9366e72 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/manifest/extensions/RepoManifest.java
@@ -0,0 +1,75 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.googlesource.gerrit.plugins.manifest.extensions; + +import java.util.Map; + +/** A very simple Map based representation of a repo manifest. + * Various attributes default values are populated per repo docs. + * See below for details. + */ +public interface RepoManifest { + String NODE_MANIFEST = "manifest"; + String NODE_DEFAULT = "default"; + String NODE_PROJECT = "project"; + String NODE_REMOTE = "remote"; + String NODE_INCLUDE = "include"; + String NODE_REMOVE_PROJECT = "remove-project"; + + String ATTR_REMOTE_NAME = "name"; + String ATTR_PROJECT_DEST_BRANCH = "dest-branch"; + String ATTR_PROJECT_NAME = "name"; + String ATTR_PROJECT_PATH = "path"; + String ATTR_PROJECT_REMOTE = "remote"; + String ATTR_PROJECT_REVISION = "revision"; + + // NOTE: These are quic-specific extensions to the repo manifest data model. + String ATTR_PROJECT_XSHIP = "x-ship"; + String ATTR_PROJECT_XQUICDIST = "x-quic-dist"; + + + Map<String, String> getDefaults(); + + Map<String, Map<String, String>> getRemotes(); + + Map<String, Map<String, String>> getProjects(); + + /** + * The following items require special handling (per repo docs) that is not + * implemented because we do not currently use these items. + * + * <!ATTLIST project dest-branch CDATA #IMPLIED> + * <!ATTLIST project groups CDATA #IMPLIED> + * <!ATTLIST project upstream CDATA #IMPLIED> + * + * The following items do not require special handling (per repo docs) + * and we do not currently use these items. + * + * <!ATTLIST project sync-c CDATA #IMPLIED> + * <!ATTLIST project sync-s CDATA #IMPLIED> + * <!ATTLIST project clone-depth CDATA #IMPLIED> + * <!ATTLIST project force-path CDATA #IMPLIED> + * <!ELEMENT notice (#PCDATA)> + * <!ATTLIST remote alias CDATA #IMPLIED> + * <!ATTLIST default sync-j CDATA #IMPLIED> + * <!ATTLIST default sync-c CDATA #IMPLIED> + * <!ATTLIST default sync-s CDATA #IMPLIED> + * <!ELEMENT manifest-server (EMPTY)> + * <!ELEMENT repo-hooks (EMPTY)> + * <!ATTLIST repo-hooks in-project CDATA #REQUIRED> + * <!ATTLIST repo-hooks enabled-list CDATA #REQUIRED> + * + */ +}
diff --git a/src/main/resources/Documentation/cmd-update.md b/src/main/resources/Documentation/cmd-update.md new file mode 100644 index 0000000..3297c73 --- /dev/null +++ b/src/main/resources/Documentation/cmd-update.md
@@ -0,0 +1,117 @@ +@PLUGIN@ udpate +=============== + +NAME +---- +@PLUGIN@ udpate - create/update a manifest + +SYNOPSIS +-------- +``` +ssh -p @SSH_PORT@ @SSH_HOST@ @PLUGIN@ update + [--src-project PROJECT] + [--src-branch BRANCH] + [--src-file FILE] + [--dst-project PROJECT] + [--dst-branch BRANCH] + [--dst-file FILE] + [--si SOFTWARE-IMAGE] + [--project-revision PROJECT REVISION] + [--create-change] +``` + +DESCRIPTION +----------- +Update or Create a manifest. + +OPTIONS +----------- +**\-\-src-project PROJECT** + +: Source PROJECT containing the manifest (default is "platform/manifest"). + +**\-\-src-branch BRANCH** + +: Source BRANCH containing the manifest (default is "master"). + +**\-\-src-file FILE** + +: Source FILE containing the manifest (default is "default.xml"). + +**\-\-dst-branch BRANCH** + +: Destination BRANCH to place the manifest on. + +**\-\-dst-file FILE** + +: Destination FILE to place the manifest in. + +**\-\-dst-project PROJECT** + +: Destination PROJECT to place the manifest in. + +**\-\-project-revision PROJECT REVISION** + +: Name of the PROJECT in the manifest to update and the REVISION to update it to. + +**\-\-commit-message COMMIT-MESSAGE** + +: COMMIT-MESSAGE to be used on the manifest update commit + +**\-\-create-change** + +: A manifest change will be uploaded for review + +Note: To help simplify specifying redundant parameters, the source +and destination parameters are tied to each other. If either one +is specified, and the other is not, then the parameters will be +equal. + +The default values are only used if neither the source nor the +destination is specified. + +ACCESS +------ +Any user who has configured an SSH key. + +SCRIPTING +--------- +This command is intended to be used in scripts. + +EXAMPLES +-------- +1) Update the default manifest to a tag for kernel/msm: + +``` + $ ssh -p 29418 review.example.com @PLUGIN@ update + --project-revision kernel/msm refs/tags/mytag + Commit: 35a6fe20991288e94902221bcca9474d51500484 +``` + +2) Update the project revision for the kernel/msm project on the LA.UM.5.7 SI: + +``` + $ ssh -p 29418 review.example.com @PLUGIN@ update + --si LA.UM.5.7 + --project-revision kernel/msm refs/heads/master + Commit: 201051e78dd1184e7cb1437f4196e53f75fe5315 +``` + +3) Upload change to the manifest that updates revision of kernel/msm: + +``` + $ ssh -p 29418 review.example.com @PLUGIN@ update + --project-revision kernel/msm master + --create-change + Change Number: 1682219 +``` + +4) Upload change to the manifest that updates multiple revisions: + +``` + $ ssh -p 29418 review.example.com @PLUGIN@ update + --project-revision kernel/msm master + --project-revision kernel/lk master + --create-change + Change Number: 1682220 +```
diff --git a/test/test_manifest_plugin.sh b/test/test_manifest_plugin.sh new file mode 100755 index 0000000..fcf6db9 --- /dev/null +++ b/test/test_manifest_plugin.sh
@@ -0,0 +1,318 @@ +#!/usr/bin/env bash + +source "$(dirname $0)/help_deploy.sh" + +plugin=$1; shift +host=$1; shift +gerrit_dir=$1; shift +port=$(get_ssh_port "$host" "$gerrit_dir") + +manifest_list_empty='[]' +manifest_list_LNX_LE_5_3=' [ { "kind": "gerritcodereview#remotemanifest", "cgsn": "ReviewAndroid", "project": "mdm/manifest", "branch": "LNX.LE.5.3", "file": "default.xml" } ]' +manifest_list_TIZEN_0_3=' [ { "kind": "gerritcodereview#remotemanifest", "cgsn": "ReviewAndroid", "project": "tizen/manifest", "branch": "tizen_0.3", "file": "default.xml" } ]' + +test_usage() { + local children= + for command in ls-manifest ls-si update; do + ssh -p $port $host $plugin --help 2>&1 | + grep --quiet "$command" & + children+=" $!" + done + wait_on $children +} + +test_ls_manifests() { + local children= + test_ls_manifests__performance & + children+=" $!" + test_ls_manifests__branch & + children+=" $!" + test_ls_manifests__hasproject & + children+=" $!" + test_ls_manifests__hascomponentrevision & + children+=" $!" + test_ls_manifests__hascomponentrevisionhistory & + children+=" $!" + wait_on $children +} + +test_ls_manifests__performance() { + echo "first query time (above): $(\ + time ssh -p $port $host $plugin ls-manifests branch:DONT_MATCH)" && + echo "second query time (above): $(\ + time ssh -p $port $host $plugin ls-manifests branch:DONT_MATCH)" +} + +test_ls_manifests__branch() { + local a=$(mktemp) + local b=$(mktemp) + local x=$(mktemp) + local y=$(mktemp) + ssh -p $port $host $plugin ls-manifests branch:LNX.LE.5.3 | + sortize_json > "$a" && + echo "$manifest_list_LNX_LE_5_3" | + sortize_json > "$b" && diff "$a" "$b" && + ssh -p $port $host $plugin ls-manifests branch:DNS | + sortize_json > "$x" && + echo "$manifest_list_empty" | + sortize_json > "$y" && diff "$x" "$y" + rval=$? + rm "$a" "$b" "$x" "$y" + return $rval +} + +test_ls_manifests__hasproject() { + local children= + test_ls_manifests__hasproject__name_and_rev & + children+=" $!" + test_ls_manifests__hasproject__name_and_revisionequals & + children+=" $!" + test_ls_manifests__hasproject__name_and_revisionhistory & + children+=" $!" + test_ls_manifests__hasproject__name_and_xship_and_xquicdist & + children+=" $!" + wait_on $children +} + +test_ls_manifests__hasproject__name_and_rev() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + 'hasproject:\"name:kernel/msm-3.10 revision:tizen_0.3\"' | + sortize_json > "$a" && + echo "$manifest_list_TIZEN_0_3" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hasproject__name_and_revisionequals() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + 'hasproject:\"name:platform/vendor/qcom-proprietary/wtf/1 revisionequals:9d66b4e9e5cd82174f3e51134048867063c7a35a\" AND hasproject:\"name:platform/vendor/qcom-proprietary/wtf/2 revisionequals:7b51568eff18afa4d0cda64072a2ee52f1405183\" AND hasproject:\"name:platform/vendor/qcom-proprietary/wtf/3 revisionequals:aec0ede05238b3204ded16f4b4d7057c0e138978\" AND hasproject:\"name:platform/vendor/qcom-proprietary/wtf/4 revisionequals:9eb53b7e477f07a59cd703078e687b6abacee7cb\"' | + sortize_json > "$a" && + echo "$manifest_list_LNX_LE_5_3" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hasproject__name_and_revisionhistory() { + local children= + test_ls_manifests__hasproject__name_and_revisionhistory__equals & + children+=" $!" + test_ls_manifests__hasproject__name_and_revisionhistory__in_history & + children+=" $!" + test_ls_manifests__hasproject__name_and_revisionhistory__not_in_history & + children+=" $!" + wait_on $children +} + +test_ls_manifests__hasproject__name_and_xship_and_xquicdist() { + local children= + test_ls_manifests__hasproject__name_and_xship_and_xquicdist_match & + children+=" $!" + test_ls_manifests__hasproject__name_and_xship_and_xquicdist_nomatch & + children+=" $!" + test_ls_manifests__hasproject__name_and_xship_and_xquicdist_match_many & + children+=" $!" + wait_on $children +} + +test_ls_manifests__hasproject__name_and_xship_and_xquicdist_match() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + 'hasproject:\"name:kernel/msm-3.10 revision:tizen_0.3 xship:oss xquicdist:la\"' | + sortize_json > "$a" && + echo "$manifest_list_TIZEN_0_3" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hasproject__name_and_xship_and_xquicdist_nomatch() { + local x=$(mktemp) + local y=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + 'hasproject:\"name:kernel/msm-3.10 revision:tizen_0.3 xship:oss xquicdist:le\"' | + sortize_json > "$x" && + echo "$manifest_list_empty" | + sortize_json > "$y" && diff "$x" "$y" + rval=$? + rm "$x" "$y" + return $rval +} + +test_ls_manifests__hasproject__name_and_xship_and_xquicdist_match_many() { + local x=$(mktemp) + local y=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + 'hasproject:\"name:kernel/msm-3.10 xship:oss xquicdist:la\"' | + sortize_json > "$x" && + echo "$manifest_list_empty" | + sortize_json > "$y" && diff "$x" "$y" > /dev/null + if [ "$?" -eq "0" ]; then + rval=1 + else + rval=0 + fi + rm "$x" "$y" + return $rval +} + +test_ls_manifests__hasproject__name_and_revisionhistory__equals() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + 'hasproject:\"name:platform/vendor/qcom-proprietary/wtf/1 revisionhistory:9d66b4e9e5cd82174f3e51134048867063c7a35a\"' | + sortize_json > "$a" && + echo "$manifest_list_LNX_LE_5_3" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hasproject__name_and_revisionhistory__in_history() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + 'hasproject:\"name:platform/vendor/qcom-proprietary/wtf/1 revisionhistory:8b5b95f987c94139692a88623f0d35d11ca78e77\"' | + sortize_json > "$a" && + echo "$manifest_list_LNX_LE_5_3" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hasproject__name_and_revisionhistory__not_in_history() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + 'hasproject:\"name:platform/vendor/qcom-proprietary/wtf/1 revisionhistory:e89f63325b41793e6f1012accdcd6c7aea2e4d9a\"' | + sortize_json > "$a" && + echo "$manifest_list_empty" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hascomponentrevision() { + local children= + test_ls_manifests__hascomponentrevision__equals & + children+=" $!" + test_ls_manifests__hascomponentrevision__in_history & + children+=" $!" + test_ls_manifests__hascomponentrevision__not_in_history & + children+=" $!" + wait_on $children +} + +test_ls_manifests__hascomponentrevision__equals() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + branch:LNX.LE.5.3 AND hascomponentrevision:pcm.gerrit.2.0-383 | + sortize_json > "$a" && + echo "$manifest_list_LNX_LE_5_3" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hascomponentrevision__in_history() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + branch:LNX.LE.5.3 AND hascomponentrevision:pcm.gerrit.2.0-374 | + sortize_json > "$a" && + echo "$manifest_list_empty" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hascomponentrevision__not_in_history() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + branch:LNX.LE.5.3 AND hascomponentrevision:pcm.gerrit.2.0-375 | + sortize_json > "$a" && + echo "$manifest_list_empty" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hascomponentrevisionhistory() { + local children= + test_ls_manifests__hascomponentrevisionhistory__equals & + children+=" $!" + test_ls_manifests__hascomponentrevisionhistory__in_history & + children+=" $!" + test_ls_manifests__hascomponentrevisionhistory__not_in_history & + children+=" $!" + wait_on $children +} + +test_ls_manifests__hascomponentrevisionhistory__equals() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + branch:LNX.LE.5.3 AND hascomponentrevisionhistory:pcm.gerrit.2.0-383 | + sortize_json > "$a" && + echo "$manifest_list_LNX_LE_5_3" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hascomponentrevisionhistory__in_history() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + branch:LNX.LE.5.3 AND hascomponentrevisionhistory:pcm.gerrit.2.0-374 | + sortize_json > "$a" && + echo "$manifest_list_LNX_LE_5_3" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +test_ls_manifests__hascomponentrevisionhistory__not_in_history() { + local a=$(mktemp) + local b=$(mktemp) + ssh -p $port $host $plugin ls-manifests \ + branch:LNX.LE.5.3 AND hascomponentrevisionhistory:pcm.gerrit.2.0-375 | + sortize_json > "$a" && + echo "$manifest_list_empty" | + sortize_json > "$b" && diff "$a" "$b" + rval=$? + rm "$a" "$b" + return $rval +} + +sortize_json() { + json_pp | sed -e 's|,$||' | sort +} + +# NOTE: test_ls_manifests may need to be updated +# depending on the component revision data in the env. +children= +test_usage & +children+=" $!" +test_ls_manifests & +children+=" $!" +wait_on $children