diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index e7479741..1023c367 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -1,27 +1,27 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "csharpier": { - "version": "1.3.0", - "commands": [ - "csharpier" - ], - "rollForward": false - }, - "husky": { - "version": "0.9.1", - "commands": [ - "husky" - ], - "rollForward": false - }, - "dotnet-outdated-tool": { - "version": "4.8.1", - "commands": [ - "dotnet-outdated" - ], - "rollForward": false - } - } +{ + "version": 1, + "isRoot": true, + "tools": { + "csharpier": { + "version": "1.3.0", + "commands": [ + "csharpier" + ], + "rollForward": false + }, + "husky": { + "version": "0.9.1", + "commands": [ + "husky" + ], + "rollForward": false + }, + "dotnet-outdated-tool": { + "version": "4.8.1", + "commands": [ + "dotnet-outdated" + ], + "rollForward": false + } + } } \ No newline at end of file diff --git a/.dockerignore b/.dockerignore index a95d5c6f..cc6a576d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,27 +1,27 @@ -**/.mount -**/.classpath -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/azds.yaml -**/bin -**/charts -**/docker-compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE -README.md +**/.mount +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md auth.conf \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index 7490f8db..b1fe4d1d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,222 +1,208 @@ -# https://editorconfig.org - -# https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names -# https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview - -# https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/coding-style.md -# https://github.com/dotnet/runtime/blob/main/.editorconfig - -# https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-format -# dotnet format style --verify-no-changes --severity=info --verbosity=detailed - -# Root config -root = true - -# Defaults -[*] -charset = utf-8 -indent_size = 4 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true - -# Markdown files -[*.md] -end_of_line = crlf -trim_trailing_whitespace = false - -# Xml files -[*.{xml,csproj,props,targets}] -end_of_line = crlf -indent_size = 2 - -# Yaml files -[*.{yml,yaml}] -end_of_line = crlf -indent_size = 2 - -# Workflow YAML is LF: Dependabot and Actions rewrite it with LF, so declaring LF keeps it consistent instead of -# mixed. git still leaves endings alone (`* -text`). This and CI (editorconfig-checker) enforce it. Other YAML is CRLF. -[.github/workflows/*.{yml,yaml}] -end_of_line = lf - -# JSON and JSONC files -[*.{json,jsonc}] -end_of_line = crlf - -# Linux scripts -[*.sh] -end_of_line = lf - -# Windows scripts -[*.{cmd,bat,ps1}] -end_of_line = crlf - -# .NET-only below: C# and ReSharper style. Everything above is the line-ending governance -# every derived repo carries. A non-.NET repo may drop from here down. - -# C# files -[*.cs] -end_of_line = crlf -# Suppressions follow CODESTYLE.md "Analyzer Diagnostics and Suppressions": prefer a -# [SuppressMessage] attribute or the owning project's .editorconfig. Relax a rule -# repo-wide here only when it applies to every project (never a brownfield batch). -dotnet_diagnostic.IDE0055.severity = none -dotnet_analyzer_diagnostic.severity = suggestion -csharp_indent_block_contents = true -csharp_indent_braces = false -csharp_indent_case_contents = true -csharp_indent_case_contents_when_block = false -csharp_indent_labels = one_less_than_current -csharp_indent_switch_labels = true -csharp_new_line_before_catch = true -csharp_new_line_before_else = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_anonymous_types = true -csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_open_brace = all -csharp_new_line_between_query_expression_clauses = true -csharp_prefer_braces = true -csharp_prefer_simple_default_expression = true -csharp_prefer_simple_using_statement = true -csharp_prefer_static_anonymous_function = true -csharp_prefer_static_local_function = true -csharp_prefer_system_threading_lock = true -csharp_preferred_modifier_order = public,private,protected,internal,file,static,abstract,sealed,virtual,override,readonly,unsafe,volatile,async,extern,new,partial:warning -csharp_preserve_single_line_blocks = true -csharp_preserve_single_line_statements = false -csharp_space_after_cast = false -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_after_comma = true -csharp_space_after_dot = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_after_semicolon_in_for_statement = true -csharp_space_around_binary_operators = before_and_after -csharp_space_around_declaration_statements = false -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_before_comma = false -csharp_space_before_dot = false -csharp_space_before_open_square_brackets = false -csharp_space_before_semicolon_in_for_statement = false -csharp_space_between_empty_square_brackets = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_declaration_name_and_open_parenthesis = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false -csharp_space_between_square_brackets = false -csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true -csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true -csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true -csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true -csharp_style_allow_embedded_statements_on_same_line_experimental = true -csharp_style_conditional_delegate_call = true -csharp_style_deconstructed_variable_declaration = true -csharp_style_expression_bodied_accessors = true -csharp_style_expression_bodied_constructors = true -csharp_style_expression_bodied_indexers = true -csharp_style_expression_bodied_lambdas = true -csharp_style_expression_bodied_local_functions = true -csharp_style_expression_bodied_methods = true -csharp_style_expression_bodied_operators = true -csharp_style_expression_bodied_properties = true -csharp_style_implicit_object_creation_when_type_is_apparent = true -csharp_style_inlined_variable_declaration = true -csharp_style_namespace_declarations = file_scoped -csharp_style_pattern_matching_over_as_with_null_check = true -csharp_style_pattern_matching_over_is_with_cast_check = true -csharp_style_prefer_extended_property_pattern = true -csharp_style_prefer_implicitly_typed_lambda_expression = true -csharp_style_prefer_index_operator = true -csharp_style_prefer_local_over_anonymous_function = true -csharp_style_prefer_method_group_conversion = true -csharp_style_prefer_not_pattern = true -csharp_style_prefer_null_check_over_type_check = true -csharp_style_prefer_pattern_matching = true -csharp_style_prefer_primary_constructors = true -csharp_style_prefer_range_operator = true -csharp_style_prefer_readonly_struct = true -csharp_style_prefer_readonly_struct_member = true -csharp_style_prefer_switch_expression = true -csharp_style_prefer_top_level_statements = true -csharp_style_prefer_tuple_swap = true -csharp_style_prefer_unbound_generic_type_in_nameof = true -csharp_style_prefer_utf8_string_literals = true -csharp_style_throw_expression = true -csharp_style_unused_value_assignment_preference = discard_variable -csharp_style_unused_value_expression_statement_preference = discard_variable -csharp_style_var_elsewhere = false -csharp_style_var_for_built_in_types = false -csharp_style_var_when_type_is_apparent = false -csharp_using_directive_placement = outside_namespace -dotnet_code_quality_unused_parameters = all -dotnet_hide_advanced_members = false -dotnet_member_insertion_location = with_other_members_of_the_same_kind -dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion -dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style -dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style -dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion -dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style -dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields -dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case -dotnet_naming_style.camel_case_underscore_style.required_prefix = _ -dotnet_naming_style.pascal_case_style.capitalization = pascal_case -dotnet_naming_style.static_prefix_style.capitalization = camel_case -dotnet_naming_style.static_prefix_style.required_prefix = s_ -dotnet_naming_symbols.constant_fields.applicable_kinds = field -dotnet_naming_symbols.constant_fields.required_modifiers = const -dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal -dotnet_naming_symbols.private_internal_fields.applicable_kinds = field -dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected -dotnet_naming_symbols.static_fields.applicable_kinds = field -dotnet_naming_symbols.static_fields.required_modifiers = static -dotnet_prefer_system_hash_code = true -dotnet_property_generation_behavior = prefer_throwing_properties -dotnet_remove_unnecessary_suppression_exclusions = none -dotnet_search_reference_assemblies = true -dotnet_separate_import_directive_groups = false -dotnet_sort_system_directives_first = true -dotnet_style_allow_multiple_blank_lines_experimental = true -dotnet_style_allow_statement_immediately_after_block_experimental = true -dotnet_style_coalesce_expression = true -dotnet_style_collection_initializer = true -dotnet_style_explicit_tuple_names = true -dotnet_style_namespace_match_folder = true -dotnet_style_null_propagation = true -dotnet_style_object_initializer = true -dotnet_style_operator_placement_when_wrapping = beginning_of_line -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity -dotnet_style_parentheses_in_other_operators = never_if_unnecessary -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity -dotnet_style_predefined_type_for_locals_parameters_members = true -dotnet_style_predefined_type_for_member_access = true -dotnet_style_prefer_auto_properties = true -dotnet_style_prefer_collection_expression = when_types_loosely_match -dotnet_style_prefer_compound_assignment = true -dotnet_style_prefer_conditional_expression_over_assignment = true -dotnet_style_prefer_conditional_expression_over_return = true -dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed -dotnet_style_prefer_inferred_anonymous_type_member_names = true -dotnet_style_prefer_inferred_tuple_names = true -dotnet_style_prefer_is_null_check_over_reference_equality_method = true -dotnet_style_prefer_simplified_boolean_expressions = true -dotnet_style_prefer_simplified_interpolation = true -dotnet_style_qualification_for_event = false -dotnet_style_qualification_for_field = false -dotnet_style_qualification_for_method = false -dotnet_style_qualification_for_property = false -dotnet_style_readonly_field = true -dotnet_style_require_accessibility_modifiers = for_non_interface_members - -# ReSharper settings -resharper_csharp_trailing_comma_in_multiline_lists = true -resharper_csharp_var_for_built_in_types = false -resharper_csharp_var_when_type_is_apparent = false -resharper_csharp_var_when_type_is_not_apparent = false +# https://editorconfig.org + +# https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names +# https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview + +# https://github.com/dotnet/runtime/blob/main/docs/coding-guidelines/coding-style.md +# https://github.com/dotnet/runtime/blob/main/.editorconfig + +# https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-format +# dotnet format style --verify-no-changes --severity=info --verbosity=detailed + +# Root config +root = true + +# Defaults: LF is the default, and only the CRLF exception below is declared. +# `.gitattributes` mirrors these two defaults as Git's normalization fallback. +# CI verifies the committed bytes against this file. +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +# Markdown files +[*.md] +trim_trailing_whitespace = false + +# Xml files +[*.{xml,csproj,props,targets}] +indent_size = 2 + +# Yaml files +[*.{yml,yaml}] +indent_size = 2 + +# Windows batch and command scripts: the one CRLF exception to the `[*]` LF default above. +[*.{bat,cmd}] +end_of_line = crlf + +# .NET-only below: C# and ReSharper style. Everything above is the line-ending governance +# every derived repo carries. A non-.NET repo may drop from here down. + +# C# files +[*.cs] +# Suppressions follow CODESTYLE.md "Analyzer Diagnostics and Suppressions": prefer a +# [SuppressMessage] attribute or the owning project's .editorconfig. Relax a rule +# repo-wide here only when it applies to every project (never a brownfield batch). +dotnet_diagnostic.IDE0055.severity = none +dotnet_analyzer_diagnostic.severity = suggestion +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true +csharp_prefer_braces = true +csharp_prefer_simple_default_expression = true +csharp_prefer_simple_using_statement = true +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_prefer_system_threading_lock = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,abstract,sealed,virtual,override,readonly,unsafe,volatile,async,extern,new,partial:warning +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true +csharp_style_conditional_delegate_call = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = true +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = true +csharp_style_expression_bodied_methods = true +csharp_style_expression_bodied_operators = true +csharp_style_expression_bodied_properties = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_namespace_declarations = file_scoped +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_implicitly_typed_lambda_expression = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true +csharp_style_prefer_switch_expression = true +csharp_style_prefer_top_level_statements = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false +csharp_using_directive_placement = outside_namespace +dotnet_code_quality_unused_parameters = all +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = with_other_members_of_the_same_kind +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style +dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +dotnet_naming_style.static_prefix_style.capitalization = camel_case +dotnet_naming_style.static_prefix_style.required_prefix = s_ +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static +dotnet_prefer_system_hash_code = true +dotnet_property_generation_behavior = prefer_throwing_properties +dotnet_remove_unnecessary_suppression_exclusions = none +dotnet_search_reference_assemblies = true +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = true +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false +dotnet_style_readonly_field = true +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# ReSharper settings +resharper_csharp_trailing_comma_in_multiline_lists = true +resharper_csharp_var_for_built_in_types = false +resharper_csharp_var_when_type_is_apparent = false +resharper_csharp_var_when_type_is_not_apparent = false diff --git a/.editorconfig-checker.json b/.editorconfig-checker.json index e019960b..22add8bf 100644 --- a/.editorconfig-checker.json +++ b/.editorconfig-checker.json @@ -1,10 +1,10 @@ -{ - "Disable": { - "Charset": true, - "Indentation": true, - "IndentSize": true, - "TrimTrailingWhitespace": true, - "InsertFinalNewline": true, - "MaxLineLength": true - } -} +{ + "Disable": { + "Charset": true, + "Indentation": true, + "IndentSize": true, + "TrimTrailingWhitespace": true, + "InsertFinalNewline": true, + "MaxLineLength": true + } +} diff --git a/.gitattributes b/.gitattributes index 726f427d..efb6dddb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,22 +1,7 @@ -# Default: do not normalize line endings (`* -text`); .editorconfig end_of_line rules guide what the editor writes. -# The exception pins below are git's own enforcement - they force LF for execution-sensitive classes regardless of editor. -# git config --global core.autocrlf false -# git add --renormalize . -# git ls-files --eol -* -text +# Normalize every detected text file to LF in the index and on checkout. +# `text=auto` leaves binary files byte-preserved. +* text=auto eol=lf -# Exception: scripts must stay LF regardless of the `* -text` default - a CRLF shebang breaks execution. `.editorconfig` -# covers `*.sh`, but extensionless executables (s6 service scripts, hooks) match no extension rule, so pin them here so -# git enforces LF on checkout and `--renormalize`. A repo shipping extensionless scripts adds an explicit path rule, -# e.g. for s6-overlay init: `Docker/s6-overlay/** text eol=lf`. -*.sh text eol=lf - -# Husky.Net ships an extensionless pre-commit hook with a /bin/sh shebang; pin it to LF so a CRLF can't break execution. -.husky/pre-commit text eol=lf - -# s6-overlay ships extensionless service/init scripts; pin the tree to LF so a CRLF shebang can't break execution. -Docker/s6-overlay/** text eol=lf - -# Dockerfiles must be LF - a CRLF breaks RUN heredocs and line continuations. -Dockerfile text eol=lf -*.Dockerfile text eol=lf +# Windows command scripts require CRLF. +*.bat text eol=crlf +*.cmd text eol=crlf diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 221ff20b..7bb069c5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,31 +1,31 @@ -name: Bug Report -description: File a bug or issue report -title: "" -labels: ["bug", "triage"] -body: - - type: markdown - attributes: - value: | - Please note that I am not affiliated with Network Optix or Digital Watchdog, and I cannot provide support for their products. - Contact [Network Optix Support](https://support.networkoptix.com/hc/en-us/community/topics) or [Digital Watchdog Support](https://digital-watchdog.com/dw-support) for any product support issues. - Search for similar [Issues](https://github.com/ptr727/NxWitness/issues) before submitting a new issue. - For help with setting up or deploying Docker images refer to your favorite internet technical help resource. - Please submit issues in English. - Only submit issues that are related to the creation or publication of the Docker images. - - type: textarea - attributes: - label: Describe the problem? - description: What happened and what did you expect to happen. - value: | - Steps to reproduce: - - I did this. - - This happened. - - I expected this to happen. - Configuration: - - OS, platform, hardware. - - Docker image name, tag, and version. - - Deployment method and configuration. - Logs: - - Log or console output. - validations: - required: true +name: Bug Report +description: File a bug or issue report +title: "" +labels: ["bug", "triage"] +body: + - type: markdown + attributes: + value: | + Please note that I am not affiliated with Network Optix or Digital Watchdog, and I cannot provide support for their products. + Contact [Network Optix Support](https://support.networkoptix.com/hc/en-us/community/topics) or [Digital Watchdog Support](https://digital-watchdog.com/dw-support) for any product support issues. + Search for similar [Issues](https://github.com/ptr727/NxWitness/issues) before submitting a new issue. + For help with setting up or deploying Docker images refer to your favorite internet technical help resource. + Please submit issues in English. + Only submit issues that are related to the creation or publication of the Docker images. + - type: textarea + attributes: + label: Describe the problem? + description: What happened and what did you expect to happen. + value: | + Steps to reproduce: + - I did this. + - This happened. + - I expected this to happen. + Configuration: + - OS, platform, hardware. + - Docker image name, tag, and version. + - Deployment method and configuration. + Logs: + - Log or console output. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 4f385e80..73d2533e 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,8 @@ -blank_issues_enabled: false -contact_links: - - name: Network Optix Support - url: https://support.networkoptix.com/hc/en-us/community/topics - about: Please report Nx product issues to Network Optix. - - name: Digital Watchdog Support - url: https://digital-watchdog.com/dw-support - about: Please report DW product issues to Digital Watchdog. +blank_issues_enabled: false +contact_links: + - name: Network Optix Support + url: https://support.networkoptix.com/hc/en-us/community/topics + about: Please report Nx product issues to Network Optix. + - name: Digital Watchdog Support + url: https://digital-watchdog.com/dw-support + about: Please report DW product issues to Digital Watchdog. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 55d823fa..767f54c9 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,154 +1,154 @@ -# Copilot Instructions - -Repository conventions for GitHub Copilot (and any other AI agent reading this file). - -The **canonical guide is [AGENTS.md](../AGENTS.md)** at the repo root - read it first, including the [PR Review Etiquette](../AGENTS.md#pr-review-etiquette) review-loop contract this file's runbook implements. This file is intentionally narrow: commit/PR-title conventions (summarized inline so VS Code's commit-message and PR-title generators have them) plus the GitHub Copilot Review Runbook. - -For code-style rules, see [`CODESTYLE.md`](../CODESTYLE.md) at the repo root - one guide with a General section plus per-language sections (.NET). - -Do not duplicate language-specific rules here. **Project-specific conventions and API/behavioral contracts also belong in [AGENTS.md](../AGENTS.md), not here** - this file is intentionally limited to the inline commit/PR-title summary and the GitHub Copilot Review Runbook. Non-Copilot agents (Claude Code, Codex, Cursor, ...) are not directed to this file and don't read it by default, so any rule a reviewer must honor has to live in `AGENTS.md` to be provider-independent. - -## Commit Messages and Pull Request Titles - -Summarized for VS Code's generators; the full rules, rationale, and examples are in [AGENTS.md "Pull Request Title and Commit Message Conventions"](../AGENTS.md#pull-request-title-and-commit-message-conventions). - -- Imperative subject, <= 72 characters, no trailing period; optional blank-line-separated body for the non-obvious *why*. -- US English, title case with lowercase short bind words; no vague titles, no `Co-Authored-By:` unless asked, no release-bump magnitude (NBGV handles versioning). Dependabot's `Bump X from Y to Z` titles are fine. -- develop PRs squash-merge (`gh pr merge --squash`), main PRs merge-commit (`--merge`); a mismatched flag is rejected by branch protection. - -## GitHub Copilot Review Runbook - -> This runbook implements the [AGENTS.md "PR Review Etiquette"](../AGENTS.md#pr-review-etiquette) review-loop contract for GitHub Copilot. Without it in-repo, an agent has no pointer to the reliable Copilot mechanics and falls back to known-broken paths (the no-op `POST /requested_reviewers`, the wrong bot-login filter). In the API snippets below, fill the `` placeholder with the PR number. - -Use this section for provider-specific mechanics. The expected review loop *contract* (request review on every push, verify head-SHA coverage, triage findings, reply + resolve, escalate when stuck) is defined in [AGENTS.md -> PR Review Etiquette](../AGENTS.md#pr-review-etiquette). This section only describes how to make GitHub Copilot reliably execute it. - -### Triggering and Polling - -Auto-review on push is configured (via the branch ruleset's `copilot_code_review` rule with `review_on_push: true`) but fires inconsistently in practice - treat it as best-effort, not guaranteed. After every push, **re-request a review programmatically** via the GraphQL `requestReviews` mutation, passing the Copilot reviewer's bot node id in `botIds`. This drives the loop end-to-end without a UI hand-off. - -**A review with no inline comments is still a completed review - not a failure, and not a reason to ask the maintainer to re-trigger.** Copilot very often posts a single formal review (GraphQL `state: COMMENTED`) whose body ends with "...reviewed N of N changed files ... and generated no comments" and adds **zero** inline threads. That review carries the head `commit.oid` and fully satisfies the loop - it is the clean-pass success case. Never read "no inline comments" as "the review didn't run," and never re-request or escalate to the maintainer because comments are absent. - -**Round 1 is normally auto-seeded - poll for it before trying to self-trigger.** Auto-review-on-open supplies the first review with no `botIds` call needed, but it can lag one to three minutes. After opening a PR (or the first push), **poll** for a Copilot review on the head SHA (see [Verify Review Covered Current Head](#verify-review-covered-current-head)) before concluding none ran. The `requestReviews` mutation below is for **re-requesting on later pushes** (a new head SHA); by then a prior review exists, so its bot node id is readable. A missing bot node id on round 1 therefore means "the auto-review has not landed yet - wait and poll," **not** "ask the maintainer to kick it off." - -> **The reviewer login differs by API.** In **GraphQL** (`gh api graphql` and `gh pr view --json reviews`, which is GraphQL-backed) the `Bot.login` is `copilot-pull-request-reviewer` - **no `[bot]` suffix**. In the **REST** API (`gh api repos/.../issues|pulls/...`) the same account's `user.login` is `copilot-pull-request-reviewer[bot]` - **with** the suffix. Each query below uses the correct form for its API; match the API, not a single spelling, when adapting them. - -```sh -# 1. PR node id + the Copilot reviewer's bot node id (read from any existing -# Copilot review; the reviewer login is `copilot-pull-request-reviewer`). -PR_NODE=$(gh pr view --json id --jq '.id') -BOT_ID=$(gh api graphql -f query=' -{ - repository(owner: "ptr727", name: "NxWitness") { - pullRequest(number: ) { - reviews(first: 50) { nodes { author { __typename login ... on Bot { id } } } } - } - } -}' --jq '[.data.repository.pullRequest.reviews.nodes[] - | select(.author.login == "copilot-pull-request-reviewer") - | .author.id] | first') - -# 2. Re-request a Copilot review on the current head. -gh api graphql -f query=' -mutation($pr: ID!, $bot: ID!) { - requestReviews(input: { pullRequestId: $pr, botIds: [$bot], union: true }) { - pullRequest { id } - } -}' -F pr="$PR_NODE" -F bot="$BOT_ID" -``` - -The bot node id is read from an existing Copilot **formal** review (`pullRequest.reviews`), so step 1 needs at least one prior formal review on the PR - the auto-review-on-open normally supplies the first one (it may have **no inline comments**; that still counts, and its bot node id is still readable). Poll for it (give auto-review-on-open a few minutes) before deciding it is missing. If Copilot posted **only an issue comment** and no formal review, the head is covered but `reviews` yields no bot node id - read the id from the Copilot issue comment's author by querying the PR's issue comments in GraphQL (`pullRequest.comments` -> author `... on Bot { id }`), or request `Copilot` once through the GitHub PR UI to produce a formal review. Manual UI seeding is the fallback specifically when no formal review exists to read the id from; then use the mutation for every subsequent re-request. - -**Do NOT post `@Copilot review` as a PR comment.** That comment triggers the Copilot *coding agent* (`copilot-swe-agent[bot]`), which makes code changes rather than posting a review. - -Known non-working request paths (don't rely on them - use the `requestReviews` mutation above instead): - -- `POST /requested_reviewers` with `reviewers=[Copilot]` can return 200 but no-op. -- `copilot-pull-request-reviewer` as a requested reviewer slug returns 422. - -### Verify Review Covered Current Head - -Before merging, confirm Copilot reviewed the current PR head SHA. Copilot may respond as either a formal review (carries an exact commit SHA) or an issue comment (no SHA - use the most recent Copilot comment for manual confirmation). Check both. - -```sh -PR_HEAD=$(gh pr view --json headRefOid --jq '.headRefOid') - -# 1. Formal review - exact SHA match. -gh pr view --json reviews --jq \ - '.reviews[] | select(.author.login=="copilot-pull-request-reviewer") | .commit.oid' \ - | grep -q "$PR_HEAD" && echo "covered via formal review" - -# 2. Issue comment - show the most recent Copilot comment for manual -# confirmation. This is the REST API, so the login carries the `[bot]` suffix. -gh api repos/ptr727/NxWitness/issues//comments --jq \ - '[.[] | select(.user.login=="copilot-pull-request-reviewer[bot]")] | last | {created_at, body: .body[:200]}' -``` - -Coverage is confirmed when (1) exits 0 - **a formal review with no inline comments still satisfies path (1)**, because coverage is about the head SHA, not the comment count. For issue comments (path 2), body content is the only reliable signal - `created_at` is not: `git log -1 --format=%cI` is the **commit** timestamp, not the push timestamp, so amended or rebased commits can have an earlier timestamp and an older Copilot comment could satisfy a time check even though Copilot never saw the current head. Treat path (2) as confirmed only when the comment body explicitly refers to the current changes. - -### Bounded Retry Workflow - -This path is only for a **genuinely missing** review - no Copilot review (formal *or* issue comment) covers the current head SHA after polling. A review that covered the head but produced no comments is a clean pass, not a missing review; do not enter this retry path for it. - -If a review did not run on the current head, retry: - -1. Wait briefly and check head-SHA coverage (see above). -1. Re-request the review via the `requestReviews` mutation (see "Triggering and Polling"); fall back to the GitHub PR UI only if the mutation no-ops. -1. Retry up to two more times (three total). -1. If still missing, mark review as blocked and escalate to the user/maintainer with what was attempted. - -### Reply and Thread Resolution Workflow - -List unresolved threads. Use `first: 100` with cursor-based pagination; if `hasNextPage` is true, re-run with `after: ""` to retrieve the next page: - -```sh -gh api graphql -f query=' -{ - repository(owner: "ptr727", name: "NxWitness") { - pullRequest(number: ) { - reviewThreads(first: 100) { - nodes { - id isResolved path - comments(first: 1) { nodes { author { login } body } } - } - pageInfo { hasNextPage endCursor } - } - } - } -}' | jq ' - .data.repository.pullRequest.reviewThreads | - (.pageInfo | "hasNextPage=\(.hasNextPage) endCursor=\(.endCursor)"), - (.nodes[] | select(.isResolved == false)) -' -``` - -Reply on a thread, then resolve it: - -```sh -gh api graphql -f query=' -mutation($threadId: ID!, $body: String!) { - addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) { - comment { id } - } -}' -F threadId="PRRT_..." -F body="Fixed in : ." - -gh api graphql -f query=' -mutation($threadId: ID!) { - resolveReviewThread(input: { threadId: $threadId }) { thread { id isResolved } } -}' -F threadId="PRRT_..." -``` - -Issue-level Copilot comments (those in `issues//comments`) have no resolution action - GitHub provides no API or UI to resolve them. Reply if the finding warrants it; no resolution step is needed or possible. - -Reply-body conventions: - -- Accepted bug/style fix: include fixing commit SHA and a one-line summary. -- Declined style comment: cite the rule (AGENTS.md or the CODESTYLE.md language section) and the existing-tree precedent. -- Declined architecture proposal: one-sentence rationale. - -After the final push, sweep-resolve stale older threads for removed code paths. - -## When in Doubt - -Read [AGENTS.md](../AGENTS.md) for this repo's conventions. For code-style rules, [`CODESTYLE.md`](../CODESTYLE.md) (its General section plus the relevant language section) is authoritative. Don't restate any of these files' rules in commit bodies or PR descriptions - keep those focused on the change itself. - -**In a derived repo:** if you find a discrepancy that should be fixed in the template itself (this file or AGENTS.md is out of date, a rule is missing, something bit this repo and would bite the next), open an issue upstream in [`ptr727/ProjectTemplate`](https://github.com/ptr727/ProjectTemplate) rather than only fixing it locally - see the template's [AGENTS.md "Staying in Sync and Reporting Drift Upstream"](https://github.com/ptr727/ProjectTemplate/blob/main/AGENTS.md#staying-in-sync-and-reporting-drift-upstream). +# Copilot Instructions + +Repository conventions for GitHub Copilot (and any other AI agent reading this file). + +The **canonical guide is [AGENTS.md](../AGENTS.md)** at the repo root - read it first, including the [PR Review Etiquette](../AGENTS.md#pr-review-etiquette) review-loop contract this file's runbook implements. This file is intentionally narrow: commit/PR-title conventions (summarized inline so VS Code's commit-message and PR-title generators have them) plus the GitHub Copilot Review Runbook. + +For code-style rules, see [`CODESTYLE.md`](../CODESTYLE.md) at the repo root - one guide with a General section plus per-language sections (.NET). + +Do not duplicate language-specific rules here. **Project-specific conventions and API/behavioral contracts also belong in [AGENTS.md](../AGENTS.md), not here** - this file is intentionally limited to the inline commit/PR-title summary and the GitHub Copilot Review Runbook. Non-Copilot agents (Claude Code, Codex, Cursor, ...) are not directed to this file and don't read it by default, so any rule a reviewer must honor has to live in `AGENTS.md` to be provider-independent. + +## Commit Messages and Pull Request Titles + +Summarized for VS Code's generators; the full rules, rationale, and examples are in [AGENTS.md "Pull Request Title and Commit Message Conventions"](../AGENTS.md#pull-request-title-and-commit-message-conventions). + +- Imperative subject, <= 72 characters, no trailing period; optional blank-line-separated body for the non-obvious *why*. +- US English, title case with lowercase short bind words; no vague titles, no `Co-Authored-By:` unless asked, no release-bump magnitude (NBGV handles versioning). Dependabot's `Bump X from Y to Z` titles are fine. +- develop PRs squash-merge (`gh pr merge --squash`), main PRs merge-commit (`--merge`); a mismatched flag is rejected by branch protection. + +## GitHub Copilot Review Runbook + +> This runbook implements the [AGENTS.md "PR Review Etiquette"](../AGENTS.md#pr-review-etiquette) review-loop contract for GitHub Copilot. Without it in-repo, an agent has no pointer to the reliable Copilot mechanics and falls back to known-broken paths (the no-op `POST /requested_reviewers`, the wrong bot-login filter). In the API snippets below, fill the `` placeholder with the PR number. + +Use this section for provider-specific mechanics. The expected review loop *contract* (request review on every push, verify head-SHA coverage, triage findings, reply + resolve, escalate when stuck) is defined in [AGENTS.md -> PR Review Etiquette](../AGENTS.md#pr-review-etiquette). This section only describes how to make GitHub Copilot reliably execute it. + +### Triggering and Polling + +Auto-review on push is configured (via the branch ruleset's `copilot_code_review` rule with `review_on_push: true`) but fires inconsistently in practice - treat it as best-effort, not guaranteed. After every push, **re-request a review programmatically** via the GraphQL `requestReviews` mutation, passing the Copilot reviewer's bot node id in `botIds`. This drives the loop end-to-end without a UI hand-off. + +**A review with no inline comments is still a completed review - not a failure, and not a reason to ask the maintainer to re-trigger.** Copilot very often posts a single formal review (GraphQL `state: COMMENTED`) whose body ends with "...reviewed N of N changed files ... and generated no comments" and adds **zero** inline threads. That review carries the head `commit.oid` and fully satisfies the loop - it is the clean-pass success case. Never read "no inline comments" as "the review didn't run," and never re-request or escalate to the maintainer because comments are absent. + +**Round 1 is normally auto-seeded - poll for it before trying to self-trigger.** Auto-review-on-open supplies the first review with no `botIds` call needed, but it can lag one to three minutes. After opening a PR (or the first push), **poll** for a Copilot review on the head SHA (see [Verify Review Covered Current Head](#verify-review-covered-current-head)) before concluding none ran. The `requestReviews` mutation below is for **re-requesting on later pushes** (a new head SHA); by then a prior review exists, so its bot node id is readable. A missing bot node id on round 1 therefore means "the auto-review has not landed yet - wait and poll," **not** "ask the maintainer to kick it off." + +> **The reviewer login differs by API.** In **GraphQL** (`gh api graphql` and `gh pr view --json reviews`, which is GraphQL-backed) the `Bot.login` is `copilot-pull-request-reviewer` - **no `[bot]` suffix**. In the **REST** API (`gh api repos/.../issues|pulls/...`) the same account's `user.login` is `copilot-pull-request-reviewer[bot]` - **with** the suffix. Each query below uses the correct form for its API; match the API, not a single spelling, when adapting them. + +```sh +# 1. PR node id + the Copilot reviewer's bot node id (read from any existing +# Copilot review; the reviewer login is `copilot-pull-request-reviewer`). +PR_NODE=$(gh pr view --json id --jq '.id') +BOT_ID=$(gh api graphql -f query=' +{ + repository(owner: "ptr727", name: "NxWitness") { + pullRequest(number: ) { + reviews(first: 50) { nodes { author { __typename login ... on Bot { id } } } } + } + } +}' --jq '[.data.repository.pullRequest.reviews.nodes[] + | select(.author.login == "copilot-pull-request-reviewer") + | .author.id] | first') + +# 2. Re-request a Copilot review on the current head. +gh api graphql -f query=' +mutation($pr: ID!, $bot: ID!) { + requestReviews(input: { pullRequestId: $pr, botIds: [$bot], union: true }) { + pullRequest { id } + } +}' -F pr="$PR_NODE" -F bot="$BOT_ID" +``` + +The bot node id is read from an existing Copilot **formal** review (`pullRequest.reviews`), so step 1 needs at least one prior formal review on the PR - the auto-review-on-open normally supplies the first one (it may have **no inline comments**; that still counts, and its bot node id is still readable). Poll for it (give auto-review-on-open a few minutes) before deciding it is missing. If Copilot posted **only an issue comment** and no formal review, the head is covered but `reviews` yields no bot node id - read the id from the Copilot issue comment's author by querying the PR's issue comments in GraphQL (`pullRequest.comments` -> author `... on Bot { id }`), or request `Copilot` once through the GitHub PR UI to produce a formal review. Manual UI seeding is the fallback specifically when no formal review exists to read the id from; then use the mutation for every subsequent re-request. + +**Do NOT post `@Copilot review` as a PR comment.** That comment triggers the Copilot *coding agent* (`copilot-swe-agent[bot]`), which makes code changes rather than posting a review. + +Known non-working request paths (don't rely on them - use the `requestReviews` mutation above instead): + +- `POST /requested_reviewers` with `reviewers=[Copilot]` can return 200 but no-op. +- `copilot-pull-request-reviewer` as a requested reviewer slug returns 422. + +### Verify Review Covered Current Head + +Before merging, confirm Copilot reviewed the current PR head SHA. Copilot may respond as either a formal review (carries an exact commit SHA) or an issue comment (no SHA - use the most recent Copilot comment for manual confirmation). Check both. + +```sh +PR_HEAD=$(gh pr view --json headRefOid --jq '.headRefOid') + +# 1. Formal review - exact SHA match. +gh pr view --json reviews --jq \ + '.reviews[] | select(.author.login=="copilot-pull-request-reviewer") | .commit.oid' \ + | grep -q "$PR_HEAD" && echo "covered via formal review" + +# 2. Issue comment - show the most recent Copilot comment for manual +# confirmation. This is the REST API, so the login carries the `[bot]` suffix. +gh api repos/ptr727/NxWitness/issues//comments --jq \ + '[.[] | select(.user.login=="copilot-pull-request-reviewer[bot]")] | last | {created_at, body: .body[:200]}' +``` + +Coverage is confirmed when (1) exits 0 - **a formal review with no inline comments still satisfies path (1)**, because coverage is about the head SHA, not the comment count. For issue comments (path 2), body content is the only reliable signal - `created_at` is not: `git log -1 --format=%cI` is the **commit** timestamp, not the push timestamp, so amended or rebased commits can have an earlier timestamp and an older Copilot comment could satisfy a time check even though Copilot never saw the current head. Treat path (2) as confirmed only when the comment body explicitly refers to the current changes. + +### Bounded Retry Workflow + +This path is only for a **genuinely missing** review - no Copilot review (formal *or* issue comment) covers the current head SHA after polling. A review that covered the head but produced no comments is a clean pass, not a missing review; do not enter this retry path for it. + +If a review did not run on the current head, retry: + +1. Wait briefly and check head-SHA coverage (see above). +1. Re-request the review via the `requestReviews` mutation (see "Triggering and Polling"); fall back to the GitHub PR UI only if the mutation no-ops. +1. Retry up to two more times (three total). +1. If still missing, mark review as blocked and escalate to the user/maintainer with what was attempted. + +### Reply and Thread Resolution Workflow + +List unresolved threads. Use `first: 100` with cursor-based pagination; if `hasNextPage` is true, re-run with `after: ""` to retrieve the next page: + +```sh +gh api graphql -f query=' +{ + repository(owner: "ptr727", name: "NxWitness") { + pullRequest(number: ) { + reviewThreads(first: 100) { + nodes { + id isResolved path + comments(first: 1) { nodes { author { login } body } } + } + pageInfo { hasNextPage endCursor } + } + } + } +}' | jq ' + .data.repository.pullRequest.reviewThreads | + (.pageInfo | "hasNextPage=\(.hasNextPage) endCursor=\(.endCursor)"), + (.nodes[] | select(.isResolved == false)) +' +``` + +Reply on a thread, then resolve it: + +```sh +gh api graphql -f query=' +mutation($threadId: ID!, $body: String!) { + addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) { + comment { id } + } +}' -F threadId="PRRT_..." -F body="Fixed in : ." + +gh api graphql -f query=' +mutation($threadId: ID!) { + resolveReviewThread(input: { threadId: $threadId }) { thread { id isResolved } } +}' -F threadId="PRRT_..." +``` + +Issue-level Copilot comments (those in `issues//comments`) have no resolution action - GitHub provides no API or UI to resolve them. Reply if the finding warrants it; no resolution step is needed or possible. + +Reply-body conventions: + +- Accepted bug/style fix: include fixing commit SHA and a one-line summary. +- Declined style comment: cite the rule (AGENTS.md or the CODESTYLE.md language section) and the existing-tree precedent. +- Declined architecture proposal: one-sentence rationale. + +After the final push, sweep-resolve stale older threads for removed code paths. + +## When in Doubt + +Read [AGENTS.md](../AGENTS.md) for this repo's conventions. For code-style rules, [`CODESTYLE.md`](../CODESTYLE.md) (its General section plus the relevant language section) is authoritative. Don't restate any of these files' rules in commit bodies or PR descriptions - keep those focused on the change itself. + +**In a derived repo:** if you find a discrepancy that should be fixed in the template itself (this file or AGENTS.md is out of date, a rule is missing, something bit this repo and would bite the next), open an issue upstream in [`ptr727/ProjectTemplate`](https://github.com/ptr727/ProjectTemplate) rather than only fixing it locally - see the template's [AGENTS.md "Staying in Sync and Reporting Drift Upstream"](https://github.com/ptr727/ProjectTemplate/blob/main/AGENTS.md#staying-in-sync-and-reporting-drift-upstream). diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4b2306c7..3eb8f80d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,97 +1,97 @@ -# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file -# -# Every ecosystem appears **twice**: once with `target-branch: "main"` -# and once with `target-branch: "develop"`. Dependabot will open -# parallel PRs against each branch, so both stay current on -# dependency versions independently of the develop -> main release -# cadence. -# -# Why dual-target and not develop-only: -# - `develop` is the integration branch and ships content forward to -# `main` through merge-commit releases, but the time between releases -# can be long (a feature branch may sit on develop for weeks). -# - Push-distribution channels (e.g. Docker Hub, distros that pull from -# main) consume `main` directly. If `main` only got dependency bumps -# via the next develop -> main release, those channels would ship -# outdated code in the interim. -# - Codegen workflows take the same dual-target shape for the same -# reason - see .github/workflows/run-codegen-pull-request-task.yml. -# -# The merge-bot's `case` statement in -# .github/workflows/merge-bot-pull-request.yml dispatches the merge -# method per base ref (squash on develop, merge on main) so both bases -# auto-merge cleanly. `develop` remains strictly forward-only: there -# are no main -> develop back-merges; each branch absorbs its own -# Dependabot PRs and codegen PRs independently. -# -# Security update PRs (CVE-driven) are opened by Dependabot against -# the repo default branch (`main`) regardless of any `target-branch` -# config - the `case` statement handles them in the same code path. -version: 2 -updates: - - # ----- nuget ----- - - - package-ecosystem: "nuget" - target-branch: "main" - directory: "/" - schedule: - interval: "daily" - groups: - nuget-deps: - patterns: - - "*" - - - package-ecosystem: "nuget" - target-branch: "develop" - directory: "/" - schedule: - interval: "daily" - groups: - nuget-deps: - patterns: - - "*" - - # ----- github-actions ----- - - - package-ecosystem: "github-actions" - target-branch: "main" - directory: "/" - schedule: - interval: "daily" - groups: - actions-deps: - patterns: - - "*" - - - package-ecosystem: "github-actions" - target-branch: "develop" - directory: "/" - schedule: - interval: "daily" - groups: - actions-deps: - patterns: - - "*" - - # ----- docker (Docker/*.Dockerfile) ----- - - - package-ecosystem: "docker" - target-branch: "main" - directory: "/Docker" - schedule: - interval: "daily" - groups: - docker-deps: - patterns: - - "*" - - - package-ecosystem: "docker" - target-branch: "develop" - directory: "/Docker" - schedule: - interval: "daily" - groups: - docker-deps: - patterns: - - "*" +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file +# +# Every ecosystem appears **twice**: once with `target-branch: "main"` +# and once with `target-branch: "develop"`. Dependabot will open +# parallel PRs against each branch, so both stay current on +# dependency versions independently of the develop -> main release +# cadence. +# +# Why dual-target and not develop-only: +# - `develop` is the integration branch and ships content forward to +# `main` through merge-commit releases, but the time between releases +# can be long (a feature branch may sit on develop for weeks). +# - Push-distribution channels (e.g. Docker Hub, distros that pull from +# main) consume `main` directly. If `main` only got dependency bumps +# via the next develop -> main release, those channels would ship +# outdated code in the interim. +# - Codegen workflows take the same dual-target shape for the same +# reason - see .github/workflows/run-codegen-pull-request-task.yml. +# +# The merge-bot's `case` statement in +# .github/workflows/merge-bot-pull-request.yml dispatches the merge +# method per base ref (squash on develop, merge on main) so both bases +# auto-merge cleanly. `develop` remains strictly forward-only: there +# are no main -> develop back-merges; each branch absorbs its own +# Dependabot PRs and codegen PRs independently. +# +# Security update PRs (CVE-driven) are opened by Dependabot against +# the repo default branch (`main`) regardless of any `target-branch` +# config - the `case` statement handles them in the same code path. +version: 2 +updates: + + # ----- nuget ----- + + - package-ecosystem: "nuget" + target-branch: "main" + directory: "/" + schedule: + interval: "daily" + groups: + nuget-deps: + patterns: + - "*" + + - package-ecosystem: "nuget" + target-branch: "develop" + directory: "/" + schedule: + interval: "daily" + groups: + nuget-deps: + patterns: + - "*" + + # ----- github-actions ----- + + - package-ecosystem: "github-actions" + target-branch: "main" + directory: "/" + schedule: + interval: "daily" + groups: + actions-deps: + patterns: + - "*" + + - package-ecosystem: "github-actions" + target-branch: "develop" + directory: "/" + schedule: + interval: "daily" + groups: + actions-deps: + patterns: + - "*" + + # ----- docker (Docker/*.Dockerfile) ----- + + - package-ecosystem: "docker" + target-branch: "main" + directory: "/Docker" + schedule: + interval: "daily" + groups: + docker-deps: + patterns: + - "*" + + - package-ecosystem: "docker" + target-branch: "develop" + directory: "/Docker" + schedule: + interval: "daily" + groups: + docker-deps: + patterns: + - "*" diff --git a/.gitignore b/.gitignore index 193ff244..e255b630 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ -[Dd]ebug/ -[Rr]elease/ -[Bb]in/ -[Oo]bj/ - -.idea -.vs -.artifacts -.DS_Store -*.user +[Dd]ebug/ +[Rr]elease/ +[Bb]in/ +[Oo]bj/ + +.idea +.vs +.artifacts +.DS_Store +*.user diff --git a/.husky/task-runner.json b/.husky/task-runner.json index dbcd9602..a40c5ebe 100644 --- a/.husky/task-runner.json +++ b/.husky/task-runner.json @@ -1,32 +1,32 @@ -{ - "$schema": "https://alirezanet.github.io/Husky.Net/schema.json", - "tasks": [ - { - "name": "CSharpier Format", - "command": "dotnet", - "args": [ - "csharpier", - "format", - "--log-level=debug", - "${staged}" - ], - "include": [ - "**/*.cs" - ] - }, - { - "name": ".NET Format", - "command": "dotnet", - "args": [ - "format", - "style", - "--verify-no-changes", - "--severity=info", - "--verbosity=detailed" - ], - "include": [ - "**/*.cs" - ] - } - ] -} +{ + "$schema": "https://alirezanet.github.io/Husky.Net/schema.json", + "tasks": [ + { + "name": "CSharpier Format", + "command": "dotnet", + "args": [ + "csharpier", + "format", + "--log-level=debug", + "${staged}" + ], + "include": [ + "**/*.cs" + ] + }, + { + "name": ".NET Format", + "command": "dotnet", + "args": [ + "format", + "style", + "--verify-no-changes", + "--severity=info", + "--verbosity=detailed" + ], + "include": [ + "**/*.cs" + ] + } + ] +} diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc index 40f5d762..d1472b06 100644 --- a/.markdownlint-cli2.jsonc +++ b/.markdownlint-cli2.jsonc @@ -1,18 +1,18 @@ -{ - "config": { - // Prose paragraphs and data-heavy tables or URLs are intentionally long. - // Reflowing at 80 columns hurts readability and churns diffs. - "MD013": false, - // MD033 (inline HTML) stays enabled so native markdown wins. - // HTML comments, used as reference-link dividers, pass it. - // The details and summary elements are allowed for GitHub collapsibles, which have no markdown equivalent. - // Every other element still flags. - "MD033": { "allowed_elements": ["details", "summary"] }, - // Require fenced code blocks over the legacy 4-space-indented style. - "MD046": { "style": "fenced" }, - // MD060 (table column style) is not enforced - allow both compact - // (`|a|b|`) and padded (`| a | b |`) table pipe spacing. - "MD060": false - }, - "gitignore": true -} +{ + "config": { + // Prose paragraphs and data-heavy tables or URLs are intentionally long. + // Reflowing at 80 columns hurts readability and churns diffs. + "MD013": false, + // MD033 (inline HTML) stays enabled so native markdown wins. + // HTML comments, used as reference-link dividers, pass it. + // The details and summary elements are allowed for GitHub collapsibles, which have no markdown equivalent. + // Every other element still flags. + "MD033": { "allowed_elements": ["details", "summary"] }, + // Require fenced code blocks over the legacy 4-space-indented style. + "MD046": { "style": "fenced" }, + // MD060 (table column style) is not enforced - allow both compact + // (`|a|b|`) and padded (`| a | b |`) table pipe spacing. + "MD060": false + }, + "gitignore": true +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 660c933a..9e0e25b8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,43 +1,43 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Create Version", - "type": "coreclr", - "request": "launch", - "preLaunchTask": ".NET Build", - "program": "${workspaceFolder}/CreateMatrix/bin/Debug/net10.0/CreateMatrix.dll", - "args": ["version", "--versionpath=./Make/Version.json"], - "cwd": "${workspaceFolder}", - "console": "internalConsole", - "stopAtEntry": false - }, - { - "name": "Create Matrix", - "type": "coreclr", - "request": "launch", - "preLaunchTask": ".NET Build", - "program": "${workspaceFolder}/CreateMatrix/bin/Debug/net10.0/CreateMatrix.dll", - "args": ["matrix", "--versionpath=./Make/Version.json", "--matrixpath=./Make/Matrix.json", "--updateversion"], - "cwd": "${workspaceFolder}", - "console": "internalConsole", - "stopAtEntry": false - }, - { - "name": "Create Docker and Compose Files", - "type": "coreclr", - "request": "launch", - "preLaunchTask": ".NET Build", - "program": "${workspaceFolder}/CreateMatrix/bin/Debug/net10.0/CreateMatrix.dll", - "args": ["make", "--versionpath=./Make/Version.json", "--makedirectory=./Make", "--dockerdirectory=./Docker", "--versionlabel=Beta"], - "cwd": "${workspaceFolder}", - "console": "internalConsole", - "stopAtEntry": false - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach" - } - ] -} +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Create Version", + "type": "coreclr", + "request": "launch", + "preLaunchTask": ".NET Build", + "program": "${workspaceFolder}/CreateMatrix/bin/Debug/net10.0/CreateMatrix.dll", + "args": ["version", "--versionpath=./Make/Version.json"], + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": "Create Matrix", + "type": "coreclr", + "request": "launch", + "preLaunchTask": ".NET Build", + "program": "${workspaceFolder}/CreateMatrix/bin/Debug/net10.0/CreateMatrix.dll", + "args": ["matrix", "--versionpath=./Make/Version.json", "--matrixpath=./Make/Matrix.json", "--updateversion"], + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": "Create Docker and Compose Files", + "type": "coreclr", + "request": "launch", + "preLaunchTask": ".NET Build", + "program": "${workspaceFolder}/CreateMatrix/bin/Debug/net10.0/CreateMatrix.dll", + "args": ["make", "--versionpath=./Make/Version.json", "--makedirectory=./Make", "--dockerdirectory=./Docker", "--versionlabel=Beta"], + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 65652b24..ac574da3 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,175 +1,175 @@ -{ - "version": "2.0.0", - "tasks": [ - // .NET language group. A non-.NET repo drops this group and adds its own - // language's tasks. The first three tasks are the .NET clean-compile set - // (CODESTYLE.md) carried verbatim; the rest are convenience/project-specific. - { - "label": ".NET Build", - "type": "process", - "command": "dotnet", - "args": [ - "build", - "${workspaceFolder}", - "--verbosity=diagnostic" - ], - "group": "build", - "problemMatcher": [ - "$msCompile" - ], - "presentation": { - "showReuseMessage": false, - "clear": false - } - }, - { - "label": ".NET Format", - "type": "process", - "command": "dotnet", - "args": [ - "format", - "style", - "--verify-no-changes", - "--severity=info", - "--verbosity=detailed" - ], - "problemMatcher": [ - "$msCompile" - ], - "presentation": { - "showReuseMessage": false, - "clear": false - }, - "dependsOrder": "sequence", - "dependsOn": [ - "CSharpier Format", - ".NET Build" - ] - }, - { - "label": "CSharpier Format", - "type": "process", - "command": "dotnet", - "args": [ - "csharpier", - "format", - "--log-level=debug", - "." - ], - "problemMatcher": [ - "$msCompile" - ], - "presentation": { - "showReuseMessage": false, - "clear": false - } - }, - // Convenience / project-specific tasks (adapt or drop per repo). - { - "label": ".NET Tool Update", - "type": "process", - "command": "dotnet", - "args": [ - "tool", - "update", - "--all" - ], - "problemMatcher": [ - "$msCompile" - ], - "presentation": { - "showReuseMessage": false, - "clear": false - } - }, - { - "label": "Husky.Net Run", - "type": "process", - "command": "dotnet", - "args": [ - "husky", - "run" - ], - "problemMatcher": [ - "$msCompile" - ], - "presentation": { - "showReuseMessage": false, - "clear": false - } - }, - { - "label": ".NET Outdated Upgrade", - "type": "process", - "command": "dotnet", - "args": [ - "outdated", - "--upgrade:prompt" - ], - "problemMatcher": [ - "$msCompile" - ], - "presentation": { - "showReuseMessage": false, - "clear": false - } - }, - // Lint group - the local full doc-lint surface (Docker at :latest), matching the CI lint set. - // Run on demand. The pre-commit hook does language formatting only. Every repo carries these. - { - "label": "Lint: EditorConfig", - "type": "process", - "command": "docker", - "args": [ "run", "--rm", "--pull=always", "-v", "${workspaceFolder}:/check", "-w", "/check", "mstruebing/editorconfig-checker:latest" ], - "problemMatcher": [], - "presentation": { - "showReuseMessage": false, - "clear": false - } - }, - { - "label": "Lint: Workflows", - "type": "process", - "command": "docker", - "args": [ "run", "--rm", "--pull=always", "-v", "${workspaceFolder}:/repo", "-w", "/repo", "rhysd/actionlint:latest", "-color" ], - "problemMatcher": [], - "presentation": { - "showReuseMessage": false, - "clear": false - } - }, - { - "label": "Lint: Markdown", - "type": "process", - "command": "docker", - "args": [ "run", "--rm", "--pull=always", "-v", "${workspaceFolder}:/workdir", "-w", "/workdir", "davidanson/markdownlint-cli2:latest", "**/*.md" ], - "problemMatcher": [], - "presentation": { - "showReuseMessage": false, - "clear": false - } - }, - { - "label": "Lint: Spelling", - "type": "process", - "command": "docker", - "args": [ "run", "--rm", "--pull=always", "-v", "${workspaceFolder}:/workdir", "-w", "/workdir", "ghcr.io/streetsidesoftware/cspell:latest", "--no-progress", "README.md", "HISTORY.md" ], - "problemMatcher": [], - "presentation": { - "showReuseMessage": false, - "clear": false - } - }, - { - "label": "Lint: All", - "dependsOrder": "sequence", - "dependsOn": [ - "Lint: EditorConfig", - "Lint: Workflows", - "Lint: Markdown", - "Lint: Spelling" - ], - "problemMatcher": [] - } - ] -} +{ + "version": "2.0.0", + "tasks": [ + // .NET language group. A non-.NET repo drops this group and adds its own + // language's tasks. The first three tasks are the .NET clean-compile set + // (CODESTYLE.md) carried verbatim; the rest are convenience/project-specific. + { + "label": ".NET Build", + "type": "process", + "command": "dotnet", + "args": [ + "build", + "${workspaceFolder}", + "--verbosity=diagnostic" + ], + "group": "build", + "problemMatcher": [ + "$msCompile" + ], + "presentation": { + "showReuseMessage": false, + "clear": false + } + }, + { + "label": ".NET Format", + "type": "process", + "command": "dotnet", + "args": [ + "format", + "style", + "--verify-no-changes", + "--severity=info", + "--verbosity=detailed" + ], + "problemMatcher": [ + "$msCompile" + ], + "presentation": { + "showReuseMessage": false, + "clear": false + }, + "dependsOrder": "sequence", + "dependsOn": [ + "CSharpier Format", + ".NET Build" + ] + }, + { + "label": "CSharpier Format", + "type": "process", + "command": "dotnet", + "args": [ + "csharpier", + "format", + "--log-level=debug", + "." + ], + "problemMatcher": [ + "$msCompile" + ], + "presentation": { + "showReuseMessage": false, + "clear": false + } + }, + // Convenience / project-specific tasks (adapt or drop per repo). + { + "label": ".NET Tool Update", + "type": "process", + "command": "dotnet", + "args": [ + "tool", + "update", + "--all" + ], + "problemMatcher": [ + "$msCompile" + ], + "presentation": { + "showReuseMessage": false, + "clear": false + } + }, + { + "label": "Husky.Net Run", + "type": "process", + "command": "dotnet", + "args": [ + "husky", + "run" + ], + "problemMatcher": [ + "$msCompile" + ], + "presentation": { + "showReuseMessage": false, + "clear": false + } + }, + { + "label": ".NET Outdated Upgrade", + "type": "process", + "command": "dotnet", + "args": [ + "outdated", + "--upgrade:prompt" + ], + "problemMatcher": [ + "$msCompile" + ], + "presentation": { + "showReuseMessage": false, + "clear": false + } + }, + // Lint group - the local full doc-lint surface (Docker at :latest), matching the CI lint set. + // Run on demand. The pre-commit hook does language formatting only. Every repo carries these. + { + "label": "Lint: EditorConfig", + "type": "process", + "command": "docker", + "args": [ "run", "--rm", "--pull=always", "-v", "${workspaceFolder}:/check", "-w", "/check", "mstruebing/editorconfig-checker:latest" ], + "problemMatcher": [], + "presentation": { + "showReuseMessage": false, + "clear": false + } + }, + { + "label": "Lint: Workflows", + "type": "process", + "command": "docker", + "args": [ "run", "--rm", "--pull=always", "-v", "${workspaceFolder}:/repo", "-w", "/repo", "rhysd/actionlint:latest", "-color" ], + "problemMatcher": [], + "presentation": { + "showReuseMessage": false, + "clear": false + } + }, + { + "label": "Lint: Markdown", + "type": "process", + "command": "docker", + "args": [ "run", "--rm", "--pull=always", "-v", "${workspaceFolder}:/workdir", "-w", "/workdir", "davidanson/markdownlint-cli2:latest", "**/*.md" ], + "problemMatcher": [], + "presentation": { + "showReuseMessage": false, + "clear": false + } + }, + { + "label": "Lint: Spelling", + "type": "process", + "command": "docker", + "args": [ "run", "--rm", "--pull=always", "-v", "${workspaceFolder}:/workdir", "-w", "/workdir", "ghcr.io/streetsidesoftware/cspell:latest", "--no-progress", "README.md", "HISTORY.md" ], + "problemMatcher": [], + "presentation": { + "showReuseMessage": false, + "clear": false + } + }, + { + "label": "Lint: All", + "dependsOrder": "sequence", + "dependsOn": [ + "Lint: EditorConfig", + "Lint: Workflows", + "Lint: Markdown", + "Lint: Spelling" + ], + "problemMatcher": [] + } + ] +} diff --git a/AGENTS.md b/AGENTS.md index 31a800ba..94e6ff61 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,237 +1,237 @@ -# Instructions for AI Coding Agents - -This repository builds and publishes Docker images for Network Optix VMS products (Nx Witness, Nx Meta, Nx Go, DW Spectrum, Wisenet WAVE). It includes base images (nx-base, nx-base-lsio) and derived product images that use the base images, plus a .NET tooling project (`CreateMatrix`) that generates Dockerfiles and build matrices and scripts/templates for packaging. There is **no NuGet publish**: the .NET project is a build-time matrix generator only; the published artifacts are exclusively the Docker Hub images. - -This file is the canonical reference for cross-cutting AI-agent rules. The CI/CD workflow contract and conventions live in [`WORKFLOW.md`](./WORKFLOW.md); the repository configuration-as-code (branch rulesets, settings, required secrets) lives in [`repo-config/`](./repo-config/); C# code-style conventions live in [`CODESTYLE.md`](./CODESTYLE.md). Copilot review *mechanics* are owned by [`.github/copilot-instructions.md`](./.github/copilot-instructions.md) - this file delegates them there explicitly (see "PR Review Etiquette" below). - -**Where rules live.** A durable project, code, or style rule belongs in this file (or `WORKFLOW.md` / `CODESTYLE.md` as appropriate), so it is versioned and read by every session and every agent. An agent's own session memory or scratch state is private and lost on restart, so it is never the system of record for a rule: when you learn or are corrected on a rule, write it into the right doc in the same change. Memory may also note it, but the committed docs are the source of truth. - -For comprehensive coding and formatting standards, follow: - -- `CODESTYLE.md` -- `.editorconfig` -- `.husky/task-runner.json` -- `.vscode/tasks.json` - -## Solution Structure - -### Projects - -- `CreateMatrix/CreateMatrix.csproj` - - .NET 10 console app. - - Fetches product release metadata and generates build inputs such as Dockerfiles and matrix data used by CI. -- `CreateMatrixTests/CreateMatrixTests.csproj` - - xUnit v3 test project with AwesomeAssertions. - -### Key Directories - -- `Docker/` - - Generated and static Dockerfiles for base images and product variants. -- `Make/` - - Build orchestration scripts and test compose files. -- `Unraid/` - - Unraid container templates. -- `version.json`, `Make/Version.json`, `Make/Matrix.json` - - Version and matrix inputs consumed by the build pipeline. - -## Build and Validation Workflow - -- Primary developer entry points are the `CreateMatrix` CLI commands invoked directly or via the scripts in `Make/`: - - `version --versionpath=./Make/Version.json`. - - `matrix --versionpath=./Make/Version.json --matrixpath=./Make/Matrix.json --updateversion`. - - `make --versionpath=./Make/Version.json --makedirectory=./Make --dockerdirectory=./Docker --versionlabel=Beta`. -- Formatting and style checks are enforced by Husky.Net and VS Code tasks. -- Required tasks are documented in `CODESTYLE.md` and `.husky/task-runner.json`. -- C# code should be formatted with CSharpier, then verified with `dotnet format` (style). -- The `.NET Format` VS Code task in `.vscode/tasks.json` must be clean and warning-free at all times. - -### Workspace and linting - -- VS Code settings, extension recommendations, and spell-check words live in the workspace file `NxWitness.code-workspace`; add new editor settings or recommended extensions there rather than in `.vscode/`. (Build/debug tasks still live in `.vscode/tasks.json` and `.vscode/launch.json`.) Open the workspace file in VS Code (not the folder) so its settings and recommendations apply. -- Linting is editor-only (no CI lint job); the extensions recommended in `NxWitness.code-workspace` cover the project's structured files: C# (Roslyn + CSharpier), Markdown, Dockerfiles/Compose, GitHub Actions workflows, and spelling. Lint changed files and clear reported problems before pushing. -- For workflow files, the GitHub Actions extension covers schema/expression checks in-editor; run the `actionlint` CLI for deeper checks (including shellcheck on `run:` steps). - -## Image Architecture - -- Base images (`nx-base`, `nx-base-lsio`) are built and pushed, then reused as `FROM` images for derived product Dockerfiles. -- Derived product images should stay aligned with the base image changes and tags (for example, the Ubuntu distro tag). - -## CI Pipeline (GitHub Actions) - -The full CI/CD contract - triggers, jobs, the one-branch publish model, versioning, and the multi-image build layer - is specified in [`WORKFLOW.md`](./WORKFLOW.md), the canonical guide. The summary below is a pointer; do not duplicate those rules. - -- CI runs on **push to every branch** (`test-pull-request.yml`): it validates (`validate-task.yml`: Husky lint + `dotnet test`) on every push, and runs a fast smoke build (`build-docker-task.yml` with `smoke: true` - NxMeta + NxMeta-LSIO, amd64, no push) only when image files (`Docker/**`, `Make/Matrix.json`, `Make/Version.json`) change, via an inline `git diff` change-gate. One aggregator job, `Check pull request workflow status job`, is the ruleset-bound required check. -- Publishing is **triggered-Docker, one branch per run** (`publish-release.yml`): triggers are the weekly schedule (rebuilds `main` only), a path-scoped push to `main` on `Make/Matrix.json` (publishes a new codegen product pin at once), and manual dispatch (publishes the started-from branch). One run computes the version once (`get-version-task.yml`), builds the shared base once (main only; develop reuses it with `build_base: false`), builds the full product matrix from `Make/Matrix.json`, and on `main` cuts the GitHub release and pushes the Docker Hub overviews. -- Merges to `main`/`develop` do not build or publish images by themselves; only the matrix-pin push (main), a schedule, or a dispatch publishes. Auto-merged Dependabot and codegen PRs land commits the next publish picks up. Do not reintroduce a two-branch publish matrix, a nested `get-version` in the build task, the date-badge or standalone docker-readme workflows, or `dorny/paths-filter`. -- Lint workflow edits before pushing (see [Workspace and linting](#workspace-and-linting)); `validate-task.yml` runs the unit tests + Husky style checks in CI. - -## Versioning - -The `version` (major.minor) in [version.json](./version.json) is the NBGV version floor; NBGV appends the git height. **`develop` leads `main` by a minor:** after a `develop -> main` release lands and main's publish completes, bump the minor in `version.json` on `develop` in an isolated `bump-version-X.Y` PR (X.Y = the new minor), so develop's NBGV prerelease version stays numerically above main's last stable. A **maintenance** `develop -> main` promotion (dependency bumps, CI/doc fixes, template re-syncs) holds main's version - `git checkout main -- version.json` on the promotion branch - so `main` advances only its NBGV height, not its minor. (NBGV's version is the GitHub release tag on `main` and the `LABEL_VERSION` build arg baked into the images; the Docker image *tags* carry the Nx product version from `Make/Matrix.json` - see [CI Pipeline](#ci-pipeline-github-actions).) - -- A significant one-time overhaul of the build/release process (such as the branch-scoped CI/CD migration) is a deliberate maintainer-directed floor bump in the PR that introduces it, distinct from the routine cadence above; routine dependency, CI/workflow, and doc edits leave `version.json` untouched. -- **`dotnet/nbgv` is consumed via `@master`, never SHA-pinned.** Its tag stream lags `master` such that Dependabot tag-tracking would only propose downgrades to stale tags; this is the sole [`WORKFLOW.md`](./WORKFLOW.md) D9.1 action-pinning exception (rationale inline in `get-version-task.yml`). Do not SHA-pin it. - -## Git and Commit Rules - -- **Default to staging, not committing.** Stage changes with `git add` and leave `git commit` to the developer unless the developer has explicitly authorized the agent to commit for the current ask ("commit this", "open a PR", etc.). Authorization is scope-bound - it covers the commits needed for that specific task, not a blanket commit license for the rest of the session. -- **All commits must be cryptographically signed (SSH or GPG).** Branch protection enforces this on both branches; unsigned commits are rejected on push. Signing depends on environment configuration - `git config commit.gpgsign true`, a configured `user.signingkey`, and a working signing agent (loaded `ssh-agent` for SSH, or `gpg-agent` for GPG). If signing is not configured in the environment, **do not commit** - surface the missing config to the developer and stop at `git add`. Verify before any agent-authored commit (`git config --get commit.gpgsign && ssh-add -L` or the GPG equivalent). **Signing must be live before the *first* commit, not retrofitted.** Turning on `Require signed commits` against a branch that already has unsigned commits forces a rewrite of that entire history to re-sign it - changing every commit SHA and making whoever does the rewrite the committer and signer of every commit (a rebase preserves the `author` field but not the original signatures; you cannot sign another contributor's commits for them). During new-repo setup, never create commits until signing is verified. -- **Commit under the committing account's own GitHub `noreply` identity - never a private, personal, or invented address.** The `author` and `committer` on every agent-authored commit are the GitHub `noreply` address of the account whose key signs the commit (above) - GitHub issues these in a `username@users.noreply.github.com` or `ID+username@users.noreply.github.com` form, and for this single-maintainer fleet it is the owner's `ptr727@users.noreply.github.com`. Do not set `user.name`/`user.email` to a fabricated persona, bot name, or product name, and do not commit under whatever identity the environment happens to carry: verify `git config --get user.email` is that GitHub `noreply` address before committing, and fix it if not. A wrong identity is not cosmetic - a private email trips GitHub's email-privacy push protection (GH007), and an unrecognized or invented author pollutes history. Identity is separate from signing: a wrong author does not by itself fail the signature rule, but the ad-hoc identities that produce it are typically also unsigned, which the signing rule above then rejects on push. -- **Never force push.** Do not run `git push --force` or `git push --force-with-lease` under any circumstances. Force pushing rewrites shared history and can cause data loss. -- **Never run destructive git commands** (`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`) without explicit developer instruction. -- **Mirror to `develop` any change that lands on `main` outside the feature -> develop -> main flow.** A reconciliation-branch fix made to resolve a `develop -> main` promotion conflict, or a security PR that merges only to `main`, leaves `develop` behind on that content - and forward-only `develop` never back-merges to catch up (the same parallel-target principle as the bots). Before basing new work on `develop`, or diagnosing a defect from it, check `git diff origin/develop origin/main`: a non-empty content diff means develop is stale and the defect may already be fixed on `main`. -- **Put issue-closing keywords (`Closes #N`) in the `develop -> main` promotion PR, not the feature or develop PR.** GitHub auto-closes an issue only from the PR (or commit) that merges to the default branch (`main`); a `Closes #N` that merges only to `develop` does not fire on promotion and leaves the issue open. Tag the promotion PR's description, or close the issue manually once the fix reaches `main`. - -## Pull Request Title and Commit Message Conventions - -### Format - -- Imperative subject summarizing the change, <=72 characters, no trailing period. ("Add 24-hour PM2.5 average sensor", not "Added X" or "Adds X".) -- Optional body, blank-line separated, explaining *why* the change is being made when that's non-obvious. The diff shows *what*. - -### Rules - -- Don't write `update stuff`, `wip`, or other vague titles. (Dependabot's default `Bump X from Y to Z` titles are fine - keep them.) -- Don't add `Co-Authored-By:` lines unless the developer explicitly asks. -- Don't put release-bump magnitude in the title - no "minor", "patch", "release v0.2.0", etc. Nerdbank.GitVersioning computes the next release version from `version.json` + git history. Dependency versions in dependency-bump titles are fine and expected. -- Use US English spelling and match the existing heading style of the file you're editing: title case with lowercase short bind words (a, an, the, and, but, or, of, in, on, at, to, by, for, from); hyphenated compounds capitalize both parts unless the second is a short preposition (*Built-in*, *EPA-Corrected*, *24-Hour*). - -### Examples - -```text -Add structured logging extensions to library -Pin softprops/action-gh-release to commit SHA -Drop net8.0 multi-targeting from console project -Bump xunit.v3 from 3.2.2 to 3.3.0 -Clarify devcontainer setup steps in README -``` - -## Documentation Style Conventions - -### Markdown - -- Use reference-style links for any URL referenced more than once or appearing in lists; alphabetize the reference definitions block. -- Inline single-use relative links (e.g. `[CODESTYLE.md](./CODESTYLE.md)`) are fine. -- One logical paragraph per line; no hard-wrap line-length limit. For an intentional hard line break within a block - stacked badges, status, or license lines - end the line with a trailing backslash (`\`); this explicit form is preferred over trailing whitespace and is not treated as a paragraph split. -- Headings follow the title-case-with-short-bind-words rule from the PR-title section. -- **Write docs in the current state, not as a change from a prior one.** The reader has no memory of the previous behavior, so describe what *is*: "X does Y", never "X *now* does Y", "X *no longer* does Z", or "changed/switched/restored to Y". Before/after framing belongs in changelogs, commit messages, and PR descriptions - not in `README.md` or other living docs. - -### Comments - -Applies to code and workflow (`#`) comments alike. - -- Comment only when the code is non-obvious or important. Self-evident code needs no comment. -- Judge "obvious" in context, not line by line. A note that reads as redundant on its own line can be essential in the larger flow - a comment marking a workflow step's exit condition, for example, even though the line itself plainly does a `return` or `exit`. -- State the non-obvious *why*, not what the code already shows. No cross-project references (do not name other repos), no historic or design narrative, no rule citations - governance lives in this file, not echoed inline. -- **One line if it fits in ~120 columns.** Do not wrap a comment at 75-80 columns; a short two-line comment that would fit on one line looks sloppy - collapse it. Go multi-line only when the content genuinely exceeds ~120, filling each line rather than narrow-wrapping. For a multi-point comment, prefer short structured lines or `-` bullets over one prose paragraph. -- **Workflows: prefer one short summary description at the top of the file** over scattering rationale across steps; comment an individual step only when its purpose is non-obvious. -- **Do not accumulate comments.** When you change code or a comment, rewrite the whole comment fresh; never bolt a new comment onto an existing one or layer explanations across edits. Comment volume should stay flat or shrink over time, not grow. -- **Leave human-authored comments and emojis exactly as written** - do not reword, trim, reflow, or "clean" them, even if they seem to bend a rule. Revise only agent-authored comments, and match the surrounding voice when you do. - -### Character Set - -- **Write ASCII in all agent-authored text** - documentation, code, comments, commit messages, and PR descriptions. The agent does not introduce non-ASCII characters. Replace typographic Unicode with its ASCII equivalent on sight: - - em dash (U+2014) and en dash (U+2013) -> hyphen `-` (use a spaced ` - ` for an em-dash-style clause break) - - right arrow (U+2192) -> `->`; double arrow (U+21D2) -> `=>` - - less-than-or-equal (U+2264) -> `<=`; greater-than-or-equal (U+2265) -> `>=` - - curly quotes (U+2018/U+2019/U+201C/U+201D) -> straight `'` and `"`; ellipsis (U+2026) -> `...` -- **Allowed non-ASCII (two narrow exceptions):** - - **Scientific or technical symbols with no clean ASCII equivalent** - e.g. ohm, micro, degree, pi. Keep the symbol; do not approximate it away. - - **Unicode the developer deliberately typed** - emoji used for emphasis or as callout markers (for example the warning/info markers a maintainer placed in `README.md`). Preserve it; never strip the developer's own characters. This carve-out is for developer-authored text, not a license for the agent to add emoji. - -### Line Endings - -- [`.editorconfig`](./.editorconfig) defines the correct ending per file type (CRLF for `.md`, `.cs`, XML/`.csproj`/`.props`, `.yml`/`.yaml`, `.json`, `.slnx`, `.cmd`/`.bat`/`.ps1`; LF for `.sh` and for workflow YAML under `.github/workflows/*.{yml,yaml}` - Dependabot and Actions rewrite workflow files with LF, so they are pinned LF while all other YAML stays CRLF, and CI's `editorconfig-checker` enforces it), and [`.gitattributes`](./.gitattributes) pins `Dockerfile`/`*.Dockerfile` and `*.sh` to LF (`text eol=lf`) and otherwise stops git from normalizing. -- **Editing an existing file: preserve its current line endings** - do not reflow them as a side effect of a content change, even if the file is already non-compliant. After any programmatic edit, verify with `git diff --stat` (only changed lines) and `grep -c $'\r'` (CRLF count), since `file` does not report CRLF for JSON. Bring a non-compliant file to its `.editorconfig` ending only as a deliberate, isolated EOL-only change. - -### Quantitative Claims - -- Any quantitative claim in `README.md` (counts, sizes, version floors, supported products) must be verified against current code/config. If a doc number is derived from a code or matrix constant, mark the dependency in a source-code comment so the next editor knows to update both. - -## PR Review Etiquette - -> **Mandatory in every derived repo.** This entire "PR Review Etiquette" section is the provider-agnostic review-loop *contract* and must be carried **verbatim** into every repo derived from this template, alongside the [`.github/copilot-instructions.md`](./.github/copilot-instructions.md) "GitHub Copilot Review Runbook" that implements it. Without both in-repo, an agent working in the derived repo has no pointer to the reliable Copilot mechanics and falls back to ad-hoc (and known-broken) behavior. - -The repo runs a review loop on every PR: local agent iteration plus remote automated review (GitHub Copilot is the configured reviewer). Treat this as a contract regardless of which local agent authored the changes. - -### Merge Gate (read this first) - -**Do not merge - and do not enable auto-merge - unless ALL of these hold:** - -1. Required status checks are green (`mergeStateStatus: CLEAN`), **and** -2. A Copilot review is confirmed on the **current head SHA** (not an earlier push), **and** -3. **Every** Copilot finding on that head SHA is closed out - all review threads resolved, **and** any issue-level Copilot comments (which have no resolve action) triaged and replied to - so zero outstanding findings remain, **and** -4. The maintainer has given **explicit** permission to merge. - -`mergeStateStatus: CLEAN` reflects **only** required statuses - it never reflects open bot review comments, so `CLEAN` alone is **never** sufficient to merge. A green/`CLEAN` PR with an unresolved Copilot finding fails this gate; treat it as "not mergeable" no matter what the merge-state field says. The agent never merges on its own (consistent with "default to staging"; merging is maintainer-authorized). - -**Merging is not releasing.** A merge to a release branch does **not** by itself publish; publishing is a separate, explicitly configured step in the repo's release pipeline (e.g. a scheduled run, a manual dispatch, or an opted-in publish-on-merge trigger), not an automatic consequence of merging. Never describe a merge as cutting a release, and never trigger a publish without explicit maintainer instruction. - -### Expected Review Loop - -1. Push changes to the PR branch. -2. Re-request a review for the **current head SHA**. Auto-trigger is unreliable, so request it explicitly via the `requestReviews` GraphQL mutation (now reliable end-to-end - see the runbook); the UI is only a fallback. -3. Wait for review activity on that head. A completed review that raises **no findings** is a valid terminal outcome for that head - proceed; do not re-trigger it or treat the absence of comments as a missing review. -4. Triage findings. -5. Apply fixes or write a rationale for declines. -6. Reply to each thread and resolve what was addressed. -7. Re-run the loop after every fix push until no actionable findings remain. - -Drive the loop to green - review confirmed on the latest head SHA and every actionable finding closed - then stop and apply the **Merge Gate** above: all four preconditions must hold, and `mergeStateStatus: CLEAN` alone never satisfies it. - -For provider-specific mechanics (how to request review, query review state, post replies, resolve threads), see the **GitHub Copilot Review Runbook** in [.github/copilot-instructions.md](./.github/copilot-instructions.md). This file owns the contract; that file owns the mechanics. - -### Triaging Review Comments - -For each comment, classify before responding: - -- **Bug** - wrong behavior, missing test coverage, or a real divergence between code and docs. Fix it. Reply with the fixing commit SHA when done. -- **Style/convention** - the comment cites a rule from this file or a language-specific style guide. Two cases: - - The cited rule matches what the existing codebase already does -> fix the offending code. - - The cited rule contradicts what's in the tree, or industry norm -> **update the rule instead of the code**. The rule is wrong, not the code. Bouncing the same code across rounds is the symptom of a wrong rule. Heuristic: three rounds on the same style category means the rule needs adjusting and the user should authorize the rule change. -- **Architectural opinion** - the comment proposes a different design ("constrain this to disabled-by-default", "move it elsewhere", "add a runtime guardrail"). This is judgment, not a bug. Surface it to the user with a recommendation; don't apply unilaterally. - -### Responding and Resolution Expectations - -Reply inline with either the fixing commit SHA (for accepted issues) or a concise rationale (for declines). Resolve review threads when addressed or intentionally declined with rationale. Issue-level comments (those at `repos/.../issues//comments` rather than tied to a specific line) have no resolution action - acknowledge with a reply if needed and move on. - -After the final push on a PR, sweep older threads from earlier rounds whose code paths no longer exist; otherwise stale unresolved markers remain in the review UI. - -### Escalating to the User - -Bring the user in when: - -- **Genuine design trade-off** surfaces (fail-open vs fail-closed, narrow vs broad refactor scope, "should we add a guardrail or trust the docstring"). Triage, recommend, ask. -- **Repeated friction** across rounds without convergence - that's the rule-needs-updating signal. Stop, summarize the pattern, and let the user authorize the rule change. -- **Architectural redesign** is requested rather than a bug fix. Surface with a recommendation; never apply unilaterally. - -Anti-pattern: don't keep flipping the code on the same style point. Flip the rule once and stick to the rule. - -## Coding Conventions (Highlights) - -- Do not use `var`; use explicit types. -- File-scoped namespaces, Allman braces, and modern C# features are preferred. -- No `#region` usage. -- UTF-8 encoding without BOM. -- Respect line ending rules in `.editorconfig`. - -## Notes for Changes - -- When modifying Dockerfiles or build scripts, ensure generated outputs stay in sync with `CreateMatrix` behavior. -- Keep base image definitions and derived image Dockerfiles aligned, since derived images build on the base images. -- Keep `README.md` and release documentation aligned with build outputs and product variants. - -## Template adaptations - -This repo derives its CI and conventions from [ptr727/ProjectTemplate](https://github.com/ptr727/ProjectTemplate). Carried artifacts are taken by full-file replacement; the deliberate deviations below are documented so they are not mistaken for drift. - -- **Triggered-Docker publisher, one branch per run.** `publish-release.yml` is `workflow_dispatch` + weekly `schedule` (main only) + a path-scoped `push` to `main` on `Make/Matrix.json` (publishes a new codegen product pin at once). It builds exactly one branch - the trigger ref (`github.ref_name`) - so NBGV classifies natively with no cross-branch leg and no `IGNORE_GITHUB_REF`. The jobs are a single `get-version` -> `build-base` (main only) -> `build-docker` -> `github-release` (main only) -> `docker-readme` (main only) -> `cleanup-artifacts` chain. A develop dispatch refreshes the `:develop` images only (no GitHub release); the earlier two-leg `build-main` / `build-develop` combined run is removed. -- **Multi-image, shared-base build layer.** This is a multi-image Docker product: the shared `nx-base` / `nx-base-lsio` images are built once on the `main` run and reused by a develop dispatch (`build_base: false`, so it never overwrites the branch-agnostic `nx-base` tag), and `build-docker-task.yml` builds every product image (NxMeta, DWSpectrum, NxGo, WisenetWAVE, ...) from `Make/Matrix.json` (`max-parallel: 4`). The template's single-target branch matrix cannot express the shared-base fan-out, so the build layer stays repo-owned. -- **Single NBGV run threaded to the build task.** `build-docker-task.yml` has no nested `get-version`; the orchestrator's single `get-version` run threads `semver2` down as the image `LABEL_VERSION`, so one classification feeds every product leg (no second NBGV run can reclassify or collide a tag). -- **Docker-only GitHub release (no `release-asset-*` files).** The `github-release` job follows the template's generic release semantics (tag on the built commit, auto source zip + README + LICENSE, `target_commitish` pinned, skip-existing guard, main-only `Verify public release version` backstop), but this repo ships no binary/package release assets - the published artifacts are the Docker Hub images. So there is no `release-asset-*` download step and `fail_on_unmatched_files` is omitted (it has no files to guard). -- **Folded Docker Hub readme.** The standalone docker-readme task is removed; `publish-release.yml` carries `docker-readme-repos` + `docker-readme` jobs gated to `main` that derive the repository list inline from `Make/Matrix.json` (lowercased `ptr727/` plus the shared base repos) and matrix `peter-evans/dockerhub-description` over it. -- **Dropped date badge.** The `build-datebadge-task.yml` workflow and its publisher job are removed, along with the README "Last Build" badge that pointed at the BYOB gist. -- **Husky.Net pre-commit hooks.** This repo installs Husky.Net Git hooks (pre-commit formatting/codegen), inverting the template's no-hooks default. The hooks run the same checks CI enforces, surfaced earlier. `validate-task.yml` (the rename of `test-release-task.yml`) runs the same Husky lint + `dotnet test` in CI as the required-check's quality gate. -- **.vscode Benchmark -> Husky.Net Run task.** The carried `.vscode/tasks.json` swaps the template's Benchmark task for a Husky.Net Run task, matching this repo's hook tooling. -- **Build-layer leaves own build specifics but follow the shared action-pin + cache rules.** The build-layer leaves (`build-docker-task.yml`, `build-base-images-task.yml`) own their per-image Dockerfiles, build args, and target matrix, but their actions are **SHA-pinned** like the orchestration layer (Dependabot still bumps SHA pins, updating the SHA + version comment), and their Docker layer cache uses **registry-tag caches** (`docker.io/ptr727/:buildcache-`, plus the base image's own tag and inline cache) rather than `type=gha`, per the template's cache policy. -- **No `merge-upstream-version` merge-bot job.** This repo tracks the upstream NX version through codegen (`run-codegen-pull-request-task.yml` updating `Make/Version.json` + `Make/Matrix.json`), so the merge-bot keeps `merge-codegen` and omits the template's `merge-upstream-version` job (it uses the separate `check-upstream-version-task.yml` mechanism this repo does not ship). +# Instructions for AI Coding Agents + +This repository builds and publishes Docker images for Network Optix VMS products (Nx Witness, Nx Meta, Nx Go, DW Spectrum, Wisenet WAVE). It includes base images (nx-base, nx-base-lsio) and derived product images that use the base images, plus a .NET tooling project (`CreateMatrix`) that generates Dockerfiles and build matrices and scripts/templates for packaging. There is **no NuGet publish**: the .NET project is a build-time matrix generator only; the published artifacts are exclusively the Docker Hub images. + +This file is the canonical reference for cross-cutting AI-agent rules. The CI/CD workflow contract and conventions live in [`WORKFLOW.md`](./WORKFLOW.md); the repository configuration-as-code (branch rulesets, settings, required secrets) lives in [`repo-config/`](./repo-config/); C# code-style conventions live in [`CODESTYLE.md`](./CODESTYLE.md). Copilot review *mechanics* are owned by [`.github/copilot-instructions.md`](./.github/copilot-instructions.md) - this file delegates them there explicitly (see "PR Review Etiquette" below). + +**Where rules live.** A durable project, code, or style rule belongs in this file (or `WORKFLOW.md` / `CODESTYLE.md` as appropriate), so it is versioned and read by every session and every agent. An agent's own session memory or scratch state is private and lost on restart, so it is never the system of record for a rule: when you learn or are corrected on a rule, write it into the right doc in the same change. Memory may also note it, but the committed docs are the source of truth. + +For comprehensive coding and formatting standards, follow: + +- `CODESTYLE.md` +- `.editorconfig` +- `.husky/task-runner.json` +- `.vscode/tasks.json` + +## Solution Structure + +### Projects + +- `CreateMatrix/CreateMatrix.csproj` + - .NET 10 console app. + - Fetches product release metadata and generates build inputs such as Dockerfiles and matrix data used by CI. +- `CreateMatrixTests/CreateMatrixTests.csproj` + - xUnit v3 test project with AwesomeAssertions. + +### Key Directories + +- `Docker/` + - Generated and static Dockerfiles for base images and product variants. +- `Make/` + - Build orchestration scripts and test compose files. +- `Unraid/` + - Unraid container templates. +- `version.json`, `Make/Version.json`, `Make/Matrix.json` + - Version and matrix inputs consumed by the build pipeline. + +## Build and Validation Workflow + +- Primary developer entry points are the `CreateMatrix` CLI commands invoked directly or via the scripts in `Make/`: + - `version --versionpath=./Make/Version.json`. + - `matrix --versionpath=./Make/Version.json --matrixpath=./Make/Matrix.json --updateversion`. + - `make --versionpath=./Make/Version.json --makedirectory=./Make --dockerdirectory=./Docker --versionlabel=Beta`. +- Formatting and style checks are enforced by Husky.Net and VS Code tasks. +- Required tasks are documented in `CODESTYLE.md` and `.husky/task-runner.json`. +- C# code should be formatted with CSharpier, then verified with `dotnet format` (style). +- The `.NET Format` VS Code task in `.vscode/tasks.json` must be clean and warning-free at all times. + +### Workspace and linting + +- VS Code settings, extension recommendations, and spell-check words live in the workspace file `NxWitness.code-workspace`; add new editor settings or recommended extensions there rather than in `.vscode/`. (Build/debug tasks still live in `.vscode/tasks.json` and `.vscode/launch.json`.) Open the workspace file in VS Code (not the folder) so its settings and recommendations apply. +- Linting is editor-only (no CI lint job); the extensions recommended in `NxWitness.code-workspace` cover the project's structured files: C# (Roslyn + CSharpier), Markdown, Dockerfiles/Compose, GitHub Actions workflows, and spelling. Lint changed files and clear reported problems before pushing. +- For workflow files, the GitHub Actions extension covers schema/expression checks in-editor; run the `actionlint` CLI for deeper checks (including shellcheck on `run:` steps). + +## Image Architecture + +- Base images (`nx-base`, `nx-base-lsio`) are built and pushed, then reused as `FROM` images for derived product Dockerfiles. +- Derived product images should stay aligned with the base image changes and tags (for example, the Ubuntu distro tag). + +## CI Pipeline (GitHub Actions) + +The full CI/CD contract - triggers, jobs, the one-branch publish model, versioning, and the multi-image build layer - is specified in [`WORKFLOW.md`](./WORKFLOW.md), the canonical guide. The summary below is a pointer; do not duplicate those rules. + +- CI runs on **push to every branch** (`test-pull-request.yml`): it validates (`validate-task.yml`: Husky lint + `dotnet test`) on every push, and runs a fast smoke build (`build-docker-task.yml` with `smoke: true` - NxMeta + NxMeta-LSIO, amd64, no push) only when image files (`Docker/**`, `Make/Matrix.json`, `Make/Version.json`) change, via an inline `git diff` change-gate. One aggregator job, `Check pull request workflow status job`, is the ruleset-bound required check. +- Publishing is **triggered-Docker, one branch per run** (`publish-release.yml`): triggers are the weekly schedule (rebuilds `main` only), a path-scoped push to `main` on `Make/Matrix.json` (publishes a new codegen product pin at once), and manual dispatch (publishes the started-from branch). One run computes the version once (`get-version-task.yml`), builds the shared base once (main only; develop reuses it with `build_base: false`), builds the full product matrix from `Make/Matrix.json`, and on `main` cuts the GitHub release and pushes the Docker Hub overviews. +- Merges to `main`/`develop` do not build or publish images by themselves; only the matrix-pin push (main), a schedule, or a dispatch publishes. Auto-merged Dependabot and codegen PRs land commits the next publish picks up. Do not reintroduce a two-branch publish matrix, a nested `get-version` in the build task, the date-badge or standalone docker-readme workflows, or `dorny/paths-filter`. +- Lint workflow edits before pushing (see [Workspace and linting](#workspace-and-linting)); `validate-task.yml` runs the unit tests + Husky style checks in CI. + +## Versioning + +The `version` (major.minor) in [version.json](./version.json) is the NBGV version floor; NBGV appends the git height. **`develop` leads `main` by a minor:** after a `develop -> main` release lands and main's publish completes, bump the minor in `version.json` on `develop` in an isolated `bump-version-X.Y` PR (X.Y = the new minor), so develop's NBGV prerelease version stays numerically above main's last stable. A **maintenance** `develop -> main` promotion (dependency bumps, CI/doc fixes, template re-syncs) holds main's version - `git checkout main -- version.json` on the promotion branch - so `main` advances only its NBGV height, not its minor. (NBGV's version is the GitHub release tag on `main` and the `LABEL_VERSION` build arg baked into the images; the Docker image *tags* carry the Nx product version from `Make/Matrix.json` - see [CI Pipeline](#ci-pipeline-github-actions).) + +- A significant one-time overhaul of the build/release process (such as the branch-scoped CI/CD migration) is a deliberate maintainer-directed floor bump in the PR that introduces it, distinct from the routine cadence above; routine dependency, CI/workflow, and doc edits leave `version.json` untouched. +- **`dotnet/nbgv` is consumed via `@master`, never SHA-pinned.** Its tag stream lags `master` such that Dependabot tag-tracking would only propose downgrades to stale tags; this is the sole [`WORKFLOW.md`](./WORKFLOW.md) D9.1 action-pinning exception (rationale inline in `get-version-task.yml`). Do not SHA-pin it. + +## Git and Commit Rules + +- **Default to staging, not committing.** Stage changes with `git add` and leave `git commit` to the developer unless the developer has explicitly authorized the agent to commit for the current ask ("commit this", "open a PR", etc.). Authorization is scope-bound - it covers the commits needed for that specific task, not a blanket commit license for the rest of the session. +- **All commits must be cryptographically signed (SSH or GPG).** Branch protection enforces this on both branches; unsigned commits are rejected on push. Signing depends on environment configuration - `git config commit.gpgsign true`, a configured `user.signingkey`, and a working signing agent (loaded `ssh-agent` for SSH, or `gpg-agent` for GPG). If signing is not configured in the environment, **do not commit** - surface the missing config to the developer and stop at `git add`. Verify before any agent-authored commit (`git config --get commit.gpgsign && ssh-add -L` or the GPG equivalent). **Signing must be live before the *first* commit, not retrofitted.** Turning on `Require signed commits` against a branch that already has unsigned commits forces a rewrite of that entire history to re-sign it - changing every commit SHA and making whoever does the rewrite the committer and signer of every commit (a rebase preserves the `author` field but not the original signatures; you cannot sign another contributor's commits for them). During new-repo setup, never create commits until signing is verified. +- **Commit under the committing account's own GitHub `noreply` identity - never a private, personal, or invented address.** The `author` and `committer` on every agent-authored commit are the GitHub `noreply` address of the account whose key signs the commit (above) - GitHub issues these in a `username@users.noreply.github.com` or `ID+username@users.noreply.github.com` form, and for this single-maintainer fleet it is the owner's `ptr727@users.noreply.github.com`. Do not set `user.name`/`user.email` to a fabricated persona, bot name, or product name, and do not commit under whatever identity the environment happens to carry: verify `git config --get user.email` is that GitHub `noreply` address before committing, and fix it if not. A wrong identity is not cosmetic - a private email trips GitHub's email-privacy push protection (GH007), and an unrecognized or invented author pollutes history. Identity is separate from signing: a wrong author does not by itself fail the signature rule, but the ad-hoc identities that produce it are typically also unsigned, which the signing rule above then rejects on push. +- **Never force push.** Do not run `git push --force` or `git push --force-with-lease` under any circumstances. Force pushing rewrites shared history and can cause data loss. +- **Never run destructive git commands** (`git reset --hard`, `git checkout .`, `git restore .`, `git clean -f`) without explicit developer instruction. +- **Mirror to `develop` any change that lands on `main` outside the feature -> develop -> main flow.** A reconciliation-branch fix made to resolve a `develop -> main` promotion conflict, or a security PR that merges only to `main`, leaves `develop` behind on that content - and forward-only `develop` never back-merges to catch up (the same parallel-target principle as the bots). Before basing new work on `develop`, or diagnosing a defect from it, check `git diff origin/develop origin/main`: a non-empty content diff means develop is stale and the defect may already be fixed on `main`. +- **Put issue-closing keywords (`Closes #N`) in the `develop -> main` promotion PR, not the feature or develop PR.** GitHub auto-closes an issue only from the PR (or commit) that merges to the default branch (`main`); a `Closes #N` that merges only to `develop` does not fire on promotion and leaves the issue open. Tag the promotion PR's description, or close the issue manually once the fix reaches `main`. + +## Pull Request Title and Commit Message Conventions + +### Format + +- Imperative subject summarizing the change, <=72 characters, no trailing period. ("Add 24-hour PM2.5 average sensor", not "Added X" or "Adds X".) +- Optional body, blank-line separated, explaining *why* the change is being made when that's non-obvious. The diff shows *what*. + +### Rules + +- Don't write `update stuff`, `wip`, or other vague titles. (Dependabot's default `Bump X from Y to Z` titles are fine - keep them.) +- Don't add `Co-Authored-By:` lines unless the developer explicitly asks. +- Don't put release-bump magnitude in the title - no "minor", "patch", "release v0.2.0", etc. Nerdbank.GitVersioning computes the next release version from `version.json` + git history. Dependency versions in dependency-bump titles are fine and expected. +- Use US English spelling and match the existing heading style of the file you're editing: title case with lowercase short bind words (a, an, the, and, but, or, of, in, on, at, to, by, for, from); hyphenated compounds capitalize both parts unless the second is a short preposition (*Built-in*, *EPA-Corrected*, *24-Hour*). + +### Examples + +```text +Add structured logging extensions to library +Pin softprops/action-gh-release to commit SHA +Drop net8.0 multi-targeting from console project +Bump xunit.v3 from 3.2.2 to 3.3.0 +Clarify devcontainer setup steps in README +``` + +## Documentation Style Conventions + +### Markdown + +- Use reference-style links for any URL referenced more than once or appearing in lists; alphabetize the reference definitions block. +- Inline single-use relative links (e.g. `[CODESTYLE.md](./CODESTYLE.md)`) are fine. +- One logical paragraph per line; no hard-wrap line-length limit. For an intentional hard line break within a block - stacked badges, status, or license lines - end the line with a trailing backslash (`\`); this explicit form is preferred over trailing whitespace and is not treated as a paragraph split. +- Headings follow the title-case-with-short-bind-words rule from the PR-title section. +- **Write docs in the current state, not as a change from a prior one.** The reader has no memory of the previous behavior, so describe what *is*: "X does Y", never "X *now* does Y", "X *no longer* does Z", or "changed/switched/restored to Y". Before/after framing belongs in changelogs, commit messages, and PR descriptions - not in `README.md` or other living docs. + +### Comments + +Applies to code and workflow (`#`) comments alike. + +- Comment only when the code is non-obvious or important. Self-evident code needs no comment. +- Judge "obvious" in context, not line by line. A note that reads as redundant on its own line can be essential in the larger flow - a comment marking a workflow step's exit condition, for example, even though the line itself plainly does a `return` or `exit`. +- State the non-obvious *why*, not what the code already shows. No cross-project references (do not name other repos), no historic or design narrative, no rule citations - governance lives in this file, not echoed inline. +- **One line if it fits in ~120 columns.** Do not wrap a comment at 75-80 columns; a short two-line comment that would fit on one line looks sloppy - collapse it. Go multi-line only when the content genuinely exceeds ~120, filling each line rather than narrow-wrapping. For a multi-point comment, prefer short structured lines or `-` bullets over one prose paragraph. +- **Workflows: prefer one short summary description at the top of the file** over scattering rationale across steps; comment an individual step only when its purpose is non-obvious. +- **Do not accumulate comments.** When you change code or a comment, rewrite the whole comment fresh; never bolt a new comment onto an existing one or layer explanations across edits. Comment volume should stay flat or shrink over time, not grow. +- **Leave human-authored comments and emojis exactly as written** - do not reword, trim, reflow, or "clean" them, even if they seem to bend a rule. Revise only agent-authored comments, and match the surrounding voice when you do. + +### Character Set + +- **Write ASCII in all agent-authored text** - documentation, code, comments, commit messages, and PR descriptions. The agent does not introduce non-ASCII characters. Replace typographic Unicode with its ASCII equivalent on sight: + - em dash (U+2014) and en dash (U+2013) -> hyphen `-` (use a spaced ` - ` for an em-dash-style clause break) + - right arrow (U+2192) -> `->`; double arrow (U+21D2) -> `=>` + - less-than-or-equal (U+2264) -> `<=`; greater-than-or-equal (U+2265) -> `>=` + - curly quotes (U+2018/U+2019/U+201C/U+201D) -> straight `'` and `"`; ellipsis (U+2026) -> `...` +- **Allowed non-ASCII (two narrow exceptions):** + - **Scientific or technical symbols with no clean ASCII equivalent** - e.g. ohm, micro, degree, pi. Keep the symbol; do not approximate it away. + - **Unicode the developer deliberately typed** - emoji used for emphasis or as callout markers (for example the warning/info markers a maintainer placed in `README.md`). Preserve it; never strip the developer's own characters. This carve-out is for developer-authored text, not a license for the agent to add emoji. + +### Line Endings + +- **LF is the default, and `*.bat`/`*.cmd` are the one exception.** [`.editorconfig`](./.editorconfig) sets `end_of_line = lf` on `[*]` and declares only the CRLF exception Windows requires, so shell scripts, Dockerfiles, workflow YAML and every extensionless executable get LF without a path-specific pin. [`.gitattributes`](./.gitattributes) mirrors those two defaults as git's normalization fallback (`* text=auto eol=lf`, `text=auto` leaving binaries byte-preserved), so git enforces on checkout and `--renormalize` what the editor is told to write. CI's `editorconfig-checker` verifies the committed bytes. +- **Editing an existing file: do not reflow its endings as a side effect of a content change.** A tool that rewrites a file in text mode can silently flip endings and turn a one-line change into a whole-file diff. After any programmatic edit, verify with `git diff --stat` (only the lines you changed) and a byte check for `\r`, since `file` does not report CRLF for JSON. Bring a non-compliant file to LF as a deliberate, isolated EOL-only change. + +### Quantitative Claims + +- Any quantitative claim in `README.md` (counts, sizes, version floors, supported products) must be verified against current code/config. If a doc number is derived from a code or matrix constant, mark the dependency in a source-code comment so the next editor knows to update both. + +## PR Review Etiquette + +> **Mandatory in every derived repo.** This entire "PR Review Etiquette" section is the provider-agnostic review-loop *contract* and must be carried **verbatim** into every repo derived from this template, alongside the [`.github/copilot-instructions.md`](./.github/copilot-instructions.md) "GitHub Copilot Review Runbook" that implements it. Without both in-repo, an agent working in the derived repo has no pointer to the reliable Copilot mechanics and falls back to ad-hoc (and known-broken) behavior. + +The repo runs a review loop on every PR: local agent iteration plus remote automated review (GitHub Copilot is the configured reviewer). Treat this as a contract regardless of which local agent authored the changes. + +### Merge Gate (read this first) + +**Do not merge - and do not enable auto-merge - unless ALL of these hold:** + +1. Required status checks are green (`mergeStateStatus: CLEAN`), **and** +2. A Copilot review is confirmed on the **current head SHA** (not an earlier push), **and** +3. **Every** Copilot finding on that head SHA is closed out - all review threads resolved, **and** any issue-level Copilot comments (which have no resolve action) triaged and replied to - so zero outstanding findings remain, **and** +4. The maintainer has given **explicit** permission to merge. + +`mergeStateStatus: CLEAN` reflects **only** required statuses - it never reflects open bot review comments, so `CLEAN` alone is **never** sufficient to merge. A green/`CLEAN` PR with an unresolved Copilot finding fails this gate; treat it as "not mergeable" no matter what the merge-state field says. The agent never merges on its own (consistent with "default to staging"; merging is maintainer-authorized). + +**Merging is not releasing.** A merge to a release branch does **not** by itself publish; publishing is a separate, explicitly configured step in the repo's release pipeline (e.g. a scheduled run, a manual dispatch, or an opted-in publish-on-merge trigger), not an automatic consequence of merging. Never describe a merge as cutting a release, and never trigger a publish without explicit maintainer instruction. + +### Expected Review Loop + +1. Push changes to the PR branch. +2. Re-request a review for the **current head SHA**. Auto-trigger is unreliable, so request it explicitly via the `requestReviews` GraphQL mutation (now reliable end-to-end - see the runbook); the UI is only a fallback. +3. Wait for review activity on that head. A completed review that raises **no findings** is a valid terminal outcome for that head - proceed; do not re-trigger it or treat the absence of comments as a missing review. +4. Triage findings. +5. Apply fixes or write a rationale for declines. +6. Reply to each thread and resolve what was addressed. +7. Re-run the loop after every fix push until no actionable findings remain. + +Drive the loop to green - review confirmed on the latest head SHA and every actionable finding closed - then stop and apply the **Merge Gate** above: all four preconditions must hold, and `mergeStateStatus: CLEAN` alone never satisfies it. + +For provider-specific mechanics (how to request review, query review state, post replies, resolve threads), see the **GitHub Copilot Review Runbook** in [.github/copilot-instructions.md](./.github/copilot-instructions.md). This file owns the contract; that file owns the mechanics. + +### Triaging Review Comments + +For each comment, classify before responding: + +- **Bug** - wrong behavior, missing test coverage, or a real divergence between code and docs. Fix it. Reply with the fixing commit SHA when done. +- **Style/convention** - the comment cites a rule from this file or a language-specific style guide. Two cases: + - The cited rule matches what the existing codebase already does -> fix the offending code. + - The cited rule contradicts what's in the tree, or industry norm -> **update the rule instead of the code**. The rule is wrong, not the code. Bouncing the same code across rounds is the symptom of a wrong rule. Heuristic: three rounds on the same style category means the rule needs adjusting and the user should authorize the rule change. +- **Architectural opinion** - the comment proposes a different design ("constrain this to disabled-by-default", "move it elsewhere", "add a runtime guardrail"). This is judgment, not a bug. Surface it to the user with a recommendation; don't apply unilaterally. + +### Responding and Resolution Expectations + +Reply inline with either the fixing commit SHA (for accepted issues) or a concise rationale (for declines). Resolve review threads when addressed or intentionally declined with rationale. Issue-level comments (those at `repos/.../issues//comments` rather than tied to a specific line) have no resolution action - acknowledge with a reply if needed and move on. + +After the final push on a PR, sweep older threads from earlier rounds whose code paths no longer exist; otherwise stale unresolved markers remain in the review UI. + +### Escalating to the User + +Bring the user in when: + +- **Genuine design trade-off** surfaces (fail-open vs fail-closed, narrow vs broad refactor scope, "should we add a guardrail or trust the docstring"). Triage, recommend, ask. +- **Repeated friction** across rounds without convergence - that's the rule-needs-updating signal. Stop, summarize the pattern, and let the user authorize the rule change. +- **Architectural redesign** is requested rather than a bug fix. Surface with a recommendation; never apply unilaterally. + +Anti-pattern: don't keep flipping the code on the same style point. Flip the rule once and stick to the rule. + +## Coding Conventions (Highlights) + +- Do not use `var`; use explicit types. +- File-scoped namespaces, Allman braces, and modern C# features are preferred. +- No `#region` usage. +- UTF-8 encoding without BOM. +- Respect line ending rules in `.editorconfig`. + +## Notes for Changes + +- When modifying Dockerfiles or build scripts, ensure generated outputs stay in sync with `CreateMatrix` behavior. +- Keep base image definitions and derived image Dockerfiles aligned, since derived images build on the base images. +- Keep `README.md` and release documentation aligned with build outputs and product variants. + +## Template adaptations + +This repo derives its CI and conventions from [ptr727/ProjectTemplate](https://github.com/ptr727/ProjectTemplate). Carried artifacts are taken by full-file replacement; the deliberate deviations below are documented so they are not mistaken for drift. + +- **Triggered-Docker publisher, one branch per run.** `publish-release.yml` is `workflow_dispatch` + weekly `schedule` (main only) + a path-scoped `push` to `main` on `Make/Matrix.json` (publishes a new codegen product pin at once). It builds exactly one branch - the trigger ref (`github.ref_name`) - so NBGV classifies natively with no cross-branch leg and no `IGNORE_GITHUB_REF`. The jobs are a single `get-version` -> `build-base` (main only) -> `build-docker` -> `github-release` (main only) -> `docker-readme` (main only) -> `cleanup-artifacts` chain. A develop dispatch refreshes the `:develop` images only (no GitHub release); the earlier two-leg `build-main` / `build-develop` combined run is removed. +- **Multi-image, shared-base build layer.** This is a multi-image Docker product: the shared `nx-base` / `nx-base-lsio` images are built once on the `main` run and reused by a develop dispatch (`build_base: false`, so it never overwrites the branch-agnostic `nx-base` tag), and `build-docker-task.yml` builds every product image (NxMeta, DWSpectrum, NxGo, WisenetWAVE, ...) from `Make/Matrix.json` (`max-parallel: 4`). The template's single-target branch matrix cannot express the shared-base fan-out, so the build layer stays repo-owned. +- **Single NBGV run threaded to the build task.** `build-docker-task.yml` has no nested `get-version`; the orchestrator's single `get-version` run threads `semver2` down as the image `LABEL_VERSION`, so one classification feeds every product leg (no second NBGV run can reclassify or collide a tag). +- **Docker-only GitHub release (no `release-asset-*` files).** The `github-release` job follows the template's generic release semantics (tag on the built commit, auto source zip + README + LICENSE, `target_commitish` pinned, skip-existing guard, main-only `Verify public release version` backstop), but this repo ships no binary/package release assets - the published artifacts are the Docker Hub images. So there is no `release-asset-*` download step and `fail_on_unmatched_files` is omitted (it has no files to guard). +- **Folded Docker Hub readme.** The standalone docker-readme task is removed; `publish-release.yml` carries `docker-readme-repos` + `docker-readme` jobs gated to `main` that derive the repository list inline from `Make/Matrix.json` (lowercased `ptr727/` plus the shared base repos) and matrix `peter-evans/dockerhub-description` over it. +- **Dropped date badge.** The `build-datebadge-task.yml` workflow and its publisher job are removed, along with the README "Last Build" badge that pointed at the BYOB gist. +- **Husky.Net pre-commit hooks.** This repo installs Husky.Net Git hooks (pre-commit formatting/codegen), inverting the template's no-hooks default. The hooks run the same checks CI enforces, surfaced earlier. `validate-task.yml` (the rename of `test-release-task.yml`) runs the same Husky lint + `dotnet test` in CI as the required-check's quality gate. +- **.vscode Benchmark -> Husky.Net Run task.** The carried `.vscode/tasks.json` swaps the template's Benchmark task for a Husky.Net Run task, matching this repo's hook tooling. +- **Build-layer leaves own build specifics but follow the shared action-pin + cache rules.** The build-layer leaves (`build-docker-task.yml`, `build-base-images-task.yml`) own their per-image Dockerfiles, build args, and target matrix, but their actions are **SHA-pinned** like the orchestration layer (Dependabot still bumps SHA pins, updating the SHA + version comment), and their Docker layer cache uses **registry-tag caches** (`docker.io/ptr727/:buildcache-`, plus the base image's own tag and inline cache) rather than `type=gha`, per the template's cache policy. +- **No `merge-upstream-version` merge-bot job.** This repo tracks the upstream NX version through codegen (`run-codegen-pull-request-task.yml` updating `Make/Version.json` + `Make/Matrix.json`), so the merge-bot keeps `merge-codegen` and omits the template's `merge-upstream-version` job (it uses the separate `check-upstream-version-task.yml` mechanism this repo does not ship). diff --git a/CODESTYLE.md b/CODESTYLE.md index ebb29ffb..0871bca1 100644 --- a/CODESTYLE.md +++ b/CODESTYLE.md @@ -1,464 +1,464 @@ -# Code Style and Formatting Rules - -This is the single code-style guide for the repo. The **General** section applies to every language. Each **language section** (.NET, Python) is self-contained: a repo reads only the section(s) for the languages it ships and ignores the rest. The whole file is carried, not trimmed - an unused-language section costs nothing and keeps re-sync a clean overwrite, the same carry-whole model as [`.editorconfig`](./.editorconfig), whose inert `[*.cs]` block a non-.NET repo keeps. - -Cross-cutting *process* rules (PR titles, branching, US English, markdown style, comments philosophy, workflow YAML, PR review etiquette) live in [AGENTS.md](./AGENTS.md) and are not repeated here. - -## General - -These rules apply to every language in the repo. - -### Tooling Names and Casing - -Use each tool's official casing in task labels, docs, and prose - `.NET` (not `.Net`), `CSharpier`, `ruff`, `pyright`, `uv`. Don't invent personal variants. - -### Clean-Compile Verification - -Each language defines a **clean-compile** verification - the combination of build, formatter, linter, and code-analysis tools that must report clean before a commit. It is exposed as one or more **named** VS Code tasks (or, where a language ships no tasks, documented commands), and those definitions are **carried verbatim** across derived repos. The concrete names live in each language section below. - -- **Run it after every code change.** The relevant language's clean-compile must pass before you commit; CI runs the same checks as a backstop. -- **The named task definition is the canonical spec** - its exact command sequence, arguments, and strictness. You may run it through the VS Code task **or** by invoking the equivalent native commands directly; either is fine **only if the sequence, arguments, and strictness match exactly**. No shortcuts and no more-lenient options (for example, never drop `--verify-no-changes` or loosen a `--severity`). -- **A local commit/pre-commit gate is the repo's choice.** No single hook runner fits every language (a `dotnet`-tool runner like Husky.Net suits .NET but not Python), so none is mandated - but that is **not** a recommendation against commit gates. CI is the authoritative backstop regardless; a local gate is an additive convenience a repo may wire and keep - Husky.Net (and `dotnet husky run` as a style step) for .NET, `pre-commit` for Python. Keeping a working gate is not drift. - -### Analyzer Diagnostics and Suppressions - -- **A new port is not a license to silence diagnostics.** Brownfield / just-ported status never justifies relaxing analyzer or linter severities or muting newly surfaced warnings - fix them. (The only brownfield allowance in this template is the one-time git-signing / line-ending migration described in [AGENTS.md](./AGENTS.md) and [README.md](./README.md), which has nothing to do with code analysis.) -- **Suppress only genuine false-positives or deliberate, documented exceptions**, always at the **narrowest scope that fits**, in this order of preference: - 1. An **in-code annotation on the specific symbol**, with a justification - the language's attribute/comment form, never a blanket pragma spanning a region. - 2. The **owning project's local config** when the exception is project-wide for one project (e.g. a test project's own `.editorconfig` / `pyproject.toml`). - 3. The **root / shared config** only when the suppression is genuinely applicable to **every** project in the repo. -- **Never blanket-relax a batch of rules project-wide** to get a port to build. The per-language mechanics (which attribute, which config key) are in each language section. - -### Markdown and Spelling - -These apply repo-wide, in every directory: - -1. **Markdown linting**: All `.md` files must be lint-clean (error and warning free) via the VS Code `markdownlint` extension. [`.markdownlint-cli2.jsonc`](./.markdownlint-cli2.jsonc) at the repo root is the single source of truth - the davidanson `markdownlint` extension and a command-line `markdownlint-cli2` run both read it, so the IDE and CLI stay in lock-step. Rules it deliberately disables (e.g. `MD013` line-length, `MD033` inline HTML) are **intentional** - do not "fix" them. Fix violations at the source rather than disabling rules. -2. **Spelling**: All spelling must be clean via the CSpell VS Code integration; words must be correctly spelled in **US English** (the repo-wide convention - see [AGENTS.md](./AGENTS.md)). Project-specific terms go in the workspace CSpell config. -3. **Spelling CI scope**: The enforced CI spell-check gate covers **`README.md` and `HISTORY.md` only** - these are the files every repo visitor sees, so they must be clean. It is deliberately **not** all `**/*.md`: repos carry many markdown files full of technical terms, and gating every one of them would mean endlessly padding `cspell.json` just to keep CI green. Broad, live spell-checking across any file (source, markdown, text) is the **cspell editor extension's** job, so typos still surface to whoever is editing. A repo owner **may** widen their own CI file list, but the template ships README + HISTORY as the default; keep every surface that runs cspell - the CI workflow and any local VS Code task or one-liner the repo has - on the same file list. The list is explicit (not a glob), so a repo that ships no `HISTORY.md` (e.g. one with no changelog) must drop it from all three surfaces and gate on `README.md` alone - cspell errors on a listed file that does not exist. Markdown *linting* (item 1) stays repo-wide `**/*.md` - it does not choke on technical terms. - -## .NET - -*This section applies only to the .NET side. A repo with no .NET projects still carries it (the file is carried whole) and ignores it.* - -This is the style guide for any **.NET projects** in this repo. - -### Build Requirements - -#### Zero Warnings Policy - -**CRITICAL**: All builds must complete without warnings. The project enforces this through: - -1. **The `.NET Format` clean-compile task** (see [Clean-Compile Verification](#clean-compile-verification)) - - The .NET clean-compile is the **`.NET Format`** VS Code task, which chains `CSharpier Format` -> `.NET Build` -> `dotnet format style --verify-no-changes`. These three task definitions are carried verbatim in [`.vscode/tasks.json`](./.vscode/tasks.json). - - After any code change it must pass before commit. Run the `.NET Format` task. To run it natively instead, reproduce that task chain from [`.vscode/tasks.json`](./.vscode/tasks.json) exactly - `CSharpier Format`, then `.NET Build`, then the `dotnet format style --verify-no-changes --severity=info ...` verify - without dropping or loosening any argument (tasks.json is the canonical command spec). Bare `dotnet format` alone, skipping CSharpier or the build, is not sufficient. - -2. **Analyzer configuration** - - `true` with `latest-all` and `All` (full analyzer set enabled) - - `true` - any diagnostic surfaced as a warning fails the build, so it must be fixed or deliberately suppressed, not left to accumulate (see [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions)) - -3. **CI lint backstop** - - CI runs the clean-compile checks on every PR as the authoritative backstop - - Git hooks are optional; a repo may wire a local runner (Husky.Net) for pre-commit enforcement, but CI is the gate that matters - -#### Build Tasks - -Available VS Code tasks (run them from VS Code's task runner - **Terminal -> Run Task** - or an agent's task-running tool). The three clean-compile tasks below are carried verbatim; a repo adds its own convenience tasks (tool updates, dependency upgrades, benchmarks) on top: - -- `.NET Build`: Build with diagnostic verbosity *(clean-compile)* -- `CSharpier Format`: Auto-format code with CSharpier *(clean-compile)* -- `.NET Format`: Run CSharpier and build, then verify formatting and style with `--verify-no-changes` *(clean-compile; the task to run after edits)* - -### Tooling and Editor - -#### Code Formatting and Tooling - -1. **CSharpier**: Primary code formatter - - Invoked by the `CSharpier Format` task / `dotnet csharpier format --log-level=debug .` -2. **dotnet format**: Style verification - - Verify no changes: `dotnet format style --verify-no-changes --severity=info --verbosity=detailed` -3. **Other tools** - - `dotnet-outdated-tool`: Dependency update checks - - Nerdbank.GitVersioning: Version management - -CI is the authoritative lint backstop. Local pre-commit hooks are optional - wire Husky.Net (or another runner) if you want local enforcement. - -#### Editor Baseline - -1. **Required VS Code extensions**: CSharpier, markdownlint, CSpell -2. **VS Code settings**: Use the workspace settings without overrides - -### Coding Standards and Conventions - -Note: Code snippets are illustrative examples only. Replace namespaces/types to match your project. - -#### C# Language Features - -1. **File-scoped namespaces** - - ```csharp - namespace Example.Project.Library; - ``` - -2. **Nullable reference types**: Enabled (`enable`) - - Use nullable annotations appropriately - - Use `required` for mandatory properties - -3. **Modern C# features**: Prefer modern language constructs - - Primary constructors when appropriate - - Top-level statements for console apps - - Pattern matching over traditional checks - - Collection expressions when types loosely match - - Extension methods - the classic `this`-parameter form, or an `extension() { ... }` block on C# 14+ - - Implicit object creation when type is apparent - - Range and index operators - -4. **Expression-bodied members**: Use for applicable members - - Methods, properties, accessors, operators, lambdas, local functions - -5. **`var` keyword**: Do NOT use `var` (always use explicit types) - - ```csharp - // Correct - int count = 42; - string name = "test"; - - // Incorrect - var count = 42; - var name = "test"; - ``` - -#### Naming Conventions - -1. **Private fields**: underscore prefix with camelCase - - ```csharp - private readonly HttpClient _httpClient; - private int _counter; - ``` - -2. **Static fields**: `s_` prefix with camelCase - - ```csharp - private static int s_instanceCount; - ``` - -3. **Constants**: PascalCase - - ```csharp - private const int MaxRetries = 3; - ``` - -#### Code Structure - -1. **Global usings**: Use `GlobalUsings.cs` for common namespaces - - ```csharp - global using System; - global using System.Net.Http; - global using System.Threading.Tasks; - global using Serilog; - ``` - -2. **Usings placement**: Outside namespace, sorted with `System` directives first - - ```csharp - using System.CommandLine; - using System.Runtime.CompilerServices; - using Example.Project.Library; - - namespace Example.Project.Console; - ``` - -3. **Braces**: Allman style - - ```csharp - public void Method() - { - if (condition) - { - // code - } - } - ``` - -4. **Indentation** - - C# files: 4 spaces - - XML/csproj files: 2 spaces - - YAML files: 2 spaces - - JSON files: 4 spaces - -5. **Line endings** - - C#, XML, YAML, JSON, Windows scripts: CRLF - - Linux scripts (`.sh`): LF - -6. **`#region`**: Do not use regions. Prefer logical file/folder/namespace organization. -7. **Member ordering (StyleCop SA1201)**: const -> static readonly -> static fields -> instance readonly fields -> instance fields -> constructors -> public (events -> properties -> indexers -> methods -> operators) -> non-public in same order -> nested types - -#### Comments and Documentation - -1. **XML documentation** - - `true` - - Missing XML comments for public APIs are suppressed (`.editorconfig`) - - Must document all public surfaces. - - Single-line summaries, additional details in remarks, document input parameters, return values, exceptions, and add crefs - - ```csharp - /// - /// Example of a single line summary. - /// - /// - /// Additional important details about usage. - /// Multiple lines if needed. - /// - /// - /// The quote category to request - /// - /// - /// A that can be used to cancel the request. - /// - /// - /// A containing the quote text. - /// - /// - /// Thrown when is not a supported value. - /// - public async Task GetQuoteOfTheDayAsync(string category, CancellationToken cancellationToken) {} - ``` - -#### Analyzer Suppressions (.NET) - -Follow the scope hierarchy in [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions). .NET mechanics, narrowest first: - -- **Never use `#pragma warning disable`** to silence an analyzer. -- **Symbol-scoped**: a `[System.Diagnostics.CodeAnalysis.SuppressMessage(...)]` attribute with a `Justification`, on the specific member or type: - - ```csharp - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Design", - "CA1034:Nested types should not be visible", - Justification = "https://github.com/dotnet/sdk/issues/51681" - )] - ``` - -- **Project-scoped** (e.g. a test project): a `dotnet_diagnostic..severity` entry in *that project's own* `.editorconfig`, with a comment explaining why. -- **Repo-wide**: a `dotnet_diagnostic..severity` entry in the root `.editorconfig`, only when the rule is genuinely not applicable to any project. Relaxing a batch of `CA*` rules (or `dotnet_analyzer_diagnostic.severity`) to push a brownfield port through the build is exactly what this forbids. - -#### Error Handling and Logging - -1. **Serilog logging**: Use structured logging - - ```csharp - logger.Error(exception, "{Function}", function); - ``` - -2. **Library log configuration**: Libraries must expose logging configuration - - Provide options or settings to supply an `ILoggerFactory` and/or `ILogger` - - Offer a global fallback logger for static usage when needed - -3. **CallerMemberName**: Use for automatic function name tracking - - ```csharp - public bool LogAndPropagate( - Exception exception, - [CallerMemberName] string function = "unknown" - ) - ``` - -4. **Logger extensions**: Use `Extensions.cs` for logger and other extension methods - - ```csharp - extension(ILogger logger) - { - public bool LogAndPropagate(Exception exception, ...) { } - } - ``` - -5. **Exceptions**: Do not swallow exceptions; log and rethrow or translate to a domain-specific exception - -#### Code Patterns - -1. **Guard clauses**: Prefer early returns for validation and error handling -2. **Async all the way**: Avoid blocking calls (`.Result`, `.Wait()`); use `async`/`await` -3. **Cancellation tokens**: Accept `CancellationToken` as the last parameter and pass it through -4. **ConfigureAwait**: In library code, use `ConfigureAwait(false)` unless context is required - - Do not call `ConfigureAwait(false)` in xUnit tests (see xUnit1030) -5. **Disposables**: Use `await using` for async disposables; prefer `using` declarations -6. **LINQ vs loops**: Use LINQ for clarity, loops for hot paths or allocations -7. **HTTP**: Reuse `HttpClient` via factory; avoid per-request instantiation -8. **Collections**: Prefer `IReadOnlyList`/`IReadOnlyCollection` for public APIs -9. **Immutability**: Prefer immutable records; use init-only setters when records are not suitable; prefer immutable or frozen collections for read-only data -10. **Exceptions as control flow**: Avoid using exceptions for expected flow -11. **Sealing classes**: Seal classes that are not designed for inheritance -12. **Read-only data**: Use immutable or frozen collections for read-only data sets -13. **Lazy initialization**: Use `Lazy` for static, thread-safe instantiation (e.g., logger factory, HTTP factory) - -#### Testing Conventions - -1. **Framework**: xUnit with AwesomeAssertions - - ```csharp - [Fact] - public void MethodName_Scenario_ExpectedBehavior() - { - // Arrange - int expected = 42; - - // Act - int actual = GetValue(); - - // Assert - actual.Should().Be(expected); - } - ``` - -2. **Organization**: Arrange-Act-Assert pattern -3. **Naming**: Descriptive names with underscores -4. **Theory tests**: Use `[Theory]` with `[InlineData]` - -### Project Configuration - -1. **Target framework**: .NET 10.0 (`net10.0`) - -2. **AOT compatibility** - - `true` - - `true` - -3. **Assembly information** - - Use semantic versioning - - Include SourceLink: `true` - - Embed untracked sources: `true` - -4. **Internal visibility**: Use `InternalsVisibleTo` for test and benchmark access (adapt the project names to your repo's test/benchmark projects) - - ```xml - - - - - ``` - -### Best Practices - -1. **Code reviews**: All changes go through pull requests - -## Python - -*This section applies only to the Python side. A repo with no Python projects still carries it (the file is carried whole) and ignores it.* - -This is the style guide for any **Python project(s)** in this repo. - -### Toolchain - -| Tool | Role | Config | -|---|---|---| -| [uv](https://docs.astral.sh/uv/) | env, deps, build, publish | `pyproject.toml` `[dependency-groups]`, `uv.lock` | -| [hatchling](https://hatch.pypa.io/latest/) | build backend | `pyproject.toml` `[build-system]` | -| [ruff](https://docs.astral.sh/ruff/) | lint + format + import sort | `pyproject.toml` `[tool.ruff]` | -| [pyright](https://microsoft.github.io/pyright/) | type checker | `pyproject.toml` `[tool.pyright]` | -| [pytest](https://docs.pytest.org/) | test runner | `pyproject.toml` `[tool.pytest.ini_options]` | - -`pyright` is consumed in two places: as a dev dependency (`uv run pyright` for CI/scripted runs) and via VS Code's **Pylance** extension (which embeds pyright). The standalone `ms-pyright.pyright` extension is in `unwantedRecommendations` because Pylance covers it. `mypy` is **not used** here - don't introduce it. - -### Local Development Loop - -From inside the Python project directory: - -```sh -uv sync # creates .venv, installs deps + dev group -uv run ruff format # auto-format -uv run ruff check --fix # auto-fix lint -uv run ruff check # verify lint clean -uv run ruff format --check # verify format clean -uv run pyright # verify types -uv run pytest # run tests -uv build # produce wheel + sdist in ./dist -``` - -The Python clean-compile (see [Clean-Compile Verification](#clean-compile-verification)) is `uv run ruff format` + `uv run ruff check` + `uv run pyright`; run it (plus `uv run pytest`) before committing. These are documented commands, not VS Code tasks. CI runs the same clean-compile commands as the authoritative backstop. Git hooks are opt-in; wire `pre-commit` for `ruff` and `pyright` yourself if you want local enforcement. - -### Layout - -`src` layout - keeps the package out of the repo root and prevents accidental imports of unbuilt code: - -```text -/ - pyproject.toml - README.md - uv.lock # committed for reproducible CI - src/ - / - __init__.py - _version.py - .py - tests/ - __init__.py - test_.py -``` - -### Code Style - -#### Formatting and Linting - -- **`ruff format` is authoritative.** Don't argue with the formatter; if it reformats your code, that's the final form. Configure (line length, target version) in `pyproject.toml` `[tool.ruff]`, not via inline `# fmt:` directives. -- **Run `ruff check --fix` before committing.** Most ruff lint rules have safe autofixes; let the tool handle them. The configured rule families are listed under `[tool.ruff.lint]` `select`. Add new rule families project-wide rather than scattering inline `# noqa` markers. -- **`# noqa` is a last resort.** When you must use one, scope it narrowly (`# noqa: E501`, not bare `# noqa`) and add a short comment on the same line explaining why. False-positive patterns that recur across the codebase belong in `[tool.ruff.lint]` `ignore` or per-file `[tool.ruff.lint.per-file-ignores]`, with a comment. Porting an existing codebase is not a license to add `ignore` / `per-file-ignores` blocks to mute newly surfaced lint - fix it (see [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions)). - -#### Comments - -- **Inline `#` comments**: keep tight and local. One line is preferred, but multi-line is fine when you need to document a non-obvious implementation constraint, a local trade-off, or coupling that future edits could easily break. Keep that rationale next to the affected block so the reviewer/maintainer sees it at edit-time. -- **Don't explain *what* the code does** - well-named identifiers handle that. Don't reference the current task ("added for X", "used by Y"); that belongs in the PR description. - -#### Docstrings - -- Follow [PEP 257](https://peps.python.org/pep-0257/). Focus docstrings primarily on the **behavior contract** (what callers and tests can rely on), public semantics, and edge-case expectations. Implementation-local rationale belongs in inline `#` comments, not docstrings. -- A short one-liner is fine for trivial functions and tests with self-documenting names. -- For non-trivial behavior - non-obvious test scenarios, contracts a test pins, edge cases callers must know about, design trade-offs that are load-bearing for future maintainers - write a one-line summary, blank line, then a details paragraph. Multi-paragraph docstrings are fine when the contract earns it. -- Design notes belong **in the code** (docstrings or inline comments). They do NOT belong in [`HISTORY.md`](./HISTORY.md) - that file is end-user release notes, not a design log. - -#### Type Hints - -- **All public APIs are typed.** Pyright runs on `src/` in strict mode (`[tool.pyright]` `strict = ["src"]`); tests run in standard mode. -- **Use modern syntax**: `list[int]` not `List[int]`, `dict[str, X]` not `Dict[str, X]`, `X | None` not `Optional[X]`, `from __future__ import annotations` only when needed for forward references. -- **Don't add `# type: ignore` to silence pyright errors without a comment** explaining the constraint. If a recurring false positive needs suppression, configure it project-wide in `[tool.pyright]`. A new port doesn't change this - fix freshly surfaced type errors rather than muting them (see [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions)). - -#### Naming - -- `snake_case` for functions, methods, variables, modules, package directories. -- `PascalCase` for classes, type aliases, type vars, enum members. -- `UPPER_SNAKE_CASE` for module-level constants. -- Single leading underscore for module-private; double leading underscore for name-mangled (rare - usually means rethink the design). - -#### Imports - -- **Let ruff sort imports.** `[tool.ruff.lint]` `select` includes the `I` rule family (isort-equivalent). Don't hand-sort. -- Standard library first, then third-party, then first-party (the project itself), each block separated by a blank line - ruff enforces this automatically. -- Avoid wildcard imports (`from x import *`) outside `__init__.py` re-exports. - -#### Patterns to Avoid - -- **Don't add backward-compat shims, `# removed` markers, or rename-to-`_` for unused vars** - just delete. Git history is the audit trail. -- **Don't add error handling for impossible cases.** Trust internal code; only validate at boundaries (user input, parsed config, external APIs). -- **Don't use exceptions for expected control flow.** Exceptions are for *unexpected* states. -- **Don't suppress errors silently** (`except Exception: pass`). Either handle the specific exception and document why it's safe, or let it propagate. - -### Tests - -- `pytest` with the configuration in `[tool.pytest.ini_options]`. Default invocation: `uv run pytest`. -- One test file per module under test, named `test_.py`. -- Test functions named `test__` - descriptive, not numbered. -- Use fixtures (defined in `conftest.py` for shared ones, or per-test for narrowly-scoped) instead of setup/teardown methods. -- **Avoid mocking when fakes work.** Hand-rolled fakes that implement the protocol you depend on are usually clearer and break less than `unittest.mock` magic. -- **Test edge cases that the docstring promises**, not implementation details. If the test breaks when you refactor *without changing behavior*, the test is asserting on an implementation detail. - -### Versioning - -`_version.py` ships with `__version__ = "0.0.0"` as a placeholder. Until you wire `_version.py` to something that increments (the usual options are `hatch-vcs`, a version.json bridge, or manual bumps), no new PyPI versions will land - publishing with `skip-existing: true` keeps a stuck placeholder version from failing the run. - -### Linter Cleanliness - -Before pushing or opening a PR: - -- VS Code's **Problems** pane should be quiet for the files you touched. The relevant linters are ruff (via the `charliermarsh.ruff` extension) and pyright (via the `ms-python.python` extension's bundled Pylance). -- The CI gate is `uv run ruff check && uv run ruff format --check && uv run pyright && uv run pytest` - same as the local commands above, run from the Python project directory. -- Markdown in this directory follows the repo-wide [Markdown and Spelling](#markdown-and-spelling) rules. +# Code Style and Formatting Rules + +This is the single code-style guide for the repo. The **General** section applies to every language. Each **language section** (.NET, Python) is self-contained: a repo reads only the section(s) for the languages it ships and ignores the rest. The whole file is carried, not trimmed - an unused-language section costs nothing and keeps re-sync a clean overwrite, the same carry-whole model as [`.editorconfig`](./.editorconfig), whose inert `[*.cs]` block a non-.NET repo keeps. + +Cross-cutting *process* rules (PR titles, branching, US English, markdown style, comments philosophy, workflow YAML, PR review etiquette) live in [AGENTS.md](./AGENTS.md) and are not repeated here. + +## General + +These rules apply to every language in the repo. + +### Tooling Names and Casing + +Use each tool's official casing in task labels, docs, and prose - `.NET` (not `.Net`), `CSharpier`, `ruff`, `pyright`, `uv`. Don't invent personal variants. + +### Clean-Compile Verification + +Each language defines a **clean-compile** verification - the combination of build, formatter, linter, and code-analysis tools that must report clean before a commit. It is exposed as one or more **named** VS Code tasks (or, where a language ships no tasks, documented commands), and those definitions are **carried verbatim** across derived repos. The concrete names live in each language section below. + +- **Run it after every code change.** The relevant language's clean-compile must pass before you commit; CI runs the same checks as a backstop. +- **The named task definition is the canonical spec** - its exact command sequence, arguments, and strictness. You may run it through the VS Code task **or** by invoking the equivalent native commands directly; either is fine **only if the sequence, arguments, and strictness match exactly**. No shortcuts and no more-lenient options (for example, never drop `--verify-no-changes` or loosen a `--severity`). +- **A local commit/pre-commit gate is the repo's choice.** No single hook runner fits every language (a `dotnet`-tool runner like Husky.Net suits .NET but not Python), so none is mandated - but that is **not** a recommendation against commit gates. CI is the authoritative backstop regardless; a local gate is an additive convenience a repo may wire and keep - Husky.Net (and `dotnet husky run` as a style step) for .NET, `pre-commit` for Python. Keeping a working gate is not drift. + +### Analyzer Diagnostics and Suppressions + +- **A new port is not a license to silence diagnostics.** Brownfield / just-ported status never justifies relaxing analyzer or linter severities or muting newly surfaced warnings - fix them. (The only brownfield allowance in this template is the one-time git-signing / line-ending migration described in [AGENTS.md](./AGENTS.md) and [README.md](./README.md), which has nothing to do with code analysis.) +- **Suppress only genuine false-positives or deliberate, documented exceptions**, always at the **narrowest scope that fits**, in this order of preference: + 1. An **in-code annotation on the specific symbol**, with a justification - the language's attribute/comment form, never a blanket pragma spanning a region. + 2. The **owning project's local config** when the exception is project-wide for one project (e.g. a test project's own `.editorconfig` / `pyproject.toml`). + 3. The **root / shared config** only when the suppression is genuinely applicable to **every** project in the repo. +- **Never blanket-relax a batch of rules project-wide** to get a port to build. The per-language mechanics (which attribute, which config key) are in each language section. + +### Markdown and Spelling + +These apply repo-wide, in every directory: + +1. **Markdown linting**: All `.md` files must be lint-clean (error and warning free) via the VS Code `markdownlint` extension. [`.markdownlint-cli2.jsonc`](./.markdownlint-cli2.jsonc) at the repo root is the single source of truth - the davidanson `markdownlint` extension and a command-line `markdownlint-cli2` run both read it, so the IDE and CLI stay in lock-step. Rules it deliberately disables (e.g. `MD013` line-length, `MD033` inline HTML) are **intentional** - do not "fix" them. Fix violations at the source rather than disabling rules. +2. **Spelling**: All spelling must be clean via the CSpell VS Code integration; words must be correctly spelled in **US English** (the repo-wide convention - see [AGENTS.md](./AGENTS.md)). Project-specific terms go in the workspace CSpell config. +3. **Spelling CI scope**: The enforced CI spell-check gate covers **`README.md` and `HISTORY.md` only** - these are the files every repo visitor sees, so they must be clean. It is deliberately **not** all `**/*.md`: repos carry many markdown files full of technical terms, and gating every one of them would mean endlessly padding `cspell.json` just to keep CI green. Broad, live spell-checking across any file (source, markdown, text) is the **cspell editor extension's** job, so typos still surface to whoever is editing. A repo owner **may** widen their own CI file list, but the template ships README + HISTORY as the default; keep every surface that runs cspell - the CI workflow and any local VS Code task or one-liner the repo has - on the same file list. The list is explicit (not a glob), so a repo that ships no `HISTORY.md` (e.g. one with no changelog) must drop it from all three surfaces and gate on `README.md` alone - cspell errors on a listed file that does not exist. Markdown *linting* (item 1) stays repo-wide `**/*.md` - it does not choke on technical terms. + +## .NET + +*This section applies only to the .NET side. A repo with no .NET projects still carries it (the file is carried whole) and ignores it.* + +This is the style guide for any **.NET projects** in this repo. + +### Build Requirements + +#### Zero Warnings Policy + +**CRITICAL**: All builds must complete without warnings. The project enforces this through: + +1. **The `.NET Format` clean-compile task** (see [Clean-Compile Verification](#clean-compile-verification)) + - The .NET clean-compile is the **`.NET Format`** VS Code task, which chains `CSharpier Format` -> `.NET Build` -> `dotnet format style --verify-no-changes`. These three task definitions are carried verbatim in [`.vscode/tasks.json`](./.vscode/tasks.json). + - After any code change it must pass before commit. Run the `.NET Format` task. To run it natively instead, reproduce that task chain from [`.vscode/tasks.json`](./.vscode/tasks.json) exactly - `CSharpier Format`, then `.NET Build`, then the `dotnet format style --verify-no-changes --severity=info ...` verify - without dropping or loosening any argument (tasks.json is the canonical command spec). Bare `dotnet format` alone, skipping CSharpier or the build, is not sufficient. + +2. **Analyzer configuration** + - `true` with `latest-all` and `All` (full analyzer set enabled) + - `true` - any diagnostic surfaced as a warning fails the build, so it must be fixed or deliberately suppressed, not left to accumulate (see [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions)) + +3. **CI lint backstop** + - CI runs the clean-compile checks on every PR as the authoritative backstop + - Git hooks are optional; a repo may wire a local runner (Husky.Net) for pre-commit enforcement, but CI is the gate that matters + +#### Build Tasks + +Available VS Code tasks (run them from VS Code's task runner - **Terminal -> Run Task** - or an agent's task-running tool). The three clean-compile tasks below are carried verbatim; a repo adds its own convenience tasks (tool updates, dependency upgrades, benchmarks) on top: + +- `.NET Build`: Build with diagnostic verbosity *(clean-compile)* +- `CSharpier Format`: Auto-format code with CSharpier *(clean-compile)* +- `.NET Format`: Run CSharpier and build, then verify formatting and style with `--verify-no-changes` *(clean-compile; the task to run after edits)* + +### Tooling and Editor + +#### Code Formatting and Tooling + +1. **CSharpier**: Primary code formatter + - Invoked by the `CSharpier Format` task / `dotnet csharpier format --log-level=debug .` +2. **dotnet format**: Style verification + - Verify no changes: `dotnet format style --verify-no-changes --severity=info --verbosity=detailed` +3. **Other tools** + - `dotnet-outdated-tool`: Dependency update checks + - Nerdbank.GitVersioning: Version management + +CI is the authoritative lint backstop. Local pre-commit hooks are optional - wire Husky.Net (or another runner) if you want local enforcement. + +#### Editor Baseline + +1. **Required VS Code extensions**: CSharpier, markdownlint, CSpell +2. **VS Code settings**: Use the workspace settings without overrides + +### Coding Standards and Conventions + +Note: Code snippets are illustrative examples only. Replace namespaces/types to match your project. + +#### C# Language Features + +1. **File-scoped namespaces** + + ```csharp + namespace Example.Project.Library; + ``` + +2. **Nullable reference types**: Enabled (`enable`) + - Use nullable annotations appropriately + - Use `required` for mandatory properties + +3. **Modern C# features**: Prefer modern language constructs + - Primary constructors when appropriate + - Top-level statements for console apps + - Pattern matching over traditional checks + - Collection expressions when types loosely match + - Extension methods - the classic `this`-parameter form, or an `extension() { ... }` block on C# 14+ + - Implicit object creation when type is apparent + - Range and index operators + +4. **Expression-bodied members**: Use for applicable members + - Methods, properties, accessors, operators, lambdas, local functions + +5. **`var` keyword**: Do NOT use `var` (always use explicit types) + + ```csharp + // Correct + int count = 42; + string name = "test"; + + // Incorrect + var count = 42; + var name = "test"; + ``` + +#### Naming Conventions + +1. **Private fields**: underscore prefix with camelCase + + ```csharp + private readonly HttpClient _httpClient; + private int _counter; + ``` + +2. **Static fields**: `s_` prefix with camelCase + + ```csharp + private static int s_instanceCount; + ``` + +3. **Constants**: PascalCase + + ```csharp + private const int MaxRetries = 3; + ``` + +#### Code Structure + +1. **Global usings**: Use `GlobalUsings.cs` for common namespaces + + ```csharp + global using System; + global using System.Net.Http; + global using System.Threading.Tasks; + global using Serilog; + ``` + +2. **Usings placement**: Outside namespace, sorted with `System` directives first + + ```csharp + using System.CommandLine; + using System.Runtime.CompilerServices; + using Example.Project.Library; + + namespace Example.Project.Console; + ``` + +3. **Braces**: Allman style + + ```csharp + public void Method() + { + if (condition) + { + // code + } + } + ``` + +4. **Indentation** + - C# files: 4 spaces + - XML/csproj files: 2 spaces + - YAML files: 2 spaces + - JSON files: 4 spaces + +5. **Line endings** + - Everything: LF, per `.editorconfig`'s `[*]` default + - Windows batch and command scripts (`.bat`, `.cmd`): CRLF, the one exception + +6. **`#region`**: Do not use regions. Prefer logical file/folder/namespace organization. +7. **Member ordering (StyleCop SA1201)**: const -> static readonly -> static fields -> instance readonly fields -> instance fields -> constructors -> public (events -> properties -> indexers -> methods -> operators) -> non-public in same order -> nested types + +#### Comments and Documentation + +1. **XML documentation** + - `true` + - Missing XML comments for public APIs are suppressed (`.editorconfig`) + - Must document all public surfaces. + - Single-line summaries, additional details in remarks, document input parameters, return values, exceptions, and add crefs + + ```csharp + /// + /// Example of a single line summary. + /// + /// + /// Additional important details about usage. + /// Multiple lines if needed. + /// + /// + /// The quote category to request + /// + /// + /// A that can be used to cancel the request. + /// + /// + /// A containing the quote text. + /// + /// + /// Thrown when is not a supported value. + /// + public async Task GetQuoteOfTheDayAsync(string category, CancellationToken cancellationToken) {} + ``` + +#### Analyzer Suppressions (.NET) + +Follow the scope hierarchy in [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions). .NET mechanics, narrowest first: + +- **Never use `#pragma warning disable`** to silence an analyzer. +- **Symbol-scoped**: a `[System.Diagnostics.CodeAnalysis.SuppressMessage(...)]` attribute with a `Justification`, on the specific member or type: + + ```csharp + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "Design", + "CA1034:Nested types should not be visible", + Justification = "https://github.com/dotnet/sdk/issues/51681" + )] + ``` + +- **Project-scoped** (e.g. a test project): a `dotnet_diagnostic..severity` entry in *that project's own* `.editorconfig`, with a comment explaining why. +- **Repo-wide**: a `dotnet_diagnostic..severity` entry in the root `.editorconfig`, only when the rule is genuinely not applicable to any project. Relaxing a batch of `CA*` rules (or `dotnet_analyzer_diagnostic.severity`) to push a brownfield port through the build is exactly what this forbids. + +#### Error Handling and Logging + +1. **Serilog logging**: Use structured logging + + ```csharp + logger.Error(exception, "{Function}", function); + ``` + +2. **Library log configuration**: Libraries must expose logging configuration + - Provide options or settings to supply an `ILoggerFactory` and/or `ILogger` + - Offer a global fallback logger for static usage when needed + +3. **CallerMemberName**: Use for automatic function name tracking + + ```csharp + public bool LogAndPropagate( + Exception exception, + [CallerMemberName] string function = "unknown" + ) + ``` + +4. **Logger extensions**: Use `Extensions.cs` for logger and other extension methods + + ```csharp + extension(ILogger logger) + { + public bool LogAndPropagate(Exception exception, ...) { } + } + ``` + +5. **Exceptions**: Do not swallow exceptions; log and rethrow or translate to a domain-specific exception + +#### Code Patterns + +1. **Guard clauses**: Prefer early returns for validation and error handling +2. **Async all the way**: Avoid blocking calls (`.Result`, `.Wait()`); use `async`/`await` +3. **Cancellation tokens**: Accept `CancellationToken` as the last parameter and pass it through +4. **ConfigureAwait**: In library code, use `ConfigureAwait(false)` unless context is required + - Do not call `ConfigureAwait(false)` in xUnit tests (see xUnit1030) +5. **Disposables**: Use `await using` for async disposables; prefer `using` declarations +6. **LINQ vs loops**: Use LINQ for clarity, loops for hot paths or allocations +7. **HTTP**: Reuse `HttpClient` via factory; avoid per-request instantiation +8. **Collections**: Prefer `IReadOnlyList`/`IReadOnlyCollection` for public APIs +9. **Immutability**: Prefer immutable records; use init-only setters when records are not suitable; prefer immutable or frozen collections for read-only data +10. **Exceptions as control flow**: Avoid using exceptions for expected flow +11. **Sealing classes**: Seal classes that are not designed for inheritance +12. **Read-only data**: Use immutable or frozen collections for read-only data sets +13. **Lazy initialization**: Use `Lazy` for static, thread-safe instantiation (e.g., logger factory, HTTP factory) + +#### Testing Conventions + +1. **Framework**: xUnit with AwesomeAssertions + + ```csharp + [Fact] + public void MethodName_Scenario_ExpectedBehavior() + { + // Arrange + int expected = 42; + + // Act + int actual = GetValue(); + + // Assert + actual.Should().Be(expected); + } + ``` + +2. **Organization**: Arrange-Act-Assert pattern +3. **Naming**: Descriptive names with underscores +4. **Theory tests**: Use `[Theory]` with `[InlineData]` + +### Project Configuration + +1. **Target framework**: .NET 10.0 (`net10.0`) + +2. **AOT compatibility** + - `true` + - `true` + +3. **Assembly information** + - Use semantic versioning + - Include SourceLink: `true` + - Embed untracked sources: `true` + +4. **Internal visibility**: Use `InternalsVisibleTo` for test and benchmark access (adapt the project names to your repo's test/benchmark projects) + + ```xml + + + + + ``` + +### Best Practices + +1. **Code reviews**: All changes go through pull requests + +## Python + +*This section applies only to the Python side. A repo with no Python projects still carries it (the file is carried whole) and ignores it.* + +This is the style guide for any **Python project(s)** in this repo. + +### Toolchain + +| Tool | Role | Config | +|---|---|---| +| [uv](https://docs.astral.sh/uv/) | env, deps, build, publish | `pyproject.toml` `[dependency-groups]`, `uv.lock` | +| [hatchling](https://hatch.pypa.io/latest/) | build backend | `pyproject.toml` `[build-system]` | +| [ruff](https://docs.astral.sh/ruff/) | lint + format + import sort | `pyproject.toml` `[tool.ruff]` | +| [pyright](https://microsoft.github.io/pyright/) | type checker | `pyproject.toml` `[tool.pyright]` | +| [pytest](https://docs.pytest.org/) | test runner | `pyproject.toml` `[tool.pytest.ini_options]` | + +`pyright` is consumed in two places: as a dev dependency (`uv run pyright` for CI/scripted runs) and via VS Code's **Pylance** extension (which embeds pyright). The standalone `ms-pyright.pyright` extension is in `unwantedRecommendations` because Pylance covers it. `mypy` is **not used** here - don't introduce it. + +### Local Development Loop + +From inside the Python project directory: + +```sh +uv sync # creates .venv, installs deps + dev group +uv run ruff format # auto-format +uv run ruff check --fix # auto-fix lint +uv run ruff check # verify lint clean +uv run ruff format --check # verify format clean +uv run pyright # verify types +uv run pytest # run tests +uv build # produce wheel + sdist in ./dist +``` + +The Python clean-compile (see [Clean-Compile Verification](#clean-compile-verification)) is `uv run ruff format` + `uv run ruff check` + `uv run pyright`; run it (plus `uv run pytest`) before committing. These are documented commands, not VS Code tasks. CI runs the same clean-compile commands as the authoritative backstop. Git hooks are opt-in; wire `pre-commit` for `ruff` and `pyright` yourself if you want local enforcement. + +### Layout + +`src` layout - keeps the package out of the repo root and prevents accidental imports of unbuilt code: + +```text +/ + pyproject.toml + README.md + uv.lock # committed for reproducible CI + src/ + / + __init__.py + _version.py + .py + tests/ + __init__.py + test_.py +``` + +### Code Style + +#### Formatting and Linting + +- **`ruff format` is authoritative.** Don't argue with the formatter; if it reformats your code, that's the final form. Configure (line length, target version) in `pyproject.toml` `[tool.ruff]`, not via inline `# fmt:` directives. +- **Run `ruff check --fix` before committing.** Most ruff lint rules have safe autofixes; let the tool handle them. The configured rule families are listed under `[tool.ruff.lint]` `select`. Add new rule families project-wide rather than scattering inline `# noqa` markers. +- **`# noqa` is a last resort.** When you must use one, scope it narrowly (`# noqa: E501`, not bare `# noqa`) and add a short comment on the same line explaining why. False-positive patterns that recur across the codebase belong in `[tool.ruff.lint]` `ignore` or per-file `[tool.ruff.lint.per-file-ignores]`, with a comment. Porting an existing codebase is not a license to add `ignore` / `per-file-ignores` blocks to mute newly surfaced lint - fix it (see [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions)). + +#### Comments + +- **Inline `#` comments**: keep tight and local. One line is preferred, but multi-line is fine when you need to document a non-obvious implementation constraint, a local trade-off, or coupling that future edits could easily break. Keep that rationale next to the affected block so the reviewer/maintainer sees it at edit-time. +- **Don't explain *what* the code does** - well-named identifiers handle that. Don't reference the current task ("added for X", "used by Y"); that belongs in the PR description. + +#### Docstrings + +- Follow [PEP 257](https://peps.python.org/pep-0257/). Focus docstrings primarily on the **behavior contract** (what callers and tests can rely on), public semantics, and edge-case expectations. Implementation-local rationale belongs in inline `#` comments, not docstrings. +- A short one-liner is fine for trivial functions and tests with self-documenting names. +- For non-trivial behavior - non-obvious test scenarios, contracts a test pins, edge cases callers must know about, design trade-offs that are load-bearing for future maintainers - write a one-line summary, blank line, then a details paragraph. Multi-paragraph docstrings are fine when the contract earns it. +- Design notes belong **in the code** (docstrings or inline comments). They do NOT belong in [`HISTORY.md`](./HISTORY.md) - that file is end-user release notes, not a design log. + +#### Type Hints + +- **All public APIs are typed.** Pyright runs on `src/` in strict mode (`[tool.pyright]` `strict = ["src"]`); tests run in standard mode. +- **Use modern syntax**: `list[int]` not `List[int]`, `dict[str, X]` not `Dict[str, X]`, `X | None` not `Optional[X]`, `from __future__ import annotations` only when needed for forward references. +- **Don't add `# type: ignore` to silence pyright errors without a comment** explaining the constraint. If a recurring false positive needs suppression, configure it project-wide in `[tool.pyright]`. A new port doesn't change this - fix freshly surfaced type errors rather than muting them (see [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions)). + +#### Naming + +- `snake_case` for functions, methods, variables, modules, package directories. +- `PascalCase` for classes, type aliases, type vars, enum members. +- `UPPER_SNAKE_CASE` for module-level constants. +- Single leading underscore for module-private; double leading underscore for name-mangled (rare - usually means rethink the design). + +#### Imports + +- **Let ruff sort imports.** `[tool.ruff.lint]` `select` includes the `I` rule family (isort-equivalent). Don't hand-sort. +- Standard library first, then third-party, then first-party (the project itself), each block separated by a blank line - ruff enforces this automatically. +- Avoid wildcard imports (`from x import *`) outside `__init__.py` re-exports. + +#### Patterns to Avoid + +- **Don't add backward-compat shims, `# removed` markers, or rename-to-`_` for unused vars** - just delete. Git history is the audit trail. +- **Don't add error handling for impossible cases.** Trust internal code; only validate at boundaries (user input, parsed config, external APIs). +- **Don't use exceptions for expected control flow.** Exceptions are for *unexpected* states. +- **Don't suppress errors silently** (`except Exception: pass`). Either handle the specific exception and document why it's safe, or let it propagate. + +### Tests + +- `pytest` with the configuration in `[tool.pytest.ini_options]`. Default invocation: `uv run pytest`. +- One test file per module under test, named `test_.py`. +- Test functions named `test__` - descriptive, not numbered. +- Use fixtures (defined in `conftest.py` for shared ones, or per-test for narrowly-scoped) instead of setup/teardown methods. +- **Avoid mocking when fakes work.** Hand-rolled fakes that implement the protocol you depend on are usually clearer and break less than `unittest.mock` magic. +- **Test edge cases that the docstring promises**, not implementation details. If the test breaks when you refactor *without changing behavior*, the test is asserting on an implementation detail. + +### Versioning + +`_version.py` ships with `__version__ = "0.0.0"` as a placeholder. Until you wire `_version.py` to something that increments (the usual options are `hatch-vcs`, a version.json bridge, or manual bumps), no new PyPI versions will land - publishing with `skip-existing: true` keeps a stuck placeholder version from failing the run. + +### Linter Cleanliness + +Before pushing or opening a PR: + +- VS Code's **Problems** pane should be quiet for the files you touched. The relevant linters are ruff (via the `charliermarsh.ruff` extension) and pyright (via the `ms-python.python` extension's bundled Pylance). +- The CI gate is `uv run ruff check && uv run ruff format --check && uv run pyright && uv run pytest` - same as the local commands above, run from the Python project directory. +- Markdown in this directory follows the repo-wide [Markdown and Spelling](#markdown-and-spelling) rules. diff --git a/CreateMatrix/.editorconfig b/CreateMatrix/.editorconfig index 75e848c1..08f2b3f8 100644 --- a/CreateMatrix/.editorconfig +++ b/CreateMatrix/.editorconfig @@ -1,10 +1,10 @@ -root = false - -# C# files -[*.cs] - -# Ignore types can be made internal -dotnet_diagnostic.CA1515.severity = none - -# Ignore casing rule for invariant culture -dotnet_diagnostic.CA1308.severity = none +root = false + +# C# files +[*.cs] + +# Ignore types can be made internal +dotnet_diagnostic.CA1515.severity = none + +# Ignore casing rule for invariant culture +dotnet_diagnostic.CA1308.severity = none diff --git a/CreateMatrix/AssemblyInfo.cs b/CreateMatrix/AssemblyInfo.cs index d5a92a5a..569bcdc2 100644 --- a/CreateMatrix/AssemblyInfo.cs +++ b/CreateMatrix/AssemblyInfo.cs @@ -1,45 +1,45 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -namespace CreateMatrix; - -internal static class AssemblyInfo -{ - internal static string AppVersion => $"{AppName} : {FileVersion} ({BuildType})"; - - internal static string RuntimeVersion => - $"{RuntimeInformation.FrameworkDescription} : {RuntimeInformation.RuntimeIdentifier}"; - - internal static string BuildType => -#if DEBUG - "Debug"; -#else - "Release"; -#endif - - internal static string AppName => GetAssembly().GetName().Name ?? string.Empty; - - internal static string InformationalVersion => - // E.g. 1.2.3+abc123.abc123 - GetAssembly() - .GetCustomAttribute() - ?.InformationalVersion - ?? string.Empty; - - internal static string FileVersion => - // E.g. 1.2.3.4 - GetAssembly().GetCustomAttribute()?.Version - ?? string.Empty; - - internal static string ReleaseVersion => - // E.g. 1.2.3 part of 1.2.3+abc123.abc123 - // Use major.minor.build from informational version - InformationalVersion.Split('+', '-')[0]; - - private static Assembly GetAssembly() - { - Assembly? assembly = Assembly.GetEntryAssembly(); - assembly ??= Assembly.GetExecutingAssembly(); - return assembly; - } -} +using System.Reflection; +using System.Runtime.InteropServices; + +namespace CreateMatrix; + +internal static class AssemblyInfo +{ + internal static string AppVersion => $"{AppName} : {FileVersion} ({BuildType})"; + + internal static string RuntimeVersion => + $"{RuntimeInformation.FrameworkDescription} : {RuntimeInformation.RuntimeIdentifier}"; + + internal static string BuildType => +#if DEBUG + "Debug"; +#else + "Release"; +#endif + + internal static string AppName => GetAssembly().GetName().Name ?? string.Empty; + + internal static string InformationalVersion => + // E.g. 1.2.3+abc123.abc123 + GetAssembly() + .GetCustomAttribute() + ?.InformationalVersion + ?? string.Empty; + + internal static string FileVersion => + // E.g. 1.2.3.4 + GetAssembly().GetCustomAttribute()?.Version + ?? string.Empty; + + internal static string ReleaseVersion => + // E.g. 1.2.3 part of 1.2.3+abc123.abc123 + // Use major.minor.build from informational version + InformationalVersion.Split('+', '-')[0]; + + private static Assembly GetAssembly() + { + Assembly? assembly = Assembly.GetEntryAssembly(); + assembly ??= Assembly.GetExecutingAssembly(); + return assembly; + } +} diff --git a/CreateMatrix/CommandLine.cs b/CreateMatrix/CommandLine.cs index adbbf7d0..6be1dcde 100644 --- a/CreateMatrix/CommandLine.cs +++ b/CreateMatrix/CommandLine.cs @@ -1,151 +1,151 @@ -using System.Collections.Frozen; -using System.CommandLine; -using System.CommandLine.Parsing; - -namespace CreateMatrix; - -internal sealed class CommandLine -{ - private static readonly Option s_versionPathOption = new Option( - "--versionpath" - ) - { - Description = "Version JSON file path.", - }.AcceptLegalFilePathsOnly(); - - private static readonly Option s_matrixPathOption = new Option( - "--matrixpath" - ) - { - Description = "Matrix JSON file path.", - }.AcceptLegalFilePathsOnly(); - - private static readonly Option s_updateVersionOption = new("--updateversion") - { - Description = "Update version information from online sources.", - DefaultValueFactory = _ => false, - }; - - private static readonly Option s_makeDirectoryOption = new Option( - "--makedirectory" - ) - { - Description = "Make directory path.", - }.AcceptExistingOnly(); - - private static readonly Option s_dockerDirectoryOption = - new Option("--dockerdirectory") - { - Description = "Docker directory path.", - }.AcceptExistingOnly(); - - private static readonly Option s_versionLabelOption = new( - "--versionlabel" - ) - { - Description = "Version label to apply.", - DefaultValueFactory = _ => VersionInfo.LabelType.Latest, - }; - - internal CommandLine(string[] args) - { - Root = CreateRootCommand(); - Result = Root.Parse(args, new ParserConfiguration { EnablePosixBundling = false }); - Result.InvocationConfiguration.EnableDefaultExceptionHandler = false; - } - - private static readonly FrozenSet s_cliBypassList = FrozenSet.Create( - StringComparer.OrdinalIgnoreCase, - "--help", - "--version" - ); - - internal RootCommand Root { get; } - internal ParseResult Result { get; } - - internal static RootCommand CreateRootCommand() - { - RootCommand rootCommand = new( - "CreateMatrix utility to create a matrix of builds from online product versions" - ); - rootCommand.Subcommands.Add(CreateVersionCommand()); - rootCommand.Subcommands.Add(CreateMatrixCommand()); - rootCommand.Subcommands.Add(CreateMakeCommand()); - return rootCommand; - } - - private static Command CreateVersionCommand() - { - Command versionCommand = new( - "version", - "Create version information file from online sources" - ) - { - s_versionPathOption, - }; - versionCommand.SetAction( - (parseResult, cancellationToken) => - new Program(CreateOptions(parseResult), cancellationToken).ExecuteVersionAsync() - ); - return versionCommand; - } - - private static Command CreateMatrixCommand() - { - Command matrixCommand = new("matrix", "Create matrix information file from online sources") - { - s_versionPathOption.AcceptExistingOnly(), - s_matrixPathOption, - s_updateVersionOption, - }; - matrixCommand.SetAction( - (parseResult, cancellationToken) => - new Program(CreateOptions(parseResult), cancellationToken).ExecuteMatrixAsync() - ); - return matrixCommand; - } - - private static Command CreateMakeCommand() - { - Command makeCommand = new("make", "Create Docker and Compose files from version file") - { - s_versionPathOption.AcceptExistingOnly(), - s_makeDirectoryOption, - s_dockerDirectoryOption, - s_versionLabelOption, - }; - makeCommand.SetAction( - (parseResult, cancellationToken) => - new Program(CreateOptions(parseResult), cancellationToken).ExecuteMakeAsync() - ); - return makeCommand; - } - - internal static Options CreateOptions(ParseResult parseResult) => - new() - { - VersionPath = parseResult.GetValue(s_versionPathOption), - MatrixPath = parseResult.GetValue(s_matrixPathOption), - UpdateVersion = parseResult.GetValue(s_updateVersionOption), - MakeDirectory = parseResult.GetValue(s_makeDirectoryOption), - DockerDirectory = parseResult.GetValue(s_dockerDirectoryOption), - VersionLabel = parseResult.GetValue(s_versionLabelOption), - }; - - internal static bool BypassStartup(ParseResult parseResult) => - parseResult.Errors.Count > 0 - || parseResult.CommandResult.Children.Any(symbolResult => - symbolResult is OptionResult optionResult - && s_cliBypassList.Contains(optionResult.Option.Name) - ); - - internal sealed class Options - { - internal required FileInfo? VersionPath { get; init; } - internal required FileInfo? MatrixPath { get; init; } - internal required bool UpdateVersion { get; init; } - internal required DirectoryInfo? MakeDirectory { get; init; } - internal required DirectoryInfo? DockerDirectory { get; init; } - internal required VersionInfo.LabelType VersionLabel { get; init; } - } -} +using System.Collections.Frozen; +using System.CommandLine; +using System.CommandLine.Parsing; + +namespace CreateMatrix; + +internal sealed class CommandLine +{ + private static readonly Option s_versionPathOption = new Option( + "--versionpath" + ) + { + Description = "Version JSON file path.", + }.AcceptLegalFilePathsOnly(); + + private static readonly Option s_matrixPathOption = new Option( + "--matrixpath" + ) + { + Description = "Matrix JSON file path.", + }.AcceptLegalFilePathsOnly(); + + private static readonly Option s_updateVersionOption = new("--updateversion") + { + Description = "Update version information from online sources.", + DefaultValueFactory = _ => false, + }; + + private static readonly Option s_makeDirectoryOption = new Option( + "--makedirectory" + ) + { + Description = "Make directory path.", + }.AcceptExistingOnly(); + + private static readonly Option s_dockerDirectoryOption = + new Option("--dockerdirectory") + { + Description = "Docker directory path.", + }.AcceptExistingOnly(); + + private static readonly Option s_versionLabelOption = new( + "--versionlabel" + ) + { + Description = "Version label to apply.", + DefaultValueFactory = _ => VersionInfo.LabelType.Latest, + }; + + internal CommandLine(string[] args) + { + Root = CreateRootCommand(); + Result = Root.Parse(args, new ParserConfiguration { EnablePosixBundling = false }); + Result.InvocationConfiguration.EnableDefaultExceptionHandler = false; + } + + private static readonly FrozenSet s_cliBypassList = FrozenSet.Create( + StringComparer.OrdinalIgnoreCase, + "--help", + "--version" + ); + + internal RootCommand Root { get; } + internal ParseResult Result { get; } + + internal static RootCommand CreateRootCommand() + { + RootCommand rootCommand = new( + "CreateMatrix utility to create a matrix of builds from online product versions" + ); + rootCommand.Subcommands.Add(CreateVersionCommand()); + rootCommand.Subcommands.Add(CreateMatrixCommand()); + rootCommand.Subcommands.Add(CreateMakeCommand()); + return rootCommand; + } + + private static Command CreateVersionCommand() + { + Command versionCommand = new( + "version", + "Create version information file from online sources" + ) + { + s_versionPathOption, + }; + versionCommand.SetAction( + (parseResult, cancellationToken) => + new Program(CreateOptions(parseResult), cancellationToken).ExecuteVersionAsync() + ); + return versionCommand; + } + + private static Command CreateMatrixCommand() + { + Command matrixCommand = new("matrix", "Create matrix information file from online sources") + { + s_versionPathOption.AcceptExistingOnly(), + s_matrixPathOption, + s_updateVersionOption, + }; + matrixCommand.SetAction( + (parseResult, cancellationToken) => + new Program(CreateOptions(parseResult), cancellationToken).ExecuteMatrixAsync() + ); + return matrixCommand; + } + + private static Command CreateMakeCommand() + { + Command makeCommand = new("make", "Create Docker and Compose files from version file") + { + s_versionPathOption.AcceptExistingOnly(), + s_makeDirectoryOption, + s_dockerDirectoryOption, + s_versionLabelOption, + }; + makeCommand.SetAction( + (parseResult, cancellationToken) => + new Program(CreateOptions(parseResult), cancellationToken).ExecuteMakeAsync() + ); + return makeCommand; + } + + internal static Options CreateOptions(ParseResult parseResult) => + new() + { + VersionPath = parseResult.GetValue(s_versionPathOption), + MatrixPath = parseResult.GetValue(s_matrixPathOption), + UpdateVersion = parseResult.GetValue(s_updateVersionOption), + MakeDirectory = parseResult.GetValue(s_makeDirectoryOption), + DockerDirectory = parseResult.GetValue(s_dockerDirectoryOption), + VersionLabel = parseResult.GetValue(s_versionLabelOption), + }; + + internal static bool BypassStartup(ParseResult parseResult) => + parseResult.Errors.Count > 0 + || parseResult.CommandResult.Children.Any(symbolResult => + symbolResult is OptionResult optionResult + && s_cliBypassList.Contains(optionResult.Option.Name) + ); + + internal sealed class Options + { + internal required FileInfo? VersionPath { get; init; } + internal required FileInfo? MatrixPath { get; init; } + internal required bool UpdateVersion { get; init; } + internal required DirectoryInfo? MakeDirectory { get; init; } + internal required DirectoryInfo? DockerDirectory { get; init; } + internal required VersionInfo.LabelType VersionLabel { get; init; } + } +} diff --git a/CreateMatrix/ComposeFile.cs b/CreateMatrix/ComposeFile.cs index 8845c396..3c1a0323 100644 --- a/CreateMatrix/ComposeFile.cs +++ b/CreateMatrix/ComposeFile.cs @@ -1,211 +1,211 @@ -namespace CreateMatrix; - -internal static class ComposeFile -{ - public static void Create(DirectoryInfo makeDirectory) - { - string makePath = makeDirectory.FullName; - - // Create local Compose file - string composeFile = CreateComposefile(null); - string filePath = Path.Combine(makePath, "Test.yml"); - Log.Logger.Information("Writing Compose file to {Path}", filePath); - File.WriteAllText(filePath, composeFile); - - // Create develop Compose file - composeFile = CreateComposefile("develop"); - filePath = Path.Combine(makePath, "Test-develop.yml"); - Log.Logger.Information("Writing Compose file to {Path}", filePath); - File.WriteAllText(filePath, composeFile); - - // Create latest Compose file - composeFile = CreateComposefile("latest"); - filePath = Path.Combine(makePath, "Test-latest.yml"); - Log.Logger.Information("Writing Compose file to {Path}", filePath); - File.WriteAllText(filePath, composeFile); - } - - private static string CreateComposefile(string? label) - { - // TODO: Switch to volume sub-paths on Moby v26+ - // https://github.com/moby/moby/pull/45687 - - // Compose file header - StringBuilder stringBuilder = new(); - _ = stringBuilder.AppendLineCrlf( - """ - # Compose file created by CreateMatrix, do not modify by hand - - """ - ); - - // Create volumes - _ = stringBuilder.AppendLineCrlf(CreateVolumes()); - // Create services - _ = stringBuilder.AppendLineCrlf(CreateServices(label)); - - return stringBuilder.ToString(); - } - - private static string CreateVolumes() - { - StringBuilder stringBuilder = new(); - _ = stringBuilder.AppendLineCrlf( - """ - volumes: - - """ - ); - - // Create a volume for every product - foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) - { - // Standard - _ = stringBuilder.AppendLineCrlf(CreateVolume(productType, false)); - - // LSIO - _ = stringBuilder.AppendLineCrlf(CreateVolume(productType, true)); - } - - return stringBuilder.ToString(); - } - - private static string CreateVolume(ProductInfo.ProductType productType, bool lsio) => - lsio - ? $$""" - # Dockerfile : {{ProductInfo.GetDocker(productType, lsio)}} - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_config: - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_media: - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_backup: - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_analytics: - - """ - : $$""" - # Dockerfile : {{ProductInfo.GetDocker(productType, lsio)}} - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_etc: - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_ini: - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_var: - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_media: - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_backup: - test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_analytics: - - """; - - private static string CreateServices(string? label) - { - StringBuilder stringBuilder = new(); - _ = stringBuilder.AppendLineCrlf( - """ - services: - - """ - ); - - // Create a service for every product - int standardPort = 7101, - lsioPort = 7201; - foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) - { - // Standard - _ = stringBuilder.AppendLineCrlf( - CreateService(productType, false, standardPort++, label) - ); - // LSIO - _ = stringBuilder.AppendLineCrlf(CreateService(productType, true, lsioPort++, label)); - } - - return stringBuilder.ToString(); - } - - private static string CreateService( - ProductInfo.ProductType productType, - bool lsio, - int port, - string? label - ) - { - string image = string.IsNullOrEmpty(label) - ? $"test_{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}" - : $"docker.io/ptr727/{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}:{label}"; - string service = $$""" - # Dockerfile : {{ProductInfo.GetDocker(productType, lsio)}} - # Port : {{port}} - {{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}: - image: {{image}} - container_name: {{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - {{port}}:7001 - - """; - - if (lsio) - { - service += $$""" - volumes: - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_config:/config - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_media:/media - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_backup:/backup - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_analytics:/analytics - - """; - } - else - { - service += $$""" - volumes: - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_etc:/opt/{{ProductInfo.GetCompany( - productType - ).ToLowerInvariant()}}/mediaserver/etc - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_ini:/home/{{ProductInfo.GetCompany( - productType - ).ToLowerInvariant()}}/.config/nx_ini - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_var:/opt/{{ProductInfo.GetCompany( - productType - ).ToLowerInvariant()}}/mediaserver/var - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_media:/media - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_backup:/backup - - test_{{ProductInfo.GetDocker( - productType, - lsio - ).ToLowerInvariant()}}_analytics:/analytics - - """; - } - - return service; - } -} +namespace CreateMatrix; + +internal static class ComposeFile +{ + public static void Create(DirectoryInfo makeDirectory) + { + string makePath = makeDirectory.FullName; + + // Create local Compose file + string composeFile = CreateComposefile(null); + string filePath = Path.Combine(makePath, "Test.yml"); + Log.Logger.Information("Writing Compose file to {Path}", filePath); + File.WriteAllText(filePath, composeFile.ReplaceLineEndings("\n").TrimEnd('\n') + "\n"); + + // Create develop Compose file + composeFile = CreateComposefile("develop"); + filePath = Path.Combine(makePath, "Test-develop.yml"); + Log.Logger.Information("Writing Compose file to {Path}", filePath); + File.WriteAllText(filePath, composeFile.ReplaceLineEndings("\n").TrimEnd('\n') + "\n"); + + // Create latest Compose file + composeFile = CreateComposefile("latest"); + filePath = Path.Combine(makePath, "Test-latest.yml"); + Log.Logger.Information("Writing Compose file to {Path}", filePath); + File.WriteAllText(filePath, composeFile.ReplaceLineEndings("\n").TrimEnd('\n') + "\n"); + } + + private static string CreateComposefile(string? label) + { + // TODO: Switch to volume sub-paths on Moby v26+ + // https://github.com/moby/moby/pull/45687 + + // Compose file header + StringBuilder stringBuilder = new(); + _ = stringBuilder.AppendLineLf( + """ + # Compose file created by CreateMatrix, do not modify by hand + + """ + ); + + // Create volumes + _ = stringBuilder.AppendLineLf(CreateVolumes()); + // Create services + _ = stringBuilder.AppendLineLf(CreateServices(label)); + + return stringBuilder.ToString(); + } + + private static string CreateVolumes() + { + StringBuilder stringBuilder = new(); + _ = stringBuilder.AppendLineLf( + """ + volumes: + + """ + ); + + // Create a volume for every product + foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) + { + // Standard + _ = stringBuilder.AppendLineLf(CreateVolume(productType, false)); + + // LSIO + _ = stringBuilder.AppendLineLf(CreateVolume(productType, true)); + } + + return stringBuilder.ToString(); + } + + private static string CreateVolume(ProductInfo.ProductType productType, bool lsio) => + lsio + ? $$""" + # Dockerfile : {{ProductInfo.GetDocker(productType, lsio)}} + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_config: + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_media: + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_backup: + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_analytics: + + """ + : $$""" + # Dockerfile : {{ProductInfo.GetDocker(productType, lsio)}} + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_etc: + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_ini: + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_var: + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_media: + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_backup: + test_{{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}_analytics: + + """; + + private static string CreateServices(string? label) + { + StringBuilder stringBuilder = new(); + _ = stringBuilder.AppendLineLf( + """ + services: + + """ + ); + + // Create a service for every product + int standardPort = 7101, + lsioPort = 7201; + foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) + { + // Standard + _ = stringBuilder.AppendLineLf( + CreateService(productType, false, standardPort++, label) + ); + // LSIO + _ = stringBuilder.AppendLineLf(CreateService(productType, true, lsioPort++, label)); + } + + return stringBuilder.ToString(); + } + + private static string CreateService( + ProductInfo.ProductType productType, + bool lsio, + int port, + string? label + ) + { + string image = string.IsNullOrEmpty(label) + ? $"test_{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}" + : $"docker.io/ptr727/{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}:{label}"; + string service = $$""" + # Dockerfile : {{ProductInfo.GetDocker(productType, lsio)}} + # Port : {{port}} + {{ProductInfo.GetDocker(productType, lsio).ToLowerInvariant()}}: + image: {{image}} + container_name: {{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - {{port}}:7001 + + """; + + if (lsio) + { + service += $$""" + volumes: + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_config:/config + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_media:/media + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_backup:/backup + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_analytics:/analytics + + """; + } + else + { + service += $$""" + volumes: + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_etc:/opt/{{ProductInfo.GetCompany( + productType + ).ToLowerInvariant()}}/mediaserver/etc + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_ini:/home/{{ProductInfo.GetCompany( + productType + ).ToLowerInvariant()}}/.config/nx_ini + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_var:/opt/{{ProductInfo.GetCompany( + productType + ).ToLowerInvariant()}}/mediaserver/var + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_media:/media + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_backup:/backup + - test_{{ProductInfo.GetDocker( + productType, + lsio + ).ToLowerInvariant()}}_analytics:/analytics + + """; + } + + return service; + } +} diff --git a/CreateMatrix/CreateMatrix.csproj b/CreateMatrix/CreateMatrix.csproj index 26aef544..47d31488 100644 --- a/CreateMatrix/CreateMatrix.csproj +++ b/CreateMatrix/CreateMatrix.csproj @@ -1,28 +1,28 @@ - - - latest-all - true - 1.0.0-pre - false - enable - Exe - false - net10.0 - 1.0.0 - - - true - true - true - - - - - - - - - - - - + + + latest-all + true + 1.0.0-pre + false + enable + Exe + false + net10.0 + 1.0.0 + + + true + true + true + + + + + + + + + + + + diff --git a/CreateMatrix/Dockerfile.cs b/CreateMatrix/Dockerfile.cs index a3bf4578..a82b55e8 100644 --- a/CreateMatrix/Dockerfile.cs +++ b/CreateMatrix/Dockerfile.cs @@ -1,338 +1,338 @@ -namespace CreateMatrix; - -internal static class DockerFile -{ - private const string UbuntuBaseTag = "noble"; - private const string BaseImage = "docker.io/ptr727/nx-base:ubuntu-" + UbuntuBaseTag; - private const string BaseImageLsio = "docker.io/ptr727/nx-base-lsio:ubuntu-" + UbuntuBaseTag; - - public static void Create( - List productList, - string dockerPath, - VersionInfo.LabelType label - ) - { - CreateBaseDockerfiles(dockerPath); - - // Create a Docker file for each product type - foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) - { - // Find the matching product - ProductInfo productInfo = - productList.Find(item => item.Product == productType) - ?? throw new InvalidOperationException($"Product not found: {productType}"); - - // Get the version for the label, not all releases include Beta and RC labels - VersionInfo? versionInfo = productInfo.Versions.Find(item => - item.Labels.Contains(label) - ); - - // If the specific label is not found, use the latest version - if (versionInfo == null) - { - Log.Logger.Warning( - "Label {Label} not found for {Product}, using latest", - label, - productType - ); - versionInfo = productInfo.Versions.Find(item => - item.Labels.Contains(VersionInfo.LabelType.Latest) - ); - } - if (versionInfo == null) - { - throw new InvalidOperationException( - $"{productType}: No version found for label {label} or Latest" - ); - } - - // Create the standard Docker file - string dockerFile = CreateDockerfile(productType, versionInfo, false); - string filePath = Path.Combine( - dockerPath, - $"{ProductInfo.GetDocker(productType, false)}.Dockerfile" - ); - WriteDockerfile(filePath, dockerFile); - - // Create the LSIO Docker file - dockerFile = CreateDockerfile(productType, versionInfo, true); - filePath = Path.Combine( - dockerPath, - $"{ProductInfo.GetDocker(productType, true)}.Dockerfile" - ); - WriteDockerfile(filePath, dockerFile); - } - } - - private static void WriteDockerfile(string filePath, string dockerFile) - { - Log.Logger.Information("Writing Dockerfile to {Path}", filePath); - File.WriteAllText(filePath, dockerFile.ReplaceLineEndings("\n").TrimEnd('\n') + "\n"); - } - - private static string CreateDockerfile( - ProductInfo.ProductType productType, - VersionInfo versionInfo, - bool lsio - ) - { - // From - StringBuilder stringBuilder = new(); - _ = stringBuilder.AppendLineCrlf(CreateFrom(productType, lsio)); - - // Args - _ = stringBuilder.AppendLineCrlf(CreateArgs(productType, versionInfo, lsio)); - // Install - _ = stringBuilder.AppendLineCrlf(CreateInstall(lsio)); - - // Entrypoint - _ = stringBuilder.AppendLineCrlf(CreateEntrypoint(productType, lsio)); - - return stringBuilder.ToString(); - } - - private static string CreateFrom(ProductInfo.ProductType productType, bool lsio) - { - string from = $$""" - # Dockerfile created by CreateMatrix, do not modify by hand - # Product: {{productType}} - # Description: {{ProductInfo.GetDescription(productType)}} - # Company: {{ProductInfo.GetCompany(productType)}} - # Release: {{ProductInfo.GetRelease(productType)}} - # LSIO: {{lsio}} - - # https://support.networkoptix.com/hc/en-us/articles/205313168-Nx-Witness-Operating-System-Support - # Latest Ubuntu supported for v6 is Noble - # Base images are built in this repo, see Docker/NxBase*.Dockerfile - - """; - if (lsio) - { - from += $$""" - FROM {{BaseImageLsio}} - - """; - } - else - { - from += $$""" - FROM {{BaseImage}} - - """; - } - return from; - } - - private static void CreateBaseDockerfiles(string dockerPath) - { - string baseDockerfile = $$""" - # Base Dockerfile for Nx Witness images - # Built from ubuntu:{{UbuntuBaseTag}} - FROM ubuntu:{{UbuntuBaseTag}} - - # Prevent EULA and confirmation prompts in installers - ARG DEBIAN_FRONTEND=noninteractive - - # Common packages used by all product images - # https://github.com/ptr727/NxWitness/issues/282 - RUN apt-get update \ - && apt-get upgrade --yes \ - && apt-get install --no-install-recommends --yes \ - ca-certificates \ - gdb \ - libdrm2 \ - sudo \ - unzip \ - wget \ - && apt-get clean \ - && apt-get autoremove --purge --yes \ - && rm -rf /var/lib/apt/lists/* - """; - - string baseFilePath = Path.Combine(dockerPath, "NxBase.Dockerfile"); - WriteDockerfile(baseFilePath, baseDockerfile); - - string baseLsioDockerfile = $$""" - # Base Dockerfile for Nx Witness LSIO images - # Built from lsiobase/ubuntu:{{UbuntuBaseTag}} - FROM lsiobase/ubuntu:{{UbuntuBaseTag}} - - # Prevent EULA and confirmation prompts in installers - ARG DEBIAN_FRONTEND=noninteractive - - # Common packages used by all product images - # https://github.com/ptr727/NxWitness/issues/282 - RUN apt-get update \ - && apt-get upgrade --yes \ - && apt-get install --no-install-recommends --yes \ - ca-certificates \ - gdb \ - libdrm2 \ - unzip \ - wget \ - && apt-get clean \ - && apt-get autoremove --purge --yes \ - && rm -rf /var/lib/apt/lists/* - """; - - string baseLsioFilePath = Path.Combine(dockerPath, "NxBase-LSIO.Dockerfile"); - WriteDockerfile(baseLsioFilePath, baseLsioDockerfile); - } - - private static string CreateArgs( - ProductInfo.ProductType productType, - VersionInfo versionInfo, - bool lsio - ) => - // Args - $$""" - # Labels - ARG LABEL_NAME="{{ProductInfo.GetDocker(productType, lsio)}}" - ARG LABEL_DESCRIPTION="{{ProductInfo.GetDescription(productType)}}" - ARG LABEL_VERSION="{{versionInfo.Version}}" - - # Download URL and version - # Current values are defined by the build pipeline - ARG DOWNLOAD_X64_URL="{{versionInfo.UriX64}}" - ARG DOWNLOAD_ARM64_URL="{{versionInfo.UriArm64}}" - ARG DOWNLOAD_VERSION="{{versionInfo.Version}}" - - # Used for ${COMPANY_NAME} setting the server user and install directory - ARG RUNTIME_NAME="{{ProductInfo.GetCompany(productType)}}" - - # Global builder variables - # https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope - ARG \ - # Platform of the build result. Eg linux/amd64, linux/arm/v7, windows/amd64 - TARGETPLATFORM \ - # Architecture component of TARGETPLATFORM - TARGETARCH \ - # Platform of the node performing the build - BUILDPLATFORM - - # Prevent EULA and confirmation prompts in installers - ARG DEBIAN_FRONTEND=noninteractive - - # Media server user and directory name - ENV COMPANY_NAME=${RUNTIME_NAME} - - # Labels - LABEL name=${LABEL_NAME}-${DOWNLOAD_VERSION} \ - description=${LABEL_DESCRIPTION} \ - version=${LABEL_VERSION} \ - maintainer="Pieter Viljoen " - - """; - - private static string CreateInstall(bool lsio) - { - // Install - string install = """ - # Download the installer file - WORKDIR /temp - COPY download.sh /temp/download.sh - RUN /bin/bash /temp/download.sh "${DOWNLOAD_X64_URL}" "${DOWNLOAD_ARM64_URL}" "${TARGETPLATFORM:-}" - - - """; - if (lsio) - { - install += """ - # Rename the LSIO "abc" user and group to ${COMPANY_NAME} - COPY lsio-rename-user.sh /temp/lsio-rename-user.sh - RUN /bin/bash /temp/lsio-rename-user.sh "${COMPANY_NAME}" - - - """; - } - - install += """ - # Install the mediaserver - RUN apt-get update \ - && apt-get install --no-install-recommends --yes \ - ./vms_server.deb \ - # Cleanup - && apt-get clean \ - && apt-get autoremove --purge --yes \ - && rm -rf /var/lib/apt/lists/* \ - && rm -rf /temp - - - """; - if (lsio) - { - install += """ - # Set ownership permissions - RUN chown --verbose ${COMPANY_NAME}:${COMPANY_NAME} /opt/${COMPANY_NAME}/mediaserver/bin \ - && chown --verbose ${COMPANY_NAME}:${COMPANY_NAME} /opt/${COMPANY_NAME}/mediaserver/bin/external.dat - - """; - } - else - { - install += """ - # Add the mediaserver ${COMPANY_NAME} user to the sudoers group - # Only allow sudo no password access to the root-tool - RUN echo "${COMPANY_NAME} ALL = NOPASSWD: /opt/${COMPANY_NAME}/mediaserver/bin/root-tool" > /etc/sudoers.d/${COMPANY_NAME} - - """; - } - - return install; - } - - private static string CreateEntrypoint(ProductInfo.ProductType productType, bool lsio) => - // Entrypoint - lsio - ? """ - # Copy etc init and services files - # https://www.linuxserver.io/blog/how-is-container-formed - COPY s6-overlay /etc/s6-overlay - - # Expose port 7001 - EXPOSE 7001 - - # Create mount points - # Config links will be created at runtime, see LSIO/etc/s6-overlay/s6-rc.d/init-nx-relocate/run - # /opt/${COMPANY_NAME}/mediaserver/etc -> /config/etc - # /opt/${COMPANY_NAME}/mediaserver/var -> /config/var - # /root/.config/nx_ini links -> /config/ini - # /config is for configuration - # /media is for recordings - # /backup is for backups - # /analytics is for analytics - VOLUME /config /media /backup /analytics - """ - : $$""" - # Copy the entrypoint.sh launch script - # entrypoint.sh will run the mediaserver and root-tool - COPY entrypoint.sh /opt/entrypoint.sh - RUN chmod +x /opt/entrypoint.sh - - # Run the entrypoint as the mediaserver ${COMPANY_NAME} user - # Note that this user exists in the container and does not directly map to a user on the host - USER ${COMPANY_NAME} - - # Runs entrypoint.sh on container start - ENTRYPOINT ["/opt/entrypoint.sh"] - - # Expose port 7001 - EXPOSE 7001 - - # Create mount points - # Link config directly to internal paths - # /mnt/config/etc:opt/{{ProductInfo.GetCompany( - productType - ).ToLowerInvariant()}}/mediaserver/etc - # /mnt/config/nx_ini:/home/{{ProductInfo.GetCompany( - productType - ).ToLowerInvariant()}}/.config/nx_ini - # /mnt/config/var:/opt/{{ProductInfo.GetCompany( - productType - ).ToLowerInvariant()}}/mediaserver/var - # /media is for recordings - # /backup is for backups - # /analytics is for analytics - VOLUME /media /backup /analytics - """; -} +namespace CreateMatrix; + +internal static class DockerFile +{ + private const string UbuntuBaseTag = "noble"; + private const string BaseImage = "docker.io/ptr727/nx-base:ubuntu-" + UbuntuBaseTag; + private const string BaseImageLsio = "docker.io/ptr727/nx-base-lsio:ubuntu-" + UbuntuBaseTag; + + public static void Create( + List productList, + string dockerPath, + VersionInfo.LabelType label + ) + { + CreateBaseDockerfiles(dockerPath); + + // Create a Docker file for each product type + foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) + { + // Find the matching product + ProductInfo productInfo = + productList.Find(item => item.Product == productType) + ?? throw new InvalidOperationException($"Product not found: {productType}"); + + // Get the version for the label, not all releases include Beta and RC labels + VersionInfo? versionInfo = productInfo.Versions.Find(item => + item.Labels.Contains(label) + ); + + // If the specific label is not found, use the latest version + if (versionInfo == null) + { + Log.Logger.Warning( + "Label {Label} not found for {Product}, using latest", + label, + productType + ); + versionInfo = productInfo.Versions.Find(item => + item.Labels.Contains(VersionInfo.LabelType.Latest) + ); + } + if (versionInfo == null) + { + throw new InvalidOperationException( + $"{productType}: No version found for label {label} or Latest" + ); + } + + // Create the standard Docker file + string dockerFile = CreateDockerfile(productType, versionInfo, false); + string filePath = Path.Combine( + dockerPath, + $"{ProductInfo.GetDocker(productType, false)}.Dockerfile" + ); + WriteDockerfile(filePath, dockerFile); + + // Create the LSIO Docker file + dockerFile = CreateDockerfile(productType, versionInfo, true); + filePath = Path.Combine( + dockerPath, + $"{ProductInfo.GetDocker(productType, true)}.Dockerfile" + ); + WriteDockerfile(filePath, dockerFile); + } + } + + private static void WriteDockerfile(string filePath, string dockerFile) + { + Log.Logger.Information("Writing Dockerfile to {Path}", filePath); + File.WriteAllText(filePath, dockerFile.ReplaceLineEndings("\n").TrimEnd('\n') + "\n"); + } + + private static string CreateDockerfile( + ProductInfo.ProductType productType, + VersionInfo versionInfo, + bool lsio + ) + { + // From + StringBuilder stringBuilder = new(); + _ = stringBuilder.AppendLineLf(CreateFrom(productType, lsio)); + + // Args + _ = stringBuilder.AppendLineLf(CreateArgs(productType, versionInfo, lsio)); + // Install + _ = stringBuilder.AppendLineLf(CreateInstall(lsio)); + + // Entrypoint + _ = stringBuilder.AppendLineLf(CreateEntrypoint(productType, lsio)); + + return stringBuilder.ToString(); + } + + private static string CreateFrom(ProductInfo.ProductType productType, bool lsio) + { + string from = $$""" + # Dockerfile created by CreateMatrix, do not modify by hand + # Product: {{productType}} + # Description: {{ProductInfo.GetDescription(productType)}} + # Company: {{ProductInfo.GetCompany(productType)}} + # Release: {{ProductInfo.GetRelease(productType)}} + # LSIO: {{lsio}} + + # https://support.networkoptix.com/hc/en-us/articles/205313168-Nx-Witness-Operating-System-Support + # Latest Ubuntu supported for v6 is Noble + # Base images are built in this repo, see Docker/NxBase*.Dockerfile + + """; + if (lsio) + { + from += $$""" + FROM {{BaseImageLsio}} + + """; + } + else + { + from += $$""" + FROM {{BaseImage}} + + """; + } + return from; + } + + private static void CreateBaseDockerfiles(string dockerPath) + { + string baseDockerfile = $$""" + # Base Dockerfile for Nx Witness images + # Built from ubuntu:{{UbuntuBaseTag}} + FROM ubuntu:{{UbuntuBaseTag}} + + # Prevent EULA and confirmation prompts in installers + ARG DEBIAN_FRONTEND=noninteractive + + # Common packages used by all product images + # https://github.com/ptr727/NxWitness/issues/282 + RUN apt-get update \ + && apt-get upgrade --yes \ + && apt-get install --no-install-recommends --yes \ + ca-certificates \ + gdb \ + libdrm2 \ + sudo \ + unzip \ + wget \ + && apt-get clean \ + && apt-get autoremove --purge --yes \ + && rm -rf /var/lib/apt/lists/* + """; + + string baseFilePath = Path.Combine(dockerPath, "NxBase.Dockerfile"); + WriteDockerfile(baseFilePath, baseDockerfile); + + string baseLsioDockerfile = $$""" + # Base Dockerfile for Nx Witness LSIO images + # Built from lsiobase/ubuntu:{{UbuntuBaseTag}} + FROM lsiobase/ubuntu:{{UbuntuBaseTag}} + + # Prevent EULA and confirmation prompts in installers + ARG DEBIAN_FRONTEND=noninteractive + + # Common packages used by all product images + # https://github.com/ptr727/NxWitness/issues/282 + RUN apt-get update \ + && apt-get upgrade --yes \ + && apt-get install --no-install-recommends --yes \ + ca-certificates \ + gdb \ + libdrm2 \ + unzip \ + wget \ + && apt-get clean \ + && apt-get autoremove --purge --yes \ + && rm -rf /var/lib/apt/lists/* + """; + + string baseLsioFilePath = Path.Combine(dockerPath, "NxBase-LSIO.Dockerfile"); + WriteDockerfile(baseLsioFilePath, baseLsioDockerfile); + } + + private static string CreateArgs( + ProductInfo.ProductType productType, + VersionInfo versionInfo, + bool lsio + ) => + // Args + $$""" + # Labels + ARG LABEL_NAME="{{ProductInfo.GetDocker(productType, lsio)}}" + ARG LABEL_DESCRIPTION="{{ProductInfo.GetDescription(productType)}}" + ARG LABEL_VERSION="{{versionInfo.Version}}" + + # Download URL and version + # Current values are defined by the build pipeline + ARG DOWNLOAD_X64_URL="{{versionInfo.UriX64}}" + ARG DOWNLOAD_ARM64_URL="{{versionInfo.UriArm64}}" + ARG DOWNLOAD_VERSION="{{versionInfo.Version}}" + + # Used for ${COMPANY_NAME} setting the server user and install directory + ARG RUNTIME_NAME="{{ProductInfo.GetCompany(productType)}}" + + # Global builder variables + # https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope + ARG \ + # Platform of the build result. Eg linux/amd64, linux/arm/v7, windows/amd64 + TARGETPLATFORM \ + # Architecture component of TARGETPLATFORM + TARGETARCH \ + # Platform of the node performing the build + BUILDPLATFORM + + # Prevent EULA and confirmation prompts in installers + ARG DEBIAN_FRONTEND=noninteractive + + # Media server user and directory name + ENV COMPANY_NAME=${RUNTIME_NAME} + + # Labels + LABEL name=${LABEL_NAME}-${DOWNLOAD_VERSION} \ + description=${LABEL_DESCRIPTION} \ + version=${LABEL_VERSION} \ + maintainer="Pieter Viljoen " + + """; + + private static string CreateInstall(bool lsio) + { + // Install + string install = """ + # Download the installer file + WORKDIR /temp + COPY download.sh /temp/download.sh + RUN /bin/bash /temp/download.sh "${DOWNLOAD_X64_URL}" "${DOWNLOAD_ARM64_URL}" "${TARGETPLATFORM:-}" + + + """; + if (lsio) + { + install += """ + # Rename the LSIO "abc" user and group to ${COMPANY_NAME} + COPY lsio-rename-user.sh /temp/lsio-rename-user.sh + RUN /bin/bash /temp/lsio-rename-user.sh "${COMPANY_NAME}" + + + """; + } + + install += """ + # Install the mediaserver + RUN apt-get update \ + && apt-get install --no-install-recommends --yes \ + ./vms_server.deb \ + # Cleanup + && apt-get clean \ + && apt-get autoremove --purge --yes \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /temp + + + """; + if (lsio) + { + install += """ + # Set ownership permissions + RUN chown --verbose ${COMPANY_NAME}:${COMPANY_NAME} /opt/${COMPANY_NAME}/mediaserver/bin \ + && chown --verbose ${COMPANY_NAME}:${COMPANY_NAME} /opt/${COMPANY_NAME}/mediaserver/bin/external.dat + + """; + } + else + { + install += """ + # Add the mediaserver ${COMPANY_NAME} user to the sudoers group + # Only allow sudo no password access to the root-tool + RUN echo "${COMPANY_NAME} ALL = NOPASSWD: /opt/${COMPANY_NAME}/mediaserver/bin/root-tool" > /etc/sudoers.d/${COMPANY_NAME} + + """; + } + + return install; + } + + private static string CreateEntrypoint(ProductInfo.ProductType productType, bool lsio) => + // Entrypoint + lsio + ? """ + # Copy etc init and services files + # https://www.linuxserver.io/blog/how-is-container-formed + COPY s6-overlay /etc/s6-overlay + + # Expose port 7001 + EXPOSE 7001 + + # Create mount points + # Config links will be created at runtime, see LSIO/etc/s6-overlay/s6-rc.d/init-nx-relocate/run + # /opt/${COMPANY_NAME}/mediaserver/etc -> /config/etc + # /opt/${COMPANY_NAME}/mediaserver/var -> /config/var + # /root/.config/nx_ini links -> /config/ini + # /config is for configuration + # /media is for recordings + # /backup is for backups + # /analytics is for analytics + VOLUME /config /media /backup /analytics + """ + : $$""" + # Copy the entrypoint.sh launch script + # entrypoint.sh will run the mediaserver and root-tool + COPY entrypoint.sh /opt/entrypoint.sh + RUN chmod +x /opt/entrypoint.sh + + # Run the entrypoint as the mediaserver ${COMPANY_NAME} user + # Note that this user exists in the container and does not directly map to a user on the host + USER ${COMPANY_NAME} + + # Runs entrypoint.sh on container start + ENTRYPOINT ["/opt/entrypoint.sh"] + + # Expose port 7001 + EXPOSE 7001 + + # Create mount points + # Link config directly to internal paths + # /mnt/config/etc:opt/{{ProductInfo.GetCompany( + productType + ).ToLowerInvariant()}}/mediaserver/etc + # /mnt/config/nx_ini:/home/{{ProductInfo.GetCompany( + productType + ).ToLowerInvariant()}}/.config/nx_ini + # /mnt/config/var:/opt/{{ProductInfo.GetCompany( + productType + ).ToLowerInvariant()}}/mediaserver/var + # /media is for recordings + # /backup is for backups + # /analytics is for analytics + VOLUME /media /backup /analytics + """; +} diff --git a/CreateMatrix/Extensions.cs b/CreateMatrix/Extensions.cs index 850ea96e..f159c5f1 100644 --- a/CreateMatrix/Extensions.cs +++ b/CreateMatrix/Extensions.cs @@ -1,43 +1,43 @@ -using System.Runtime.CompilerServices; - -namespace CreateMatrix; - -internal static class LogExtensions -{ - extension(ILogger logger) - { - internal bool LogAndPropagate( - Exception exception, - [CallerMemberName] string function = "unknown" - ) - { - logger.Error(exception, "{Function}", function); - return false; - } - - internal bool LogAndHandle( - Exception exception, - [CallerMemberName] string function = "unknown" - ) - { - logger.Error(exception, "{Function}", function); - return true; - } - } -} - -internal static class StringBuilderExtensions -{ - extension(StringBuilder sb) - { - internal StringBuilder AppendLineCrlf(string? value = null) - { - if (value is not null) - { - _ = sb.Append(value); - } - - return sb.Append("\r\n"); - } - } -} +using System.Runtime.CompilerServices; + +namespace CreateMatrix; + +internal static class LogExtensions +{ + extension(ILogger logger) + { + internal bool LogAndPropagate( + Exception exception, + [CallerMemberName] string function = "unknown" + ) + { + logger.Error(exception, "{Function}", function); + return false; + } + + internal bool LogAndHandle( + Exception exception, + [CallerMemberName] string function = "unknown" + ) + { + logger.Error(exception, "{Function}", function); + return true; + } + } +} + +internal static class StringBuilderExtensions +{ + extension(StringBuilder sb) + { + internal StringBuilder AppendLineLf(string? value = null) + { + if (value is not null) + { + _ = sb.Append(value); + } + + return sb.Append('\n'); + } + } +} diff --git a/CreateMatrix/GlobalUsings.cs b/CreateMatrix/GlobalUsings.cs index 71c49a03..0ac7a04c 100644 --- a/CreateMatrix/GlobalUsings.cs +++ b/CreateMatrix/GlobalUsings.cs @@ -1,14 +1,14 @@ -global using System; -global using System.Collections.Generic; -global using System.ComponentModel; -global using System.Diagnostics; -global using System.Globalization; -global using System.IO; -global using System.Linq; -global using System.Net.Http; -global using System.Text; -global using System.Text.Json; -global using System.Text.Json.Serialization; -global using System.Threading; -global using System.Threading.Tasks; -global using Serilog; +global using System; +global using System.Collections.Generic; +global using System.ComponentModel; +global using System.Diagnostics; +global using System.Globalization; +global using System.IO; +global using System.Linq; +global using System.Net.Http; +global using System.Text; +global using System.Text.Json; +global using System.Text.Json.Serialization; +global using System.Threading; +global using System.Threading.Tasks; +global using Serilog; diff --git a/CreateMatrix/HttpClientFactory.cs b/CreateMatrix/HttpClientFactory.cs index 76fd050c..075db4da 100644 --- a/CreateMatrix/HttpClientFactory.cs +++ b/CreateMatrix/HttpClientFactory.cs @@ -1,69 +1,69 @@ -using System.Net.Http.Headers; -using Microsoft.Extensions.Http.Resilience; -using Polly; - -namespace CreateMatrix; - -internal static class HttpClientFactory -{ - private static readonly Lazy s_httpClient = new(CreateHttpClient); - private static readonly Lazy s_resilienceHandler = new( - CreateResilienceHandler - ); - - internal static HttpClient GetHttpClient() => s_httpClient.Value; - - private static ResilienceHandler GetResilienceHandler() => s_resilienceHandler.Value; - - private static ResilienceHandler CreateResilienceHandler() => - new( - new ResiliencePipelineBuilder() - .AddRetry( - new Polly.Retry.RetryStrategyOptions - { - MaxRetryAttempts = 3, - BackoffType = DelayBackoffType.Exponential, - UseJitter = true, - Delay = TimeSpan.FromSeconds(1), - MaxDelay = TimeSpan.FromSeconds(30), - ShouldHandle = args => - ValueTask.FromResult( - args.Outcome.Exception != null - || args.Outcome.Result is { IsSuccessStatusCode: false } - ), - } - ) - .AddCircuitBreaker( - new Polly.CircuitBreaker.CircuitBreakerStrategyOptions - { - FailureRatio = 0.2, - MinimumThroughput = 10, - SamplingDuration = TimeSpan.FromSeconds(60), - BreakDuration = TimeSpan.FromSeconds(30), - ShouldHandle = args => - ValueTask.FromResult( - args.Outcome.Exception != null - || args.Outcome.Result is { IsSuccessStatusCode: false } - ), - } - ) - .AddTimeout(TimeSpan.FromSeconds(30)) - .Build() - ) - { - InnerHandler = new SocketsHttpHandler - { - PooledConnectionLifetime = TimeSpan.FromMinutes(15), - PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), - }, - }; - - private static HttpClient CreateHttpClient() - { - HttpClient httpClient = new(GetResilienceHandler()) { Timeout = TimeSpan.FromSeconds(120) }; - httpClient.DefaultRequestHeaders.UserAgent.Add( - new ProductInfoHeaderValue(AssemblyInfo.AppName, AssemblyInfo.InformationalVersion) - ); - return httpClient; - } -} +using System.Net.Http.Headers; +using Microsoft.Extensions.Http.Resilience; +using Polly; + +namespace CreateMatrix; + +internal static class HttpClientFactory +{ + private static readonly Lazy s_httpClient = new(CreateHttpClient); + private static readonly Lazy s_resilienceHandler = new( + CreateResilienceHandler + ); + + internal static HttpClient GetHttpClient() => s_httpClient.Value; + + private static ResilienceHandler GetResilienceHandler() => s_resilienceHandler.Value; + + private static ResilienceHandler CreateResilienceHandler() => + new( + new ResiliencePipelineBuilder() + .AddRetry( + new Polly.Retry.RetryStrategyOptions + { + MaxRetryAttempts = 3, + BackoffType = DelayBackoffType.Exponential, + UseJitter = true, + Delay = TimeSpan.FromSeconds(1), + MaxDelay = TimeSpan.FromSeconds(30), + ShouldHandle = args => + ValueTask.FromResult( + args.Outcome.Exception != null + || args.Outcome.Result is { IsSuccessStatusCode: false } + ), + } + ) + .AddCircuitBreaker( + new Polly.CircuitBreaker.CircuitBreakerStrategyOptions + { + FailureRatio = 0.2, + MinimumThroughput = 10, + SamplingDuration = TimeSpan.FromSeconds(60), + BreakDuration = TimeSpan.FromSeconds(30), + ShouldHandle = args => + ValueTask.FromResult( + args.Outcome.Exception != null + || args.Outcome.Result is { IsSuccessStatusCode: false } + ), + } + ) + .AddTimeout(TimeSpan.FromSeconds(30)) + .Build() + ) + { + InnerHandler = new SocketsHttpHandler + { + PooledConnectionLifetime = TimeSpan.FromMinutes(15), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), + }, + }; + + private static HttpClient CreateHttpClient() + { + HttpClient httpClient = new(GetResilienceHandler()) { Timeout = TimeSpan.FromSeconds(120) }; + httpClient.DefaultRequestHeaders.UserAgent.Add( + new ProductInfoHeaderValue(AssemblyInfo.AppName, AssemblyInfo.InformationalVersion) + ); + return httpClient; + } +} diff --git a/CreateMatrix/ImageInfo.cs b/CreateMatrix/ImageInfo.cs index 1a533b3d..f83fb784 100644 --- a/CreateMatrix/ImageInfo.cs +++ b/CreateMatrix/ImageInfo.cs @@ -1,144 +1,144 @@ -using System.Collections.Immutable; - -namespace CreateMatrix; - -internal class ImageInfo -{ - public enum BranchType - { - None, - Main, - Develop, - } - - public enum BaseType - { - None, - Ubuntu, - LSIO, - } - - public string Name { get; set; } = string.Empty; - - [JsonConverter(typeof(LowercaseEnumConverter))] - public ProductInfo.ProductType Product { get; set; } = ProductInfo.ProductType.None; - - [JsonConverter(typeof(LowercaseEnumConverter))] - public BranchType Branch { get; set; } = BranchType.None; - - [JsonConverter(typeof(LowercaseEnumConverter))] - public BaseType Base { get; set; } = BaseType.None; - - public List Tags { get; set; } = []; - public List Args { get; set; } = []; - - private const string Registry = "docker.io/ptr727"; - - private ImageInfo(ProductInfo.ProductType productType, BranchType branchType, BaseType baseType) - { - Product = productType; - Branch = branchType; - Base = baseType; - // The image name matches the Dockerfile name - Name = ProductInfo.GetDocker(productType, baseType == BaseType.LSIO); - } - - private void AddArgs(VersionInfo versionInfo) - { - Args.Add($"{DownloadVersion}={versionInfo.Version}"); - Args.Add($"{DownloadX64Url}={versionInfo.UriX64}"); - Args.Add($"{DownloadArm64Url}={versionInfo.UriArm64}"); - } - - private void AddTag(string tag) - { - if (Branch == BranchType.Develop) - { - tag = $"develop-{tag}"; - } - Tags.Add($"{Registry}/{Name.ToLowerInvariant()}:{tag.ToLowerInvariant()}"); - } - - private void AddTag(VersionInfo.LabelType labelType) - { - string tag = labelType.ToString().ToLowerInvariant(); - if (Branch == BranchType.Develop) - { - tag = labelType == VersionInfo.LabelType.Latest ? $"develop" : $"develop-{tag}"; - } - Tags.Add($"{Registry}/{Name.ToLowerInvariant()}:{tag.ToLowerInvariant()}"); - } - - public static List CreateImages(List productList) - { - // Create images for all products - List imageList = []; - productList.ForEach(product => - { - imageList.AddRange(CreateImages(product, BaseType.Ubuntu, BranchType.Main)); - imageList.AddRange(CreateImages(product, BaseType.Ubuntu, BranchType.Develop)); - imageList.AddRange(CreateImages(product, BaseType.LSIO, BranchType.Main)); - imageList.AddRange(CreateImages(product, BaseType.LSIO, BranchType.Develop)); - }); - - return imageList; - } - - private static List CreateImages( - ProductInfo productInfo, - BaseType baseType, - BranchType branchType - ) - { - // Create a set by unique versions - ImmutableSortedSet versionSet = productInfo.Versions.ToImmutableSortedSet( - new VersionInfoComparer() - ); - Debug.Assert(versionSet.Count == productInfo.Versions.Count); - - // Create images for each version - List imageList = []; - foreach (VersionInfo version in versionSet) - { - // Create image - ImageInfo imageInfo = new(productInfo.Product, branchType, baseType); - - // Add a tag for the version - imageInfo.AddTag(version.Version); - - // Add tags for all labels - version.Labels.ForEach(imageInfo.AddTag); - - // Add args - imageInfo.AddArgs(version); - - // Sort args and tags to make diffs easier - imageInfo.Args.Sort(); - imageInfo.Tags.Sort(); - - // Add image to list - imageList.Add(imageInfo); - } - - // Done - return imageList; - } - - public void LogInformation() - { - Log.Logger.Information( - "Name: {Name}, Base: {Base}, Branch: {Branch}, Tags: {Tags}, Args: {Args}", - Name, - Base, - Branch, - Tags.Count, - Args.Count - ); - Tags.ForEach(item => Log.Logger.Information("Name: {Name}, Tag: {Tag}", Name, item)); - Args.ForEach(item => Log.Logger.Information("Name: {Name}, Arg: {Arg}", Name, item)); - } - - private const string DownloadVersion = "DOWNLOAD_VERSION"; - private const string DownloadX64Url = "DOWNLOAD_X64_URL"; - private const string DownloadArm64Url = "DOWNLOAD_ARM64_URL"; -} +using System.Collections.Immutable; + +namespace CreateMatrix; + +internal class ImageInfo +{ + public enum BranchType + { + None, + Main, + Develop, + } + + public enum BaseType + { + None, + Ubuntu, + LSIO, + } + + public string Name { get; set; } = string.Empty; + + [JsonConverter(typeof(LowercaseEnumConverter))] + public ProductInfo.ProductType Product { get; set; } = ProductInfo.ProductType.None; + + [JsonConverter(typeof(LowercaseEnumConverter))] + public BranchType Branch { get; set; } = BranchType.None; + + [JsonConverter(typeof(LowercaseEnumConverter))] + public BaseType Base { get; set; } = BaseType.None; + + public List Tags { get; set; } = []; + public List Args { get; set; } = []; + + private const string Registry = "docker.io/ptr727"; + + private ImageInfo(ProductInfo.ProductType productType, BranchType branchType, BaseType baseType) + { + Product = productType; + Branch = branchType; + Base = baseType; + // The image name matches the Dockerfile name + Name = ProductInfo.GetDocker(productType, baseType == BaseType.LSIO); + } + + private void AddArgs(VersionInfo versionInfo) + { + Args.Add($"{DownloadVersion}={versionInfo.Version}"); + Args.Add($"{DownloadX64Url}={versionInfo.UriX64}"); + Args.Add($"{DownloadArm64Url}={versionInfo.UriArm64}"); + } + + private void AddTag(string tag) + { + if (Branch == BranchType.Develop) + { + tag = $"develop-{tag}"; + } + Tags.Add($"{Registry}/{Name.ToLowerInvariant()}:{tag.ToLowerInvariant()}"); + } + + private void AddTag(VersionInfo.LabelType labelType) + { + string tag = labelType.ToString().ToLowerInvariant(); + if (Branch == BranchType.Develop) + { + tag = labelType == VersionInfo.LabelType.Latest ? $"develop" : $"develop-{tag}"; + } + Tags.Add($"{Registry}/{Name.ToLowerInvariant()}:{tag.ToLowerInvariant()}"); + } + + public static List CreateImages(List productList) + { + // Create images for all products + List imageList = []; + productList.ForEach(product => + { + imageList.AddRange(CreateImages(product, BaseType.Ubuntu, BranchType.Main)); + imageList.AddRange(CreateImages(product, BaseType.Ubuntu, BranchType.Develop)); + imageList.AddRange(CreateImages(product, BaseType.LSIO, BranchType.Main)); + imageList.AddRange(CreateImages(product, BaseType.LSIO, BranchType.Develop)); + }); + + return imageList; + } + + private static List CreateImages( + ProductInfo productInfo, + BaseType baseType, + BranchType branchType + ) + { + // Create a set by unique versions + ImmutableSortedSet versionSet = productInfo.Versions.ToImmutableSortedSet( + new VersionInfoComparer() + ); + Debug.Assert(versionSet.Count == productInfo.Versions.Count); + + // Create images for each version + List imageList = []; + foreach (VersionInfo version in versionSet) + { + // Create image + ImageInfo imageInfo = new(productInfo.Product, branchType, baseType); + + // Add a tag for the version + imageInfo.AddTag(version.Version); + + // Add tags for all labels + version.Labels.ForEach(imageInfo.AddTag); + + // Add args + imageInfo.AddArgs(version); + + // Sort args and tags to make diffs easier + imageInfo.Args.Sort(); + imageInfo.Tags.Sort(); + + // Add image to list + imageList.Add(imageInfo); + } + + // Done + return imageList; + } + + public void LogInformation() + { + Log.Logger.Information( + "Name: {Name}, Base: {Base}, Branch: {Branch}, Tags: {Tags}, Args: {Args}", + Name, + Base, + Branch, + Tags.Count, + Args.Count + ); + Tags.ForEach(item => Log.Logger.Information("Name: {Name}, Tag: {Tag}", Name, item)); + Args.ForEach(item => Log.Logger.Information("Name: {Name}, Arg: {Arg}", Name, item)); + } + + private const string DownloadVersion = "DOWNLOAD_VERSION"; + private const string DownloadX64Url = "DOWNLOAD_X64_URL"; + private const string DownloadArm64Url = "DOWNLOAD_ARM64_URL"; +} diff --git a/CreateMatrix/MatrixJsonSchema.cs b/CreateMatrix/MatrixJsonSchema.cs index 61ff55c7..60bf1392 100644 --- a/CreateMatrix/MatrixJsonSchema.cs +++ b/CreateMatrix/MatrixJsonSchema.cs @@ -1,61 +1,61 @@ -namespace CreateMatrix; - -internal class MatrixJsonSchemaBase -{ - [JsonRequired] - [JsonPropertyOrder(-2)] - public int SchemaVersion { get; set; } = MatrixJsonSchema.Version; -} - -internal class MatrixJsonSchema : MatrixJsonSchemaBase -{ - public const int Version = 2; - - [JsonRequired] - public List Images { get; set; } = []; - - public static void ToFile(string path, MatrixJsonSchema json) - { - json.SchemaVersion = Version; - File.WriteAllText(path, ToJson(json)); - } - - private static string ToJson(MatrixJsonSchema json) => - JsonSerializer.Serialize(json, MatrixJsonContext.Default.MatrixJsonSchema); -} - -internal sealed class LowercaseEnumConverter : JsonConverter - where T : struct, Enum -{ - public override T Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options - ) - { - string? value = - reader.GetString() ?? throw new JsonException($"Expected {typeof(T).Name} value."); - - return !Enum.TryParse(value, true, out T result) - ? throw new JsonException($"Invalid {typeof(T).Name} value '{value}'.") - : result; - } - - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => - writer.WriteStringValue(value.ToString().ToLowerInvariant()); -} - -[JsonSourceGenerationOptions( - AllowTrailingCommas = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - IncludeFields = true, - NumberHandling = JsonNumberHandling.AllowReadingFromString, - PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, - ReadCommentHandling = JsonCommentHandling.Skip, - UseStringEnumConverter = true, - WriteIndented = true, - NewLine = "\r\n" -)] -[JsonSerializable(typeof(MatrixJsonSchema))] -[JsonSerializable(typeof(MatrixJsonSchemaBase))] -internal partial class MatrixJsonContext : JsonSerializerContext; +namespace CreateMatrix; + +internal class MatrixJsonSchemaBase +{ + [JsonRequired] + [JsonPropertyOrder(-2)] + public int SchemaVersion { get; set; } = MatrixJsonSchema.Version; +} + +internal class MatrixJsonSchema : MatrixJsonSchemaBase +{ + public const int Version = 2; + + [JsonRequired] + public List Images { get; set; } = []; + + public static void ToFile(string path, MatrixJsonSchema json) + { + json.SchemaVersion = Version; + File.WriteAllText(path, ToJson(json)); + } + + private static string ToJson(MatrixJsonSchema json) => + JsonSerializer.Serialize(json, MatrixJsonContext.Default.MatrixJsonSchema); +} + +internal sealed class LowercaseEnumConverter : JsonConverter + where T : struct, Enum +{ + public override T Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + string? value = + reader.GetString() ?? throw new JsonException($"Expected {typeof(T).Name} value."); + + return !Enum.TryParse(value, true, out T result) + ? throw new JsonException($"Invalid {typeof(T).Name} value '{value}'.") + : result; + } + + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => + writer.WriteStringValue(value.ToString().ToLowerInvariant()); +} + +[JsonSourceGenerationOptions( + AllowTrailingCommas = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IncludeFields = true, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, + ReadCommentHandling = JsonCommentHandling.Skip, + UseStringEnumConverter = true, + WriteIndented = true, + NewLine = "\n" +)] +[JsonSerializable(typeof(MatrixJsonSchema))] +[JsonSerializable(typeof(MatrixJsonSchemaBase))] +internal partial class MatrixJsonContext : JsonSerializerContext; diff --git a/CreateMatrix/PackagesJsonSchema.cs b/CreateMatrix/PackagesJsonSchema.cs index ad59f1ae..239183b2 100644 --- a/CreateMatrix/PackagesJsonSchema.cs +++ b/CreateMatrix/PackagesJsonSchema.cs @@ -1,104 +1,104 @@ -namespace CreateMatrix; - -// https://updates.networkoptix.com/{product}/{build}/packages.json -// https://updates.networkoptix.com/metavms/35134/packages.json -// https://updates.networkoptix.com/default/35270/packages.json -// https://updates.networkoptix.com/digitalwatchdog/35271/packages.json - -internal sealed class Variant -{ - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} - -internal sealed class Package -{ - [JsonPropertyName("component")] - public string Component { get; set; } = string.Empty; - - [JsonPropertyName("platform")] - public string PlatformName { get; set; } = string.Empty; - - [JsonPropertyName("file")] - public string File { get; set; } = string.Empty; - - [JsonPropertyName("variants")] - public List Variants { get; set; } = []; - - public bool IsX64Server() => - // Test for Server and x64 and Ubuntu - Component.Equals("server", StringComparison.OrdinalIgnoreCase) - && PlatformName.Equals("linux_x64", StringComparison.OrdinalIgnoreCase) - && Variants.Any(variant => - variant.Name.Equals("ubuntu", StringComparison.OrdinalIgnoreCase) - ); - - public bool IsArm64Server() => - // Test for Server and Arm64 and Ubuntu - Component.Equals("server", StringComparison.OrdinalIgnoreCase) - && PlatformName.Equals("linux_arm64", StringComparison.OrdinalIgnoreCase) - && Variants.Any(variant => - variant.Name.Equals("ubuntu", StringComparison.OrdinalIgnoreCase) - ); -} - -internal sealed class PackagesJsonSchema -{ - [JsonPropertyName("packages")] - public List Packages { get; set; } = []; - - private static PackagesJsonSchema FromJson(string json) - { - PackagesJsonSchema? jsonSchema = JsonSerializer.Deserialize( - json, - PackagesJsonContext.Default.PackagesJsonSchema - ); - ArgumentNullException.ThrowIfNull(jsonSchema); - return jsonSchema; - } - - public static async Task> GetPackagesAsync( - string releaseName, - int buildNumber, - CancellationToken cancellationToken - ) - { - // Load packages JSON - // https://updates.networkoptix.com/{product}/{build}/packages.json - ArgumentNullException.ThrowIfNull(releaseName); - - HttpClient httpClient = HttpClientFactory.GetHttpClient(); - Uri packagesUri = new( - $"https://updates.networkoptix.com/{releaseName}/{buildNumber}/packages.json" - ); - Log.Logger.Information("Getting package information from {Uri}", packagesUri); - string jsonString = await httpClient - .GetStringAsync(packagesUri, cancellationToken) - .ConfigureAwait(false); - - // Deserialize JSON - PackagesJsonSchema packagesSchema = FromJson(jsonString); - ArgumentNullException.ThrowIfNull(packagesSchema); - if (packagesSchema.Packages.Count == 0) - { - throw new InvalidOperationException($"No packages found in {packagesUri}"); - } - - // Return packages - return packagesSchema.Packages; - } -} - -[JsonSourceGenerationOptions( - AllowTrailingCommas = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - IncludeFields = true, - NumberHandling = JsonNumberHandling.AllowReadingFromString, - PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, - ReadCommentHandling = JsonCommentHandling.Skip, - UseStringEnumConverter = true, - WriteIndented = true, - NewLine = "\r\n" -)] -[JsonSerializable(typeof(PackagesJsonSchema))] -internal partial class PackagesJsonContext : JsonSerializerContext; +namespace CreateMatrix; + +// https://updates.networkoptix.com/{product}/{build}/packages.json +// https://updates.networkoptix.com/metavms/35134/packages.json +// https://updates.networkoptix.com/default/35270/packages.json +// https://updates.networkoptix.com/digitalwatchdog/35271/packages.json + +internal sealed class Variant +{ + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; +} + +internal sealed class Package +{ + [JsonPropertyName("component")] + public string Component { get; set; } = string.Empty; + + [JsonPropertyName("platform")] + public string PlatformName { get; set; } = string.Empty; + + [JsonPropertyName("file")] + public string File { get; set; } = string.Empty; + + [JsonPropertyName("variants")] + public List Variants { get; set; } = []; + + public bool IsX64Server() => + // Test for Server and x64 and Ubuntu + Component.Equals("server", StringComparison.OrdinalIgnoreCase) + && PlatformName.Equals("linux_x64", StringComparison.OrdinalIgnoreCase) + && Variants.Any(variant => + variant.Name.Equals("ubuntu", StringComparison.OrdinalIgnoreCase) + ); + + public bool IsArm64Server() => + // Test for Server and Arm64 and Ubuntu + Component.Equals("server", StringComparison.OrdinalIgnoreCase) + && PlatformName.Equals("linux_arm64", StringComparison.OrdinalIgnoreCase) + && Variants.Any(variant => + variant.Name.Equals("ubuntu", StringComparison.OrdinalIgnoreCase) + ); +} + +internal sealed class PackagesJsonSchema +{ + [JsonPropertyName("packages")] + public List Packages { get; set; } = []; + + private static PackagesJsonSchema FromJson(string json) + { + PackagesJsonSchema? jsonSchema = JsonSerializer.Deserialize( + json, + PackagesJsonContext.Default.PackagesJsonSchema + ); + ArgumentNullException.ThrowIfNull(jsonSchema); + return jsonSchema; + } + + public static async Task> GetPackagesAsync( + string releaseName, + int buildNumber, + CancellationToken cancellationToken + ) + { + // Load packages JSON + // https://updates.networkoptix.com/{product}/{build}/packages.json + ArgumentNullException.ThrowIfNull(releaseName); + + HttpClient httpClient = HttpClientFactory.GetHttpClient(); + Uri packagesUri = new( + $"https://updates.networkoptix.com/{releaseName}/{buildNumber}/packages.json" + ); + Log.Logger.Information("Getting package information from {Uri}", packagesUri); + string jsonString = await httpClient + .GetStringAsync(packagesUri, cancellationToken) + .ConfigureAwait(false); + + // Deserialize JSON + PackagesJsonSchema packagesSchema = FromJson(jsonString); + ArgumentNullException.ThrowIfNull(packagesSchema); + if (packagesSchema.Packages.Count == 0) + { + throw new InvalidOperationException($"No packages found in {packagesUri}"); + } + + // Return packages + return packagesSchema.Packages; + } +} + +[JsonSourceGenerationOptions( + AllowTrailingCommas = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IncludeFields = true, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, + ReadCommentHandling = JsonCommentHandling.Skip, + UseStringEnumConverter = true, + WriteIndented = true, + NewLine = "\n" +)] +[JsonSerializable(typeof(PackagesJsonSchema))] +internal partial class PackagesJsonContext : JsonSerializerContext; diff --git a/CreateMatrix/ProductInfo.cs b/CreateMatrix/ProductInfo.cs index 8746a534..8f2f1e5f 100644 --- a/CreateMatrix/ProductInfo.cs +++ b/CreateMatrix/ProductInfo.cs @@ -1,440 +1,440 @@ -namespace CreateMatrix; - -internal sealed class ProductInfo -{ - public enum ProductType - { - None, - NxGo, - NxMeta, - NxWitness, - DWSpectrum, - WisenetWAVE, - } - - public ProductType Product { get; set; } - - public List Versions { get; set; } = []; - - public string GetCompany() => GetCompany(Product); - - public string GetRelease() => GetRelease(Product); - - public string GetDescription() => GetDescription(Product); - - public string GetDocker(bool lsio) => GetDocker(Product, lsio); - - // Used for release JSON API https://updates.vmsproxy.com/{release}/releases.json path - public static string GetRelease(ProductType productType) => - productType switch - { - ProductType.NxGo => "nxgo", - ProductType.NxMeta => "metavms", - ProductType.NxWitness => "default", - ProductType.DWSpectrum => "digitalwatchdog", - ProductType.WisenetWAVE => "hanwha", - ProductType.None => throw new InvalidOperationException( - $"{nameof(ProductType)} is None" - ), - _ => throw new InvalidEnumArgumentException( - nameof(productType), - (int)productType, - typeof(ProductType) - ), - }; - - // Used for ${COMPANY_NAME} mediaserver install path and user account - public static string GetCompany(ProductType productType) => - productType switch - { - ProductType.NxGo => "networkoptix", - ProductType.NxMeta => "networkoptix-metavms", - ProductType.NxWitness => "networkoptix", - ProductType.DWSpectrum => "digitalwatchdog", - ProductType.WisenetWAVE => "hanwha", - ProductType.None => throw new InvalidOperationException( - $"{nameof(ProductType)} is None" - ), - _ => throw new InvalidEnumArgumentException( - nameof(productType), - (int)productType, - typeof(ProductType) - ), - }; - - // Used for ${LABEL_DESCRIPTION} in Dockerfile - public static string GetDescription(ProductType productType) => - productType switch - { - ProductType.NxGo => "Nx Go VMS", - ProductType.NxMeta => "Nx Meta VMS", - ProductType.NxWitness => "Nx Witness VMS", - ProductType.DWSpectrum => "DW Spectrum IPVMS", - ProductType.WisenetWAVE => "Wisenet WAVE VMS", - ProductType.None => throw new InvalidOperationException( - $"{nameof(ProductType)} is None" - ), - _ => throw new InvalidEnumArgumentException( - nameof(productType), - (int)productType, - typeof(ProductType) - ), - }; - - // Dockerfile name, excluding the .Dockerfile extension. - // This is the single source of the image/Dockerfile naming convention. ImageInfo derives its - // name from here. The base variant is a bool (Ubuntu vs LSIO). Promote it to an enum only if a - // third base type is ever added (which would also ripple through ComposeFile/DockerFile). - public static string GetDocker(ProductType productType, bool lsio) => - $"{productType}{(lsio ? "-LSIO" : "")}"; - - public static IEnumerable GetProductTypes() => - // Create list of product types - [.. Enum.GetValues().Where(productType => productType != ProductType.None)]; - - public static List GetProducts() => - // Create list of all known products - [ - .. from ProductType productType in GetProductTypes() - select new ProductInfo { Product = productType }, - ]; - - public async Task FetchVersionsAsync(CancellationToken cancellationToken) - { - // Get version information using releases.json and package.json - Log.Logger.Information("{Product}: Getting online release information...", Product); - try - { - // Get all releases - List releasesList = await ReleasesJsonSchema - .GetReleasesAsync(GetRelease(), cancellationToken) - .ConfigureAwait(false); - foreach (Release release in releasesList) - { - // Only process "vms" products - if (!release.Product.Equals(Release.VmsProduct, StringComparison.OrdinalIgnoreCase)) - { - Log.Logger.Warning( - "{Product}: Skipping {ReleaseProduct}", - Product, - release.Product - ); - continue; - } - - // Set the version and label - VersionInfo versionInfo = CreateVersionInfo(release); - - // Get the build number from the version - int buildNumber = versionInfo.GetBuildNumber(); - - // Get available packages for this release - List packageList = await PackagesJsonSchema - .GetPackagesAsync(GetRelease(), buildNumber, cancellationToken) - .ConfigureAwait(false); - - // Get the x64 and arm64 server ubuntu server packages - Package packageX64 = - packageList.Find(item => item.IsX64Server()) - ?? throw new InvalidOperationException( - $"{Product}: No x64 Ubuntu server package found for build {buildNumber} (version {versionInfo.Version})" - ); - if (string.IsNullOrEmpty(packageX64.File)) - { - throw new InvalidOperationException( - $"{Product}: x64 Ubuntu server package for build {buildNumber} (version {versionInfo.Version}) has no file name" - ); - } - Package packageArm64 = - packageList.Find(item => item.IsArm64Server()) - ?? throw new InvalidOperationException( - $"{Product}: No arm64 Ubuntu server package found for build {buildNumber} (version {versionInfo.Version})" - ); - if (string.IsNullOrEmpty(packageArm64.File)) - { - throw new InvalidOperationException( - $"{Product}: arm64 Ubuntu server package for build {buildNumber} (version {versionInfo.Version}) has no file name" - ); - } - - // Create the download URLs - // https://updates.networkoptix.com/{product}/{build}/{file} - versionInfo.UriX64 = - $"https://updates.networkoptix.com/{GetRelease()}/{buildNumber}/{packageX64.File}"; - versionInfo.UriArm64 = - $"https://updates.networkoptix.com/{GetRelease()}/{buildNumber}/{packageArm64.File}"; - - // Verify and add to list - if (VerifyVersion(versionInfo)) - { - Versions.Add(versionInfo); - } - } - - // Make sure all labels are correct - VerifyLabels(); - } - catch (Exception e) when (Log.Logger.LogAndHandle(e)) - { - // Log and rethrow - throw; - } - } - - public VersionInfo CreateVersionInfo(Release release) - { - // Create a version with its label from the release. - // Note: AddLabel() may move the label off other versions already in the list. - if (string.IsNullOrEmpty(release.Version)) - { - throw new InvalidOperationException( - $"{Product}: Release has no version (publication type '{release.PublicationType}')" - ); - } - VersionInfo versionInfo = new(); - versionInfo.SetVersion(release.Version); - AddLabel(versionInfo, release.GetLabel()); - return versionInfo; - } - - private bool VerifyVersion(VersionInfo versionInfo) - { - // Static rules: - - // Ubuntu Noble requires version 6.0 or later - if (versionInfo.CompareTo("6.0") >= 0) - { - return true; - } - - Log.Logger.Warning( - "{Product}:{Version} : Ubuntu Noble requires v6.0+", - Product, - versionInfo.Version - ); - return false; - } - - public void AddLabel(VersionInfo versionInfo, VersionInfo.LabelType label) - { - // Ignore if label is None - if (label == VersionInfo.LabelType.None) - { - return; - } - - // Does this label already exists in other versions - VersionInfo? existingVersion = Versions.Find(item => item.Labels.Contains(label)); - if (existingVersion == null) - { - // New label - versionInfo.Labels.Add(label); - return; - } - - // Is this version larger than the other version - if (versionInfo.CompareTo(existingVersion) <= 0) - { - return; - } - - Log.Logger.Warning( - "{Product}: Replacing {Label} from {ExistingVersion} to {NewVersion}", - Product, - label, - existingVersion.Version, - versionInfo.Version - ); - - // Remove from other version and add to this version - _ = existingVersion.Labels.Remove(label); - versionInfo.Labels.Add(label); - } - - private VersionInfo? FindMissingLabel( - VersionInfo.LabelType targetLabel, - List sourceLabels - ) - { - foreach (VersionInfo.LabelType label in sourceLabels) - { - // Find last matching item, must be sorted - VersionInfo? version = Versions.FindLast(item => item.Labels.Contains(label)); - if (version == null) - { - continue; - } - - Log.Logger.Warning( - "{Product}: Using {SourceLabel} for {TargetLabel}", - Product, - label, - targetLabel - ); - return version; - } - return null; - } - - public void VerifyLabels() - { - // Sort by version number - Versions.Sort(new VersionInfoComparer()); - - // If no Latest label is set, use Stable or RC or Beta as Latest - if (!Versions.Any(item => item.Labels.Contains(VersionInfo.LabelType.Latest))) - { - VersionInfo latest = - FindMissingLabel( - VersionInfo.LabelType.Latest, - [ - VersionInfo.LabelType.Stable, - VersionInfo.LabelType.RC, - VersionInfo.LabelType.Beta, - ] - ) ?? throw new InvalidOperationException("Latest label could not be resolved."); - latest.Labels.Add(VersionInfo.LabelType.Latest); - } - - // If no Stable label is set, use Latest as stable - if (!Versions.Any(item => item.Labels.Contains(VersionInfo.LabelType.Stable))) - { - VersionInfo stable = - FindMissingLabel(VersionInfo.LabelType.Stable, [VersionInfo.LabelType.Latest]) - ?? throw new InvalidOperationException("Stable label could not be resolved."); - stable.Labels.Add(VersionInfo.LabelType.Stable); - } - - // Remove all versions without labels - _ = Versions.RemoveAll(item => item.Labels.Count == 0); - // Sort by label - Versions.ForEach(item => item.Labels.Sort()); - - // Must have 1 Latest and 1 Stable label - ArgumentOutOfRangeException.ThrowIfNotEqual( - Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)), - 1 - ); - ArgumentOutOfRangeException.ThrowIfNotEqual( - Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)), - 1 - ); - - // Must have no more than 1 Beta or RC labels - ArgumentOutOfRangeException.ThrowIfGreaterThan( - Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)), - 1 - ); - ArgumentOutOfRangeException.ThrowIfGreaterThan( - Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)), - 1 - ); - - // Must have no duplicate version numbers - VerifyNoDuplicateVersions(); - } - - public void VerifyNoDuplicateVersions() - { - // Each version number must appear at most once. - // Duplicates collapse when the matrix builds a set keyed by version number - // (see ImageInfo.CreateImages() and VersionInfoComparer) and must be rejected - // rather than written to the version file. - List duplicateVersions = - [ - .. Versions - .GroupBy(item => VersionInfo.ParseVersion(item.Version)) - .Where(group => group.Count() > 1) - .Select(group => group.First().Version), - ]; - if (duplicateVersions.Count > 0) - { - throw new InvalidOperationException( - $"{Product}: Duplicate version numbers found: {string.Join(", ", duplicateVersions)}" - ); - } - } - - public void LogInformation() - { - foreach (VersionInfo version in Versions) - { - Log.Logger.Information( - "{Product}: Version: {Version}, Label: {Labels}, UriX64: {UriX64}, UriArm64: {UriArm64}", - Product, - version.Version, - version.Labels, - version.UriX64, - version.UriArm64 - ); - } - } - - public async Task VerifyUrlsAsync(CancellationToken cancellationToken) - { - try - { - HttpClient httpClient = HttpClientFactory.GetHttpClient(); - foreach (VersionInfo versionUri in Versions) - { - // Will throw on error - await VerifyUrlAsync(httpClient, new Uri(versionUri.UriX64), cancellationToken) - .ConfigureAwait(false); - await VerifyUrlAsync(httpClient, new Uri(versionUri.UriArm64), cancellationToken) - .ConfigureAwait(false); - } - } - catch (Exception e) when (Log.Logger.LogAndHandle(e)) - { - // Log and rethrow - throw; - } - } - - private static async Task VerifyUrlAsync( - HttpClient httpClient, - Uri url, - CancellationToken cancellationToken - ) - { - // Will throw on failure - ArgumentNullException.ThrowIfNull(httpClient); - ArgumentNullException.ThrowIfNull(url); - - // Get URL - Log.Logger.Information("Verifying Url: {Url}", url); - using HttpResponseMessage httpResponse = await httpClient - .GetAsync(url, cancellationToken) - .ConfigureAwait(false); - _ = httpResponse.EnsureSuccessStatusCode(); - - // Get filename from httpResponse or Uri path - string? fileName = httpResponse.Content.Headers.ContentDisposition?.FileName; - fileName ??= Path.GetFileName(url.LocalPath); - - // Log details - Log.Logger.Information( - "File Name: {FileName}, File Size: {FileSize}, Last Modified: {LastModified}", - fileName, - httpResponse.Content.Headers.ContentLength, - httpResponse.Content.Headers.LastModified - ); - } - - public void Verify() - { - // Match verification logic executed during GetVersions() - - // Verify each version - List removeVersions = []; - foreach (VersionInfo version in Versions.Where(version => !VerifyVersion(version))) - { - Log.Logger.Warning("{Product} : Removing {Version}", Product, version.Version); - removeVersions.Add(version); - } - _ = Versions.RemoveAll(removeVersions.Contains); - - // Verify labels - VerifyLabels(); - } -} +namespace CreateMatrix; + +internal sealed class ProductInfo +{ + public enum ProductType + { + None, + NxGo, + NxMeta, + NxWitness, + DWSpectrum, + WisenetWAVE, + } + + public ProductType Product { get; set; } + + public List Versions { get; set; } = []; + + public string GetCompany() => GetCompany(Product); + + public string GetRelease() => GetRelease(Product); + + public string GetDescription() => GetDescription(Product); + + public string GetDocker(bool lsio) => GetDocker(Product, lsio); + + // Used for release JSON API https://updates.vmsproxy.com/{release}/releases.json path + public static string GetRelease(ProductType productType) => + productType switch + { + ProductType.NxGo => "nxgo", + ProductType.NxMeta => "metavms", + ProductType.NxWitness => "default", + ProductType.DWSpectrum => "digitalwatchdog", + ProductType.WisenetWAVE => "hanwha", + ProductType.None => throw new InvalidOperationException( + $"{nameof(ProductType)} is None" + ), + _ => throw new InvalidEnumArgumentException( + nameof(productType), + (int)productType, + typeof(ProductType) + ), + }; + + // Used for ${COMPANY_NAME} mediaserver install path and user account + public static string GetCompany(ProductType productType) => + productType switch + { + ProductType.NxGo => "networkoptix", + ProductType.NxMeta => "networkoptix-metavms", + ProductType.NxWitness => "networkoptix", + ProductType.DWSpectrum => "digitalwatchdog", + ProductType.WisenetWAVE => "hanwha", + ProductType.None => throw new InvalidOperationException( + $"{nameof(ProductType)} is None" + ), + _ => throw new InvalidEnumArgumentException( + nameof(productType), + (int)productType, + typeof(ProductType) + ), + }; + + // Used for ${LABEL_DESCRIPTION} in Dockerfile + public static string GetDescription(ProductType productType) => + productType switch + { + ProductType.NxGo => "Nx Go VMS", + ProductType.NxMeta => "Nx Meta VMS", + ProductType.NxWitness => "Nx Witness VMS", + ProductType.DWSpectrum => "DW Spectrum IPVMS", + ProductType.WisenetWAVE => "Wisenet WAVE VMS", + ProductType.None => throw new InvalidOperationException( + $"{nameof(ProductType)} is None" + ), + _ => throw new InvalidEnumArgumentException( + nameof(productType), + (int)productType, + typeof(ProductType) + ), + }; + + // Dockerfile name, excluding the .Dockerfile extension. + // This is the single source of the image/Dockerfile naming convention. ImageInfo derives its + // name from here. The base variant is a bool (Ubuntu vs LSIO). Promote it to an enum only if a + // third base type is ever added (which would also ripple through ComposeFile/DockerFile). + public static string GetDocker(ProductType productType, bool lsio) => + $"{productType}{(lsio ? "-LSIO" : "")}"; + + public static IEnumerable GetProductTypes() => + // Create list of product types + [.. Enum.GetValues().Where(productType => productType != ProductType.None)]; + + public static List GetProducts() => + // Create list of all known products + [ + .. from ProductType productType in GetProductTypes() + select new ProductInfo { Product = productType }, + ]; + + public async Task FetchVersionsAsync(CancellationToken cancellationToken) + { + // Get version information using releases.json and package.json + Log.Logger.Information("{Product}: Getting online release information...", Product); + try + { + // Get all releases + List releasesList = await ReleasesJsonSchema + .GetReleasesAsync(GetRelease(), cancellationToken) + .ConfigureAwait(false); + foreach (Release release in releasesList) + { + // Only process "vms" products + if (!release.Product.Equals(Release.VmsProduct, StringComparison.OrdinalIgnoreCase)) + { + Log.Logger.Warning( + "{Product}: Skipping {ReleaseProduct}", + Product, + release.Product + ); + continue; + } + + // Set the version and label + VersionInfo versionInfo = CreateVersionInfo(release); + + // Get the build number from the version + int buildNumber = versionInfo.GetBuildNumber(); + + // Get available packages for this release + List packageList = await PackagesJsonSchema + .GetPackagesAsync(GetRelease(), buildNumber, cancellationToken) + .ConfigureAwait(false); + + // Get the x64 and arm64 server ubuntu server packages + Package packageX64 = + packageList.Find(item => item.IsX64Server()) + ?? throw new InvalidOperationException( + $"{Product}: No x64 Ubuntu server package found for build {buildNumber} (version {versionInfo.Version})" + ); + if (string.IsNullOrEmpty(packageX64.File)) + { + throw new InvalidOperationException( + $"{Product}: x64 Ubuntu server package for build {buildNumber} (version {versionInfo.Version}) has no file name" + ); + } + Package packageArm64 = + packageList.Find(item => item.IsArm64Server()) + ?? throw new InvalidOperationException( + $"{Product}: No arm64 Ubuntu server package found for build {buildNumber} (version {versionInfo.Version})" + ); + if (string.IsNullOrEmpty(packageArm64.File)) + { + throw new InvalidOperationException( + $"{Product}: arm64 Ubuntu server package for build {buildNumber} (version {versionInfo.Version}) has no file name" + ); + } + + // Create the download URLs + // https://updates.networkoptix.com/{product}/{build}/{file} + versionInfo.UriX64 = + $"https://updates.networkoptix.com/{GetRelease()}/{buildNumber}/{packageX64.File}"; + versionInfo.UriArm64 = + $"https://updates.networkoptix.com/{GetRelease()}/{buildNumber}/{packageArm64.File}"; + + // Verify and add to list + if (VerifyVersion(versionInfo)) + { + Versions.Add(versionInfo); + } + } + + // Make sure all labels are correct + VerifyLabels(); + } + catch (Exception e) when (Log.Logger.LogAndHandle(e)) + { + // Log and rethrow + throw; + } + } + + public VersionInfo CreateVersionInfo(Release release) + { + // Create a version with its label from the release. + // Note: AddLabel() may move the label off other versions already in the list. + if (string.IsNullOrEmpty(release.Version)) + { + throw new InvalidOperationException( + $"{Product}: Release has no version (publication type '{release.PublicationType}')" + ); + } + VersionInfo versionInfo = new(); + versionInfo.SetVersion(release.Version); + AddLabel(versionInfo, release.GetLabel()); + return versionInfo; + } + + private bool VerifyVersion(VersionInfo versionInfo) + { + // Static rules: + + // Ubuntu Noble requires version 6.0 or later + if (versionInfo.CompareTo("6.0") >= 0) + { + return true; + } + + Log.Logger.Warning( + "{Product}:{Version} : Ubuntu Noble requires v6.0+", + Product, + versionInfo.Version + ); + return false; + } + + public void AddLabel(VersionInfo versionInfo, VersionInfo.LabelType label) + { + // Ignore if label is None + if (label == VersionInfo.LabelType.None) + { + return; + } + + // Does this label already exists in other versions + VersionInfo? existingVersion = Versions.Find(item => item.Labels.Contains(label)); + if (existingVersion == null) + { + // New label + versionInfo.Labels.Add(label); + return; + } + + // Is this version larger than the other version + if (versionInfo.CompareTo(existingVersion) <= 0) + { + return; + } + + Log.Logger.Warning( + "{Product}: Replacing {Label} from {ExistingVersion} to {NewVersion}", + Product, + label, + existingVersion.Version, + versionInfo.Version + ); + + // Remove from other version and add to this version + _ = existingVersion.Labels.Remove(label); + versionInfo.Labels.Add(label); + } + + private VersionInfo? FindMissingLabel( + VersionInfo.LabelType targetLabel, + List sourceLabels + ) + { + foreach (VersionInfo.LabelType label in sourceLabels) + { + // Find last matching item, must be sorted + VersionInfo? version = Versions.FindLast(item => item.Labels.Contains(label)); + if (version == null) + { + continue; + } + + Log.Logger.Warning( + "{Product}: Using {SourceLabel} for {TargetLabel}", + Product, + label, + targetLabel + ); + return version; + } + return null; + } + + public void VerifyLabels() + { + // Sort by version number + Versions.Sort(new VersionInfoComparer()); + + // If no Latest label is set, use Stable or RC or Beta as Latest + if (!Versions.Any(item => item.Labels.Contains(VersionInfo.LabelType.Latest))) + { + VersionInfo latest = + FindMissingLabel( + VersionInfo.LabelType.Latest, + [ + VersionInfo.LabelType.Stable, + VersionInfo.LabelType.RC, + VersionInfo.LabelType.Beta, + ] + ) ?? throw new InvalidOperationException("Latest label could not be resolved."); + latest.Labels.Add(VersionInfo.LabelType.Latest); + } + + // If no Stable label is set, use Latest as stable + if (!Versions.Any(item => item.Labels.Contains(VersionInfo.LabelType.Stable))) + { + VersionInfo stable = + FindMissingLabel(VersionInfo.LabelType.Stable, [VersionInfo.LabelType.Latest]) + ?? throw new InvalidOperationException("Stable label could not be resolved."); + stable.Labels.Add(VersionInfo.LabelType.Stable); + } + + // Remove all versions without labels + _ = Versions.RemoveAll(item => item.Labels.Count == 0); + // Sort by label + Versions.ForEach(item => item.Labels.Sort()); + + // Must have 1 Latest and 1 Stable label + ArgumentOutOfRangeException.ThrowIfNotEqual( + Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)), + 1 + ); + ArgumentOutOfRangeException.ThrowIfNotEqual( + Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)), + 1 + ); + + // Must have no more than 1 Beta or RC labels + ArgumentOutOfRangeException.ThrowIfGreaterThan( + Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)), + 1 + ); + ArgumentOutOfRangeException.ThrowIfGreaterThan( + Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)), + 1 + ); + + // Must have no duplicate version numbers + VerifyNoDuplicateVersions(); + } + + public void VerifyNoDuplicateVersions() + { + // Each version number must appear at most once. + // Duplicates collapse when the matrix builds a set keyed by version number + // (see ImageInfo.CreateImages() and VersionInfoComparer) and must be rejected + // rather than written to the version file. + List duplicateVersions = + [ + .. Versions + .GroupBy(item => VersionInfo.ParseVersion(item.Version)) + .Where(group => group.Count() > 1) + .Select(group => group.First().Version), + ]; + if (duplicateVersions.Count > 0) + { + throw new InvalidOperationException( + $"{Product}: Duplicate version numbers found: {string.Join(", ", duplicateVersions)}" + ); + } + } + + public void LogInformation() + { + foreach (VersionInfo version in Versions) + { + Log.Logger.Information( + "{Product}: Version: {Version}, Label: {Labels}, UriX64: {UriX64}, UriArm64: {UriArm64}", + Product, + version.Version, + version.Labels, + version.UriX64, + version.UriArm64 + ); + } + } + + public async Task VerifyUrlsAsync(CancellationToken cancellationToken) + { + try + { + HttpClient httpClient = HttpClientFactory.GetHttpClient(); + foreach (VersionInfo versionUri in Versions) + { + // Will throw on error + await VerifyUrlAsync(httpClient, new Uri(versionUri.UriX64), cancellationToken) + .ConfigureAwait(false); + await VerifyUrlAsync(httpClient, new Uri(versionUri.UriArm64), cancellationToken) + .ConfigureAwait(false); + } + } + catch (Exception e) when (Log.Logger.LogAndHandle(e)) + { + // Log and rethrow + throw; + } + } + + private static async Task VerifyUrlAsync( + HttpClient httpClient, + Uri url, + CancellationToken cancellationToken + ) + { + // Will throw on failure + ArgumentNullException.ThrowIfNull(httpClient); + ArgumentNullException.ThrowIfNull(url); + + // Get URL + Log.Logger.Information("Verifying Url: {Url}", url); + using HttpResponseMessage httpResponse = await httpClient + .GetAsync(url, cancellationToken) + .ConfigureAwait(false); + _ = httpResponse.EnsureSuccessStatusCode(); + + // Get filename from httpResponse or Uri path + string? fileName = httpResponse.Content.Headers.ContentDisposition?.FileName; + fileName ??= Path.GetFileName(url.LocalPath); + + // Log details + Log.Logger.Information( + "File Name: {FileName}, File Size: {FileSize}, Last Modified: {LastModified}", + fileName, + httpResponse.Content.Headers.ContentLength, + httpResponse.Content.Headers.LastModified + ); + } + + public void Verify() + { + // Match verification logic executed during GetVersions() + + // Verify each version + List removeVersions = []; + foreach (VersionInfo version in Versions.Where(version => !VerifyVersion(version))) + { + Log.Logger.Warning("{Product} : Removing {Version}", Product, version.Version); + removeVersions.Add(version); + } + _ = Versions.RemoveAll(removeVersions.Contains); + + // Verify labels + VerifyLabels(); + } +} diff --git a/CreateMatrix/Program.cs b/CreateMatrix/Program.cs index 770f72b8..0973d468 100644 --- a/CreateMatrix/Program.cs +++ b/CreateMatrix/Program.cs @@ -1,188 +1,188 @@ -using Serilog.Sinks.SystemConsole.Themes; - -// dotnet publish --self-contained false --output ./publish -// ./publish/CreateMatrix matrix --updateversion --matrixpath ./JSON/Matrix.json --versionpath ./JSON/Version.json -// echo $? - -namespace CreateMatrix; - -internal sealed class Program( - CommandLine.Options commandLineOptions, - CancellationToken cancellationToken -) -{ - internal static async Task Main(string[] args) - { - try - { - // Parse commandline - CommandLine commandLine = new(args); - - // Bypass startup for errors or help and version commands - if (CommandLine.BypassStartup(commandLine.Result)) - { - return await commandLine.Result.InvokeAsync().ConfigureAwait(false); - } - - // Log to the console - LoggerConfiguration loggerConfiguration = new LoggerConfiguration() - .Enrich.WithThreadId() - .WriteTo.Console( - theme: AnsiConsoleTheme.Code, - formatProvider: CultureInfo.InvariantCulture, - outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] [t:{ThreadId}{ThreadName}] {Message:lj}{NewLine}{Exception}" - ); - Log.Logger = loggerConfiguration.CreateLogger(); - - // Invoke command - return await commandLine.Result.InvokeAsync().ConfigureAwait(false); - } - catch (OperationCanceledException) - { - Log.Logger.Warning("Operation was cancelled."); - return 130; // POSIX standard for SIGINT - } - catch (Exception ex) when (Log.Logger.LogAndHandle(ex)) - { - return 1; - } - finally - { - await Log.CloseAndFlushAsync().ConfigureAwait(false); - } - } - - internal async Task ExecuteVersionAsync() - { - ArgumentNullException.ThrowIfNull(commandLineOptions.VersionPath); - - // Get versions for all products using releases API - VersionJsonSchema versionSchema = new(); - Log.Logger.Information("Getting version information online..."); - versionSchema.Products.AddRange(ProductInfo.GetProducts()); - foreach (ProductInfo productInfo in versionSchema.Products) - { - await productInfo.FetchVersionsAsync(cancellationToken).ConfigureAwait(false); - productInfo.LogInformation(); - await productInfo.VerifyUrlsAsync(cancellationToken).ConfigureAwait(false); - } - - // Write to file - Log.Logger.Information( - "Writing version information to {Path}", - commandLineOptions.VersionPath.FullName - ); - VersionJsonSchema.ToFile(commandLineOptions.VersionPath.FullName, versionSchema); - - return 0; - } - - internal async Task ExecuteMatrixAsync() - { - ArgumentNullException.ThrowIfNull(commandLineOptions.VersionPath); - ArgumentNullException.ThrowIfNull(commandLineOptions.MatrixPath); - - // Load version info from file - Log.Logger.Information( - "Reading version information from {Path}", - commandLineOptions.VersionPath.FullName - ); - VersionJsonSchema fileSchema = VersionJsonSchema.FromFile( - commandLineOptions.VersionPath.FullName - ); - - // Re-verify as rules may have changed after file was written - foreach (ProductInfo productInfo in fileSchema.Products) - { - productInfo.Verify(); - productInfo.LogInformation(); - } - - // Update version information - if (commandLineOptions.UpdateVersion) - { - // Get versions for all products using releases API - VersionJsonSchema onlineSchema = new(); - Log.Logger.Information("Getting version information online..."); - onlineSchema.Products.AddRange(ProductInfo.GetProducts()); - foreach (ProductInfo productInfo in onlineSchema.Products) - { - await productInfo.FetchVersionsAsync(cancellationToken).ConfigureAwait(false); - productInfo.LogInformation(); - } - - // Make sure the labelled version numbers do not regress - ReleaseVersionForward.Verify(fileSchema.Products, onlineSchema.Products); - - // Make sure the forward merge did not introduce duplicate version numbers - onlineSchema.Products.ForEach(productInfo => productInfo.VerifyNoDuplicateVersions()); - - // Verify URL's - foreach (ProductInfo productInfo in onlineSchema.Products) - { - await productInfo.VerifyUrlsAsync(cancellationToken).ConfigureAwait(false); - } - - // Update the file version with the online version - Log.Logger.Information( - "Writing version information to {Path}", - commandLineOptions.VersionPath.FullName - ); - VersionJsonSchema.ToFile(commandLineOptions.VersionPath.FullName, onlineSchema); - fileSchema = onlineSchema; - } - else - { - // Verify URL's - foreach (ProductInfo productInfo in fileSchema.Products) - { - await productInfo.VerifyUrlsAsync(cancellationToken).ConfigureAwait(false); - } - } - - // Create matrix - Log.Logger.Information("Creating Matrix from versions"); - MatrixJsonSchema matrixSchema = new(); - matrixSchema.Images.AddRange(ImageInfo.CreateImages(fileSchema.Products)); - Log.Logger.Information("Created {Count} images in matrix", matrixSchema.Images.Count); - matrixSchema.Images.ForEach(imageInfo => imageInfo.LogInformation()); - - // Write matrix - Log.Logger.Information( - "Writing matrix information to {Path}", - commandLineOptions.MatrixPath.FullName - ); - MatrixJsonSchema.ToFile(commandLineOptions.MatrixPath.FullName, matrixSchema); - - return 0; - } - - internal Task ExecuteMakeAsync() - { - ArgumentNullException.ThrowIfNull(commandLineOptions.VersionPath); - ArgumentNullException.ThrowIfNull(commandLineOptions.MakeDirectory); - ArgumentNullException.ThrowIfNull(commandLineOptions.DockerDirectory); - - // Load version info from file - Log.Logger.Information( - "Reading version information from {Path}", - commandLineOptions.VersionPath.FullName - ); - VersionJsonSchema versionSchema = VersionJsonSchema.FromFile( - commandLineOptions.VersionPath.FullName - ); - - // Create Compose files - ComposeFile.Create(commandLineOptions.MakeDirectory); - - // Create Docker files - List products = [.. versionSchema.Products]; - DockerFile.Create( - products, - commandLineOptions.DockerDirectory.FullName, - commandLineOptions.VersionLabel - ); - - return Task.FromResult(0); - } -} +using Serilog.Sinks.SystemConsole.Themes; + +// dotnet publish --self-contained false --output ./publish +// ./publish/CreateMatrix matrix --updateversion --matrixpath ./JSON/Matrix.json --versionpath ./JSON/Version.json +// echo $? + +namespace CreateMatrix; + +internal sealed class Program( + CommandLine.Options commandLineOptions, + CancellationToken cancellationToken +) +{ + internal static async Task Main(string[] args) + { + try + { + // Parse commandline + CommandLine commandLine = new(args); + + // Bypass startup for errors or help and version commands + if (CommandLine.BypassStartup(commandLine.Result)) + { + return await commandLine.Result.InvokeAsync().ConfigureAwait(false); + } + + // Log to the console + LoggerConfiguration loggerConfiguration = new LoggerConfiguration() + .Enrich.WithThreadId() + .WriteTo.Console( + theme: AnsiConsoleTheme.Code, + formatProvider: CultureInfo.InvariantCulture, + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] [t:{ThreadId}{ThreadName}] {Message:lj}{NewLine}{Exception}" + ); + Log.Logger = loggerConfiguration.CreateLogger(); + + // Invoke command + return await commandLine.Result.InvokeAsync().ConfigureAwait(false); + } + catch (OperationCanceledException) + { + Log.Logger.Warning("Operation was cancelled."); + return 130; // POSIX standard for SIGINT + } + catch (Exception ex) when (Log.Logger.LogAndHandle(ex)) + { + return 1; + } + finally + { + await Log.CloseAndFlushAsync().ConfigureAwait(false); + } + } + + internal async Task ExecuteVersionAsync() + { + ArgumentNullException.ThrowIfNull(commandLineOptions.VersionPath); + + // Get versions for all products using releases API + VersionJsonSchema versionSchema = new(); + Log.Logger.Information("Getting version information online..."); + versionSchema.Products.AddRange(ProductInfo.GetProducts()); + foreach (ProductInfo productInfo in versionSchema.Products) + { + await productInfo.FetchVersionsAsync(cancellationToken).ConfigureAwait(false); + productInfo.LogInformation(); + await productInfo.VerifyUrlsAsync(cancellationToken).ConfigureAwait(false); + } + + // Write to file + Log.Logger.Information( + "Writing version information to {Path}", + commandLineOptions.VersionPath.FullName + ); + VersionJsonSchema.ToFile(commandLineOptions.VersionPath.FullName, versionSchema); + + return 0; + } + + internal async Task ExecuteMatrixAsync() + { + ArgumentNullException.ThrowIfNull(commandLineOptions.VersionPath); + ArgumentNullException.ThrowIfNull(commandLineOptions.MatrixPath); + + // Load version info from file + Log.Logger.Information( + "Reading version information from {Path}", + commandLineOptions.VersionPath.FullName + ); + VersionJsonSchema fileSchema = VersionJsonSchema.FromFile( + commandLineOptions.VersionPath.FullName + ); + + // Re-verify as rules may have changed after file was written + foreach (ProductInfo productInfo in fileSchema.Products) + { + productInfo.Verify(); + productInfo.LogInformation(); + } + + // Update version information + if (commandLineOptions.UpdateVersion) + { + // Get versions for all products using releases API + VersionJsonSchema onlineSchema = new(); + Log.Logger.Information("Getting version information online..."); + onlineSchema.Products.AddRange(ProductInfo.GetProducts()); + foreach (ProductInfo productInfo in onlineSchema.Products) + { + await productInfo.FetchVersionsAsync(cancellationToken).ConfigureAwait(false); + productInfo.LogInformation(); + } + + // Make sure the labelled version numbers do not regress + ReleaseVersionForward.Verify(fileSchema.Products, onlineSchema.Products); + + // Make sure the forward merge did not introduce duplicate version numbers + onlineSchema.Products.ForEach(productInfo => productInfo.VerifyNoDuplicateVersions()); + + // Verify URL's + foreach (ProductInfo productInfo in onlineSchema.Products) + { + await productInfo.VerifyUrlsAsync(cancellationToken).ConfigureAwait(false); + } + + // Update the file version with the online version + Log.Logger.Information( + "Writing version information to {Path}", + commandLineOptions.VersionPath.FullName + ); + VersionJsonSchema.ToFile(commandLineOptions.VersionPath.FullName, onlineSchema); + fileSchema = onlineSchema; + } + else + { + // Verify URL's + foreach (ProductInfo productInfo in fileSchema.Products) + { + await productInfo.VerifyUrlsAsync(cancellationToken).ConfigureAwait(false); + } + } + + // Create matrix + Log.Logger.Information("Creating Matrix from versions"); + MatrixJsonSchema matrixSchema = new(); + matrixSchema.Images.AddRange(ImageInfo.CreateImages(fileSchema.Products)); + Log.Logger.Information("Created {Count} images in matrix", matrixSchema.Images.Count); + matrixSchema.Images.ForEach(imageInfo => imageInfo.LogInformation()); + + // Write matrix + Log.Logger.Information( + "Writing matrix information to {Path}", + commandLineOptions.MatrixPath.FullName + ); + MatrixJsonSchema.ToFile(commandLineOptions.MatrixPath.FullName, matrixSchema); + + return 0; + } + + internal Task ExecuteMakeAsync() + { + ArgumentNullException.ThrowIfNull(commandLineOptions.VersionPath); + ArgumentNullException.ThrowIfNull(commandLineOptions.MakeDirectory); + ArgumentNullException.ThrowIfNull(commandLineOptions.DockerDirectory); + + // Load version info from file + Log.Logger.Information( + "Reading version information from {Path}", + commandLineOptions.VersionPath.FullName + ); + VersionJsonSchema versionSchema = VersionJsonSchema.FromFile( + commandLineOptions.VersionPath.FullName + ); + + // Create Compose files + ComposeFile.Create(commandLineOptions.MakeDirectory); + + // Create Docker files + List products = [.. versionSchema.Products]; + DockerFile.Create( + products, + commandLineOptions.DockerDirectory.FullName, + commandLineOptions.VersionLabel + ); + + return Task.FromResult(0); + } +} diff --git a/CreateMatrix/Properties/launchSettings.json b/CreateMatrix/Properties/launchSettings.json index 04b34339..57b77928 100644 --- a/CreateMatrix/Properties/launchSettings.json +++ b/CreateMatrix/Properties/launchSettings.json @@ -1,28 +1,28 @@ -{ - "profiles": { - "WSL": { - "commandName": "WSL2", - "distributionName": "" - }, - "Matrix": { - "commandName": "Project", - "commandLineArgs": "matrix --versionpath=./Make/Version.json --matrixpath=./Make/Matrix.json --updateversion", - "workingDirectory": "$(SolutionDir)" - }, - "Version": { - "commandName": "Project", - "commandLineArgs": "version --versionpath=./Make/Version.json", - "workingDirectory": "$(SolutionDir)" - }, - "Schema": { - "commandName": "Project", - "commandLineArgs": "schema --versionschemapath=./JSON/Version.schema.json --matrixschemapath=./JSON/Matrix.schema.json", - "workingDirectory": "$(ProjectDir)" - }, - "Make": { - "commandName": "Project", - "commandLineArgs": "make --versionpath=./Make/Version.json --makedirectory=./Make --dockerdirectory=./Docker --versionlabel=Beta", - "workingDirectory": "$(SolutionDir)" - } - } -} +{ + "profiles": { + "WSL": { + "commandName": "WSL2", + "distributionName": "" + }, + "Matrix": { + "commandName": "Project", + "commandLineArgs": "matrix --versionpath=./Make/Version.json --matrixpath=./Make/Matrix.json --updateversion", + "workingDirectory": "$(SolutionDir)" + }, + "Version": { + "commandName": "Project", + "commandLineArgs": "version --versionpath=./Make/Version.json", + "workingDirectory": "$(SolutionDir)" + }, + "Schema": { + "commandName": "Project", + "commandLineArgs": "schema --versionschemapath=./JSON/Version.schema.json --matrixschemapath=./JSON/Matrix.schema.json", + "workingDirectory": "$(ProjectDir)" + }, + "Make": { + "commandName": "Project", + "commandLineArgs": "make --versionpath=./Make/Version.json --makedirectory=./Make --dockerdirectory=./Docker --versionlabel=Beta", + "workingDirectory": "$(SolutionDir)" + } + } +} diff --git a/CreateMatrix/ReleaseVersionForward.cs b/CreateMatrix/ReleaseVersionForward.cs index 14e0fcd4..c593759f 100644 --- a/CreateMatrix/ReleaseVersionForward.cs +++ b/CreateMatrix/ReleaseVersionForward.cs @@ -1,138 +1,138 @@ -namespace CreateMatrix; - -internal static class ReleaseVersionForward -{ - public static void Verify(List oldProductList, List newProductList) - { - // newProductList will be updated in-place - - // Verify against all products in the old list - foreach (ProductInfo oldProduct in oldProductList) - { - // Find matching new product, must be present - ProductInfo newProduct = newProductList.First(item => - item.Product == oldProduct.Product - ); - - // Verify all labels - foreach (VersionInfo.LabelType label in VersionInfo.GetLabelTypes()) - { - Verify(oldProduct, newProduct, label); - } - } - } - - private static void Verify( - ProductInfo oldProduct, - ProductInfo newProduct, - VersionInfo.LabelType label - ) - { - // NOTE: Forward-only is intentional. A published tag must not regress to a lesser version. - // If a label is released, then pulled, then re-released with a lesser version, we keep the - // old (higher) version. This is harmless while the old build is still downloadable, and if - // its files were actually removed the run fails loudly in VerifyUrlsAsync (404) for a human - // to resolve, e.g. by manually adjusting Version.json. There is no silent-corruption path. - - // Find label in old and new product, skip if not present - VersionInfo? oldVersion = oldProduct.Versions.Find(item => item.Labels.Contains(label)); - if (oldVersion == null) - { - Log.Logger.Warning( - "{Product}:{Label} : Label not found in old versions", - oldProduct.Product, - label - ); - return; - } - - // Find label in new product, skip if not present - VersionInfo? newVersion = newProduct.Versions.Find(item => item.Labels.Contains(label)); - if (newVersion == null) - { - Log.Logger.Warning( - "{Product}:{Label} : Label not found in new versions", - newProduct.Product, - label - ); - return; - } - - // New version must be >= old version - if (oldVersion.CompareTo(newVersion) <= 0) - { - return; - } - - Log.Logger.Error( - "{Product}:{Label} : OldVersion: {OldVersion} > NewVersion: {NewVersion}", - newProduct.Product, - label, - oldVersion.Version, - newVersion.Version - ); - - // Do all the labels match - if (oldVersion.Labels.SequenceEqual(newVersion.Labels)) - { - Log.Logger.Warning( - "{Product}:{Label} Using OldVersion: {OldVersion} instead of NewVersion: {NewVersion}", - newProduct.Product, - label, - oldVersion.Version, - newVersion.Version - ); - - // Remove the regressed new version - _ = newProduct.Versions.Remove(newVersion); - - // The old version number may already be present in the new list under a different - // label (e.g. restoring Latest onto a version that is already Stable). Fold the - // old version's labels into that existing entry instead of adding a duplicate row, - // otherwise two entries would share a version number. - VersionInfo? existingVersion = newProduct.Versions.Find(item => - item.CompareTo(oldVersion) == 0 - ); - if (existingVersion == null) - { - // No existing entry, add the old version - newProduct.Versions.Add(oldVersion); - } - else - { - // Fold the old version's labels into the existing entry - Log.Logger.Warning( - "{Product}:{Label} Folding OldVersion: {OldVersion} labels into existing version", - newProduct.Product, - label, - oldVersion.Version - ); - foreach (VersionInfo.LabelType oldLabel in oldVersion.Labels) - { - if (!existingVersion.Labels.Contains(oldLabel)) - { - existingVersion.Labels.Add(oldLabel); - } - } - existingVersion.Labels.Sort(); - } - } - else - { - // The label moved between versions that carry different label sets, so a surgical - // per-version-label swap is ambiguous. Rather than attempting to unwind individual - // version-label pairs, take the conservative approach and revert the whole product to - // the last-known-good versions. This may discard newer online versions until the - // regression clears, but it avoids producing an inconsistent label arrangement. - Log.Logger.Warning( - "{Product}:{Label} : Labels differ, reverting all versions to old versions", - newProduct.Product, - label - ); - - // Replace all versions if the labels do not match - newProduct.Versions.Clear(); - newProduct.Versions.AddRange(oldProduct.Versions); - } - } -} +namespace CreateMatrix; + +internal static class ReleaseVersionForward +{ + public static void Verify(List oldProductList, List newProductList) + { + // newProductList will be updated in-place + + // Verify against all products in the old list + foreach (ProductInfo oldProduct in oldProductList) + { + // Find matching new product, must be present + ProductInfo newProduct = newProductList.First(item => + item.Product == oldProduct.Product + ); + + // Verify all labels + foreach (VersionInfo.LabelType label in VersionInfo.GetLabelTypes()) + { + Verify(oldProduct, newProduct, label); + } + } + } + + private static void Verify( + ProductInfo oldProduct, + ProductInfo newProduct, + VersionInfo.LabelType label + ) + { + // NOTE: Forward-only is intentional. A published tag must not regress to a lesser version. + // If a label is released, then pulled, then re-released with a lesser version, we keep the + // old (higher) version. This is harmless while the old build is still downloadable, and if + // its files were actually removed the run fails loudly in VerifyUrlsAsync (404) for a human + // to resolve, e.g. by manually adjusting Version.json. There is no silent-corruption path. + + // Find label in old and new product, skip if not present + VersionInfo? oldVersion = oldProduct.Versions.Find(item => item.Labels.Contains(label)); + if (oldVersion == null) + { + Log.Logger.Warning( + "{Product}:{Label} : Label not found in old versions", + oldProduct.Product, + label + ); + return; + } + + // Find label in new product, skip if not present + VersionInfo? newVersion = newProduct.Versions.Find(item => item.Labels.Contains(label)); + if (newVersion == null) + { + Log.Logger.Warning( + "{Product}:{Label} : Label not found in new versions", + newProduct.Product, + label + ); + return; + } + + // New version must be >= old version + if (oldVersion.CompareTo(newVersion) <= 0) + { + return; + } + + Log.Logger.Error( + "{Product}:{Label} : OldVersion: {OldVersion} > NewVersion: {NewVersion}", + newProduct.Product, + label, + oldVersion.Version, + newVersion.Version + ); + + // Do all the labels match + if (oldVersion.Labels.SequenceEqual(newVersion.Labels)) + { + Log.Logger.Warning( + "{Product}:{Label} Using OldVersion: {OldVersion} instead of NewVersion: {NewVersion}", + newProduct.Product, + label, + oldVersion.Version, + newVersion.Version + ); + + // Remove the regressed new version + _ = newProduct.Versions.Remove(newVersion); + + // The old version number may already be present in the new list under a different + // label (e.g. restoring Latest onto a version that is already Stable). Fold the + // old version's labels into that existing entry instead of adding a duplicate row, + // otherwise two entries would share a version number. + VersionInfo? existingVersion = newProduct.Versions.Find(item => + item.CompareTo(oldVersion) == 0 + ); + if (existingVersion == null) + { + // No existing entry, add the old version + newProduct.Versions.Add(oldVersion); + } + else + { + // Fold the old version's labels into the existing entry + Log.Logger.Warning( + "{Product}:{Label} Folding OldVersion: {OldVersion} labels into existing version", + newProduct.Product, + label, + oldVersion.Version + ); + foreach (VersionInfo.LabelType oldLabel in oldVersion.Labels) + { + if (!existingVersion.Labels.Contains(oldLabel)) + { + existingVersion.Labels.Add(oldLabel); + } + } + existingVersion.Labels.Sort(); + } + } + else + { + // The label moved between versions that carry different label sets, so a surgical + // per-version-label swap is ambiguous. Rather than attempting to unwind individual + // version-label pairs, take the conservative approach and revert the whole product to + // the last-known-good versions. This may discard newer online versions until the + // regression clears, but it avoids producing an inconsistent label arrangement. + Log.Logger.Warning( + "{Product}:{Label} : Labels differ, reverting all versions to old versions", + newProduct.Product, + label + ); + + // Replace all versions if the labels do not match + newProduct.Versions.Clear(); + newProduct.Versions.AddRange(oldProduct.Versions); + } + } +} diff --git a/CreateMatrix/ReleasesJsonSchema.cs b/CreateMatrix/ReleasesJsonSchema.cs index d44909ca..e398dac1 100644 --- a/CreateMatrix/ReleasesJsonSchema.cs +++ b/CreateMatrix/ReleasesJsonSchema.cs @@ -1,159 +1,159 @@ -namespace CreateMatrix; - -// https://updates.vmsproxy.com/{release}/releases.json -// https://updates.vmsproxy.com/default/releases.json -// https://updates.vmsproxy.com/metavms/releases.json -// https://updates.vmsproxy.com/digitalwatchdog/releases.json -// https://updates.vmsproxy.com/hanwha/releases.json - -internal class Release -{ - [JsonPropertyName("product")] - public string Product { get; set; } = string.Empty; - - [JsonPropertyName("version")] - public string Version { get; set; } = string.Empty; - - [JsonPropertyName("publication_type")] - public string PublicationType { get; set; } = string.Empty; - - [JsonPropertyName("release_date")] - public long? ReleaseDate { get; set; } - - [JsonPropertyName("release_delivery_days")] - public long? ReleaseDeliveryDays { get; set; } - - public VersionInfo.LabelType GetLabel() => - // Determine the equivalent label - PublicationType switch - { - // Use Stable or Latest based on if published or not - ReleasePublication => IsPublished() - ? VersionInfo.LabelType.Stable - : VersionInfo.LabelType.Latest, - RcPublication => VersionInfo.LabelType.RC, - BetaPublication => VersionInfo.LabelType.Beta, - _ => throw new InvalidOperationException( - $"Unknown publication type '{PublicationType}' for version {Version}" - ), - }; - - public const string ReleasePublication = "release"; - public const string RcPublication = "rc"; - public const string BetaPublication = "beta"; - public const string VmsProduct = "vms"; - public const string DesktopProduct = "desktop_client"; - - private bool IsPublished() => - // Logic follows similar patterns as used in C++ Desktop Client - // https://github.com/networkoptix/nx_open/blob/526967920636d3119c92a5220290ecc10957bf12/vms/libs/nx_vms_update/src/nx/vms/update/releases_info.cpp#L57 - // releases_info.cpp: ReleasesInfo::selectVmsRelease(), isBuildPublished(), canReceiveUnpublishedBuild() - ReleaseDate > 0 - && ReleaseDeliveryDays >= 0; -} - -internal class ReleasesJsonSchema -{ - [JsonPropertyName("releases")] - public List Releases { get; set; } = []; - - private static ReleasesJsonSchema FromJson(string json) - { - ReleasesJsonSchema? jsonSchema = JsonSerializer.Deserialize( - json, - ReleasesJsonContext.Default.ReleasesJsonSchema - ); - ArgumentNullException.ThrowIfNull(jsonSchema); - return jsonSchema; - } - - public static async Task> GetReleasesAsync( - string productName, - CancellationToken cancellationToken - ) - { - // Load releases JSON - // https://updates.vmsproxy.com/{product}/releases.json - ArgumentNullException.ThrowIfNull(productName); - - HttpClient httpClient = HttpClientFactory.GetHttpClient(); - Uri releasesUri = new($"https://updates.vmsproxy.com/{productName}/releases.json"); - Log.Logger.Information("Getting release information from {Uri}", releasesUri); - string jsonString = await httpClient - .GetStringAsync(releasesUri, cancellationToken) - .ConfigureAwait(false); - - // Deserialize JSON - ReleasesJsonSchema releasesSchema = FromJson(jsonString); - ArgumentNullException.ThrowIfNull(releasesSchema); - if (releasesSchema.Releases.Count == 0) - { - throw new InvalidOperationException($"No releases found in {releasesUri}"); - } - - // Verify the vendor data and return the folded releases - return VerifyReleases(releasesSchema.Releases); - } - - public static List VerifyReleases(List releases) - { - // A version number must carry a single, unambiguous publication type. - // Fold benign duplicates (same version and publication type), reject conflicts. - ArgumentNullException.ThrowIfNull(releases); - - // A version number must be present and parseable, otherwise grouping below would - // throw a context-free FormatException from Version parsing. - foreach (Release release in releases) - { - if ( - string.IsNullOrEmpty(release.Version) - || !Version.TryParse(VersionInfo.NormalizeVersion(release.Version), out _) - ) - { - throw new InvalidOperationException( - $"{release.Product}: Invalid or missing version '{release.Version}' (publication type '{release.PublicationType}')" - ); - } - } - - List verifiedReleases = []; - foreach ( - IGrouping<(string Product, Version Version), Release> group in releases.GroupBy( - release => (release.Product, VersionInfo.ParseVersion(release.Version)) - ) - ) - { - // A version must not be tagged with more than one publication type - List publicationTypes = - [ - .. group - .Select(release => release.PublicationType) - .Distinct(StringComparer.OrdinalIgnoreCase), - ]; - if (publicationTypes.Count > 1) - { - throw new InvalidOperationException( - $"{group.Key.Product}: Version {group.First().Version} has conflicting publication types: {string.Join(", ", publicationTypes)}" - ); - } - - // Keep the first entry, folding any same-version same-type duplicates - verifiedReleases.Add(group.First()); - } - return verifiedReleases; - } -} - -[JsonSourceGenerationOptions( - AllowTrailingCommas = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - IncludeFields = true, - NumberHandling = JsonNumberHandling.AllowReadingFromString, - PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, - ReadCommentHandling = JsonCommentHandling.Skip, - UseStringEnumConverter = true, - WriteIndented = true, - NewLine = "\r\n" -)] -[JsonSerializable(typeof(ReleasesJsonSchema))] -internal partial class ReleasesJsonContext : JsonSerializerContext; +namespace CreateMatrix; + +// https://updates.vmsproxy.com/{release}/releases.json +// https://updates.vmsproxy.com/default/releases.json +// https://updates.vmsproxy.com/metavms/releases.json +// https://updates.vmsproxy.com/digitalwatchdog/releases.json +// https://updates.vmsproxy.com/hanwha/releases.json + +internal class Release +{ + [JsonPropertyName("product")] + public string Product { get; set; } = string.Empty; + + [JsonPropertyName("version")] + public string Version { get; set; } = string.Empty; + + [JsonPropertyName("publication_type")] + public string PublicationType { get; set; } = string.Empty; + + [JsonPropertyName("release_date")] + public long? ReleaseDate { get; set; } + + [JsonPropertyName("release_delivery_days")] + public long? ReleaseDeliveryDays { get; set; } + + public VersionInfo.LabelType GetLabel() => + // Determine the equivalent label + PublicationType switch + { + // Use Stable or Latest based on if published or not + ReleasePublication => IsPublished() + ? VersionInfo.LabelType.Stable + : VersionInfo.LabelType.Latest, + RcPublication => VersionInfo.LabelType.RC, + BetaPublication => VersionInfo.LabelType.Beta, + _ => throw new InvalidOperationException( + $"Unknown publication type '{PublicationType}' for version {Version}" + ), + }; + + public const string ReleasePublication = "release"; + public const string RcPublication = "rc"; + public const string BetaPublication = "beta"; + public const string VmsProduct = "vms"; + public const string DesktopProduct = "desktop_client"; + + private bool IsPublished() => + // Logic follows similar patterns as used in C++ Desktop Client + // https://github.com/networkoptix/nx_open/blob/526967920636d3119c92a5220290ecc10957bf12/vms/libs/nx_vms_update/src/nx/vms/update/releases_info.cpp#L57 + // releases_info.cpp: ReleasesInfo::selectVmsRelease(), isBuildPublished(), canReceiveUnpublishedBuild() + ReleaseDate > 0 + && ReleaseDeliveryDays >= 0; +} + +internal class ReleasesJsonSchema +{ + [JsonPropertyName("releases")] + public List Releases { get; set; } = []; + + private static ReleasesJsonSchema FromJson(string json) + { + ReleasesJsonSchema? jsonSchema = JsonSerializer.Deserialize( + json, + ReleasesJsonContext.Default.ReleasesJsonSchema + ); + ArgumentNullException.ThrowIfNull(jsonSchema); + return jsonSchema; + } + + public static async Task> GetReleasesAsync( + string productName, + CancellationToken cancellationToken + ) + { + // Load releases JSON + // https://updates.vmsproxy.com/{product}/releases.json + ArgumentNullException.ThrowIfNull(productName); + + HttpClient httpClient = HttpClientFactory.GetHttpClient(); + Uri releasesUri = new($"https://updates.vmsproxy.com/{productName}/releases.json"); + Log.Logger.Information("Getting release information from {Uri}", releasesUri); + string jsonString = await httpClient + .GetStringAsync(releasesUri, cancellationToken) + .ConfigureAwait(false); + + // Deserialize JSON + ReleasesJsonSchema releasesSchema = FromJson(jsonString); + ArgumentNullException.ThrowIfNull(releasesSchema); + if (releasesSchema.Releases.Count == 0) + { + throw new InvalidOperationException($"No releases found in {releasesUri}"); + } + + // Verify the vendor data and return the folded releases + return VerifyReleases(releasesSchema.Releases); + } + + public static List VerifyReleases(List releases) + { + // A version number must carry a single, unambiguous publication type. + // Fold benign duplicates (same version and publication type), reject conflicts. + ArgumentNullException.ThrowIfNull(releases); + + // A version number must be present and parseable, otherwise grouping below would + // throw a context-free FormatException from Version parsing. + foreach (Release release in releases) + { + if ( + string.IsNullOrEmpty(release.Version) + || !Version.TryParse(VersionInfo.NormalizeVersion(release.Version), out _) + ) + { + throw new InvalidOperationException( + $"{release.Product}: Invalid or missing version '{release.Version}' (publication type '{release.PublicationType}')" + ); + } + } + + List verifiedReleases = []; + foreach ( + IGrouping<(string Product, Version Version), Release> group in releases.GroupBy( + release => (release.Product, VersionInfo.ParseVersion(release.Version)) + ) + ) + { + // A version must not be tagged with more than one publication type + List publicationTypes = + [ + .. group + .Select(release => release.PublicationType) + .Distinct(StringComparer.OrdinalIgnoreCase), + ]; + if (publicationTypes.Count > 1) + { + throw new InvalidOperationException( + $"{group.Key.Product}: Version {group.First().Version} has conflicting publication types: {string.Join(", ", publicationTypes)}" + ); + } + + // Keep the first entry, folding any same-version same-type duplicates + verifiedReleases.Add(group.First()); + } + return verifiedReleases; + } +} + +[JsonSourceGenerationOptions( + AllowTrailingCommas = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IncludeFields = true, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, + ReadCommentHandling = JsonCommentHandling.Skip, + UseStringEnumConverter = true, + WriteIndented = true, + NewLine = "\n" +)] +[JsonSerializable(typeof(ReleasesJsonSchema))] +internal partial class ReleasesJsonContext : JsonSerializerContext; diff --git a/CreateMatrix/VersionInfo.cs b/CreateMatrix/VersionInfo.cs index ada52c1d..1c3dd7fb 100644 --- a/CreateMatrix/VersionInfo.cs +++ b/CreateMatrix/VersionInfo.cs @@ -1,67 +1,67 @@ -namespace CreateMatrix; - -internal sealed class VersionInfo -{ - public enum LabelType - { - None, - Stable, - Latest, - Beta, - RC, - } - - public string Version { get; set; } = string.Empty; - public string UriX64 { get; set; } = string.Empty; - public string UriArm64 { get; set; } = string.Empty; - public List Labels { get; set; } = []; - - public int GetBuildNumber() => - // Extract the build number using the Version class (vs. regex) - // 5.0.0.35271 -> 35271 - // 5.1.0.35151 R1 -> 35151 - ParseVersion(Version).Revision; - - public void SetVersion(string version) => - // Store the normalized version number - Version = NormalizeVersion(version); - - public static string NormalizeVersion(string version) - { - // Remove the " Rxx" suffix from the version string - // "5.0.0.35134 R10" -> "5.0.0.35134" - int spaceIndex = version.IndexOf(' ', StringComparison.Ordinal); - return spaceIndex == -1 ? version : version[..spaceIndex]; - } - - public static Version ParseVersion(string version) => - // Parse the version number using the Version class, ignoring any " Rxx" suffix - new(NormalizeVersion(version)); - - public int CompareTo(VersionInfo rhs) => Compare(this, rhs); - - public int CompareTo(string rhs) => Compare(Version, rhs); - - public static int Compare(string lhs, string rhs) => - // Compare version numbers using Version class - ParseVersion(lhs).CompareTo(ParseVersion(rhs)); - - public static int Compare(VersionInfo lhs, VersionInfo rhs) => - Compare(lhs.Version, rhs.Version); - - public static IEnumerable GetLabelTypes() => - // Create list of label types - [.. Enum.GetValues().Where(labelType => labelType != LabelType.None)]; -} - -internal sealed class VersionInfoComparer : Comparer -{ - // Compare using version numbers - public override int Compare(VersionInfo? x, VersionInfo? y) => - x switch - { - null when y == null => 0, - null => -1, - _ => y == null ? 1 : VersionInfo.Compare(x, y), - }; -} +namespace CreateMatrix; + +internal sealed class VersionInfo +{ + public enum LabelType + { + None, + Stable, + Latest, + Beta, + RC, + } + + public string Version { get; set; } = string.Empty; + public string UriX64 { get; set; } = string.Empty; + public string UriArm64 { get; set; } = string.Empty; + public List Labels { get; set; } = []; + + public int GetBuildNumber() => + // Extract the build number using the Version class (vs. regex) + // 5.0.0.35271 -> 35271 + // 5.1.0.35151 R1 -> 35151 + ParseVersion(Version).Revision; + + public void SetVersion(string version) => + // Store the normalized version number + Version = NormalizeVersion(version); + + public static string NormalizeVersion(string version) + { + // Remove the " Rxx" suffix from the version string + // "5.0.0.35134 R10" -> "5.0.0.35134" + int spaceIndex = version.IndexOf(' ', StringComparison.Ordinal); + return spaceIndex == -1 ? version : version[..spaceIndex]; + } + + public static Version ParseVersion(string version) => + // Parse the version number using the Version class, ignoring any " Rxx" suffix + new(NormalizeVersion(version)); + + public int CompareTo(VersionInfo rhs) => Compare(this, rhs); + + public int CompareTo(string rhs) => Compare(Version, rhs); + + public static int Compare(string lhs, string rhs) => + // Compare version numbers using Version class + ParseVersion(lhs).CompareTo(ParseVersion(rhs)); + + public static int Compare(VersionInfo lhs, VersionInfo rhs) => + Compare(lhs.Version, rhs.Version); + + public static IEnumerable GetLabelTypes() => + // Create list of label types + [.. Enum.GetValues().Where(labelType => labelType != LabelType.None)]; +} + +internal sealed class VersionInfoComparer : Comparer +{ + // Compare using version numbers + public override int Compare(VersionInfo? x, VersionInfo? y) => + x switch + { + null when y == null => 0, + null => -1, + _ => y == null ? 1 : VersionInfo.Compare(x, y), + }; +} diff --git a/CreateMatrix/VersionJsonSchema.cs b/CreateMatrix/VersionJsonSchema.cs index 168b2d01..eba17e11 100644 --- a/CreateMatrix/VersionJsonSchema.cs +++ b/CreateMatrix/VersionJsonSchema.cs @@ -1,72 +1,72 @@ -namespace CreateMatrix; - -internal class VersionJsonSchemaBase -{ - [JsonRequired] - [JsonPropertyOrder(-2)] - public int SchemaVersion { get; set; } = VersionJsonSchema.Version; -} - -internal class VersionJsonSchema : VersionJsonSchemaBase -{ - public const int Version = 2; - - [JsonRequired] - public List Products { get; set; } = []; - - public static VersionJsonSchema FromFile(string path) => FromJson(File.ReadAllText(path)); - - public static void ToFile(string path, VersionJsonSchema json) - { - json.SchemaVersion = Version; - File.WriteAllText(path, ToJson(json)); - } - - private static string ToJson(VersionJsonSchema json) => - JsonSerializer.Serialize(json, VersionJsonContext.Default.VersionJsonSchema); - - private static VersionJsonSchema FromJson(string json) - { - VersionJsonSchemaBase? versionJsonSchemaBase = JsonSerializer.Deserialize( - json, - VersionJsonContext.Default.VersionJsonSchemaBase - ); - ArgumentNullException.ThrowIfNull(versionJsonSchemaBase); - - // Deserialize the correct version - int schemaVersion = versionJsonSchemaBase.SchemaVersion; - switch (schemaVersion) - { - case Version: - VersionJsonSchema? schema = JsonSerializer.Deserialize( - json, - VersionJsonContext.Default.VersionJsonSchema - ); - ArgumentNullException.ThrowIfNull(schema); - return schema; - // case 1: - // VersionInfo::Uri was replaced with UriX64 and UriArm64 was added - // Breaking change, UriArm64 is required in ARM64 docker builds - // Unknown version - default: - throw new NotSupportedException( - $"Unsupported schema version: {schemaVersion} (expected {Version})" - ); - } - } -} - -[JsonSourceGenerationOptions( - AllowTrailingCommas = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - IncludeFields = true, - NumberHandling = JsonNumberHandling.AllowReadingFromString, - PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, - ReadCommentHandling = JsonCommentHandling.Skip, - UseStringEnumConverter = true, - WriteIndented = true, - NewLine = "\r\n" -)] -[JsonSerializable(typeof(VersionJsonSchema))] -[JsonSerializable(typeof(VersionJsonSchemaBase))] -internal partial class VersionJsonContext : JsonSerializerContext; +namespace CreateMatrix; + +internal class VersionJsonSchemaBase +{ + [JsonRequired] + [JsonPropertyOrder(-2)] + public int SchemaVersion { get; set; } = VersionJsonSchema.Version; +} + +internal class VersionJsonSchema : VersionJsonSchemaBase +{ + public const int Version = 2; + + [JsonRequired] + public List Products { get; set; } = []; + + public static VersionJsonSchema FromFile(string path) => FromJson(File.ReadAllText(path)); + + public static void ToFile(string path, VersionJsonSchema json) + { + json.SchemaVersion = Version; + File.WriteAllText(path, ToJson(json)); + } + + private static string ToJson(VersionJsonSchema json) => + JsonSerializer.Serialize(json, VersionJsonContext.Default.VersionJsonSchema); + + private static VersionJsonSchema FromJson(string json) + { + VersionJsonSchemaBase? versionJsonSchemaBase = JsonSerializer.Deserialize( + json, + VersionJsonContext.Default.VersionJsonSchemaBase + ); + ArgumentNullException.ThrowIfNull(versionJsonSchemaBase); + + // Deserialize the correct version + int schemaVersion = versionJsonSchemaBase.SchemaVersion; + switch (schemaVersion) + { + case Version: + VersionJsonSchema? schema = JsonSerializer.Deserialize( + json, + VersionJsonContext.Default.VersionJsonSchema + ); + ArgumentNullException.ThrowIfNull(schema); + return schema; + // case 1: + // VersionInfo::Uri was replaced with UriX64 and UriArm64 was added + // Breaking change, UriArm64 is required in ARM64 docker builds + // Unknown version + default: + throw new NotSupportedException( + $"Unsupported schema version: {schemaVersion} (expected {Version})" + ); + } + } +} + +[JsonSourceGenerationOptions( + AllowTrailingCommas = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IncludeFields = true, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, + ReadCommentHandling = JsonCommentHandling.Skip, + UseStringEnumConverter = true, + WriteIndented = true, + NewLine = "\n" +)] +[JsonSerializable(typeof(VersionJsonSchema))] +[JsonSerializable(typeof(VersionJsonSchemaBase))] +internal partial class VersionJsonContext : JsonSerializerContext; diff --git a/CreateMatrixTests/.editorconfig b/CreateMatrixTests/.editorconfig index 1b0e9335..94be680f 100644 --- a/CreateMatrixTests/.editorconfig +++ b/CreateMatrixTests/.editorconfig @@ -1,19 +1,19 @@ -root = false - -# C# files -[*.cs] - -# Allow underscores in test method names -dotnet_diagnostic.CA1707.severity = none - -# Ignore unused private members -dotnet_diagnostic.IDE0052.severity = none - -# Ignore expression value is never used -dotnet_diagnostic.IDE0058.severity = none - -# Ignore missing XML docs for public test APIs -dotnet_diagnostic.CS1591.severity = none - -# Ignore making public types internal -dotnet_diagnostic.CA1515.severity = none +root = false + +# C# files +[*.cs] + +# Allow underscores in test method names +dotnet_diagnostic.CA1707.severity = none + +# Ignore unused private members +dotnet_diagnostic.IDE0052.severity = none + +# Ignore expression value is never used +dotnet_diagnostic.IDE0058.severity = none + +# Ignore missing XML docs for public test APIs +dotnet_diagnostic.CS1591.severity = none + +# Ignore making public types internal +dotnet_diagnostic.CA1515.severity = none diff --git a/CreateMatrixTests/CommandLineTests.cs b/CreateMatrixTests/CommandLineTests.cs index 2cc28804..cf6d8627 100644 --- a/CreateMatrixTests/CommandLineTests.cs +++ b/CreateMatrixTests/CommandLineTests.cs @@ -1,115 +1,115 @@ -using CreateMatrix; - -namespace CreateMatrixTests; - -public sealed class CommandLineTests -{ - [Fact] - public void CreateOptions_UsesDefaultsForMakeCommand() - { - DirectoryInfo tempDirectory = CreateTempDirectory(); - try - { - FileInfo versionFile = new(Path.Combine(tempDirectory.FullName, "Version.json")); - File.WriteAllText(versionFile.FullName, "{}"); - DirectoryInfo makeDirectory = new(Path.Combine(tempDirectory.FullName, "Make")); - DirectoryInfo dockerDirectory = new(Path.Combine(tempDirectory.FullName, "Docker")); - makeDirectory.Create(); - dockerDirectory.Create(); - - CommandLine commandLine = new([ - "make", - $"--versionpath={versionFile.FullName}", - $"--makedirectory={makeDirectory.FullName}", - $"--dockerdirectory={dockerDirectory.FullName}", - ]); - - CommandLine.Options options = CommandLine.CreateOptions(commandLine.Result); - - options.VersionLabel.Should().Be(VersionInfo.LabelType.Latest); - options.VersionPath?.FullName.Should().Be(versionFile.FullName); - options.MakeDirectory?.FullName.Should().Be(makeDirectory.FullName); - options.DockerDirectory?.FullName.Should().Be(dockerDirectory.FullName); - } - finally - { - DeleteTempDirectory(tempDirectory); - } - } - - [Fact] - public void CreateOptions_ParsesExplicitVersionLabel() - { - DirectoryInfo tempDirectory = CreateTempDirectory(); - try - { - FileInfo versionFile = new(Path.Combine(tempDirectory.FullName, "Version.json")); - File.WriteAllText(versionFile.FullName, "{}"); - DirectoryInfo makeDirectory = new(Path.Combine(tempDirectory.FullName, "Make")); - DirectoryInfo dockerDirectory = new(Path.Combine(tempDirectory.FullName, "Docker")); - makeDirectory.Create(); - dockerDirectory.Create(); - - CommandLine commandLine = new([ - "make", - $"--versionpath={versionFile.FullName}", - $"--makedirectory={makeDirectory.FullName}", - $"--dockerdirectory={dockerDirectory.FullName}", - "--versionlabel=Beta", - ]); - - CommandLine.Options options = CommandLine.CreateOptions(commandLine.Result); - - options.VersionLabel.Should().Be(VersionInfo.LabelType.Beta); - } - finally - { - DeleteTempDirectory(tempDirectory); - } - } - - [Fact] - public void BypassStartup_ReturnsTrueOnHelp() - { - CommandLine commandLine = new(["--help"]); - - bool bypass = CommandLine.BypassStartup(commandLine.Result); - - bypass.Should().BeTrue(); - } - - [Fact] - public void BypassStartup_ReturnsTrueOnParseErrors() - { - CommandLine commandLine = new([ - "matrix", - "--versionpath=missing.json", - "--matrixpath=matrix.json", - ]); - - bool bypass = CommandLine.BypassStartup(commandLine.Result); - - bypass.Should().BeTrue(); - } - - private static DirectoryInfo CreateTempDirectory() - { - string directoryPath = Path.Combine( - Path.GetTempPath(), - "CreateMatrixTests", - Guid.NewGuid().ToString("N") - ); - DirectoryInfo directoryInfo = Directory.CreateDirectory(directoryPath); - return directoryInfo; - } - - private static void DeleteTempDirectory(DirectoryInfo directoryInfo) - { - if (!directoryInfo.Exists) - { - return; - } - - directoryInfo.Delete(true); - } -} +using CreateMatrix; + +namespace CreateMatrixTests; + +public sealed class CommandLineTests +{ + [Fact] + public void CreateOptions_UsesDefaultsForMakeCommand() + { + DirectoryInfo tempDirectory = CreateTempDirectory(); + try + { + FileInfo versionFile = new(Path.Combine(tempDirectory.FullName, "Version.json")); + File.WriteAllText(versionFile.FullName, "{}"); + DirectoryInfo makeDirectory = new(Path.Combine(tempDirectory.FullName, "Make")); + DirectoryInfo dockerDirectory = new(Path.Combine(tempDirectory.FullName, "Docker")); + makeDirectory.Create(); + dockerDirectory.Create(); + + CommandLine commandLine = new([ + "make", + $"--versionpath={versionFile.FullName}", + $"--makedirectory={makeDirectory.FullName}", + $"--dockerdirectory={dockerDirectory.FullName}", + ]); + + CommandLine.Options options = CommandLine.CreateOptions(commandLine.Result); + + options.VersionLabel.Should().Be(VersionInfo.LabelType.Latest); + options.VersionPath?.FullName.Should().Be(versionFile.FullName); + options.MakeDirectory?.FullName.Should().Be(makeDirectory.FullName); + options.DockerDirectory?.FullName.Should().Be(dockerDirectory.FullName); + } + finally + { + DeleteTempDirectory(tempDirectory); + } + } + + [Fact] + public void CreateOptions_ParsesExplicitVersionLabel() + { + DirectoryInfo tempDirectory = CreateTempDirectory(); + try + { + FileInfo versionFile = new(Path.Combine(tempDirectory.FullName, "Version.json")); + File.WriteAllText(versionFile.FullName, "{}"); + DirectoryInfo makeDirectory = new(Path.Combine(tempDirectory.FullName, "Make")); + DirectoryInfo dockerDirectory = new(Path.Combine(tempDirectory.FullName, "Docker")); + makeDirectory.Create(); + dockerDirectory.Create(); + + CommandLine commandLine = new([ + "make", + $"--versionpath={versionFile.FullName}", + $"--makedirectory={makeDirectory.FullName}", + $"--dockerdirectory={dockerDirectory.FullName}", + "--versionlabel=Beta", + ]); + + CommandLine.Options options = CommandLine.CreateOptions(commandLine.Result); + + options.VersionLabel.Should().Be(VersionInfo.LabelType.Beta); + } + finally + { + DeleteTempDirectory(tempDirectory); + } + } + + [Fact] + public void BypassStartup_ReturnsTrueOnHelp() + { + CommandLine commandLine = new(["--help"]); + + bool bypass = CommandLine.BypassStartup(commandLine.Result); + + bypass.Should().BeTrue(); + } + + [Fact] + public void BypassStartup_ReturnsTrueOnParseErrors() + { + CommandLine commandLine = new([ + "matrix", + "--versionpath=missing.json", + "--matrixpath=matrix.json", + ]); + + bool bypass = CommandLine.BypassStartup(commandLine.Result); + + bypass.Should().BeTrue(); + } + + private static DirectoryInfo CreateTempDirectory() + { + string directoryPath = Path.Combine( + Path.GetTempPath(), + "CreateMatrixTests", + Guid.NewGuid().ToString("N") + ); + DirectoryInfo directoryInfo = Directory.CreateDirectory(directoryPath); + return directoryInfo; + } + + private static void DeleteTempDirectory(DirectoryInfo directoryInfo) + { + if (!directoryInfo.Exists) + { + return; + } + + directoryInfo.Delete(true); + } +} diff --git a/CreateMatrixTests/ComposeFileTests.cs b/CreateMatrixTests/ComposeFileTests.cs index 25d3ed0e..577d3a31 100644 --- a/CreateMatrixTests/ComposeFileTests.cs +++ b/CreateMatrixTests/ComposeFileTests.cs @@ -1,76 +1,76 @@ -using CreateMatrix; - -namespace CreateMatrixTests; - -public sealed class ComposeFileTests -{ - [Fact] - public void Create_WritesAllComposeFiles() - { - DirectoryInfo tempDirectory = CreateTempDirectory(); - try - { - ComposeFile.Create(tempDirectory); - - string testFile = Path.Combine(tempDirectory.FullName, "Test.yml"); - string developFile = Path.Combine(tempDirectory.FullName, "Test-develop.yml"); - string latestFile = Path.Combine(tempDirectory.FullName, "Test-latest.yml"); - - bool testExists = File.Exists(testFile); - bool developExists = File.Exists(developFile); - bool latestExists = File.Exists(latestFile); - - testExists.Should().BeTrue(); - developExists.Should().BeTrue(); - latestExists.Should().BeTrue(); - } - finally - { - DeleteTempDirectory(tempDirectory); - } - } - - [Fact] - public void Create_UsesLabelForTaggedImages() - { - DirectoryInfo tempDirectory = CreateTempDirectory(); - try - { - ComposeFile.Create(tempDirectory); - - string testFile = Path.Combine(tempDirectory.FullName, "Test.yml"); - string latestFile = Path.Combine(tempDirectory.FullName, "Test-latest.yml"); - - string testContents = File.ReadAllText(testFile); - string latestContents = File.ReadAllText(latestFile); - - testContents.Should().Contain("image: test_nxwitness"); - latestContents.Should().Contain("image: docker.io/ptr727/nxwitness:latest"); - } - finally - { - DeleteTempDirectory(tempDirectory); - } - } - - private static DirectoryInfo CreateTempDirectory() - { - string directoryPath = Path.Combine( - Path.GetTempPath(), - "CreateMatrixTests", - Guid.NewGuid().ToString("N") - ); - DirectoryInfo directoryInfo = Directory.CreateDirectory(directoryPath); - return directoryInfo; - } - - private static void DeleteTempDirectory(DirectoryInfo directoryInfo) - { - if (!directoryInfo.Exists) - { - return; - } - - directoryInfo.Delete(true); - } -} +using CreateMatrix; + +namespace CreateMatrixTests; + +public sealed class ComposeFileTests +{ + [Fact] + public void Create_WritesAllComposeFiles() + { + DirectoryInfo tempDirectory = CreateTempDirectory(); + try + { + ComposeFile.Create(tempDirectory); + + string testFile = Path.Combine(tempDirectory.FullName, "Test.yml"); + string developFile = Path.Combine(tempDirectory.FullName, "Test-develop.yml"); + string latestFile = Path.Combine(tempDirectory.FullName, "Test-latest.yml"); + + bool testExists = File.Exists(testFile); + bool developExists = File.Exists(developFile); + bool latestExists = File.Exists(latestFile); + + testExists.Should().BeTrue(); + developExists.Should().BeTrue(); + latestExists.Should().BeTrue(); + } + finally + { + DeleteTempDirectory(tempDirectory); + } + } + + [Fact] + public void Create_UsesLabelForTaggedImages() + { + DirectoryInfo tempDirectory = CreateTempDirectory(); + try + { + ComposeFile.Create(tempDirectory); + + string testFile = Path.Combine(tempDirectory.FullName, "Test.yml"); + string latestFile = Path.Combine(tempDirectory.FullName, "Test-latest.yml"); + + string testContents = File.ReadAllText(testFile); + string latestContents = File.ReadAllText(latestFile); + + testContents.Should().Contain("image: test_nxwitness"); + latestContents.Should().Contain("image: docker.io/ptr727/nxwitness:latest"); + } + finally + { + DeleteTempDirectory(tempDirectory); + } + } + + private static DirectoryInfo CreateTempDirectory() + { + string directoryPath = Path.Combine( + Path.GetTempPath(), + "CreateMatrixTests", + Guid.NewGuid().ToString("N") + ); + DirectoryInfo directoryInfo = Directory.CreateDirectory(directoryPath); + return directoryInfo; + } + + private static void DeleteTempDirectory(DirectoryInfo directoryInfo) + { + if (!directoryInfo.Exists) + { + return; + } + + directoryInfo.Delete(true); + } +} diff --git a/CreateMatrixTests/CreateMatrixTests.csproj b/CreateMatrixTests/CreateMatrixTests.csproj index 0c328567..81526959 100644 --- a/CreateMatrixTests/CreateMatrixTests.csproj +++ b/CreateMatrixTests/CreateMatrixTests.csproj @@ -1,27 +1,27 @@ - - - latest-all - true - false - true - enable - net10.0 - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - + + + latest-all + true + false + true + enable + net10.0 + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + diff --git a/CreateMatrixTests/DockerFileTests.cs b/CreateMatrixTests/DockerFileTests.cs index c204645b..6197a595 100644 --- a/CreateMatrixTests/DockerFileTests.cs +++ b/CreateMatrixTests/DockerFileTests.cs @@ -1,126 +1,126 @@ -using CreateMatrix; - -namespace CreateMatrixTests; - -public sealed class DockerFileTests -{ - [Fact] - public void Create_WritesDockerfilesForAllProducts() - { - DirectoryInfo tempDirectory = CreateTempDirectory(); - try - { - List products = CreateProducts(); - - DockerFile.Create(products, tempDirectory.FullName, VersionInfo.LabelType.Latest); - - foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) - { - string standardFile = Path.Combine( - tempDirectory.FullName, - $"{ProductInfo.GetDocker(productType, false)}.Dockerfile" - ); - string lsioFile = Path.Combine( - tempDirectory.FullName, - $"{ProductInfo.GetDocker(productType, true)}.Dockerfile" - ); - - bool standardExists = File.Exists(standardFile); - bool lsioExists = File.Exists(lsioFile); - standardExists.Should().BeTrue(); - lsioExists.Should().BeTrue(); - } - } - finally - { - DeleteTempDirectory(tempDirectory); - } - } - - [Fact] - public void Create_UsesLatestWhenLabelMissing() - { - DirectoryInfo tempDirectory = CreateTempDirectory(); - try - { - List products = CreateProducts(); - - DockerFile.Create(products, tempDirectory.FullName, VersionInfo.LabelType.Beta); - - string nxGoFile = Path.Combine( - tempDirectory.FullName, - $"{ProductInfo.GetDocker(ProductInfo.ProductType.NxGo, false)}.Dockerfile" - ); - string nxMetaFile = Path.Combine( - tempDirectory.FullName, - $"{ProductInfo.GetDocker(ProductInfo.ProductType.NxMeta, false)}.Dockerfile" - ); - - string nxGoContents = File.ReadAllText(nxGoFile); - string nxMetaContents = File.ReadAllText(nxMetaFile); - - nxGoContents.Should().Contain("ARG LABEL_VERSION=\"5.1.0.12345\""); - nxMetaContents.Should().Contain("ARG LABEL_VERSION=\"5.2.0.23456\""); - } - finally - { - DeleteTempDirectory(tempDirectory); - } - } - - private static List CreateProducts() - { - List products = []; - foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) - { - ProductInfo productInfo = new() { Product = productType }; - - VersionInfo latest = new() - { - Version = "5.1.0.12345", - UriX64 = "https://example.com/x64.deb", - UriArm64 = "https://example.com/arm64.deb", - }; - latest.Labels.Add(VersionInfo.LabelType.Latest); - latest.Labels.Add(VersionInfo.LabelType.Stable); - productInfo.Versions.Add(latest); - - if (productType == ProductInfo.ProductType.NxMeta) - { - VersionInfo beta = new() - { - Version = "5.2.0.23456", - UriX64 = "https://example.com/x64-beta.deb", - UriArm64 = "https://example.com/arm64-beta.deb", - }; - beta.Labels.Add(VersionInfo.LabelType.Beta); - productInfo.Versions.Add(beta); - } - - products.Add(productInfo); - } - - return products; - } - - private static DirectoryInfo CreateTempDirectory() - { - string directoryPath = Path.Combine( - Path.GetTempPath(), - "CreateMatrixTests", - Guid.NewGuid().ToString("N") - ); - DirectoryInfo directoryInfo = Directory.CreateDirectory(directoryPath); - return directoryInfo; - } - - private static void DeleteTempDirectory(DirectoryInfo directoryInfo) - { - if (!directoryInfo.Exists) - { - return; - } - - directoryInfo.Delete(true); - } -} +using CreateMatrix; + +namespace CreateMatrixTests; + +public sealed class DockerFileTests +{ + [Fact] + public void Create_WritesDockerfilesForAllProducts() + { + DirectoryInfo tempDirectory = CreateTempDirectory(); + try + { + List products = CreateProducts(); + + DockerFile.Create(products, tempDirectory.FullName, VersionInfo.LabelType.Latest); + + foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) + { + string standardFile = Path.Combine( + tempDirectory.FullName, + $"{ProductInfo.GetDocker(productType, false)}.Dockerfile" + ); + string lsioFile = Path.Combine( + tempDirectory.FullName, + $"{ProductInfo.GetDocker(productType, true)}.Dockerfile" + ); + + bool standardExists = File.Exists(standardFile); + bool lsioExists = File.Exists(lsioFile); + standardExists.Should().BeTrue(); + lsioExists.Should().BeTrue(); + } + } + finally + { + DeleteTempDirectory(tempDirectory); + } + } + + [Fact] + public void Create_UsesLatestWhenLabelMissing() + { + DirectoryInfo tempDirectory = CreateTempDirectory(); + try + { + List products = CreateProducts(); + + DockerFile.Create(products, tempDirectory.FullName, VersionInfo.LabelType.Beta); + + string nxGoFile = Path.Combine( + tempDirectory.FullName, + $"{ProductInfo.GetDocker(ProductInfo.ProductType.NxGo, false)}.Dockerfile" + ); + string nxMetaFile = Path.Combine( + tempDirectory.FullName, + $"{ProductInfo.GetDocker(ProductInfo.ProductType.NxMeta, false)}.Dockerfile" + ); + + string nxGoContents = File.ReadAllText(nxGoFile); + string nxMetaContents = File.ReadAllText(nxMetaFile); + + nxGoContents.Should().Contain("ARG LABEL_VERSION=\"5.1.0.12345\""); + nxMetaContents.Should().Contain("ARG LABEL_VERSION=\"5.2.0.23456\""); + } + finally + { + DeleteTempDirectory(tempDirectory); + } + } + + private static List CreateProducts() + { + List products = []; + foreach (ProductInfo.ProductType productType in ProductInfo.GetProductTypes()) + { + ProductInfo productInfo = new() { Product = productType }; + + VersionInfo latest = new() + { + Version = "5.1.0.12345", + UriX64 = "https://example.com/x64.deb", + UriArm64 = "https://example.com/arm64.deb", + }; + latest.Labels.Add(VersionInfo.LabelType.Latest); + latest.Labels.Add(VersionInfo.LabelType.Stable); + productInfo.Versions.Add(latest); + + if (productType == ProductInfo.ProductType.NxMeta) + { + VersionInfo beta = new() + { + Version = "5.2.0.23456", + UriX64 = "https://example.com/x64-beta.deb", + UriArm64 = "https://example.com/arm64-beta.deb", + }; + beta.Labels.Add(VersionInfo.LabelType.Beta); + productInfo.Versions.Add(beta); + } + + products.Add(productInfo); + } + + return products; + } + + private static DirectoryInfo CreateTempDirectory() + { + string directoryPath = Path.Combine( + Path.GetTempPath(), + "CreateMatrixTests", + Guid.NewGuid().ToString("N") + ); + DirectoryInfo directoryInfo = Directory.CreateDirectory(directoryPath); + return directoryInfo; + } + + private static void DeleteTempDirectory(DirectoryInfo directoryInfo) + { + if (!directoryInfo.Exists) + { + return; + } + + directoryInfo.Delete(true); + } +} diff --git a/CreateMatrixTests/Fixture.cs b/CreateMatrixTests/Fixture.cs index 9fb93050..065a8fd1 100644 --- a/CreateMatrixTests/Fixture.cs +++ b/CreateMatrixTests/Fixture.cs @@ -1,18 +1,18 @@ -// Single instance for all tests in assembly -[assembly: AssemblyFixture(typeof(CreateMatrixTests.SingleInstanceFixture))] - -namespace CreateMatrixTests; - -// Sequential execution fixture -[CollectionDefinition("Sequential Test Collection", DisableParallelization = true)] -public class SequentialCollectionDefinition; - -[System.Diagnostics.CodeAnalysis.SuppressMessage( - "Design", - "CA1063:Implement IDisposable Correctly", - Justification = "Demonstration only" -)] -public class SingleInstanceFixture : IDisposable -{ - public void Dispose() => GC.SuppressFinalize(this); -} +// Single instance for all tests in assembly +[assembly: AssemblyFixture(typeof(CreateMatrixTests.SingleInstanceFixture))] + +namespace CreateMatrixTests; + +// Sequential execution fixture +[CollectionDefinition("Sequential Test Collection", DisableParallelization = true)] +public class SequentialCollectionDefinition; + +[System.Diagnostics.CodeAnalysis.SuppressMessage( + "Design", + "CA1063:Implement IDisposable Correctly", + Justification = "Demonstration only" +)] +public class SingleInstanceFixture : IDisposable +{ + public void Dispose() => GC.SuppressFinalize(this); +} diff --git a/CreateMatrixTests/GlobalUsings.cs b/CreateMatrixTests/GlobalUsings.cs index 7d75b0c6..ab417b3c 100644 --- a/CreateMatrixTests/GlobalUsings.cs +++ b/CreateMatrixTests/GlobalUsings.cs @@ -1,6 +1,6 @@ -global using System; -global using System.Collections.Generic; -global using System.IO; -global using System.Linq; -global using AwesomeAssertions; -global using Xunit; +global using System; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using AwesomeAssertions; +global using Xunit; diff --git a/CreateMatrixTests/ReleasesTests.cs b/CreateMatrixTests/ReleasesTests.cs index 0e6e9ec7..3cb33ff3 100644 --- a/CreateMatrixTests/ReleasesTests.cs +++ b/CreateMatrixTests/ReleasesTests.cs @@ -1,322 +1,322 @@ -using CreateMatrix; - -namespace CreateMatrixTests; - -public sealed class ReleasesTests -{ - [Fact] - public void MatchLabels() - { - // Create test releases - ReleasesJsonSchema releasesSchema = new() - { - Releases = - { - // Stable, published and released - new Release - { - PublicationType = Release.ReleasePublication, - ReleaseDate = 1, - ReleaseDeliveryDays = 1, - Version = "1.0", - }, - // Latest, published not released - new Release { PublicationType = Release.ReleasePublication, Version = "2.0" }, - // RC - new Release { PublicationType = Release.RcPublication, Version = "3.0" }, - // Beta - new Release { PublicationType = Release.BetaPublication, Version = "4.0" }, - }, - }; - - // Create ProductInfo from schema - ProductInfo productInfo = CreateProductInfo(releasesSchema); - - // 4 versions - productInfo.Versions.Should().HaveCount(4); - // 1 Latest - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) - .Should() - .Be(1); - // 1 Stable - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) - .Should() - .Be(1); - // 1 RC - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) - .Should() - .Be(1); - // 1 Beta - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) - .Should() - .Be(1); - // 1 label per version - productInfo.Versions.Count(item => item.Labels.Count == 1).Should().Be(4); - } - - [Fact] - public void MissingLatest() - { - // Similar to MissingStable() - - // Create test releases - ReleasesJsonSchema releasesSchema = new() - { - Releases = - { - // Stable, published and released - new Release - { - PublicationType = Release.ReleasePublication, - ReleaseDate = 1, - ReleaseDeliveryDays = 1, - Version = "1.0", - }, - // RC - new Release { PublicationType = Release.RcPublication, Version = "3.0" }, - // Beta - new Release { PublicationType = Release.BetaPublication, Version = "4.0" }, - }, - }; - - // Create ProductInfo from schema - ProductInfo productInfo = CreateProductInfo(releasesSchema); - - // 3 versions - productInfo.Versions.Should().HaveCount(3); - // 1 Latest - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) - .Should() - .Be(1); - // 1 Stable - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) - .Should() - .Be(1); - // 1 RC - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) - .Should() - .Be(1); - // 1 Beta - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) - .Should() - .Be(1); - - // Select all Latest or Stable labels - IEnumerable latestVersions = productInfo.Versions.Where(item => - item.Labels.Contains(VersionInfo.LabelType.Latest) - || item.Labels.Contains(VersionInfo.LabelType.Stable) - ); - // Should just be 1 entry - latestVersions.Should().ContainSingle(); - // Should have Latest and Stable labels - VersionInfo version = latestVersions.First(); - version.Labels.Count.Should().Be(2); - } - - [Fact] - public void MissingStable() - { - // Similar to MissingLatest() - - // Create test releases - ReleasesJsonSchema releasesSchema = new() - { - Releases = - { - // Latest, published not released - new Release { PublicationType = Release.ReleasePublication, Version = "1.0" }, - // RC - new Release { PublicationType = Release.RcPublication, Version = "3.0" }, - // Beta - new Release { PublicationType = Release.BetaPublication, Version = "4.0" }, - }, - }; - - // Create ProductInfo from schema - ProductInfo productInfo = CreateProductInfo(releasesSchema); - - // 3 versions - productInfo.Versions.Should().HaveCount(3); - // 1 Latest - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) - .Should() - .Be(1); - // 1 Stable - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) - .Should() - .Be(1); - // 1 RC - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) - .Should() - .Be(1); - // 1 Beta - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) - .Should() - .Be(1); - - // Select all Latest or Stable labels - IEnumerable latestVersions = productInfo.Versions.Where(item => - item.Labels.Contains(VersionInfo.LabelType.Latest) - || item.Labels.Contains(VersionInfo.LabelType.Stable) - ); - // Should just be 1 entry - latestVersions.Should().ContainSingle(); - // Should have Latest and Stable labels - VersionInfo version = latestVersions.First(); - version.Labels.Count.Should().Be(2); - } - - [Fact] - public void MultipleReleases() - { - // Create test releases - ReleasesJsonSchema releasesSchema = new() - { - Releases = - { - // Published not released - new Release { PublicationType = Release.ReleasePublication, Version = "2.0" }, - new Release { PublicationType = Release.ReleasePublication, Version = "3.0" }, - new Release { PublicationType = Release.ReleasePublication, Version = "4.0" }, - }, - }; - - // Create ProductInfo from schema - ProductInfo productInfo = CreateProductInfo(releasesSchema); - - // 1 version - productInfo.Versions.Should().ContainSingle(); - // 2 labels per version - productInfo.Versions.Count(item => item.Labels.Count == 2).Should().Be(1); - // 1 Latest - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) - .Should() - .Be(1); - // 1 Stable - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) - .Should() - .Be(1); - - // Select all Latest or Stable labels - IEnumerable latestVersions = productInfo.Versions.Where(item => - item.Labels.Contains(VersionInfo.LabelType.Latest) - || item.Labels.Contains(VersionInfo.LabelType.Stable) - ); - // Should just be 1 entry - latestVersions.Should().ContainSingle(); - // Should have Latest and Stable labels - VersionInfo version = latestVersions.First(); - version.Labels.Count.Should().Be(2); - - // Should be the v4.0 version - version.Version.Should().Be("4.0"); - } - - [Fact] - public void ConflictingPublicationTypes_Throws() - { - // The same version number tagged with two different publication types is contradictory - // vendor data and must be rejected rather than folded into our data. - ReleasesJsonSchema releasesSchema = new() - { - Releases = - { - new Release - { - PublicationType = Release.ReleasePublication, - ReleaseDate = 1, - ReleaseDeliveryDays = 1, - Version = "6.1.2.42921", - }, - new Release { PublicationType = Release.BetaPublication, Version = "6.1.2.42921" }, - }, - }; - - Action act = () => CreateProductInfo(releasesSchema); - act.Should().Throw(); - } - - [Fact] - public void DuplicatePublicationType_Folds() - { - // The same version number listed twice with the same publication type is a benign - // duplicate and is folded to a single entry. - ReleasesJsonSchema releasesSchema = new() - { - Releases = - { - new Release - { - PublicationType = Release.ReleasePublication, - ReleaseDate = 1, - ReleaseDeliveryDays = 1, - Version = "6.1.2.42921", - }, - new Release - { - PublicationType = Release.ReleasePublication, - ReleaseDate = 1, - ReleaseDeliveryDays = 1, - Version = "6.1.2.42921", - }, - }, - }; - - ProductInfo productInfo = CreateProductInfo(releasesSchema); - - // Folded to a single version - productInfo.Versions.Should().ContainSingle(); - productInfo.Versions.First().Version.Should().Be("6.1.2.42921"); - } - - [Fact] - public void MissingVersion_Throws() - { - // A release with a missing or unparseable version must be rejected with a clear - // error rather than a context-free version-parsing failure. - ReleasesJsonSchema releasesSchema = new() - { - Releases = - { - new Release - { - PublicationType = Release.ReleasePublication, - ReleaseDate = 1, - ReleaseDeliveryDays = 1, - Version = "", - }, - }, - }; - - Action act = () => CreateProductInfo(releasesSchema); - act.Should().Throw(); - } - - private static ProductInfo CreateProductInfo(ReleasesJsonSchema releasesSchema) - { - // Mirror of the non-network portion of ProductInfo.FetchVersionsAsync(), - // sharing the version and label logic via ProductInfo.CreateVersionInfo(). - ProductInfo productInfo = new(); - foreach (Release release in ReleasesJsonSchema.VerifyReleases(releasesSchema.Releases)) - { - productInfo.Versions.Add(productInfo.CreateVersionInfo(release)); - } - productInfo.VerifyLabels(); - - return productInfo; - } -} +using CreateMatrix; + +namespace CreateMatrixTests; + +public sealed class ReleasesTests +{ + [Fact] + public void MatchLabels() + { + // Create test releases + ReleasesJsonSchema releasesSchema = new() + { + Releases = + { + // Stable, published and released + new Release + { + PublicationType = Release.ReleasePublication, + ReleaseDate = 1, + ReleaseDeliveryDays = 1, + Version = "1.0", + }, + // Latest, published not released + new Release { PublicationType = Release.ReleasePublication, Version = "2.0" }, + // RC + new Release { PublicationType = Release.RcPublication, Version = "3.0" }, + // Beta + new Release { PublicationType = Release.BetaPublication, Version = "4.0" }, + }, + }; + + // Create ProductInfo from schema + ProductInfo productInfo = CreateProductInfo(releasesSchema); + + // 4 versions + productInfo.Versions.Should().HaveCount(4); + // 1 Latest + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) + .Should() + .Be(1); + // 1 Stable + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) + .Should() + .Be(1); + // 1 RC + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) + .Should() + .Be(1); + // 1 Beta + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) + .Should() + .Be(1); + // 1 label per version + productInfo.Versions.Count(item => item.Labels.Count == 1).Should().Be(4); + } + + [Fact] + public void MissingLatest() + { + // Similar to MissingStable() + + // Create test releases + ReleasesJsonSchema releasesSchema = new() + { + Releases = + { + // Stable, published and released + new Release + { + PublicationType = Release.ReleasePublication, + ReleaseDate = 1, + ReleaseDeliveryDays = 1, + Version = "1.0", + }, + // RC + new Release { PublicationType = Release.RcPublication, Version = "3.0" }, + // Beta + new Release { PublicationType = Release.BetaPublication, Version = "4.0" }, + }, + }; + + // Create ProductInfo from schema + ProductInfo productInfo = CreateProductInfo(releasesSchema); + + // 3 versions + productInfo.Versions.Should().HaveCount(3); + // 1 Latest + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) + .Should() + .Be(1); + // 1 Stable + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) + .Should() + .Be(1); + // 1 RC + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) + .Should() + .Be(1); + // 1 Beta + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) + .Should() + .Be(1); + + // Select all Latest or Stable labels + IEnumerable latestVersions = productInfo.Versions.Where(item => + item.Labels.Contains(VersionInfo.LabelType.Latest) + || item.Labels.Contains(VersionInfo.LabelType.Stable) + ); + // Should just be 1 entry + latestVersions.Should().ContainSingle(); + // Should have Latest and Stable labels + VersionInfo version = latestVersions.First(); + version.Labels.Count.Should().Be(2); + } + + [Fact] + public void MissingStable() + { + // Similar to MissingLatest() + + // Create test releases + ReleasesJsonSchema releasesSchema = new() + { + Releases = + { + // Latest, published not released + new Release { PublicationType = Release.ReleasePublication, Version = "1.0" }, + // RC + new Release { PublicationType = Release.RcPublication, Version = "3.0" }, + // Beta + new Release { PublicationType = Release.BetaPublication, Version = "4.0" }, + }, + }; + + // Create ProductInfo from schema + ProductInfo productInfo = CreateProductInfo(releasesSchema); + + // 3 versions + productInfo.Versions.Should().HaveCount(3); + // 1 Latest + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) + .Should() + .Be(1); + // 1 Stable + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) + .Should() + .Be(1); + // 1 RC + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) + .Should() + .Be(1); + // 1 Beta + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) + .Should() + .Be(1); + + // Select all Latest or Stable labels + IEnumerable latestVersions = productInfo.Versions.Where(item => + item.Labels.Contains(VersionInfo.LabelType.Latest) + || item.Labels.Contains(VersionInfo.LabelType.Stable) + ); + // Should just be 1 entry + latestVersions.Should().ContainSingle(); + // Should have Latest and Stable labels + VersionInfo version = latestVersions.First(); + version.Labels.Count.Should().Be(2); + } + + [Fact] + public void MultipleReleases() + { + // Create test releases + ReleasesJsonSchema releasesSchema = new() + { + Releases = + { + // Published not released + new Release { PublicationType = Release.ReleasePublication, Version = "2.0" }, + new Release { PublicationType = Release.ReleasePublication, Version = "3.0" }, + new Release { PublicationType = Release.ReleasePublication, Version = "4.0" }, + }, + }; + + // Create ProductInfo from schema + ProductInfo productInfo = CreateProductInfo(releasesSchema); + + // 1 version + productInfo.Versions.Should().ContainSingle(); + // 2 labels per version + productInfo.Versions.Count(item => item.Labels.Count == 2).Should().Be(1); + // 1 Latest + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) + .Should() + .Be(1); + // 1 Stable + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) + .Should() + .Be(1); + + // Select all Latest or Stable labels + IEnumerable latestVersions = productInfo.Versions.Where(item => + item.Labels.Contains(VersionInfo.LabelType.Latest) + || item.Labels.Contains(VersionInfo.LabelType.Stable) + ); + // Should just be 1 entry + latestVersions.Should().ContainSingle(); + // Should have Latest and Stable labels + VersionInfo version = latestVersions.First(); + version.Labels.Count.Should().Be(2); + + // Should be the v4.0 version + version.Version.Should().Be("4.0"); + } + + [Fact] + public void ConflictingPublicationTypes_Throws() + { + // The same version number tagged with two different publication types is contradictory + // vendor data and must be rejected rather than folded into our data. + ReleasesJsonSchema releasesSchema = new() + { + Releases = + { + new Release + { + PublicationType = Release.ReleasePublication, + ReleaseDate = 1, + ReleaseDeliveryDays = 1, + Version = "6.1.2.42921", + }, + new Release { PublicationType = Release.BetaPublication, Version = "6.1.2.42921" }, + }, + }; + + Action act = () => CreateProductInfo(releasesSchema); + act.Should().Throw(); + } + + [Fact] + public void DuplicatePublicationType_Folds() + { + // The same version number listed twice with the same publication type is a benign + // duplicate and is folded to a single entry. + ReleasesJsonSchema releasesSchema = new() + { + Releases = + { + new Release + { + PublicationType = Release.ReleasePublication, + ReleaseDate = 1, + ReleaseDeliveryDays = 1, + Version = "6.1.2.42921", + }, + new Release + { + PublicationType = Release.ReleasePublication, + ReleaseDate = 1, + ReleaseDeliveryDays = 1, + Version = "6.1.2.42921", + }, + }, + }; + + ProductInfo productInfo = CreateProductInfo(releasesSchema); + + // Folded to a single version + productInfo.Versions.Should().ContainSingle(); + productInfo.Versions.First().Version.Should().Be("6.1.2.42921"); + } + + [Fact] + public void MissingVersion_Throws() + { + // A release with a missing or unparseable version must be rejected with a clear + // error rather than a context-free version-parsing failure. + ReleasesJsonSchema releasesSchema = new() + { + Releases = + { + new Release + { + PublicationType = Release.ReleasePublication, + ReleaseDate = 1, + ReleaseDeliveryDays = 1, + Version = "", + }, + }, + }; + + Action act = () => CreateProductInfo(releasesSchema); + act.Should().Throw(); + } + + private static ProductInfo CreateProductInfo(ReleasesJsonSchema releasesSchema) + { + // Mirror of the non-network portion of ProductInfo.FetchVersionsAsync(), + // sharing the version and label logic via ProductInfo.CreateVersionInfo(). + ProductInfo productInfo = new(); + foreach (Release release in ReleasesJsonSchema.VerifyReleases(releasesSchema.Releases)) + { + productInfo.Versions.Add(productInfo.CreateVersionInfo(release)); + } + productInfo.VerifyLabels(); + + return productInfo; + } +} diff --git a/CreateMatrixTests/VersionForwardTests.cs b/CreateMatrixTests/VersionForwardTests.cs index cc8e8f85..1b6d6b23 100644 --- a/CreateMatrixTests/VersionForwardTests.cs +++ b/CreateMatrixTests/VersionForwardTests.cs @@ -1,269 +1,269 @@ -using CreateMatrix; - -namespace CreateMatrixTests; - -public class VersionForwardTests -{ - [Fact] - public void VersionForward() - { - // Create test releases - List oldProductList = - [ - new ProductInfo - { - Product = ProductInfo.ProductType.NxMeta, - Versions = - { - new VersionInfo { Version = "1.0", Labels = { VersionInfo.LabelType.Stable } }, - new VersionInfo { Version = "2.0", Labels = { VersionInfo.LabelType.Latest } }, - new VersionInfo { Version = "3.0", Labels = { VersionInfo.LabelType.RC } }, - new VersionInfo { Version = "4.0", Labels = { VersionInfo.LabelType.Beta } }, - }, - }, - ]; - List newProductList = - [ - new ProductInfo - { - Product = ProductInfo.ProductType.NxMeta, - Versions = - { - new VersionInfo { Version = "1.1", Labels = { VersionInfo.LabelType.Stable } }, - new VersionInfo { Version = "2.1", Labels = { VersionInfo.LabelType.Latest } }, - new VersionInfo { Version = "3.1", Labels = { VersionInfo.LabelType.RC } }, - new VersionInfo { Version = "4.1", Labels = { VersionInfo.LabelType.Beta } }, - }, - }, - ]; - - // newProductList will be updated in-place - // Only Stable and Latest is tested - // Versions with multiple labels will update the version not the individual labels - ReleaseVersionForward.Verify(oldProductList, newProductList); - ProductInfo productInfo = newProductList.First(); - - // 4 versions - productInfo.Versions.Should().HaveCount(4); - // 1 Latest - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) - .Should() - .Be(1); - // 1 Stable - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) - .Should() - .Be(1); - // 1 RC - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) - .Should() - .Be(1); - // 1 Beta - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) - .Should() - .Be(1); - // 1 label per version - productInfo.Versions.Count(item => item.Labels.Count == 1).Should().Be(4); - - // Stable 1.1 - string? stableVersion = productInfo - .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) - ?.Version; - stableVersion.Should().Be("1.1"); - // Latest 2.1 - string? latestVersion = productInfo - .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) - ?.Version; - latestVersion.Should().Be("2.1"); - // RC 3.1 - string? rcVersion = productInfo - .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.RC)) - ?.Version; - rcVersion.Should().Be("3.1"); - // Beta 4.1 - string? betaVersion = productInfo - .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) - ?.Version; - betaVersion.Should().Be("4.1"); - } - - [Fact] - public void VersionRegress() - { - // Create test releases - List oldProductList = - [ - new ProductInfo - { - Product = ProductInfo.ProductType.NxMeta, - Versions = - { - new VersionInfo { Version = "1.0", Labels = { VersionInfo.LabelType.Stable } }, - new VersionInfo { Version = "2.0", Labels = { VersionInfo.LabelType.Latest } }, - new VersionInfo { Version = "3.0", Labels = { VersionInfo.LabelType.RC } }, - new VersionInfo { Version = "4.0", Labels = { VersionInfo.LabelType.Beta } }, - }, - }, - ]; - List newProductList = - [ - new ProductInfo - { - Product = ProductInfo.ProductType.NxMeta, - Versions = - { - new VersionInfo { Version = "0.9", Labels = { VersionInfo.LabelType.Stable } }, - new VersionInfo { Version = "1.9", Labels = { VersionInfo.LabelType.Latest } }, - new VersionInfo { Version = "2.9", Labels = { VersionInfo.LabelType.RC } }, - new VersionInfo { Version = "3.9", Labels = { VersionInfo.LabelType.Beta } }, - }, - }, - ]; - - // newProductList will be updated in-place - // Only Stable and Latest is tested - // Versions with multiple labels will update the version not the individual labels - ReleaseVersionForward.Verify(oldProductList, newProductList); - ProductInfo productInfo = newProductList.First(); - - // 4 versions - productInfo.Versions.Should().HaveCount(4); - // 1 Latest - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) - .Should() - .Be(1); - // 1 Stable - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) - .Should() - .Be(1); - // 1 RC - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) - .Should() - .Be(1); - // 1 Beta - productInfo - .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) - .Should() - .Be(1); - // 1 label per version - productInfo.Versions.Count(item => item.Labels.Count == 1).Should().Be(4); - - // Stable 1.0 - string? stableVersion = productInfo - .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) - ?.Version; - stableVersion.Should().Be("1.0"); - // Latest 2.0 - string? latestVersion = productInfo - .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) - ?.Version; - latestVersion.Should().Be("2.0"); - // RC 3.0 - string? rcVersion = productInfo - .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.RC)) - ?.Version; - rcVersion.Should().Be("3.0"); - // Beta 4.0 - string? betaVersion = productInfo - .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) - ?.Version; - betaVersion.Should().Be("4.0"); - } - - [Fact] - public void VersionRegress_FoldsOntoExistingVersion() - { - // Reproduces the WisenetWAVE codegen failure: the online "Latest" regressed below a - // version that is already present as "Stable", so restoring the old "Latest" must fold - // onto the existing entry instead of adding a duplicate version row. - List oldProductList = - [ - new ProductInfo - { - Product = ProductInfo.ProductType.WisenetWAVE, - Versions = - { - new VersionInfo - { - Version = "6.0.5.41290", - Labels = { VersionInfo.LabelType.Stable }, - }, - new VersionInfo - { - Version = "6.1.2.42921", - Labels = { VersionInfo.LabelType.Latest }, - }, - }, - }, - ]; - List newProductList = - [ - new ProductInfo - { - Product = ProductInfo.ProductType.WisenetWAVE, - Versions = - { - new VersionInfo - { - Version = "6.1.1.42624", - Labels = { VersionInfo.LabelType.Latest }, - }, - new VersionInfo - { - Version = "6.1.2.42921", - Labels = { VersionInfo.LabelType.Stable }, - }, - }, - }, - ]; - - ReleaseVersionForward.Verify(oldProductList, newProductList); - ProductInfo productInfo = newProductList.First(); - - // Folded to a single 6.1.2.42921 entry carrying both Stable and Latest - productInfo.Versions.Should().ContainSingle(); - VersionInfo version = productInfo.Versions.First(); - version.Version.Should().Be("6.1.2.42921"); - version - .Labels.Should() - .BeEquivalentTo([VersionInfo.LabelType.Stable, VersionInfo.LabelType.Latest]); - - // No duplicate version numbers - productInfo.Versions.Select(item => item.Version).Should().OnlyHaveUniqueItems(); - productInfo.Invoking(item => item.VerifyNoDuplicateVersions()).Should().NotThrow(); - } - - [Fact] - public void VerifyNoDuplicateVersions_Throws() - { - // Two entries sharing a version number must be rejected - ProductInfo productInfo = new() - { - Product = ProductInfo.ProductType.NxMeta, - Versions = - { - new VersionInfo - { - Version = "6.1.2.42921", - Labels = { VersionInfo.LabelType.Stable }, - }, - new VersionInfo - { - Version = "6.1.2.42921", - Labels = { VersionInfo.LabelType.Latest }, - }, - }, - }; - - productInfo - .Invoking(item => item.VerifyNoDuplicateVersions()) - .Should() - .Throw(); - } -} +using CreateMatrix; + +namespace CreateMatrixTests; + +public class VersionForwardTests +{ + [Fact] + public void VersionForward() + { + // Create test releases + List oldProductList = + [ + new ProductInfo + { + Product = ProductInfo.ProductType.NxMeta, + Versions = + { + new VersionInfo { Version = "1.0", Labels = { VersionInfo.LabelType.Stable } }, + new VersionInfo { Version = "2.0", Labels = { VersionInfo.LabelType.Latest } }, + new VersionInfo { Version = "3.0", Labels = { VersionInfo.LabelType.RC } }, + new VersionInfo { Version = "4.0", Labels = { VersionInfo.LabelType.Beta } }, + }, + }, + ]; + List newProductList = + [ + new ProductInfo + { + Product = ProductInfo.ProductType.NxMeta, + Versions = + { + new VersionInfo { Version = "1.1", Labels = { VersionInfo.LabelType.Stable } }, + new VersionInfo { Version = "2.1", Labels = { VersionInfo.LabelType.Latest } }, + new VersionInfo { Version = "3.1", Labels = { VersionInfo.LabelType.RC } }, + new VersionInfo { Version = "4.1", Labels = { VersionInfo.LabelType.Beta } }, + }, + }, + ]; + + // newProductList will be updated in-place + // Only Stable and Latest is tested + // Versions with multiple labels will update the version not the individual labels + ReleaseVersionForward.Verify(oldProductList, newProductList); + ProductInfo productInfo = newProductList.First(); + + // 4 versions + productInfo.Versions.Should().HaveCount(4); + // 1 Latest + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) + .Should() + .Be(1); + // 1 Stable + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) + .Should() + .Be(1); + // 1 RC + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) + .Should() + .Be(1); + // 1 Beta + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) + .Should() + .Be(1); + // 1 label per version + productInfo.Versions.Count(item => item.Labels.Count == 1).Should().Be(4); + + // Stable 1.1 + string? stableVersion = productInfo + .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) + ?.Version; + stableVersion.Should().Be("1.1"); + // Latest 2.1 + string? latestVersion = productInfo + .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) + ?.Version; + latestVersion.Should().Be("2.1"); + // RC 3.1 + string? rcVersion = productInfo + .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.RC)) + ?.Version; + rcVersion.Should().Be("3.1"); + // Beta 4.1 + string? betaVersion = productInfo + .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) + ?.Version; + betaVersion.Should().Be("4.1"); + } + + [Fact] + public void VersionRegress() + { + // Create test releases + List oldProductList = + [ + new ProductInfo + { + Product = ProductInfo.ProductType.NxMeta, + Versions = + { + new VersionInfo { Version = "1.0", Labels = { VersionInfo.LabelType.Stable } }, + new VersionInfo { Version = "2.0", Labels = { VersionInfo.LabelType.Latest } }, + new VersionInfo { Version = "3.0", Labels = { VersionInfo.LabelType.RC } }, + new VersionInfo { Version = "4.0", Labels = { VersionInfo.LabelType.Beta } }, + }, + }, + ]; + List newProductList = + [ + new ProductInfo + { + Product = ProductInfo.ProductType.NxMeta, + Versions = + { + new VersionInfo { Version = "0.9", Labels = { VersionInfo.LabelType.Stable } }, + new VersionInfo { Version = "1.9", Labels = { VersionInfo.LabelType.Latest } }, + new VersionInfo { Version = "2.9", Labels = { VersionInfo.LabelType.RC } }, + new VersionInfo { Version = "3.9", Labels = { VersionInfo.LabelType.Beta } }, + }, + }, + ]; + + // newProductList will be updated in-place + // Only Stable and Latest is tested + // Versions with multiple labels will update the version not the individual labels + ReleaseVersionForward.Verify(oldProductList, newProductList); + ProductInfo productInfo = newProductList.First(); + + // 4 versions + productInfo.Versions.Should().HaveCount(4); + // 1 Latest + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) + .Should() + .Be(1); + // 1 Stable + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) + .Should() + .Be(1); + // 1 RC + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.RC)) + .Should() + .Be(1); + // 1 Beta + productInfo + .Versions.Count(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) + .Should() + .Be(1); + // 1 label per version + productInfo.Versions.Count(item => item.Labels.Count == 1).Should().Be(4); + + // Stable 1.0 + string? stableVersion = productInfo + .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Stable)) + ?.Version; + stableVersion.Should().Be("1.0"); + // Latest 2.0 + string? latestVersion = productInfo + .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Latest)) + ?.Version; + latestVersion.Should().Be("2.0"); + // RC 3.0 + string? rcVersion = productInfo + .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.RC)) + ?.Version; + rcVersion.Should().Be("3.0"); + // Beta 4.0 + string? betaVersion = productInfo + .Versions.FirstOrDefault(item => item.Labels.Contains(VersionInfo.LabelType.Beta)) + ?.Version; + betaVersion.Should().Be("4.0"); + } + + [Fact] + public void VersionRegress_FoldsOntoExistingVersion() + { + // Reproduces the WisenetWAVE codegen failure: the online "Latest" regressed below a + // version that is already present as "Stable", so restoring the old "Latest" must fold + // onto the existing entry instead of adding a duplicate version row. + List oldProductList = + [ + new ProductInfo + { + Product = ProductInfo.ProductType.WisenetWAVE, + Versions = + { + new VersionInfo + { + Version = "6.0.5.41290", + Labels = { VersionInfo.LabelType.Stable }, + }, + new VersionInfo + { + Version = "6.1.2.42921", + Labels = { VersionInfo.LabelType.Latest }, + }, + }, + }, + ]; + List newProductList = + [ + new ProductInfo + { + Product = ProductInfo.ProductType.WisenetWAVE, + Versions = + { + new VersionInfo + { + Version = "6.1.1.42624", + Labels = { VersionInfo.LabelType.Latest }, + }, + new VersionInfo + { + Version = "6.1.2.42921", + Labels = { VersionInfo.LabelType.Stable }, + }, + }, + }, + ]; + + ReleaseVersionForward.Verify(oldProductList, newProductList); + ProductInfo productInfo = newProductList.First(); + + // Folded to a single 6.1.2.42921 entry carrying both Stable and Latest + productInfo.Versions.Should().ContainSingle(); + VersionInfo version = productInfo.Versions.First(); + version.Version.Should().Be("6.1.2.42921"); + version + .Labels.Should() + .BeEquivalentTo([VersionInfo.LabelType.Stable, VersionInfo.LabelType.Latest]); + + // No duplicate version numbers + productInfo.Versions.Select(item => item.Version).Should().OnlyHaveUniqueItems(); + productInfo.Invoking(item => item.VerifyNoDuplicateVersions()).Should().NotThrow(); + } + + [Fact] + public void VerifyNoDuplicateVersions_Throws() + { + // Two entries sharing a version number must be rejected + ProductInfo productInfo = new() + { + Product = ProductInfo.ProductType.NxMeta, + Versions = + { + new VersionInfo + { + Version = "6.1.2.42921", + Labels = { VersionInfo.LabelType.Stable }, + }, + new VersionInfo + { + Version = "6.1.2.42921", + Labels = { VersionInfo.LabelType.Latest }, + }, + }, + }; + + productInfo + .Invoking(item => item.VerifyNoDuplicateVersions()) + .Should() + .Throw(); + } +} diff --git a/CreateMatrixTests/VersionInfoTests.cs b/CreateMatrixTests/VersionInfoTests.cs index 5cb2f32a..884baea7 100644 --- a/CreateMatrixTests/VersionInfoTests.cs +++ b/CreateMatrixTests/VersionInfoTests.cs @@ -1,26 +1,26 @@ -using CreateMatrix; - -namespace CreateMatrixTests; - -public sealed class VersionInfoTests -{ - [Fact] - public void SetVersion_RemovesReleaseSuffix() - { - VersionInfo versionInfo = new(); - - versionInfo.SetVersion("5.1.0.35151 R1"); - - versionInfo.Version.Should().Be("5.1.0.35151"); - } - - [Fact] - public void BuildNumber_ExtractsRevision() - { - VersionInfo versionInfo = new() { Version = "5.0.0.35271" }; - - int buildNumber = versionInfo.GetBuildNumber(); - - buildNumber.Should().Be(35271); - } -} +using CreateMatrix; + +namespace CreateMatrixTests; + +public sealed class VersionInfoTests +{ + [Fact] + public void SetVersion_RemovesReleaseSuffix() + { + VersionInfo versionInfo = new(); + + versionInfo.SetVersion("5.1.0.35151 R1"); + + versionInfo.Version.Should().Be("5.1.0.35151"); + } + + [Fact] + public void BuildNumber_ExtractsRevision() + { + VersionInfo versionInfo = new() { Version = "5.0.0.35271" }; + + int buildNumber = versionInfo.GetBuildNumber(); + + buildNumber.Should().Be(35271); + } +} diff --git a/Docker/README.md b/Docker/README.md index 4f74a4f2..ff35546a 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -1,12 +1,12 @@ -# Docker Projects for Network Optix VMS Products - -This is a project to build and publish docker images for various [Network Optix](https://www.networkoptix.com/) VMS products. - -## License - -Licensed under the [MIT License](https://github.com/ptr727/NxWitness/blob/main/LICENSE) -![GitHub License](https://img.shields.io/github/license/ptr727/NxWitness) - -## Project - -Refer to the [GitHub NxWitness](https://github.com/ptr727/NxWitness/) project for usage and details. +# Docker Projects for Network Optix VMS Products + +This is a project to build and publish docker images for various [Network Optix](https://www.networkoptix.com/) VMS products. + +## License + +Licensed under the [MIT License](https://github.com/ptr727/NxWitness/blob/main/LICENSE) +![GitHub License](https://img.shields.io/github/license/ptr727/NxWitness) + +## Project + +Refer to the [GitHub NxWitness](https://github.com/ptr727/NxWitness/) project for usage and details. diff --git a/HISTORY.md b/HISTORY.md index 91036fe0..b70ffb20 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,63 +1,63 @@ -# Docker Projects for Network Optix VMS Products - -This is a project to build and publish docker images for various [Network Optix][networkoptix-link] VMS products. - -## Release History - -- Version 2.14: - - Fixed an LSIO permissions race where the startup `chown` could run before the `PUID`/`PGID` remap, leaving `/config` and `/media` owned by the wrong user and breaking media writes. -- Version 2.13: - - Fixed a regression bug that surfaced when Nx released an older version under the same tag, triggering the version-forward-release only logic. -- Version 2.12: - - Reworked the CI pipeline: pull requests run a fast representative amd64 smoke build (NxMeta and NxMeta-LSIO) instead of the full matrix, publishing moved to a weekly schedule (and manual trigger) that builds both the `main` and `develop` branches in one run, and merges no longer republish images. This speeds up PR feedback, reduces GH Actions usage, and stops no-op image updates for consumers. -- Version 2.11: - - Add `currentOsVariantOverride=docker` to `mediaserver.conf` following the pattern used in the current Nx [Dockerfile][nxvmsdockerfile-link], and documented in this [support][nxpackage-link] article. -- Version 2.10: - - Split the docker images into a base image and product images, improves build times and reduces GH actions cache utilization. -- Version 2.9: - - Refactoring to match layout and style used in other projects. - - Updated to .NET 10, adding support for Nullable, and AOT. - - Restructured CLI handling logic. - - Update GHA cache usage to prevent the GH actions cache from running out of space. -- Version 2.8: - - Add default `analytics` volume, along with `media` and `backup` volumes, media server complains when sharing analytics storage. -- Version 2.7: - - Modified version update checking actions to create a PR vs. committing changes directly. -- Version 2.6: - - Updated to Ubuntu Noble 24.04 LTS base images in [support][nxossupport-link] of v6 products. - - Update `CreateMatrix` tool to use .NET 9 and stricter `dotnet format` using `.editorconfig`. -- Version 2.5: - - Added [NxGo][nxgo-link] builds, a version of Nx Witness targeted at the transportation sector, [PR][nxgopr-link] by @kinnairdclan, thank you. -- Version 2.4: - - Added [Hanwha Vision][hanwhavision-link] [Wisenet WAVE VMS][hanwhawave-link] builds, another US OEM whitelabel version Nx Witness. - - Using the `CreateMatrix` utility instead of M4 to create Docker and Compose files for all product variants. -- Version 2.3: - - Added unit test project to verify the release and upgrade control logic. - - Switched from `Newtonsoft.Json` to .NET native `Text.Json`. - - Modified builds to account for v6 Beta installers requiring the `file` package but not listing it in DEB `Depends`, see [#142][filedepends-link]. -- Version 2.2: - - Simplified `Dockerfile` creation by using shell scripts instead of a `Makefile`. -- Version 2.1: - - Added ARM64 images per user [request][arm64request-link]. - - Note that testing was limited to verifying that the containers run on a Raspberry Pi 5. - - Updated build scripts to use `docker compose` (vs. `docker-compose`) and `docker buildx` (vs. `docker build`) per current Docker/Moby v25+ [release][dockerinstall-link]. - - Updated `CreateMatrix` tooling to use the newest version for the `latest` tag when multiple versions are available. -- Version 2.0: - - Added a build release [version][version-link], this version is independent of Nx release versions, and only identifies the version of the build environment, and is used in the image label. - - Nx released v5.1 across all product brands, v5.1 [supports][nxossupport-link] Ubuntu Jammy 22.04 LTS, and all base images have been updated to Jammy. - - Due to the Jammy dependency versions older than v5.1 are no longer being built. - - Build scripts removed support for old v4 variants. - - Added a link from `/root/.config/nx_ini` to `/config/ini` for additional INI configuration files. - -[arm64request-link]: https://github.com/ptr727/NxWitness/issues/131 -[dockerinstall-link]: https://docs.docker.com/engine/install/ -[filedepends-link]: https://github.com/ptr727/NxWitness/issues/142 -[hanwhavision-link]: https://hanwhavisionamerica.com/ -[hanwhawave-link]: https://wavevms.com/ -[networkoptix-link]: https://www.networkoptix.com/ -[nxgo-link]: https://www.networkoptix.com/nx-go -[nxgopr-link]: https://github.com/ptr727/NxWitness/pull/172 -[nxossupport-link]: https://support.networkoptix.com/hc/en-us/articles/205313168-Nx-Witness-Operating-System-Support -[nxpackage-link]: https://support.networkoptix.com/hc/en-us/articles/32917149024535-Creating-Update-Packages-for-Custom-Linux-and-or-ARM-Servers -[nxvmsdockerfile-link]: https://github.com/networkoptix/nxvms-docker/blob/master/Dockerfile -[version-link]: ./version.json +# Docker Projects for Network Optix VMS Products + +This is a project to build and publish docker images for various [Network Optix][networkoptix-link] VMS products. + +## Release History + +- Version 2.14: + - Fixed an LSIO permissions race where the startup `chown` could run before the `PUID`/`PGID` remap, leaving `/config` and `/media` owned by the wrong user and breaking media writes. +- Version 2.13: + - Fixed a regression bug that surfaced when Nx released an older version under the same tag, triggering the version-forward-release-only logic. +- Version 2.12: + - Reworked the CI pipeline: pull requests run a fast representative amd64 smoke build (NxMeta and NxMeta-LSIO) instead of the full matrix, publishing moved to a weekly schedule (and manual trigger) that builds both the `main` and `develop` branches in one run, and merges no longer republish images. This speeds up PR feedback, reduces GH Actions usage, and stops no-op image updates for consumers. +- Version 2.11: + - Add `currentOsVariantOverride=docker` to `mediaserver.conf` following the pattern used in the current Nx [Dockerfile][nxvmsdockerfile-link], and documented in this [support][nxpackage-link] article. +- Version 2.10: + - Split the docker images into a base image and product images, improves build times and reduces GH actions cache utilization. +- Version 2.9: + - Refactoring to match layout and style used in other projects. + - Updated to .NET 10, adding support for Nullable, and AOT. + - Restructured CLI handling logic. + - Update GHA cache usage to prevent the GH actions cache from running out of space. +- Version 2.8: + - Add default `analytics` volume, along with `media` and `backup` volumes, media server complains when sharing analytics storage. +- Version 2.7: + - Modified version update checking actions to create a PR vs. committing changes directly. +- Version 2.6: + - Updated to Ubuntu Noble 24.04 LTS base images in [support][nxossupport-link] of v6 products. + - Update `CreateMatrix` tool to use .NET 9 and stricter `dotnet format` using `.editorconfig`. +- Version 2.5: + - Added [NxGo][nxgo-link] builds, a version of Nx Witness targeted at the transportation sector, [PR][nxgopr-link] by @kinnairdclan, thank you. +- Version 2.4: + - Added [Hanwha Vision][hanwhavision-link] [Wisenet WAVE VMS][hanwhawave-link] builds, another US OEM whitelabel version Nx Witness. + - Using the `CreateMatrix` utility instead of M4 to create Docker and Compose files for all product variants. +- Version 2.3: + - Added unit test project to verify the release and upgrade control logic. + - Switched from `Newtonsoft.Json` to .NET native `Text.Json`. + - Modified builds to account for v6 Beta installers requiring the `file` package but not listing it in DEB `Depends`, see [#142][filedepends-link]. +- Version 2.2: + - Simplified `Dockerfile` creation by using shell scripts instead of a `Makefile`. +- Version 2.1: + - Added ARM64 images per user [request][arm64request-link]. + - Note that testing was limited to verifying that the containers run on a Raspberry Pi 5. + - Updated build scripts to use `docker compose` (vs. `docker-compose`) and `docker buildx` (vs. `docker build`) per current Docker/Moby v25+ [release][dockerinstall-link]. + - Updated `CreateMatrix` tooling to use the newest version for the `latest` tag when multiple versions are available. +- Version 2.0: + - Added a build release [version][version-link], this version is independent of Nx release versions, and only identifies the version of the build environment, and is used in the image label. + - Nx released v5.1 across all product brands, v5.1 [supports][nxossupport-link] Ubuntu Jammy 22.04 LTS, and all base images have been updated to Jammy. + - Due to the Jammy dependency versions older than v5.1 are no longer being built. + - Build scripts removed support for old v4 variants. + - Added a link from `/root/.config/nx_ini` to `/config/ini` for additional INI configuration files. + +[arm64request-link]: https://github.com/ptr727/NxWitness/issues/131 +[dockerinstall-link]: https://docs.docker.com/engine/install/ +[filedepends-link]: https://github.com/ptr727/NxWitness/issues/142 +[hanwhavision-link]: https://hanwhavisionamerica.com/ +[hanwhawave-link]: https://wavevms.com/ +[networkoptix-link]: https://www.networkoptix.com/ +[nxgo-link]: https://www.networkoptix.com/nx-go +[nxgopr-link]: https://github.com/ptr727/NxWitness/pull/172 +[nxossupport-link]: https://support.networkoptix.com/hc/en-us/articles/205313168-Nx-Witness-Operating-System-Support +[nxpackage-link]: https://support.networkoptix.com/hc/en-us/articles/32917149024535-Creating-Update-Packages-for-Custom-Linux-and-or-ARM-Servers +[nxvmsdockerfile-link]: https://github.com/networkoptix/nxvms-docker/blob/master/Dockerfile +[version-link]: ./version.json diff --git a/LICENSE b/LICENSE index c07453d5..51539ce9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2026 Pieter Viljoen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +MIT License + +Copyright (c) 2026 Pieter Viljoen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Make/Matrix.json b/Make/Matrix.json index bcead07e..0f7a7f40 100644 --- a/Make/Matrix.json +++ b/Make/Matrix.json @@ -1,605 +1,605 @@ -{ - "SchemaVersion": 2, - "Images": [ - { - "Name": "NxGo", - "Product": "nxgo", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxgo:6.1.2.42921", - "docker.io/ptr727/nxgo:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxGo", - "Product": "nxgo", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxgo:6.1.3.43301", - "docker.io/ptr727/nxgo:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxGo", - "Product": "nxgo", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxgo:develop-6.1.2.42921", - "docker.io/ptr727/nxgo:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxGo", - "Product": "nxgo", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxgo:develop", - "docker.io/ptr727/nxgo:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxGo-LSIO", - "Product": "nxgo", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxgo-lsio:6.1.2.42921", - "docker.io/ptr727/nxgo-lsio:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxGo-LSIO", - "Product": "nxgo", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxgo-lsio:6.1.3.43301", - "docker.io/ptr727/nxgo-lsio:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxGo-LSIO", - "Product": "nxgo", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxgo-lsio:develop-6.1.2.42921", - "docker.io/ptr727/nxgo-lsio:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxGo-LSIO", - "Product": "nxgo", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxgo-lsio:develop", - "docker.io/ptr727/nxgo-lsio:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxMeta", - "Product": "nxmeta", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxmeta:6.1.2.42921", - "docker.io/ptr727/nxmeta:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxMeta", - "Product": "nxmeta", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxmeta:6.1.3.43301", - "docker.io/ptr727/nxmeta:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxMeta", - "Product": "nxmeta", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxmeta:develop-6.1.2.42921", - "docker.io/ptr727/nxmeta:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxMeta", - "Product": "nxmeta", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxmeta:develop", - "docker.io/ptr727/nxmeta:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxMeta-LSIO", - "Product": "nxmeta", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxmeta-lsio:6.1.2.42921", - "docker.io/ptr727/nxmeta-lsio:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxMeta-LSIO", - "Product": "nxmeta", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxmeta-lsio:6.1.3.43301", - "docker.io/ptr727/nxmeta-lsio:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxMeta-LSIO", - "Product": "nxmeta", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxmeta-lsio:develop-6.1.2.42921", - "docker.io/ptr727/nxmeta-lsio:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxMeta-LSIO", - "Product": "nxmeta", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxmeta-lsio:develop", - "docker.io/ptr727/nxmeta-lsio:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxWitness", - "Product": "nxwitness", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxwitness:6.1.2.42921", - "docker.io/ptr727/nxwitness:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxWitness", - "Product": "nxwitness", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxwitness:6.1.3.43301", - "docker.io/ptr727/nxwitness:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxWitness", - "Product": "nxwitness", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxwitness:develop-6.1.2.42921", - "docker.io/ptr727/nxwitness:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxWitness", - "Product": "nxwitness", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/nxwitness:develop", - "docker.io/ptr727/nxwitness:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxWitness-LSIO", - "Product": "nxwitness", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxwitness-lsio:6.1.2.42921", - "docker.io/ptr727/nxwitness-lsio:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxWitness-LSIO", - "Product": "nxwitness", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxwitness-lsio:6.1.3.43301", - "docker.io/ptr727/nxwitness-lsio:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "NxWitness-LSIO", - "Product": "nxwitness", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxwitness-lsio:develop-6.1.2.42921", - "docker.io/ptr727/nxwitness-lsio:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "NxWitness-LSIO", - "Product": "nxwitness", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/nxwitness-lsio:develop", - "docker.io/ptr727/nxwitness-lsio:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "DWSpectrum", - "Product": "dwspectrum", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/dwspectrum:6.1.1.42624", - "docker.io/ptr727/dwspectrum:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.1.42624", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip" - ] - }, - { - "Name": "DWSpectrum", - "Product": "dwspectrum", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/dwspectrum:6.1.3.43301", - "docker.io/ptr727/dwspectrum:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "DWSpectrum", - "Product": "dwspectrum", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/dwspectrum:develop-6.1.1.42624", - "docker.io/ptr727/dwspectrum:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.1.42624", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip" - ] - }, - { - "Name": "DWSpectrum", - "Product": "dwspectrum", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/dwspectrum:develop", - "docker.io/ptr727/dwspectrum:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "DWSpectrum-LSIO", - "Product": "dwspectrum", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/dwspectrum-lsio:6.1.1.42624", - "docker.io/ptr727/dwspectrum-lsio:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.1.42624", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip" - ] - }, - { - "Name": "DWSpectrum-LSIO", - "Product": "dwspectrum", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/dwspectrum-lsio:6.1.3.43301", - "docker.io/ptr727/dwspectrum-lsio:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "DWSpectrum-LSIO", - "Product": "dwspectrum", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/dwspectrum-lsio:develop-6.1.1.42624", - "docker.io/ptr727/dwspectrum-lsio:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.1.42624", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip" - ] - }, - { - "Name": "DWSpectrum-LSIO", - "Product": "dwspectrum", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/dwspectrum-lsio:develop", - "docker.io/ptr727/dwspectrum-lsio:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "WisenetWAVE", - "Product": "wisenetwave", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/wisenetwave:6.1.2.42921", - "docker.io/ptr727/wisenetwave:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "WisenetWAVE", - "Product": "wisenetwave", - "Branch": "main", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/wisenetwave:6.1.3.43301", - "docker.io/ptr727/wisenetwave:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "WisenetWAVE", - "Product": "wisenetwave", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/wisenetwave:develop-6.1.2.42921", - "docker.io/ptr727/wisenetwave:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "WisenetWAVE", - "Product": "wisenetwave", - "Branch": "develop", - "Base": "ubuntu", - "Tags": [ - "docker.io/ptr727/wisenetwave:develop", - "docker.io/ptr727/wisenetwave:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "WisenetWAVE-LSIO", - "Product": "wisenetwave", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/wisenetwave-lsio:6.1.2.42921", - "docker.io/ptr727/wisenetwave-lsio:stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "WisenetWAVE-LSIO", - "Product": "wisenetwave", - "Branch": "main", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/wisenetwave-lsio:6.1.3.43301", - "docker.io/ptr727/wisenetwave-lsio:latest" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip" - ] - }, - { - "Name": "WisenetWAVE-LSIO", - "Product": "wisenetwave", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/wisenetwave-lsio:develop-6.1.2.42921", - "docker.io/ptr727/wisenetwave-lsio:develop-stable" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.2.42921", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip" - ] - }, - { - "Name": "WisenetWAVE-LSIO", - "Product": "wisenetwave", - "Branch": "develop", - "Base": "lsio", - "Tags": [ - "docker.io/ptr727/wisenetwave-lsio:develop", - "docker.io/ptr727/wisenetwave-lsio:develop-6.1.3.43301" - ], - "Args": [ - "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", - "DOWNLOAD_VERSION=6.1.3.43301", - "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip" - ] - } - ] +{ + "SchemaVersion": 2, + "Images": [ + { + "Name": "NxGo", + "Product": "nxgo", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxgo:6.1.2.42921", + "docker.io/ptr727/nxgo:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxGo", + "Product": "nxgo", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxgo:6.1.3.43301", + "docker.io/ptr727/nxgo:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxGo", + "Product": "nxgo", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxgo:develop-6.1.2.42921", + "docker.io/ptr727/nxgo:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxGo", + "Product": "nxgo", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxgo:develop", + "docker.io/ptr727/nxgo:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxGo-LSIO", + "Product": "nxgo", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxgo-lsio:6.1.2.42921", + "docker.io/ptr727/nxgo-lsio:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxGo-LSIO", + "Product": "nxgo", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxgo-lsio:6.1.3.43301", + "docker.io/ptr727/nxgo-lsio:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxGo-LSIO", + "Product": "nxgo", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxgo-lsio:develop-6.1.2.42921", + "docker.io/ptr727/nxgo-lsio:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxGo-LSIO", + "Product": "nxgo", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxgo-lsio:develop", + "docker.io/ptr727/nxgo-lsio:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxMeta", + "Product": "nxmeta", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxmeta:6.1.2.42921", + "docker.io/ptr727/nxmeta:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxMeta", + "Product": "nxmeta", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxmeta:6.1.3.43301", + "docker.io/ptr727/nxmeta:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxMeta", + "Product": "nxmeta", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxmeta:develop-6.1.2.42921", + "docker.io/ptr727/nxmeta:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxMeta", + "Product": "nxmeta", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxmeta:develop", + "docker.io/ptr727/nxmeta:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxMeta-LSIO", + "Product": "nxmeta", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxmeta-lsio:6.1.2.42921", + "docker.io/ptr727/nxmeta-lsio:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxMeta-LSIO", + "Product": "nxmeta", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxmeta-lsio:6.1.3.43301", + "docker.io/ptr727/nxmeta-lsio:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxMeta-LSIO", + "Product": "nxmeta", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxmeta-lsio:develop-6.1.2.42921", + "docker.io/ptr727/nxmeta-lsio:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxMeta-LSIO", + "Product": "nxmeta", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxmeta-lsio:develop", + "docker.io/ptr727/nxmeta-lsio:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxWitness", + "Product": "nxwitness", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxwitness:6.1.2.42921", + "docker.io/ptr727/nxwitness:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxWitness", + "Product": "nxwitness", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxwitness:6.1.3.43301", + "docker.io/ptr727/nxwitness:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxWitness", + "Product": "nxwitness", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxwitness:develop-6.1.2.42921", + "docker.io/ptr727/nxwitness:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxWitness", + "Product": "nxwitness", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/nxwitness:develop", + "docker.io/ptr727/nxwitness:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxWitness-LSIO", + "Product": "nxwitness", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxwitness-lsio:6.1.2.42921", + "docker.io/ptr727/nxwitness-lsio:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxWitness-LSIO", + "Product": "nxwitness", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxwitness-lsio:6.1.3.43301", + "docker.io/ptr727/nxwitness-lsio:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "NxWitness-LSIO", + "Product": "nxwitness", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxwitness-lsio:develop-6.1.2.42921", + "docker.io/ptr727/nxwitness-lsio:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "NxWitness-LSIO", + "Product": "nxwitness", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/nxwitness-lsio:develop", + "docker.io/ptr727/nxwitness-lsio:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "DWSpectrum", + "Product": "dwspectrum", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/dwspectrum:6.1.1.42624", + "docker.io/ptr727/dwspectrum:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.1.42624", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip" + ] + }, + { + "Name": "DWSpectrum", + "Product": "dwspectrum", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/dwspectrum:6.1.3.43301", + "docker.io/ptr727/dwspectrum:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "DWSpectrum", + "Product": "dwspectrum", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/dwspectrum:develop-6.1.1.42624", + "docker.io/ptr727/dwspectrum:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.1.42624", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip" + ] + }, + { + "Name": "DWSpectrum", + "Product": "dwspectrum", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/dwspectrum:develop", + "docker.io/ptr727/dwspectrum:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "DWSpectrum-LSIO", + "Product": "dwspectrum", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/dwspectrum-lsio:6.1.1.42624", + "docker.io/ptr727/dwspectrum-lsio:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.1.42624", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip" + ] + }, + { + "Name": "DWSpectrum-LSIO", + "Product": "dwspectrum", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/dwspectrum-lsio:6.1.3.43301", + "docker.io/ptr727/dwspectrum-lsio:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "DWSpectrum-LSIO", + "Product": "dwspectrum", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/dwspectrum-lsio:develop-6.1.1.42624", + "docker.io/ptr727/dwspectrum-lsio:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.1.42624", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip" + ] + }, + { + "Name": "DWSpectrum-LSIO", + "Product": "dwspectrum", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/dwspectrum-lsio:develop", + "docker.io/ptr727/dwspectrum-lsio:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "WisenetWAVE", + "Product": "wisenetwave", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/wisenetwave:6.1.2.42921", + "docker.io/ptr727/wisenetwave:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "WisenetWAVE", + "Product": "wisenetwave", + "Branch": "main", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/wisenetwave:6.1.3.43301", + "docker.io/ptr727/wisenetwave:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "WisenetWAVE", + "Product": "wisenetwave", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/wisenetwave:develop-6.1.2.42921", + "docker.io/ptr727/wisenetwave:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "WisenetWAVE", + "Product": "wisenetwave", + "Branch": "develop", + "Base": "ubuntu", + "Tags": [ + "docker.io/ptr727/wisenetwave:develop", + "docker.io/ptr727/wisenetwave:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "WisenetWAVE-LSIO", + "Product": "wisenetwave", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/wisenetwave-lsio:6.1.2.42921", + "docker.io/ptr727/wisenetwave-lsio:stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "WisenetWAVE-LSIO", + "Product": "wisenetwave", + "Branch": "main", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/wisenetwave-lsio:6.1.3.43301", + "docker.io/ptr727/wisenetwave-lsio:latest" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip" + ] + }, + { + "Name": "WisenetWAVE-LSIO", + "Product": "wisenetwave", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/wisenetwave-lsio:develop-6.1.2.42921", + "docker.io/ptr727/wisenetwave-lsio:develop-stable" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.2.42921", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip" + ] + }, + { + "Name": "WisenetWAVE-LSIO", + "Product": "wisenetwave", + "Branch": "develop", + "Base": "lsio", + "Tags": [ + "docker.io/ptr727/wisenetwave-lsio:develop", + "docker.io/ptr727/wisenetwave-lsio:develop-6.1.3.43301" + ], + "Args": [ + "DOWNLOAD_ARM64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", + "DOWNLOAD_VERSION=6.1.3.43301", + "DOWNLOAD_X64_URL=https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip" + ] + } + ] } \ No newline at end of file diff --git a/Make/Test-develop.yml b/Make/Test-develop.yml index 86dd9c9d..13cf131e 100644 --- a/Make/Test-develop.yml +++ b/Make/Test-develop.yml @@ -1,258 +1,256 @@ -# Compose file created by CreateMatrix, do not modify by hand - -volumes: - - # Dockerfile : NxGo - test_nxgo_etc: - test_nxgo_ini: - test_nxgo_var: - test_nxgo_media: - test_nxgo_backup: - test_nxgo_analytics: - - # Dockerfile : NxGo-LSIO - test_nxgo-lsio_config: - test_nxgo-lsio_media: - test_nxgo-lsio_backup: - test_nxgo-lsio_analytics: - - # Dockerfile : NxMeta - test_nxmeta_etc: - test_nxmeta_ini: - test_nxmeta_var: - test_nxmeta_media: - test_nxmeta_backup: - test_nxmeta_analytics: - - # Dockerfile : NxMeta-LSIO - test_nxmeta-lsio_config: - test_nxmeta-lsio_media: - test_nxmeta-lsio_backup: - test_nxmeta-lsio_analytics: - - # Dockerfile : NxWitness - test_nxwitness_etc: - test_nxwitness_ini: - test_nxwitness_var: - test_nxwitness_media: - test_nxwitness_backup: - test_nxwitness_analytics: - - # Dockerfile : NxWitness-LSIO - test_nxwitness-lsio_config: - test_nxwitness-lsio_media: - test_nxwitness-lsio_backup: - test_nxwitness-lsio_analytics: - - # Dockerfile : DWSpectrum - test_dwspectrum_etc: - test_dwspectrum_ini: - test_dwspectrum_var: - test_dwspectrum_media: - test_dwspectrum_backup: - test_dwspectrum_analytics: - - # Dockerfile : DWSpectrum-LSIO - test_dwspectrum-lsio_config: - test_dwspectrum-lsio_media: - test_dwspectrum-lsio_backup: - test_dwspectrum-lsio_analytics: - - # Dockerfile : WisenetWAVE - test_wisenetwave_etc: - test_wisenetwave_ini: - test_wisenetwave_var: - test_wisenetwave_media: - test_wisenetwave_backup: - test_wisenetwave_analytics: - - # Dockerfile : WisenetWAVE-LSIO - test_wisenetwave-lsio_config: - test_wisenetwave-lsio_media: - test_wisenetwave-lsio_backup: - test_wisenetwave-lsio_analytics: - - -services: - - # Dockerfile : NxGo - # Port : 7101 - nxgo: - image: docker.io/ptr727/nxgo:develop - container_name: nxgo-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7101:7001 - volumes: - - test_nxgo_etc:/opt/networkoptix/mediaserver/etc - - test_nxgo_ini:/home/networkoptix/.config/nx_ini - - test_nxgo_var:/opt/networkoptix/mediaserver/var - - test_nxgo_media:/media - - test_nxgo_backup:/backup - - test_nxgo_analytics:/analytics - - # Dockerfile : NxGo-LSIO - # Port : 7201 - nxgo-lsio: - image: docker.io/ptr727/nxgo-lsio:develop - container_name: nxgo-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7201:7001 - volumes: - - test_nxgo-lsio_config:/config - - test_nxgo-lsio_media:/media - - test_nxgo-lsio_backup:/backup - - test_nxgo-lsio_analytics:/analytics - - # Dockerfile : NxMeta - # Port : 7102 - nxmeta: - image: docker.io/ptr727/nxmeta:develop - container_name: nxmeta-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7102:7001 - volumes: - - test_nxmeta_etc:/opt/networkoptix-metavms/mediaserver/etc - - test_nxmeta_ini:/home/networkoptix-metavms/.config/nx_ini - - test_nxmeta_var:/opt/networkoptix-metavms/mediaserver/var - - test_nxmeta_media:/media - - test_nxmeta_backup:/backup - - test_nxmeta_analytics:/analytics - - # Dockerfile : NxMeta-LSIO - # Port : 7202 - nxmeta-lsio: - image: docker.io/ptr727/nxmeta-lsio:develop - container_name: nxmeta-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7202:7001 - volumes: - - test_nxmeta-lsio_config:/config - - test_nxmeta-lsio_media:/media - - test_nxmeta-lsio_backup:/backup - - test_nxmeta-lsio_analytics:/analytics - - # Dockerfile : NxWitness - # Port : 7103 - nxwitness: - image: docker.io/ptr727/nxwitness:develop - container_name: nxwitness-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7103:7001 - volumes: - - test_nxwitness_etc:/opt/networkoptix/mediaserver/etc - - test_nxwitness_ini:/home/networkoptix/.config/nx_ini - - test_nxwitness_var:/opt/networkoptix/mediaserver/var - - test_nxwitness_media:/media - - test_nxwitness_backup:/backup - - test_nxwitness_analytics:/analytics - - # Dockerfile : NxWitness-LSIO - # Port : 7203 - nxwitness-lsio: - image: docker.io/ptr727/nxwitness-lsio:develop - container_name: nxwitness-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7203:7001 - volumes: - - test_nxwitness-lsio_config:/config - - test_nxwitness-lsio_media:/media - - test_nxwitness-lsio_backup:/backup - - test_nxwitness-lsio_analytics:/analytics - - # Dockerfile : DWSpectrum - # Port : 7104 - dwspectrum: - image: docker.io/ptr727/dwspectrum:develop - container_name: dwspectrum-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7104:7001 - volumes: - - test_dwspectrum_etc:/opt/digitalwatchdog/mediaserver/etc - - test_dwspectrum_ini:/home/digitalwatchdog/.config/nx_ini - - test_dwspectrum_var:/opt/digitalwatchdog/mediaserver/var - - test_dwspectrum_media:/media - - test_dwspectrum_backup:/backup - - test_dwspectrum_analytics:/analytics - - # Dockerfile : DWSpectrum-LSIO - # Port : 7204 - dwspectrum-lsio: - image: docker.io/ptr727/dwspectrum-lsio:develop - container_name: dwspectrum-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7204:7001 - volumes: - - test_dwspectrum-lsio_config:/config - - test_dwspectrum-lsio_media:/media - - test_dwspectrum-lsio_backup:/backup - - test_dwspectrum-lsio_analytics:/analytics - - # Dockerfile : WisenetWAVE - # Port : 7105 - wisenetwave: - image: docker.io/ptr727/wisenetwave:develop - container_name: wisenetwave-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7105:7001 - volumes: - - test_wisenetwave_etc:/opt/hanwha/mediaserver/etc - - test_wisenetwave_ini:/home/hanwha/.config/nx_ini - - test_wisenetwave_var:/opt/hanwha/mediaserver/var - - test_wisenetwave_media:/media - - test_wisenetwave_backup:/backup - - test_wisenetwave_analytics:/analytics - - # Dockerfile : WisenetWAVE-LSIO - # Port : 7205 - wisenetwave-lsio: - image: docker.io/ptr727/wisenetwave-lsio:develop - container_name: wisenetwave-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7205:7001 - volumes: - - test_wisenetwave-lsio_config:/config - - test_wisenetwave-lsio_media:/media - - test_wisenetwave-lsio_backup:/backup - - test_wisenetwave-lsio_analytics:/analytics - - +# Compose file created by CreateMatrix, do not modify by hand + +volumes: + + # Dockerfile : NxGo + test_nxgo_etc: + test_nxgo_ini: + test_nxgo_var: + test_nxgo_media: + test_nxgo_backup: + test_nxgo_analytics: + + # Dockerfile : NxGo-LSIO + test_nxgo-lsio_config: + test_nxgo-lsio_media: + test_nxgo-lsio_backup: + test_nxgo-lsio_analytics: + + # Dockerfile : NxMeta + test_nxmeta_etc: + test_nxmeta_ini: + test_nxmeta_var: + test_nxmeta_media: + test_nxmeta_backup: + test_nxmeta_analytics: + + # Dockerfile : NxMeta-LSIO + test_nxmeta-lsio_config: + test_nxmeta-lsio_media: + test_nxmeta-lsio_backup: + test_nxmeta-lsio_analytics: + + # Dockerfile : NxWitness + test_nxwitness_etc: + test_nxwitness_ini: + test_nxwitness_var: + test_nxwitness_media: + test_nxwitness_backup: + test_nxwitness_analytics: + + # Dockerfile : NxWitness-LSIO + test_nxwitness-lsio_config: + test_nxwitness-lsio_media: + test_nxwitness-lsio_backup: + test_nxwitness-lsio_analytics: + + # Dockerfile : DWSpectrum + test_dwspectrum_etc: + test_dwspectrum_ini: + test_dwspectrum_var: + test_dwspectrum_media: + test_dwspectrum_backup: + test_dwspectrum_analytics: + + # Dockerfile : DWSpectrum-LSIO + test_dwspectrum-lsio_config: + test_dwspectrum-lsio_media: + test_dwspectrum-lsio_backup: + test_dwspectrum-lsio_analytics: + + # Dockerfile : WisenetWAVE + test_wisenetwave_etc: + test_wisenetwave_ini: + test_wisenetwave_var: + test_wisenetwave_media: + test_wisenetwave_backup: + test_wisenetwave_analytics: + + # Dockerfile : WisenetWAVE-LSIO + test_wisenetwave-lsio_config: + test_wisenetwave-lsio_media: + test_wisenetwave-lsio_backup: + test_wisenetwave-lsio_analytics: + + +services: + + # Dockerfile : NxGo + # Port : 7101 + nxgo: + image: docker.io/ptr727/nxgo:develop + container_name: nxgo-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7101:7001 + volumes: + - test_nxgo_etc:/opt/networkoptix/mediaserver/etc + - test_nxgo_ini:/home/networkoptix/.config/nx_ini + - test_nxgo_var:/opt/networkoptix/mediaserver/var + - test_nxgo_media:/media + - test_nxgo_backup:/backup + - test_nxgo_analytics:/analytics + + # Dockerfile : NxGo-LSIO + # Port : 7201 + nxgo-lsio: + image: docker.io/ptr727/nxgo-lsio:develop + container_name: nxgo-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7201:7001 + volumes: + - test_nxgo-lsio_config:/config + - test_nxgo-lsio_media:/media + - test_nxgo-lsio_backup:/backup + - test_nxgo-lsio_analytics:/analytics + + # Dockerfile : NxMeta + # Port : 7102 + nxmeta: + image: docker.io/ptr727/nxmeta:develop + container_name: nxmeta-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7102:7001 + volumes: + - test_nxmeta_etc:/opt/networkoptix-metavms/mediaserver/etc + - test_nxmeta_ini:/home/networkoptix-metavms/.config/nx_ini + - test_nxmeta_var:/opt/networkoptix-metavms/mediaserver/var + - test_nxmeta_media:/media + - test_nxmeta_backup:/backup + - test_nxmeta_analytics:/analytics + + # Dockerfile : NxMeta-LSIO + # Port : 7202 + nxmeta-lsio: + image: docker.io/ptr727/nxmeta-lsio:develop + container_name: nxmeta-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7202:7001 + volumes: + - test_nxmeta-lsio_config:/config + - test_nxmeta-lsio_media:/media + - test_nxmeta-lsio_backup:/backup + - test_nxmeta-lsio_analytics:/analytics + + # Dockerfile : NxWitness + # Port : 7103 + nxwitness: + image: docker.io/ptr727/nxwitness:develop + container_name: nxwitness-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7103:7001 + volumes: + - test_nxwitness_etc:/opt/networkoptix/mediaserver/etc + - test_nxwitness_ini:/home/networkoptix/.config/nx_ini + - test_nxwitness_var:/opt/networkoptix/mediaserver/var + - test_nxwitness_media:/media + - test_nxwitness_backup:/backup + - test_nxwitness_analytics:/analytics + + # Dockerfile : NxWitness-LSIO + # Port : 7203 + nxwitness-lsio: + image: docker.io/ptr727/nxwitness-lsio:develop + container_name: nxwitness-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7203:7001 + volumes: + - test_nxwitness-lsio_config:/config + - test_nxwitness-lsio_media:/media + - test_nxwitness-lsio_backup:/backup + - test_nxwitness-lsio_analytics:/analytics + + # Dockerfile : DWSpectrum + # Port : 7104 + dwspectrum: + image: docker.io/ptr727/dwspectrum:develop + container_name: dwspectrum-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7104:7001 + volumes: + - test_dwspectrum_etc:/opt/digitalwatchdog/mediaserver/etc + - test_dwspectrum_ini:/home/digitalwatchdog/.config/nx_ini + - test_dwspectrum_var:/opt/digitalwatchdog/mediaserver/var + - test_dwspectrum_media:/media + - test_dwspectrum_backup:/backup + - test_dwspectrum_analytics:/analytics + + # Dockerfile : DWSpectrum-LSIO + # Port : 7204 + dwspectrum-lsio: + image: docker.io/ptr727/dwspectrum-lsio:develop + container_name: dwspectrum-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7204:7001 + volumes: + - test_dwspectrum-lsio_config:/config + - test_dwspectrum-lsio_media:/media + - test_dwspectrum-lsio_backup:/backup + - test_dwspectrum-lsio_analytics:/analytics + + # Dockerfile : WisenetWAVE + # Port : 7105 + wisenetwave: + image: docker.io/ptr727/wisenetwave:develop + container_name: wisenetwave-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7105:7001 + volumes: + - test_wisenetwave_etc:/opt/hanwha/mediaserver/etc + - test_wisenetwave_ini:/home/hanwha/.config/nx_ini + - test_wisenetwave_var:/opt/hanwha/mediaserver/var + - test_wisenetwave_media:/media + - test_wisenetwave_backup:/backup + - test_wisenetwave_analytics:/analytics + + # Dockerfile : WisenetWAVE-LSIO + # Port : 7205 + wisenetwave-lsio: + image: docker.io/ptr727/wisenetwave-lsio:develop + container_name: wisenetwave-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7205:7001 + volumes: + - test_wisenetwave-lsio_config:/config + - test_wisenetwave-lsio_media:/media + - test_wisenetwave-lsio_backup:/backup + - test_wisenetwave-lsio_analytics:/analytics diff --git a/Make/Test-latest.yml b/Make/Test-latest.yml index 6c771495..e1db1d54 100644 --- a/Make/Test-latest.yml +++ b/Make/Test-latest.yml @@ -1,258 +1,256 @@ -# Compose file created by CreateMatrix, do not modify by hand - -volumes: - - # Dockerfile : NxGo - test_nxgo_etc: - test_nxgo_ini: - test_nxgo_var: - test_nxgo_media: - test_nxgo_backup: - test_nxgo_analytics: - - # Dockerfile : NxGo-LSIO - test_nxgo-lsio_config: - test_nxgo-lsio_media: - test_nxgo-lsio_backup: - test_nxgo-lsio_analytics: - - # Dockerfile : NxMeta - test_nxmeta_etc: - test_nxmeta_ini: - test_nxmeta_var: - test_nxmeta_media: - test_nxmeta_backup: - test_nxmeta_analytics: - - # Dockerfile : NxMeta-LSIO - test_nxmeta-lsio_config: - test_nxmeta-lsio_media: - test_nxmeta-lsio_backup: - test_nxmeta-lsio_analytics: - - # Dockerfile : NxWitness - test_nxwitness_etc: - test_nxwitness_ini: - test_nxwitness_var: - test_nxwitness_media: - test_nxwitness_backup: - test_nxwitness_analytics: - - # Dockerfile : NxWitness-LSIO - test_nxwitness-lsio_config: - test_nxwitness-lsio_media: - test_nxwitness-lsio_backup: - test_nxwitness-lsio_analytics: - - # Dockerfile : DWSpectrum - test_dwspectrum_etc: - test_dwspectrum_ini: - test_dwspectrum_var: - test_dwspectrum_media: - test_dwspectrum_backup: - test_dwspectrum_analytics: - - # Dockerfile : DWSpectrum-LSIO - test_dwspectrum-lsio_config: - test_dwspectrum-lsio_media: - test_dwspectrum-lsio_backup: - test_dwspectrum-lsio_analytics: - - # Dockerfile : WisenetWAVE - test_wisenetwave_etc: - test_wisenetwave_ini: - test_wisenetwave_var: - test_wisenetwave_media: - test_wisenetwave_backup: - test_wisenetwave_analytics: - - # Dockerfile : WisenetWAVE-LSIO - test_wisenetwave-lsio_config: - test_wisenetwave-lsio_media: - test_wisenetwave-lsio_backup: - test_wisenetwave-lsio_analytics: - - -services: - - # Dockerfile : NxGo - # Port : 7101 - nxgo: - image: docker.io/ptr727/nxgo:latest - container_name: nxgo-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7101:7001 - volumes: - - test_nxgo_etc:/opt/networkoptix/mediaserver/etc - - test_nxgo_ini:/home/networkoptix/.config/nx_ini - - test_nxgo_var:/opt/networkoptix/mediaserver/var - - test_nxgo_media:/media - - test_nxgo_backup:/backup - - test_nxgo_analytics:/analytics - - # Dockerfile : NxGo-LSIO - # Port : 7201 - nxgo-lsio: - image: docker.io/ptr727/nxgo-lsio:latest - container_name: nxgo-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7201:7001 - volumes: - - test_nxgo-lsio_config:/config - - test_nxgo-lsio_media:/media - - test_nxgo-lsio_backup:/backup - - test_nxgo-lsio_analytics:/analytics - - # Dockerfile : NxMeta - # Port : 7102 - nxmeta: - image: docker.io/ptr727/nxmeta:latest - container_name: nxmeta-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7102:7001 - volumes: - - test_nxmeta_etc:/opt/networkoptix-metavms/mediaserver/etc - - test_nxmeta_ini:/home/networkoptix-metavms/.config/nx_ini - - test_nxmeta_var:/opt/networkoptix-metavms/mediaserver/var - - test_nxmeta_media:/media - - test_nxmeta_backup:/backup - - test_nxmeta_analytics:/analytics - - # Dockerfile : NxMeta-LSIO - # Port : 7202 - nxmeta-lsio: - image: docker.io/ptr727/nxmeta-lsio:latest - container_name: nxmeta-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7202:7001 - volumes: - - test_nxmeta-lsio_config:/config - - test_nxmeta-lsio_media:/media - - test_nxmeta-lsio_backup:/backup - - test_nxmeta-lsio_analytics:/analytics - - # Dockerfile : NxWitness - # Port : 7103 - nxwitness: - image: docker.io/ptr727/nxwitness:latest - container_name: nxwitness-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7103:7001 - volumes: - - test_nxwitness_etc:/opt/networkoptix/mediaserver/etc - - test_nxwitness_ini:/home/networkoptix/.config/nx_ini - - test_nxwitness_var:/opt/networkoptix/mediaserver/var - - test_nxwitness_media:/media - - test_nxwitness_backup:/backup - - test_nxwitness_analytics:/analytics - - # Dockerfile : NxWitness-LSIO - # Port : 7203 - nxwitness-lsio: - image: docker.io/ptr727/nxwitness-lsio:latest - container_name: nxwitness-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7203:7001 - volumes: - - test_nxwitness-lsio_config:/config - - test_nxwitness-lsio_media:/media - - test_nxwitness-lsio_backup:/backup - - test_nxwitness-lsio_analytics:/analytics - - # Dockerfile : DWSpectrum - # Port : 7104 - dwspectrum: - image: docker.io/ptr727/dwspectrum:latest - container_name: dwspectrum-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7104:7001 - volumes: - - test_dwspectrum_etc:/opt/digitalwatchdog/mediaserver/etc - - test_dwspectrum_ini:/home/digitalwatchdog/.config/nx_ini - - test_dwspectrum_var:/opt/digitalwatchdog/mediaserver/var - - test_dwspectrum_media:/media - - test_dwspectrum_backup:/backup - - test_dwspectrum_analytics:/analytics - - # Dockerfile : DWSpectrum-LSIO - # Port : 7204 - dwspectrum-lsio: - image: docker.io/ptr727/dwspectrum-lsio:latest - container_name: dwspectrum-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7204:7001 - volumes: - - test_dwspectrum-lsio_config:/config - - test_dwspectrum-lsio_media:/media - - test_dwspectrum-lsio_backup:/backup - - test_dwspectrum-lsio_analytics:/analytics - - # Dockerfile : WisenetWAVE - # Port : 7105 - wisenetwave: - image: docker.io/ptr727/wisenetwave:latest - container_name: wisenetwave-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7105:7001 - volumes: - - test_wisenetwave_etc:/opt/hanwha/mediaserver/etc - - test_wisenetwave_ini:/home/hanwha/.config/nx_ini - - test_wisenetwave_var:/opt/hanwha/mediaserver/var - - test_wisenetwave_media:/media - - test_wisenetwave_backup:/backup - - test_wisenetwave_analytics:/analytics - - # Dockerfile : WisenetWAVE-LSIO - # Port : 7205 - wisenetwave-lsio: - image: docker.io/ptr727/wisenetwave-lsio:latest - container_name: wisenetwave-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7205:7001 - volumes: - - test_wisenetwave-lsio_config:/config - - test_wisenetwave-lsio_media:/media - - test_wisenetwave-lsio_backup:/backup - - test_wisenetwave-lsio_analytics:/analytics - - +# Compose file created by CreateMatrix, do not modify by hand + +volumes: + + # Dockerfile : NxGo + test_nxgo_etc: + test_nxgo_ini: + test_nxgo_var: + test_nxgo_media: + test_nxgo_backup: + test_nxgo_analytics: + + # Dockerfile : NxGo-LSIO + test_nxgo-lsio_config: + test_nxgo-lsio_media: + test_nxgo-lsio_backup: + test_nxgo-lsio_analytics: + + # Dockerfile : NxMeta + test_nxmeta_etc: + test_nxmeta_ini: + test_nxmeta_var: + test_nxmeta_media: + test_nxmeta_backup: + test_nxmeta_analytics: + + # Dockerfile : NxMeta-LSIO + test_nxmeta-lsio_config: + test_nxmeta-lsio_media: + test_nxmeta-lsio_backup: + test_nxmeta-lsio_analytics: + + # Dockerfile : NxWitness + test_nxwitness_etc: + test_nxwitness_ini: + test_nxwitness_var: + test_nxwitness_media: + test_nxwitness_backup: + test_nxwitness_analytics: + + # Dockerfile : NxWitness-LSIO + test_nxwitness-lsio_config: + test_nxwitness-lsio_media: + test_nxwitness-lsio_backup: + test_nxwitness-lsio_analytics: + + # Dockerfile : DWSpectrum + test_dwspectrum_etc: + test_dwspectrum_ini: + test_dwspectrum_var: + test_dwspectrum_media: + test_dwspectrum_backup: + test_dwspectrum_analytics: + + # Dockerfile : DWSpectrum-LSIO + test_dwspectrum-lsio_config: + test_dwspectrum-lsio_media: + test_dwspectrum-lsio_backup: + test_dwspectrum-lsio_analytics: + + # Dockerfile : WisenetWAVE + test_wisenetwave_etc: + test_wisenetwave_ini: + test_wisenetwave_var: + test_wisenetwave_media: + test_wisenetwave_backup: + test_wisenetwave_analytics: + + # Dockerfile : WisenetWAVE-LSIO + test_wisenetwave-lsio_config: + test_wisenetwave-lsio_media: + test_wisenetwave-lsio_backup: + test_wisenetwave-lsio_analytics: + + +services: + + # Dockerfile : NxGo + # Port : 7101 + nxgo: + image: docker.io/ptr727/nxgo:latest + container_name: nxgo-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7101:7001 + volumes: + - test_nxgo_etc:/opt/networkoptix/mediaserver/etc + - test_nxgo_ini:/home/networkoptix/.config/nx_ini + - test_nxgo_var:/opt/networkoptix/mediaserver/var + - test_nxgo_media:/media + - test_nxgo_backup:/backup + - test_nxgo_analytics:/analytics + + # Dockerfile : NxGo-LSIO + # Port : 7201 + nxgo-lsio: + image: docker.io/ptr727/nxgo-lsio:latest + container_name: nxgo-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7201:7001 + volumes: + - test_nxgo-lsio_config:/config + - test_nxgo-lsio_media:/media + - test_nxgo-lsio_backup:/backup + - test_nxgo-lsio_analytics:/analytics + + # Dockerfile : NxMeta + # Port : 7102 + nxmeta: + image: docker.io/ptr727/nxmeta:latest + container_name: nxmeta-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7102:7001 + volumes: + - test_nxmeta_etc:/opt/networkoptix-metavms/mediaserver/etc + - test_nxmeta_ini:/home/networkoptix-metavms/.config/nx_ini + - test_nxmeta_var:/opt/networkoptix-metavms/mediaserver/var + - test_nxmeta_media:/media + - test_nxmeta_backup:/backup + - test_nxmeta_analytics:/analytics + + # Dockerfile : NxMeta-LSIO + # Port : 7202 + nxmeta-lsio: + image: docker.io/ptr727/nxmeta-lsio:latest + container_name: nxmeta-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7202:7001 + volumes: + - test_nxmeta-lsio_config:/config + - test_nxmeta-lsio_media:/media + - test_nxmeta-lsio_backup:/backup + - test_nxmeta-lsio_analytics:/analytics + + # Dockerfile : NxWitness + # Port : 7103 + nxwitness: + image: docker.io/ptr727/nxwitness:latest + container_name: nxwitness-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7103:7001 + volumes: + - test_nxwitness_etc:/opt/networkoptix/mediaserver/etc + - test_nxwitness_ini:/home/networkoptix/.config/nx_ini + - test_nxwitness_var:/opt/networkoptix/mediaserver/var + - test_nxwitness_media:/media + - test_nxwitness_backup:/backup + - test_nxwitness_analytics:/analytics + + # Dockerfile : NxWitness-LSIO + # Port : 7203 + nxwitness-lsio: + image: docker.io/ptr727/nxwitness-lsio:latest + container_name: nxwitness-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7203:7001 + volumes: + - test_nxwitness-lsio_config:/config + - test_nxwitness-lsio_media:/media + - test_nxwitness-lsio_backup:/backup + - test_nxwitness-lsio_analytics:/analytics + + # Dockerfile : DWSpectrum + # Port : 7104 + dwspectrum: + image: docker.io/ptr727/dwspectrum:latest + container_name: dwspectrum-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7104:7001 + volumes: + - test_dwspectrum_etc:/opt/digitalwatchdog/mediaserver/etc + - test_dwspectrum_ini:/home/digitalwatchdog/.config/nx_ini + - test_dwspectrum_var:/opt/digitalwatchdog/mediaserver/var + - test_dwspectrum_media:/media + - test_dwspectrum_backup:/backup + - test_dwspectrum_analytics:/analytics + + # Dockerfile : DWSpectrum-LSIO + # Port : 7204 + dwspectrum-lsio: + image: docker.io/ptr727/dwspectrum-lsio:latest + container_name: dwspectrum-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7204:7001 + volumes: + - test_dwspectrum-lsio_config:/config + - test_dwspectrum-lsio_media:/media + - test_dwspectrum-lsio_backup:/backup + - test_dwspectrum-lsio_analytics:/analytics + + # Dockerfile : WisenetWAVE + # Port : 7105 + wisenetwave: + image: docker.io/ptr727/wisenetwave:latest + container_name: wisenetwave-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7105:7001 + volumes: + - test_wisenetwave_etc:/opt/hanwha/mediaserver/etc + - test_wisenetwave_ini:/home/hanwha/.config/nx_ini + - test_wisenetwave_var:/opt/hanwha/mediaserver/var + - test_wisenetwave_media:/media + - test_wisenetwave_backup:/backup + - test_wisenetwave_analytics:/analytics + + # Dockerfile : WisenetWAVE-LSIO + # Port : 7205 + wisenetwave-lsio: + image: docker.io/ptr727/wisenetwave-lsio:latest + container_name: wisenetwave-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7205:7001 + volumes: + - test_wisenetwave-lsio_config:/config + - test_wisenetwave-lsio_media:/media + - test_wisenetwave-lsio_backup:/backup + - test_wisenetwave-lsio_analytics:/analytics diff --git a/Make/Test.yml b/Make/Test.yml index 42bc581f..0e382a36 100644 --- a/Make/Test.yml +++ b/Make/Test.yml @@ -1,258 +1,256 @@ -# Compose file created by CreateMatrix, do not modify by hand - -volumes: - - # Dockerfile : NxGo - test_nxgo_etc: - test_nxgo_ini: - test_nxgo_var: - test_nxgo_media: - test_nxgo_backup: - test_nxgo_analytics: - - # Dockerfile : NxGo-LSIO - test_nxgo-lsio_config: - test_nxgo-lsio_media: - test_nxgo-lsio_backup: - test_nxgo-lsio_analytics: - - # Dockerfile : NxMeta - test_nxmeta_etc: - test_nxmeta_ini: - test_nxmeta_var: - test_nxmeta_media: - test_nxmeta_backup: - test_nxmeta_analytics: - - # Dockerfile : NxMeta-LSIO - test_nxmeta-lsio_config: - test_nxmeta-lsio_media: - test_nxmeta-lsio_backup: - test_nxmeta-lsio_analytics: - - # Dockerfile : NxWitness - test_nxwitness_etc: - test_nxwitness_ini: - test_nxwitness_var: - test_nxwitness_media: - test_nxwitness_backup: - test_nxwitness_analytics: - - # Dockerfile : NxWitness-LSIO - test_nxwitness-lsio_config: - test_nxwitness-lsio_media: - test_nxwitness-lsio_backup: - test_nxwitness-lsio_analytics: - - # Dockerfile : DWSpectrum - test_dwspectrum_etc: - test_dwspectrum_ini: - test_dwspectrum_var: - test_dwspectrum_media: - test_dwspectrum_backup: - test_dwspectrum_analytics: - - # Dockerfile : DWSpectrum-LSIO - test_dwspectrum-lsio_config: - test_dwspectrum-lsio_media: - test_dwspectrum-lsio_backup: - test_dwspectrum-lsio_analytics: - - # Dockerfile : WisenetWAVE - test_wisenetwave_etc: - test_wisenetwave_ini: - test_wisenetwave_var: - test_wisenetwave_media: - test_wisenetwave_backup: - test_wisenetwave_analytics: - - # Dockerfile : WisenetWAVE-LSIO - test_wisenetwave-lsio_config: - test_wisenetwave-lsio_media: - test_wisenetwave-lsio_backup: - test_wisenetwave-lsio_analytics: - - -services: - - # Dockerfile : NxGo - # Port : 7101 - nxgo: - image: test_nxgo - container_name: nxgo-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7101:7001 - volumes: - - test_nxgo_etc:/opt/networkoptix/mediaserver/etc - - test_nxgo_ini:/home/networkoptix/.config/nx_ini - - test_nxgo_var:/opt/networkoptix/mediaserver/var - - test_nxgo_media:/media - - test_nxgo_backup:/backup - - test_nxgo_analytics:/analytics - - # Dockerfile : NxGo-LSIO - # Port : 7201 - nxgo-lsio: - image: test_nxgo-lsio - container_name: nxgo-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7201:7001 - volumes: - - test_nxgo-lsio_config:/config - - test_nxgo-lsio_media:/media - - test_nxgo-lsio_backup:/backup - - test_nxgo-lsio_analytics:/analytics - - # Dockerfile : NxMeta - # Port : 7102 - nxmeta: - image: test_nxmeta - container_name: nxmeta-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7102:7001 - volumes: - - test_nxmeta_etc:/opt/networkoptix-metavms/mediaserver/etc - - test_nxmeta_ini:/home/networkoptix-metavms/.config/nx_ini - - test_nxmeta_var:/opt/networkoptix-metavms/mediaserver/var - - test_nxmeta_media:/media - - test_nxmeta_backup:/backup - - test_nxmeta_analytics:/analytics - - # Dockerfile : NxMeta-LSIO - # Port : 7202 - nxmeta-lsio: - image: test_nxmeta-lsio - container_name: nxmeta-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7202:7001 - volumes: - - test_nxmeta-lsio_config:/config - - test_nxmeta-lsio_media:/media - - test_nxmeta-lsio_backup:/backup - - test_nxmeta-lsio_analytics:/analytics - - # Dockerfile : NxWitness - # Port : 7103 - nxwitness: - image: test_nxwitness - container_name: nxwitness-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7103:7001 - volumes: - - test_nxwitness_etc:/opt/networkoptix/mediaserver/etc - - test_nxwitness_ini:/home/networkoptix/.config/nx_ini - - test_nxwitness_var:/opt/networkoptix/mediaserver/var - - test_nxwitness_media:/media - - test_nxwitness_backup:/backup - - test_nxwitness_analytics:/analytics - - # Dockerfile : NxWitness-LSIO - # Port : 7203 - nxwitness-lsio: - image: test_nxwitness-lsio - container_name: nxwitness-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7203:7001 - volumes: - - test_nxwitness-lsio_config:/config - - test_nxwitness-lsio_media:/media - - test_nxwitness-lsio_backup:/backup - - test_nxwitness-lsio_analytics:/analytics - - # Dockerfile : DWSpectrum - # Port : 7104 - dwspectrum: - image: test_dwspectrum - container_name: dwspectrum-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7104:7001 - volumes: - - test_dwspectrum_etc:/opt/digitalwatchdog/mediaserver/etc - - test_dwspectrum_ini:/home/digitalwatchdog/.config/nx_ini - - test_dwspectrum_var:/opt/digitalwatchdog/mediaserver/var - - test_dwspectrum_media:/media - - test_dwspectrum_backup:/backup - - test_dwspectrum_analytics:/analytics - - # Dockerfile : DWSpectrum-LSIO - # Port : 7204 - dwspectrum-lsio: - image: test_dwspectrum-lsio - container_name: dwspectrum-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7204:7001 - volumes: - - test_dwspectrum-lsio_config:/config - - test_dwspectrum-lsio_media:/media - - test_dwspectrum-lsio_backup:/backup - - test_dwspectrum-lsio_analytics:/analytics - - # Dockerfile : WisenetWAVE - # Port : 7105 - wisenetwave: - image: test_wisenetwave - container_name: wisenetwave-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7105:7001 - volumes: - - test_wisenetwave_etc:/opt/hanwha/mediaserver/etc - - test_wisenetwave_ini:/home/hanwha/.config/nx_ini - - test_wisenetwave_var:/opt/hanwha/mediaserver/var - - test_wisenetwave_media:/media - - test_wisenetwave_backup:/backup - - test_wisenetwave_analytics:/analytics - - # Dockerfile : WisenetWAVE-LSIO - # Port : 7205 - wisenetwave-lsio: - image: test_wisenetwave-lsio - container_name: wisenetwave-lsio-container - restart: unless-stopped - environment: - - TZ=Americas/Los_Angeles - network_mode: bridge - ports: - - 7205:7001 - volumes: - - test_wisenetwave-lsio_config:/config - - test_wisenetwave-lsio_media:/media - - test_wisenetwave-lsio_backup:/backup - - test_wisenetwave-lsio_analytics:/analytics - - +# Compose file created by CreateMatrix, do not modify by hand + +volumes: + + # Dockerfile : NxGo + test_nxgo_etc: + test_nxgo_ini: + test_nxgo_var: + test_nxgo_media: + test_nxgo_backup: + test_nxgo_analytics: + + # Dockerfile : NxGo-LSIO + test_nxgo-lsio_config: + test_nxgo-lsio_media: + test_nxgo-lsio_backup: + test_nxgo-lsio_analytics: + + # Dockerfile : NxMeta + test_nxmeta_etc: + test_nxmeta_ini: + test_nxmeta_var: + test_nxmeta_media: + test_nxmeta_backup: + test_nxmeta_analytics: + + # Dockerfile : NxMeta-LSIO + test_nxmeta-lsio_config: + test_nxmeta-lsio_media: + test_nxmeta-lsio_backup: + test_nxmeta-lsio_analytics: + + # Dockerfile : NxWitness + test_nxwitness_etc: + test_nxwitness_ini: + test_nxwitness_var: + test_nxwitness_media: + test_nxwitness_backup: + test_nxwitness_analytics: + + # Dockerfile : NxWitness-LSIO + test_nxwitness-lsio_config: + test_nxwitness-lsio_media: + test_nxwitness-lsio_backup: + test_nxwitness-lsio_analytics: + + # Dockerfile : DWSpectrum + test_dwspectrum_etc: + test_dwspectrum_ini: + test_dwspectrum_var: + test_dwspectrum_media: + test_dwspectrum_backup: + test_dwspectrum_analytics: + + # Dockerfile : DWSpectrum-LSIO + test_dwspectrum-lsio_config: + test_dwspectrum-lsio_media: + test_dwspectrum-lsio_backup: + test_dwspectrum-lsio_analytics: + + # Dockerfile : WisenetWAVE + test_wisenetwave_etc: + test_wisenetwave_ini: + test_wisenetwave_var: + test_wisenetwave_media: + test_wisenetwave_backup: + test_wisenetwave_analytics: + + # Dockerfile : WisenetWAVE-LSIO + test_wisenetwave-lsio_config: + test_wisenetwave-lsio_media: + test_wisenetwave-lsio_backup: + test_wisenetwave-lsio_analytics: + + +services: + + # Dockerfile : NxGo + # Port : 7101 + nxgo: + image: test_nxgo + container_name: nxgo-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7101:7001 + volumes: + - test_nxgo_etc:/opt/networkoptix/mediaserver/etc + - test_nxgo_ini:/home/networkoptix/.config/nx_ini + - test_nxgo_var:/opt/networkoptix/mediaserver/var + - test_nxgo_media:/media + - test_nxgo_backup:/backup + - test_nxgo_analytics:/analytics + + # Dockerfile : NxGo-LSIO + # Port : 7201 + nxgo-lsio: + image: test_nxgo-lsio + container_name: nxgo-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7201:7001 + volumes: + - test_nxgo-lsio_config:/config + - test_nxgo-lsio_media:/media + - test_nxgo-lsio_backup:/backup + - test_nxgo-lsio_analytics:/analytics + + # Dockerfile : NxMeta + # Port : 7102 + nxmeta: + image: test_nxmeta + container_name: nxmeta-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7102:7001 + volumes: + - test_nxmeta_etc:/opt/networkoptix-metavms/mediaserver/etc + - test_nxmeta_ini:/home/networkoptix-metavms/.config/nx_ini + - test_nxmeta_var:/opt/networkoptix-metavms/mediaserver/var + - test_nxmeta_media:/media + - test_nxmeta_backup:/backup + - test_nxmeta_analytics:/analytics + + # Dockerfile : NxMeta-LSIO + # Port : 7202 + nxmeta-lsio: + image: test_nxmeta-lsio + container_name: nxmeta-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7202:7001 + volumes: + - test_nxmeta-lsio_config:/config + - test_nxmeta-lsio_media:/media + - test_nxmeta-lsio_backup:/backup + - test_nxmeta-lsio_analytics:/analytics + + # Dockerfile : NxWitness + # Port : 7103 + nxwitness: + image: test_nxwitness + container_name: nxwitness-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7103:7001 + volumes: + - test_nxwitness_etc:/opt/networkoptix/mediaserver/etc + - test_nxwitness_ini:/home/networkoptix/.config/nx_ini + - test_nxwitness_var:/opt/networkoptix/mediaserver/var + - test_nxwitness_media:/media + - test_nxwitness_backup:/backup + - test_nxwitness_analytics:/analytics + + # Dockerfile : NxWitness-LSIO + # Port : 7203 + nxwitness-lsio: + image: test_nxwitness-lsio + container_name: nxwitness-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7203:7001 + volumes: + - test_nxwitness-lsio_config:/config + - test_nxwitness-lsio_media:/media + - test_nxwitness-lsio_backup:/backup + - test_nxwitness-lsio_analytics:/analytics + + # Dockerfile : DWSpectrum + # Port : 7104 + dwspectrum: + image: test_dwspectrum + container_name: dwspectrum-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7104:7001 + volumes: + - test_dwspectrum_etc:/opt/digitalwatchdog/mediaserver/etc + - test_dwspectrum_ini:/home/digitalwatchdog/.config/nx_ini + - test_dwspectrum_var:/opt/digitalwatchdog/mediaserver/var + - test_dwspectrum_media:/media + - test_dwspectrum_backup:/backup + - test_dwspectrum_analytics:/analytics + + # Dockerfile : DWSpectrum-LSIO + # Port : 7204 + dwspectrum-lsio: + image: test_dwspectrum-lsio + container_name: dwspectrum-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7204:7001 + volumes: + - test_dwspectrum-lsio_config:/config + - test_dwspectrum-lsio_media:/media + - test_dwspectrum-lsio_backup:/backup + - test_dwspectrum-lsio_analytics:/analytics + + # Dockerfile : WisenetWAVE + # Port : 7105 + wisenetwave: + image: test_wisenetwave + container_name: wisenetwave-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7105:7001 + volumes: + - test_wisenetwave_etc:/opt/hanwha/mediaserver/etc + - test_wisenetwave_ini:/home/hanwha/.config/nx_ini + - test_wisenetwave_var:/opt/hanwha/mediaserver/var + - test_wisenetwave_media:/media + - test_wisenetwave_backup:/backup + - test_wisenetwave_analytics:/analytics + + # Dockerfile : WisenetWAVE-LSIO + # Port : 7205 + wisenetwave-lsio: + image: test_wisenetwave-lsio + container_name: wisenetwave-lsio-container + restart: unless-stopped + environment: + - TZ=Americas/Los_Angeles + network_mode: bridge + ports: + - 7205:7001 + volumes: + - test_wisenetwave-lsio_config:/config + - test_wisenetwave-lsio_media:/media + - test_wisenetwave-lsio_backup:/backup + - test_wisenetwave-lsio_analytics:/analytics diff --git a/Make/Version.json b/Make/Version.json index 63cff28d..b44e7a34 100644 --- a/Make/Version.json +++ b/Make/Version.json @@ -1,110 +1,110 @@ -{ - "SchemaVersion": 2, - "Products": [ - { - "Product": "NxGo", - "Versions": [ - { - "Version": "6.1.2.42921", - "UriX64": "https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", - "Labels": [ - "Stable" - ] - }, - { - "Version": "6.1.3.43301", - "UriX64": "https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", - "Labels": [ - "Latest" - ] - } - ] - }, - { - "Product": "NxMeta", - "Versions": [ - { - "Version": "6.1.2.42921", - "UriX64": "https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", - "Labels": [ - "Stable" - ] - }, - { - "Version": "6.1.3.43301", - "UriX64": "https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", - "Labels": [ - "Latest" - ] - } - ] - }, - { - "Product": "NxWitness", - "Versions": [ - { - "Version": "6.1.2.42921", - "UriX64": "https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", - "Labels": [ - "Stable" - ] - }, - { - "Version": "6.1.3.43301", - "UriX64": "https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", - "Labels": [ - "Latest" - ] - } - ] - }, - { - "Product": "DWSpectrum", - "Versions": [ - { - "Version": "6.1.1.42624", - "UriX64": "https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", - "Labels": [ - "Stable" - ] - }, - { - "Version": "6.1.3.43301", - "UriX64": "https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", - "Labels": [ - "Latest" - ] - } - ] - }, - { - "Product": "WisenetWAVE", - "Versions": [ - { - "Version": "6.1.2.42921", - "UriX64": "https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", - "Labels": [ - "Stable" - ] - }, - { - "Version": "6.1.3.43301", - "UriX64": "https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip", - "UriArm64": "https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", - "Labels": [ - "Latest" - ] - } - ] - } - ] +{ + "SchemaVersion": 2, + "Products": [ + { + "Product": "NxGo", + "Versions": [ + { + "Version": "6.1.2.42921", + "UriX64": "https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/nxgo/42921/nxgo-server_update-6.1.2.42921-linux_arm64.zip", + "Labels": [ + "Stable" + ] + }, + { + "Version": "6.1.3.43301", + "UriX64": "https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/nxgo/43301/nxgo-server_update-6.1.3.43301-linux_arm64.zip", + "Labels": [ + "Latest" + ] + } + ] + }, + { + "Product": "NxMeta", + "Versions": [ + { + "Version": "6.1.2.42921", + "UriX64": "https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/metavms/42921/metavms-server_update-6.1.2.42921-linux_arm64.zip", + "Labels": [ + "Stable" + ] + }, + { + "Version": "6.1.3.43301", + "UriX64": "https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/metavms/43301/metavms-server_update-6.1.3.43301-linux_arm64.zip", + "Labels": [ + "Latest" + ] + } + ] + }, + { + "Product": "NxWitness", + "Versions": [ + { + "Version": "6.1.2.42921", + "UriX64": "https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/default/42921/nxwitness-server_update-6.1.2.42921-linux_arm64.zip", + "Labels": [ + "Stable" + ] + }, + { + "Version": "6.1.3.43301", + "UriX64": "https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/default/43301/nxwitness-server_update-6.1.3.43301-linux_arm64.zip", + "Labels": [ + "Latest" + ] + } + ] + }, + { + "Product": "DWSpectrum", + "Versions": [ + { + "Version": "6.1.1.42624", + "UriX64": "https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/digitalwatchdog/42624/dwspectrum-server_update-6.1.1.42624-linux_arm64.zip", + "Labels": [ + "Stable" + ] + }, + { + "Version": "6.1.3.43301", + "UriX64": "https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/digitalwatchdog/43301/dwspectrum-server_update-6.1.3.43301-linux_arm64.zip", + "Labels": [ + "Latest" + ] + } + ] + }, + { + "Product": "WisenetWAVE", + "Versions": [ + { + "Version": "6.1.2.42921", + "UriX64": "https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/hanwha/42921/wave-server_update-6.1.2.42921-linux_arm64.zip", + "Labels": [ + "Stable" + ] + }, + { + "Version": "6.1.3.43301", + "UriX64": "https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_x64.zip", + "UriArm64": "https://updates.networkoptix.com/hanwha/43301/wave-server_update-6.1.3.43301-linux_arm64.zip", + "Labels": [ + "Latest" + ] + } + ] + } + ] } \ No newline at end of file diff --git a/NxWitness.slnx b/NxWitness.slnx index e0b717a3..07407740 100644 --- a/NxWitness.slnx +++ b/NxWitness.slnx @@ -1,36 +1,36 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index a1afffd5..d739e40b 100644 --- a/README.md +++ b/README.md @@ -1,731 +1,731 @@ -# Docker Projects for Network Optix VMS Products - -This is a project to build and publish docker images for various [Network Optix][networkoptix-link] VMS products. - -## Build and Distribution - -### Build Status - -[![Release Status][releasebuildstatus-shield]][actions-link]\ -[![Last Commit][lastcommit-shield]][github-link] - -### Release Notes - -**Version: 2.14**: - -**Summary**: - -- Fixed an LSIO permissions race where the startup `chown` could run before the `PUID`/`PGID` remap, leaving `/config` and `/media` owned by the wrong user and breaking media writes. - -See [Release History](./HISTORY.md) for complete release notes and older versions. - -## Getting Started - -**Getting started with a simple test compose file**: - -```yaml -# compose.yaml - -# Test using non persistent docker volumes -volumes: - test_nxwitness-lsio_config: - test_nxwitness-lsio_media: - test_nxwitness-lsio_backup: - test_nxwitness-lsio_analytics: - -services: - nxwitness-lsio: - # Use the image matching your product - image: docker.io/ptr727/nxwitness-lsio:stable - container_name: nxwitness-lsio-test-container - restart: unless-stopped - network_mode: bridge - ports: - # Expose the service on port 7203 - - 7203:7001 - environment: - - TZ=America/Los_Angeles - volumes: - # Map to your real storage in production - - test_nxwitness-lsio_config:/config - - test_nxwitness-lsio_media:/media - - test_nxwitness-lsio_backup:/backup - - test_nxwitness-lsio_analytics:/analytics - tmpfs: - # Keep mediaserver's Unix socket and tmp files in RAM - - /tmp:size=1g,mode=1777 -``` - -```shell -# Launch the service -docker compose up --detach - -# Open your web browser on the local machine port 7203 -echo "Nx Witness LSIO:" "https://$HOSTNAME:7203/" - -# Shut the service down -docker compose down -``` - -**Example of a service in production**: - -```yaml -networks: - - public_network: # External macvlan network - name: ${PUBLIC_NETWORK_NAME} - external: true - local_network: # External bridge network - name: ${LOCAL_NETWORK_NAME} - external: true - stack_network: # Stack network - - -services: - - nxmeta: - image: docker.io/ptr727/nxmeta-lsio:latest - container_name: nxmeta - hostname: nxmeta - domainname: ${DOMAIN_NAME} - restart: unless-stopped - user: root - group_add: - - ${DOCKER_GROUP_ID} - security_opt: # Set with care - - seccomp=unconfined - - apparmor=unconfined - devices: # Pass through Intel/AMD iGPU for hardware accelerated video - - /dev/dri - environment: - - TZ=${TZ} - - PUID=${USER_NONROOT_ID} # Run as non-root user - - PGID=${USERS_GROUP_ID} - volumes: # ZFS volumes - - ${APPDATA_DIR}/nxmeta/config:/config - - ${NVR_DIR}/media:/media # ssdpool/nvr-media - - ${NVR_DIR}/backup:/backup # hddpool/nvr-backup - - ${NVR_DIR}/analytics:/analytics # ssdpool/nvr-analytics - tmpfs: # Keep mediaserver's Unix socket and tmp files in RAM - - /tmp:size=1g,mode=1777 - networks: - public_network: - ipv4_address: ${NXMETA_IP} # Static IP - mac_address: ${NXMETA_MAC} # Static MAC - local_network: - stack_network: - labels: - - traefik.enable=true # Traefik SSL proxy - # Two-hostname pattern (DNS records configured externally, e.g. - # in your LAN DNS or hosts file): an A/AAAA `nxmeta -> ${NXMETA_IP}` - # points at the dedicated macvlan IP above for direct access, - # and a CNAME `nxmeta-web -> ` fronts the service - # with SSL termination. Traefik routes only the `-web` hostname; - # the bare hostname bypasses Traefik entirely. - - traefik.http.routers.nxmeta.rule=HostRegexp(`^nxmeta-web${DOMAIN_REGEX}$$`) - - traefik.http.services.nxmeta.loadbalancer.server.scheme=https - - traefik.http.services.nxmeta.loadbalancer.server.port=7001 -``` - -## Table of Contents - -- [Build and Distribution](#build-and-distribution) - - [Build Status](#build-status) - - [Release Notes](#release-notes) -- [Getting Started](#getting-started) -- [Table of Contents](#table-of-contents) -- [Products](#products) -- [Releases](#releases) -- [Overview](#overview) - - [Introduction](#introduction) - - [Base Images](#base-images) - - [LinuxServer](#linuxserver) -- [Configuration](#configuration) - - [LSIO Volumes](#lsio-volumes) - - [Non-LSIO Volumes](#non-lsio-volumes) - - [Ports](#ports) - - [Environment Variables](#environment-variables) - - [Network Mode](#network-mode) -- [Examples](#examples) - - [LSIO Docker Create](#lsio-docker-create) - - [LSIO Docker Compose](#lsio-docker-compose) - - [Non-LSIO Docker Compose](#non-lsio-docker-compose) - - [Unraid Template](#unraid-template) -- [Product Information](#product-information) - - [Release Information](#release-information) - - [Advanced Configuration](#advanced-configuration) -- [Build Process](#build-process) -- [Known Issues](#known-issues) -- [Troubleshooting](#troubleshooting) - - [Missing Storage](#missing-storage) -- [License](#license) - -## Products - -The project supports the following product variants: - -- [Network Optix][networkoptix-link] [Nx Witness VMS][nxwitness-link] (not available for purchase in the US) -- [Network Optix][networkoptix-link] [Nx Meta VMS][nxmeta-link] (developer and early access version of Nx Witness) -- [Network Optix][networkoptix-link] [Nx Go VMS][nxgo-link] (version of Nx Witness targeted at transportation sector) -- [Digital Watchdog][digitalwatchdog-link] [DW Spectrum IPVMS][dwspectrum-link] (US licensed and OEM branded version of Nx Witness) -- [Hanwha Vision][hanwhavision-link] [Wisenet WAVE VMS][dwspectrum-link] (US licensed and OEM branded version of Nx Witness) - -## Releases - -Images are published on [Docker Hub][hub-link]: - -- [NxWitness][hubnxwitness-link]: `docker pull docker.io/ptr727/nxwitness` -- [NxWitness-LSIO][hubnxwitnesslsio-link]: `docker pull docker.io/ptr727/nxwitness-lsio` -- [NxMeta][hubnxmeta-link]: `docker pull docker.io/ptr727/nxmeta` -- [NxMeta-LSIO][hubnxmetalsio-link]: `docker pull docker.io/ptr727/nxmeta-lsio` -- [NxGo][hubnxgo-link]: `docker pull docker.io/ptr727/nxgo` -- [NxGo-LSIO][hubnxwitnesslsio-link]: `docker pull docker.io/ptr727/nxgo-lsio` -- [DWSpectrum][hubdwspectrum-link]: `docker pull docker.io/ptr727/dwspectrum` -- [DWSpectrum-LSIO][hubdwspectrumlsio-link]: `docker pull docker.io/ptr727/dwspectrum-lsio` -- [WisenetWAVE][hubwisenetwave-link]: `docker pull docker.io/ptr727/wisenetwave` -- [WisenetWAVE-LSIO][hubwisenetwavelsio-link]: `docker pull docker.io/ptr727/wisenetwave-lsio` - -Images are tagged as follows: - -- `latest`: Latest published version, e.g. `docker pull docker.io/ptr727/nxmeta:latest`. -- `stable`: Latest released version, e.g. `docker pull docker.io/ptr727/nxmeta:stable`. -- `rc`: Latest RC version, e.g. `docker pull docker.io/ptr727/nxmeta:rc`. -- `beta`: Latest Beta version, e.g. `docker pull docker.io/ptr727/nxmeta:beta` -- `develop`: Builds created from the develop branch, e.g. `docker pull docker.io/ptr727/nxmeta:develop`. -- `[version]`: Release version number, e.g. `docker pull docker.io/ptr727/nxmeta:5.2.2.37996`. - -Notes: - -- `latest` and `stable` may be the same version if all builds are released builds. -- `rc` and `beta` tags are only built when RC and Beta builds are published by Nx, and may be older than current `latest` or `stable` builds. -- Images are published once a week on a schedule (and on-demand via manual trigger), picking up the latest upstream Ubuntu updates and newly released Nx product versions. A single scheduled run publishes both the `main` tags (`latest`, `stable`, version numbers) and the `develop` tags. Merging code or dependency updates does not republish images, so the published images only change when there is an actual content change. -- See [Build Process](#build-process) for more details. - -**Docker releases**: - -[NxWitness][hubnxwitness-link]:\ -[![NxWitness Stable][hubnxwitnessstable-shield]][hubnxwitness-link] -[![NxWitness Latest][hubnxwitnesslatest-shield]][hubnxwitness-link] -[![NxWitness RC][hubnxwitnessrc-shield]][hubnxwitness-link] -[![NxWitness Beta][hubnxwitnessbeta-shield]][hubnxwitness-link] - -[NxWitness-LSIO][hubnxwitnesslsio-link]:\ -[![NxWitness-LSIO Stable][hubnxwitnesslsiostable-shield]][hubnxwitnesslsio-link] -[![NxWitness-LSIO Latest][hubnxwitnesslsiolatest-shield]][hubnxwitnesslsio-link] -[![NxWitness-LSIO RC][hubnxwitnesslsiorc-shield]][hubnxwitnesslsio-link] -[![NxWitness-LSIO Beta][hubnxwitnesslsiobeta-shield]][hubnxwitnesslsio-link] - -[NxMeta][hubnxmeta-link]:\ -[![NxMeta Stable][hubnxmetastable-shield]][hubnxmeta-link] -[![NxMeta Latest][hubnxmetalatest-shield]][hubnxmeta-link] -[![NxMeta RC][hubnxmetarc-shield]][hubnxmeta-link] -[![NxMeta Beta][hubnxmetabeta-shield]][hubnxmeta-link] - -[NxMeta-LSIO][hubnxmetalsio-link]:\ -[![NxMeta-LSIO Stable][hubnxmetalsiostable-shield]][hubnxmetalsio-link] -[![NxMeta-LSIO Latest][hubnxmetalsiolatest-shield]][hubnxmetalsio-link] -[![NxMeta-LSIO RC][hubnxmetalsiorc-shield]][hubnxmetalsio-link] -[![NxMeta-LSIO Beta][hubnxmetalsiobeta-shield]][hubnxmetalsio-link] - -[NxGo][hubnxgo-link]:\ -[![NxGo Stable][hubnxgostable-shield]][hubnxgo-link] -[![NxGo Latest][hubnxgolatest-shield]][hubnxgo-link] -[![NxGo RC][hubnxgorc-shield]][hubnxgo-link] -[![NxGo Beta][hubnxgobeta-shield]][hubnxgo-link] - -[NxGo-LSIO][hubnxgolsio-link]:\ -[![NxGo-LSIO Stable][hubnxgolsiostable-shield]][hubnxgolsio-link] -[![NxGo-LSIO Latest][hubnxgolsiolatest-shield]][hubnxgolsio-link] -[![NxGo-LSIO RC][hubnxgolsiorc-shield]][hubnxgolsio-link] -[![NxGo-LSIO Beta][hubnxgolsiobeta-shield]][hubnxgolsio-link] - -[DWSpectrum][hubdwspectrum-link]:\ -[![DWSpectrum Stable][hubdwspectrumstable-shield]][hubdwspectrum-link] -[![DWSpectrum Latest][hubdwspectrumlatest-shield]][hubdwspectrum-link] -[![DWSpectrum RC][hubdwspectrumrc-shield]][hubdwspectrum-link] -[![DWSpectrum Beta][hubdwspectrumbeta-shield]][hubdwspectrum-link] - -[DWSpectrum-LSIO][hubdwspectrumlsio-link]:\ -[![DWSpectrum-LSIO Stable][hubdwspectrumlsiostable-shield]][hubdwspectrumlsio-link] -[![DWSpectrum-LSIO Latest][hubdwspectrumlsiolatest-shield]][hubdwspectrumlsio-link] -[![DWSpectrum-LSIO RC][hubdwspectrumlsiorc-shield]][hubdwspectrumlsio-link] -[![DWSpectrum-LSIO Beta][hubdwspectrumlsiobeta-shield]][hubdwspectrumlsio-link] - -[WisenetWAVE][hubwisenetwave-link]:\ -[![WisenetWAVE Stable][hubwisenetwavestable-shield]][hubwisenetwave-link] -[![WisenetWAVE Latest][hubwisenetwavelatest-shield]][hubwisenetwave-link] -[![WisenetWAVE RC][hubwisenetwaverc-shield]][hubwisenetwave-link] -[![WisenetWAVE Beta][hubwisenetwavebeta-shield]][hubwisenetwave-link] - -[WisenetWAVE-LSIO][hubwisenetwavelsio-link]:\ -[![WisenetWAVE-LSIO Stable][hubwisenetwavelsiostable-shield]][hubwisenetwavelsio-link] -[![WisenetWAVE-LSIO Latest][hubwisenetwavelsiolatest-shield]][hubwisenetwavelsio-link] -[![WisenetWAVE-LSIO RC][hubwisenetwavelsiorc-shield]][hubwisenetwavelsio-link] -[![WisenetWAVE-LSIO Beta][hubwisenetwavelsiobeta-shield]][hubwisenetwavelsio-link] - -## Overview - -### Introduction - -I ran DW Spectrum in my home lab on an Ubuntu Virtual Machine, and was looking for a way to run it in Docker. At the time Network Optix provided no support for Docker, but I did find the [The Home Repot NxWitness][thehomegithub-link] project, that inspired me to create this project.\ -I started with individual repositories for Nx Witness, Nx Meta, and DW Spectrum, but that soon became cumbersome with lots of duplication, and I combined all product flavors into this one project. - -Today Network Optix supports [Docker][nxdocker-link], and they publish [build scripts][nxgithubdocker-link], but they do not publish container images. - -### Base Images - -The project creates two variants of each product using different base images: - -- [Ubuntu][ubuntu-link] using [ubuntu:noble][ubuntudocker-link] base image. -- [LinuxServer][lsio-link] using [lsiobase/ubuntu:noble][ubuntulsiodocker-link] base image. - -The build pipeline also publishes internal base images (`ptr727/nx-base:ubuntu-noble` and -`ptr727/nx-base-lsio:ubuntu-noble`) built from `ubuntu:noble` and `lsiobase/ubuntu:noble` -to reduce CI cache churn. These base images are not intended for end-user deployments. - -Note that smaller base images like [Alpine][alpine-link] are not [supported][nxossupport-link] by the mediaserver. - -### LinuxServer - -The [LinuxServer (LSIO)][lsio-link] base images provide valuable container functionality: - -- The LSIO images are based on [s6-overlay][s6overlay-link], are updated weekly, and LSIO [produces][lsiofleet-link] containers for many popular open source applications. -- LSIO allows us to [specify][lsiopuid-link] the user account to use when running the mediaserver, while still running the `root-tool` as `root` (required for license enforcement). -- Running as non-root is a [best practice][dockernonroot-link], and required if we need user specific permissions when accessing mapped volumes. -- The [nxvms-docker][nxgithubcompose-link] project takes a different approach running a compose stack that runs the mediaserver in one instance under the `${COMPANY_NAME}` account, and the root-tool in a second instance under the `root` account, using a shared `/tmp` volume for socket IPC between the mediaserver and root-tool, but the user account `${COMPANY_NAME}` does not readily map to a user on the host system. - -## Configuration - -User accounts and directory names are based on the product variant exposed by the `${COMPANY_NAME}` variable: - -- NxWitness: `networkoptix` -- DWSpectrum: `digitalwatchdog` -- NxMeta: `networkoptix-metavms` -- WisenetWAVE: `hanwha` - -### LSIO Volumes - -The LSIO images [re-link](./LSIO/etc/s6-overlay/s6-rc.d/init-nx-relocate/run) various internal paths to `/config`. - -- `/config` : Configuration files: - - `/opt/${COMPANY_NAME}/mediaserver/etc` links to `/config/etc` : Configuration. - - `/root/.config/nx_ini` links to `/config/ini` : Additional configuration. - - `/opt/${COMPANY_NAME}/mediaserver/var` links to `/config/var` : State and logs. -- `/media` : Recording files. - -### Non-LSIO Volumes - -The non-LSIO images must be mapped directly to the installed paths, refer to the [nxvms-docker][nxgithubvolumes-link] page for details. - -- `/opt/${COMPANY_NAME}/mediaserver/etc` : Configuration. -- `/home/${COMPANY_NAME}/.config/nx_ini` : Additional configuration. -- `/opt/${COMPANY_NAME}/mediaserver/var` : State and logs. -- `/media` : Recording files. - -### Ports - -- `7001` : Default server port. - -### Environment Variables - -- `PUID` : User Id, LSIO only, optional. -- `PGID` : Group Id, LSIO only, optional. -- `TZ` : Timezone, e.g. `America/Los_Angeles`. - -See [LSIO docs][lsiopuid-link] for usage of `PUID` and `PGID` that allow the mediaserver to run under a user account and the root-tool to run as root. - -### Network Mode - -Any network mode can be used, but due to the hardware bound licensing, `host` mode is [recommended][nxgithubnetworking-link]. - -## Examples - -### LSIO Docker Create - -```shell -docker create \ - --name=nxwitness-lsio-test-container \ - --hostname=nxwitness-lsio-test-host \ - --domainname=foo.bar.net \ - --restart=unless-stopped \ - --network=host \ - --env TZ=America/Los_Angeles \ - --volume /mnt/nxwitness/config:/config:rw \ - --volume /mnt/nxwitness/media:/media:rw \ - docker.io/ptr727/nxwitness-lsio:stable - -docker start nxwitness-lsio-test-container -``` - -### LSIO Docker Compose - -```yaml -services: - nxwitness: - image: docker.io/ptr727/nxwitness-lsio:stable - container_name: nxwitness-lsio-test-container - restart: unless-stopped - network_mode: host - environment: - # - PUID=65534 # id $user - # - PGID=65534 # id $group - - TZ=America/Los_Angeles - volumes: - - /mnt/nxwitness/config:/config - - /mnt/nxwitness/media:/media - tmpfs: - # Keep mediaserver's Unix socket and tmp files in RAM - - /tmp:size=1g,mode=1777 -``` - -### Non-LSIO Docker Compose - -```yaml -services: - nxwitness: - image: docker.io/ptr727/nxwitness:stable - container_name: nxwitness-test-container - restart: unless-stopped - network_mode: host - volumes: - - /mnt/nxwitness/config/etc:/opt/networkoptix/mediaserver/etc - - /mnt/nxwitness/config/nx_ini:/home/networkoptix/.config/nx_ini - - /mnt/nxwitness/config/var:/opt/networkoptix/mediaserver/var - - /mnt/nxwitness/media:/media - tmpfs: - # Keep mediaserver's Unix socket and tmp files in RAM - - /tmp:size=1g,mode=1777 -``` - -### Unraid Template - -- Add the template [URL](./Unraid) `https://github.com/ptr727/NxWitness/tree/main/Unraid` to the "Template Repositories" section, at the bottom of the "Docker" configuration tab, and click "Save". -- Create a new container by clicking the "Add Container" button, select the desired product template from the dropdown. -- If using Unassigned Devices for media storage, use `RW/Slave` access mode. -- Use `nobody` and `users` identifiers, `PUID=99` and `PGID=100`. -- Register the Unraid filesystems in the `additionalLocalFsTypes` advanced settings, see the [Missing Storage](#missing-storage) section for help. - -## Product Information - -### Release Information - -- Nx Witness: - - [Releases JSON API][nxwitnessreleases-link] - - [Downloads][nxwitnessdownload-link] - - [Beta Downloads][nxwitnessbetadownload-link] - - [Release Notes][nxwitnessreleasenotes-link] -- Nx Meta: - - [Releases JSON API][nxmetareleases-link] - - [Signup for Nx Meta][getstartedwithmeta-link] - - [Request Developer Licenses][getalicense-link] - - [Downloads][nxmetadownload-link] - - [Beta Downloads][nxmetabetadownload-link] -- Nx Go: - - [Releases JSON API][nxgoreleases-link] - - [Downloads][nxgodownload-link] - - [Beta Downloads][nxgobetadownload-link] - - [Release Notes][nxgoreleasenotes-link] -- DW Spectrum: - - [Releases JSON API][dwspectrumreleases-link] - - [Downloads][dwspectrumdownload-link] - - [Release Notes][dwspectrumreleasenotes-link] -- Wisenet WAVE: - - [Releases JSON API][wisenetwavereleases-link] - - [Downloads][wisenetwavedownload-link] - - [Release Notes][wisenetwavereleasenotes-link] - -### Advanced Configuration - -- `mediaserver.conf` [Configuration][configoptions-link]: `https://[hostname]:[port]/#/server-documentation` -- `nx_vms_server.ini` [Configuration][iniconfig-link]: `https://[hostname]:[port]/api/iniConfig/` -- Advanced Server Configuration: `https://[hostname]:[port]/#/settings/advanced` -- Storage Reporting: `https://[hostname]:[port]/#/health/storages` - -## Build Process - -**Build overview**: - -- [`CreateMatrix`](./CreateMatrix/) is used to update available product versions, and to create Docker files for all product permutations. -- [`Version.json`](./Make/Version.json) is updated using the mediaserver [Releases JSON API][nxwitnessreleases-link] and [Packages API][packages-link]. -- The logic follows the same pattern as used by the [Nx Open][releaseinfo-link] desktop client logic. -- The "released" status of a build follows the same method as Nx uses in [`isBuildPublished()`][isbuildpublished-link] where `release_date` and `release_delivery_days` from the [Releases JSON API][nxwitnessreleases-link] must be greater than `0` -- [`Matrix.json`](./Make/Matrix.json) is created from the `Version.json` file and is used during pipeline builds using a [Matrix][matrix-link] strategy. -- Automated builds use [GitHub Actions](https://docs.github.com/en/actions): - - Pull requests run unit tests, and when image files change, a fast representative amd64 smoke build of `NxMeta` and `NxMeta-LSIO` ([`test-pull-request.yml`](./.github/workflows/test-pull-request.yml)) -- the full matrix is not built on every PR. - - Publishing happens only on a weekly schedule or manual trigger ([`publish-release.yml`](./.github/workflows/publish-release.yml)), which builds and pushes the full matrix for both the `main` and `develop` branches. Merges to `main`/`develop` (including auto-merged Dependabot and codegen updates) do not publish; the next scheduled run picks them up. -- Version history is maintained and used by `CreateMatrix` such that generic tags, e.g. `latest`, will never result in a lesser version number, i.e. break-fix-forward only, see [Issue #62](https://github.com/ptr727/NxWitness/issues/62) for details on Nx re-publishing "released" builds using an older version breaking already upgraded systems. - -**Local testing**: - -- Run `cd ./Make` and [`./Test.sh`](./Make/Test.sh), the following will be executed: - - [`Create.sh`](./Make/Create.sh): Create `Dockerfile`'s and update the latest version information using `CreateMatrix`. - - [`Build.sh`](./Make/Build.sh): Builds the `Dockerfile`'s using `docker buildx build`. - - [`Up.sh`](./Make/Up.sh): Launch a docker compose stack [`Test.yaml`](./Make/Test.yml) to run all product variants. -- Ctrl-Click on the links to launch the web UI for each of the product variants. -- Run [`Clean.sh`](./Make/Clean.sh) to shutdown the compose stack and cleanup images. - -## Known Issues - -- Licensing: - - Camera recording license keys are activated and bound to hardware attributes of the host server collected by the `root-tool` that is required to run as `root`. - - Requiring the `root-tool` to run as root overly complicates running the `mediaserver` as a non-root user, and requires the container to run using `host` networking to not break the hardware license checks. - - Docker containers are supposed to be portable, and moving containers between hosts will break license activation. - - Nx's own [`nxvms-docker`][nxgithubcompose-link] reference image [disabled `root-tool` in late 2025](https://github.com/networkoptix/nxvms-docker/commit/4285f93) by setting `ignoreRootTool=true` in `mediaserver.conf` and dropping their separate `root-tool` container. This trades hardware-ID license enforcement for a simpler unprivileged container. NxWitness does **not** follow this change - licensed deployments would lose activation - and will revisit only if Nx publishes a clearer official position on Docker licensing without `root-tool`. - - Nx to fix: Associate licenses with the [Cloud Account][nxcloud-link] not the local hardware. -- Storage Management: - - The mediaserver attempts to automatically decide what storage to use. - - Filesystem types are filtered out if not on the [supported list][nxgithubstorage-link]. - - Mounted volumes are ignored if backed by the same physical storage, even if logically separate. - - Unwanted `Nx MetaVMS Media` directories are created on any discoverable writable storage. - - Nx to fix: Eliminate the elaborate filesystem filter logic and use only the admin specified storage locations. -- Configuration Files: - - `.conf` configuration files are located in a static `mediaserver/etc` location while `.ini` configuration files are in a user-account dependent location, e.g. `/home/networkoptix/.config/nx_ini` or `/root/.config/nx_ini`. - - There is no value in having a server use per-user configuration directories, and it is inconsistent to mix configuration file locations. - - Nx to fix: Store all configuration files in `mediaserver/etc`. -- External Plugins: - - Custom or [Marketplace][nxmarketplace-link] plugins are installed in the `mediaserver/bin/plugins` directory. - - The `mediaserver/bin/plugins` directory is already pre-populated with Nx installed plugins. - - It is not possible to use external plugins from a mounted volume as the directory is already in-use. - - Nx to fix: Load plugins from `mediaserver/var/plugins` or from sub-directories mounted below `mediaserver/bin/plugins`, e.g. `mediaserver/bin/plugins/external` -- Lifetime Upgrades: - - Nx is a cloud product, free to view, free upgrades, comes with ongoing costs of hosting, maintenance, and support, it is [unfeasible][nxcrunchbase-link] to sustain a business with ongoing costs using perpetual one-off licenses. - - My personal experience with [Digital Watchdog][digitalwatchdog-link] and their [Lifetime Upgrades and No Annual Agreements][dwupgrades-link] is an inflexible policy of three activations per license and you have to buy a new license, thus the "license lifetime" is a multiplier of the "hardware lifetime". - - Nx to fix: Yearly camera license renewals covering the cost of support and upgrades. -- Archiving: - - Nx makes no distinction between recording and archiving storage, archive is basically just a recording mirror without any capacity or retention benefit. - - Recording storage is typically high speed low latency high cost low capacity SSD/NVMe arrays, while archival playback storage is very high capacity low cost magnetic media arrays. - - Nx to fix: Implement something akin to archiving in [Milestone XProtect VMS][milestone-link] where recording storage is separate from long term archival storage. -- Image Publication: - - Nx relies on end-users or projects like this one to create and publish docker images. - - Nx to fix: Publish up-to-date images for all product variants and release channels. -- Break-Fix-Version-Forward: - - Nx product versions published via their releases API occasionally go backwards, e.g. `release`: v4.3 -> v5.0 -> v4.3. - - Nx supports forward-only in-place upgrades, e.g. v4.3 to v5.0, but not v5.0 to v4.3. - - Publishing generic tags, e.g. `latest`, using a version that regresses, e.g. v4.3 -> v5.0 -> v4.3 breaks deployments, see [Issue #62](https://github.com/ptr727/NxWitness/issues/62) for details. - - `CreateMatrix` tooling keeps track of published versions, and prevents version regression of generic `latest`, `rc` and `beta` tags. - - Nx to fix: Release break-fix-version-forward only via release API's. - -## Troubleshooting - -I am not affiliated with Network Optix, I cannot provide support for their products, please contact [Network Optix Support][nxsupport-link] for product support issues.\ -If there are issues with the docker build scripts used in this project, please create a [GitHub Issue][issues-link].\ -Note that I only test and run `nxmeta-lsio:stable` in my home lab, other images get very little to no testing, please test accordingly. - -### Missing Storage - -The following section will help troubleshoot common problems with missing storage.\ -If this does not help, please contact [Network Optix Support][nxsupport-link].\ -Please do not open a GitHub issue unless you are positive the issue is with the `Dockerfile`. - -Confirm that all the mounted volumes are listed in the available storage locations in the [web admin][nxwebadmin-link] portal. - -Enable [debug logging][nxdebuglogging-link] in the mediaserver:\ -Edit `mediaserver.conf`, set `logLevel=verbose`, restart the server.\ -Look for clues in `/config/var/log/log_file.log`. - -E.g. - -```log -VERBOSE nx::vms::server::fs: shfs /media fuse.shfs - duplicate -VERBOSE nx::vms::server::fs: /dev/sdb8 /media btrfs - duplicate -DEBUG QnStorageSpaceRestHandler(0x7f85043b0b00): Return 0 storages and 1 protocols -``` - -Get a list of the mapped volume mounts in the running container, and verify that `/config` and `/media` are listed in the `Mounts` section: - -```shell -docker ps --no-trunc -docker container inspect [containername] -``` - -Launch a shell in the running container and get a list of filesystems mounts: - -```shell -docker ps --no-trunc -docker exec --interactive --tty [containername] /bin/bash -cat /proc/mounts -exit -``` - -Example output for ZFS (note that ZFS support [was added][nxreleasenotes-link] in v5.0): - -```shell -ssdpool/appdata /config zfs rw,noatime,xattr,posixacl 0 0 -nvrpool/nvr /media zfs rw,noatime,xattr,posixacl 0 0 -ssdpool/docker /archive zfs rw,noatime,xattr,posixacl 0 0 -``` - -Mount `/config` is on device `ssdpool/appdata` and filesystem is `zfs`.\ -Mount `/media` is on device `nvrpool/nvr` and filesystem is `zfs`.\ -Mount `/archive` is on device `ssdpool/docker` and filesystem is `zfs`. - -In this case the devices are unique and will not be filtered, but `zfs` is not supported and needs to be registered. - -Example output for UnRaid FUSE: - -```shell -shfs /config fuse.shfs rw,nosuid,nodev,noatime,user_id=0,group_id=0,allow_other 0 0 -shfs /media fuse.shfs rw,nosuid,nodev,noatime,user_id=0,group_id=0,allow_other 0 0 -shfs /archive fuse.shfs rw,nosuid,nodev,noatime,user_id=0,group_id=0,allow_other 0 0 -``` - -In this case there are two issues, the device is `/shfs` for all three mounts and will be filtered, and the filesystem type is `fuse.shfs` that is not supported and needs to be registered. - -Log file output for Unraid FUSE: - -```log -VERBOSE nx::vms::server::fs: shfs /config fuse.shfs - added -VERBOSE nx::vms::server::fs: shfs /media fuse.shfs - added -VERBOSE nx::vms::server::fs: shfs /archive fuse.shfs - duplicate -``` - -The `/archive` mount is classified as a duplicate and ignored, map just `/media`, do not map `/archive`.\ -Alternative use the "Unassigned Devices" plugin and dedicate e.g. a XFS formatted SSD drive to `/media` and/or `/config`. - -Example output for Unraid BTRFS: - -```shell -/dev/sdb8 /test btrfs rw,relatime,space_cache,subvolid=5,subvol=/test 0 0 -/dev/sdb8 /config btrfs rw,relatime,space_cache,subvolid=5,subvol=/config 0 0 -/dev/sdb8 /media btrfs rw,relatime,space_cache,subvolid=5,subvol=/media 0 0 -/dev/sdb8 /archive btrfs rw,relatime,space_cache,subvolid=5,subvol=/archive 0 0 -``` - -```log -VERBOSE nx::vms::server::fs: /dev/sdb8 /test btrfs - added -VERBOSE nx::vms::server::fs: /dev/sdb8 /config btrfs - duplicate -VERBOSE nx::vms::server::fs: /dev/sdb8 /media btrfs - duplicate -VERBOSE nx::vms::server::fs: /dev/sdb8 /archive btrfs - duplicate -``` - -In this example the `/test` volume was accepted, but all other volumes on `/dev/sdb8` was ignored as duplicates. - -Add the required filesystem types in the [advanced configuration](#advanced-configuration) menu. -Edit the `additionalLocalFsTypes` option and add the required filesystem types, e.g. `fuse.shfs,btrfs,zfs`, restart the server. - -Alternatively call the configuration API directly:\ -`wget --no-check-certificate --user=[username] --password=[password] https://[hostname]:[port]/api/systemSettings?additionalLocalFsTypes=fuse.shfs,btrfs,zfs`. - -To my knowledge there is no solution to duplicate devices being filtered, please contact [Network Optix Support][nxsupport-link] and ask them to stop filtering filesystem types and devices. - -## License - -Licensed under the [MIT License][license-link]\ -![GitHub License][license-shield] - -[actions-link]: https://github.com/ptr727/NxWitness/actions -[alpine-link]: https://alpinelinux.org/ -[configoptions-link]: https://support.networkoptix.com/hc/en-us/articles/360036389693-How-to-access-Nx-Server-configuration-options -[digitalwatchdog-link]: https://digital-watchdog.com/ -[dockernonroot-link]: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user -[dwspectrum-link]: https://dwspectrum.com/ -[dwspectrumdownload-link]: https://dwspectrum.digital-watchdog.com/download/linux -[dwspectrumreleasenotes-link]: https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html -[dwspectrumreleases-link]: https://updates.vmsproxy.com/digitalwatchdog/releases.json -[dwupgrades-link]: https://dwspectrum.com/upgrades/ -[getalicense-link]: https://support.networkoptix.com/hc/en-us/articles/8693698259607-Get-a-License-for-Developers -[getstartedwithmeta-link]: https://www.networkoptix.com/nx-meta/get-started-with-meta -[github-link]: https://github.com/ptr727/NxWitness -[hanwhavision-link]: https://hanwhavisionamerica.com/ -[hub-link]: https://hub.docker.com/u/ptr727 -[hubdwspectrum-link]: https://hub.docker.com/r/ptr727/dwspectrum -[hubdwspectrumbeta-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum/beta?label=beta&logo=docker -[hubdwspectrumlatest-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum/latest?label=latest&logo=docker -[hubdwspectrumlsio-link]: https://hub.docker.com/r/ptr727/dwspectrum-lsio -[hubdwspectrumlsiobeta-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum-lsio/beta?label=beta&logo=docker -[hubdwspectrumlsiolatest-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum-lsio/latest?label=latest&logo=docker -[hubdwspectrumlsiorc-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum-lsio/rc?label=rc&logo=docker -[hubdwspectrumlsiostable-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum-lsio/stable?label=stable&logo=docker -[hubdwspectrumrc-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum/rc?label=rc&logo=docker -[hubdwspectrumstable-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum/stable?label=stable&logo=docker -[hubnxgo-link]: https://hub.docker.com/r/ptr727/nxgo -[hubnxgobeta-shield]: https://img.shields.io/docker/v/ptr727/nxgo/beta?label=beta&logo=docker -[hubnxgolatest-shield]: https://img.shields.io/docker/v/ptr727/nxgo/latest?label=latest&logo=docker -[hubnxgolsio-link]: https://hub.docker.com/r/ptr727/nxgo-lsio -[hubnxgolsiobeta-shield]: https://img.shields.io/docker/v/ptr727/nxgo-lsio/beta?label=beta&logo=docker -[hubnxgolsiolatest-shield]: https://img.shields.io/docker/v/ptr727/nxgo-lsio/latest?label=latest&logo=docker -[hubnxgolsiorc-shield]: https://img.shields.io/docker/v/ptr727/nxgo-lsio/rc?label=rc&logo=docker -[hubnxgolsiostable-shield]: https://img.shields.io/docker/v/ptr727/nxgo-lsio/stable?label=stable&logo=docker -[hubnxgorc-shield]: https://img.shields.io/docker/v/ptr727/nxgo/rc?label=rc&logo=docker -[hubnxgostable-shield]: https://img.shields.io/docker/v/ptr727/nxgo/stable?label=stable&logo=docker -[hubnxmeta-link]: https://hub.docker.com/r/ptr727/nxmeta -[hubnxmetabeta-shield]: https://img.shields.io/docker/v/ptr727/nxmeta/beta?label=beta&logo=docker -[hubnxmetalatest-shield]: https://img.shields.io/docker/v/ptr727/nxmeta/latest?label=latest&logo=docker -[hubnxmetalsio-link]: https://hub.docker.com/r/ptr727/nxmeta-lsio -[hubnxmetalsiobeta-shield]: https://img.shields.io/docker/v/ptr727/nxmeta-lsio/beta?label=beta&logo=docker -[hubnxmetalsiolatest-shield]: https://img.shields.io/docker/v/ptr727/nxmeta-lsio/latest?label=latest&logo=docker -[hubnxmetalsiorc-shield]: https://img.shields.io/docker/v/ptr727/nxmeta-lsio/rc?label=rc&logo=docker -[hubnxmetalsiostable-shield]: https://img.shields.io/docker/v/ptr727/nxmeta-lsio/stable?label=stable&logo=docker -[hubnxmetarc-shield]: https://img.shields.io/docker/v/ptr727/nxmeta/rc?label=rc&logo=docker -[hubnxmetastable-shield]: https://img.shields.io/docker/v/ptr727/nxmeta/stable?label=stable&logo=docker -[hubnxwitness-link]: https://hub.docker.com/r/ptr727/nxwitness -[hubnxwitnessbeta-shield]: https://img.shields.io/docker/v/ptr727/nxwitness/beta?label=beta&logo=docker -[hubnxwitnesslatest-shield]: https://img.shields.io/docker/v/ptr727/nxwitness/latest?label=latest&logo=docker -[hubnxwitnesslsio-link]: https://hub.docker.com/r/ptr727/nxwitness-lsio -[hubnxwitnesslsiobeta-shield]: https://img.shields.io/docker/v/ptr727/nxwitness-lsio/beta?label=beta&logo=docker -[hubnxwitnesslsiolatest-shield]: https://img.shields.io/docker/v/ptr727/nxwitness-lsio/latest?label=latest&logo=docker -[hubnxwitnesslsiorc-shield]: https://img.shields.io/docker/v/ptr727/nxwitness-lsio/rc?label=rc&logo=docker -[hubnxwitnesslsiostable-shield]: https://img.shields.io/docker/v/ptr727/nxwitness-lsio/stable?label=stable&logo=docker -[hubnxwitnessrc-shield]: https://img.shields.io/docker/v/ptr727/nxwitness/rc?label=rc&logo=docker -[hubnxwitnessstable-shield]: https://img.shields.io/docker/v/ptr727/nxwitness/stable?label=stable&logo=docker -[hubwisenetwave-link]: https://hub.docker.com/r/ptr727/wisenetwave -[hubwisenetwavebeta-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave/beta?label=beta&logo=docker -[hubwisenetwavelatest-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave/latest?label=latest&logo=docker -[hubwisenetwavelsio-link]: https://hub.docker.com/r/ptr727/wisenetwave-lsio -[hubwisenetwavelsiobeta-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave-lsio/beta?label=beta&logo=docker -[hubwisenetwavelsiolatest-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave-lsio/latest?label=latest&logo=docker -[hubwisenetwavelsiorc-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave-lsio/rc?label=rc&logo=docker -[hubwisenetwavelsiostable-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave-lsio/stable?label=stable&logo=docker -[hubwisenetwaverc-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave/rc?label=rc&logo=docker -[hubwisenetwavestable-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave/stable?label=stable&logo=docker -[iniconfig-link]: https://meta.nxvms.com/docs/developers/knowledgebase/241-configuring-via-ini-files--iniconfig -[isbuildpublished-link]: https://github.com/networkoptix/nx_open/blob/526967920636d3119c92a5220290ecc10957bf12/vms/libs/nx_vms_update/src/nx/vms/update/releases_info.cpp#L31 -[issues-link]: https://github.com/ptr727/NxWitness/issues -[lastcommit-shield]: https://img.shields.io/github/last-commit/ptr727/NxWitness?logo=github&label=Last%20Commit -[license-link]: ./LICENSE -[license-shield]: https://img.shields.io/github/license/ptr727/NxWitness -[lsio-link]: https://www.linuxserver.io/ -[lsiofleet-link]: https://fleet.linuxserver.io/ -[lsiopuid-link]: https://docs.linuxserver.io/general/understanding-puid-and-pgid -[matrix-link]: https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs -[milestone-link]: https://doc.milestonesys.com/latest/en-US/standard_features/sf_mc/sf_systemoverview/mc_storageandarchivingexplained.htm -[networkoptix-link]: https://www.networkoptix.com/ -[nxcloud-link]: https://www.networkoptix.com/nx-witness/nx-witness-cloud/ -[nxcrunchbase-link]: https://www.crunchbase.com/organization/network-optix -[nxdebuglogging-link]: https://support.networkoptix.com/hc/en-us/articles/236033688-How-to-change-software-logging-level-and-how-to-get-logs -[nxdocker-link]: https://support.networkoptix.com/hc/en-us/articles/360037973573-Docker -[nxgithubcompose-link]: https://github.com/networkoptix/nxvms-docker/blob/master/docker-compose.yaml -[nxgithubdocker-link]: https://github.com/networkoptix/nxvms-docker -[nxgithubnetworking-link]: https://github.com/networkoptix/nxvms-docker#networking -[nxgithubstorage-link]: https://github.com/networkoptix/nxvms-docker#notes-about-storage -[nxgithubvolumes-link]: https://github.com/networkoptix/nxvms-docker#volumes-description -[nxgo-link]: https://updates.networkoptix.com/nxgo -[nxgobetadownload-link]: https://cloud.nxgo.io/download/betas/linux -[nxgodownload-link]: https://cloud.nxgo.io/download/releases/linux -[nxgoreleasenotes-link]: https://updates.networkoptix.com/nxgo/#releases_list -[nxgoreleases-link]: https://updates.networkoptix.com/nxgo/releases.json -[nxmarketplace-link]: https://www.networkoptix.com/nx-meta/nx-integrations-marketplace/ -[nxmeta-link]: https://meta.nxvms.com/ -[nxmetabetadownload-link]: https://meta.nxvms.com/downloads/betas -[nxmetadownload-link]: https://meta.nxvms.com/download/linux -[nxmetareleases-link]: https://updates.vmsproxy.com/metavms/releases.json -[nxossupport-link]: https://support.networkoptix.com/hc/en-us/articles/205313168-Nx-Witness-Operating-System-Support -[nxreleasenotes-link]: https://support.networkoptix.com/hc/en-us/articles/360042751193-Current-and-Past-Releases-Downloads-Release-Notes -[nxsupport-link]: https://support.networkoptix.com/hc/en-us/community/topics -[nxwebadmin-link]: https://support.networkoptix.com/hc/en-us/articles/115012831028-Nx-Server-Web-Admin -[nxwitness-link]: https://www.networkoptix.com/nx-witness/ -[nxwitnessbetadownload-link]: https://beta.networkoptix.com/beta-builds/default -[nxwitnessdownload-link]: https://nxvms.com/download/linux -[nxwitnessreleasenotes-link]: https://www.networkoptix.com/all-nx-witness-release-notes -[nxwitnessreleases-link]: https://updates.vmsproxy.com/default/releases.json -[packages-link]: https://updates.networkoptix.com/default/38363/packages.json -[releasebuildstatus-shield]: https://img.shields.io/github/actions/workflow/status/ptr727/NxWitness/publish-release.yml?branch=main&logo=github&label=Build%20Status -[releaseinfo-link]: https://github.com/networkoptix/nx_open/blob/master/vms/libs/nx_vms_update/src/nx/vms/update/releases_info.cpp -[s6overlay-link]: https://github.com/just-containers/s6-overlay -[thehomegithub-link]: https://github.com/thehomerepot/nxwitness -[ubuntu-link]: https://ubuntu.com/ -[ubuntudocker-link]: https://hub.docker.com/_/ubuntu -[ubuntulsiodocker-link]: https://hub.docker.com/r/lsiobase/ubuntu -[wisenetwavedownload-link]: https://wavevms.com/download/linux -[wisenetwavereleasenotes-link]: https://wavevms.com/release-notes/ -[wisenetwavereleases-link]: https://updates.vmsproxy.com/hanwha/releases.json +# Docker Projects for Network Optix VMS Products + +This is a project to build and publish docker images for various [Network Optix][networkoptix-link] VMS products. + +## Build and Distribution + +### Build Status + +[![Release Status][releasebuildstatus-shield]][actions-link]\ +[![Last Commit][lastcommit-shield]][github-link] + +### Release Notes + +**Version: 2.14**: + +**Summary**: + +- Fixed an LSIO permissions race where the startup `chown` could run before the `PUID`/`PGID` remap, leaving `/config` and `/media` owned by the wrong user and breaking media writes. + +See [Release History](./HISTORY.md) for complete release notes and older versions. + +## Getting Started + +**Getting started with a simple test compose file**: + +```yaml +# compose.yaml + +# Test using non persistent docker volumes +volumes: + test_nxwitness-lsio_config: + test_nxwitness-lsio_media: + test_nxwitness-lsio_backup: + test_nxwitness-lsio_analytics: + +services: + nxwitness-lsio: + # Use the image matching your product + image: docker.io/ptr727/nxwitness-lsio:stable + container_name: nxwitness-lsio-test-container + restart: unless-stopped + network_mode: bridge + ports: + # Expose the service on port 7203 + - 7203:7001 + environment: + - TZ=America/Los_Angeles + volumes: + # Map to your real storage in production + - test_nxwitness-lsio_config:/config + - test_nxwitness-lsio_media:/media + - test_nxwitness-lsio_backup:/backup + - test_nxwitness-lsio_analytics:/analytics + tmpfs: + # Keep mediaserver's Unix socket and tmp files in RAM + - /tmp:size=1g,mode=1777 +``` + +```shell +# Launch the service +docker compose up --detach + +# Open your web browser on the local machine port 7203 +echo "Nx Witness LSIO:" "https://$HOSTNAME:7203/" + +# Shut the service down +docker compose down +``` + +**Example of a service in production**: + +```yaml +networks: + + public_network: # External macvlan network + name: ${PUBLIC_NETWORK_NAME} + external: true + local_network: # External bridge network + name: ${LOCAL_NETWORK_NAME} + external: true + stack_network: # Stack network + + +services: + + nxmeta: + image: docker.io/ptr727/nxmeta-lsio:latest + container_name: nxmeta + hostname: nxmeta + domainname: ${DOMAIN_NAME} + restart: unless-stopped + user: root + group_add: + - ${DOCKER_GROUP_ID} + security_opt: # Set with care + - seccomp=unconfined + - apparmor=unconfined + devices: # Pass through Intel/AMD iGPU for hardware accelerated video + - /dev/dri + environment: + - TZ=${TZ} + - PUID=${USER_NONROOT_ID} # Run as non-root user + - PGID=${USERS_GROUP_ID} + volumes: # ZFS volumes + - ${APPDATA_DIR}/nxmeta/config:/config + - ${NVR_DIR}/media:/media # ssdpool/nvr-media + - ${NVR_DIR}/backup:/backup # hddpool/nvr-backup + - ${NVR_DIR}/analytics:/analytics # ssdpool/nvr-analytics + tmpfs: # Keep mediaserver's Unix socket and tmp files in RAM + - /tmp:size=1g,mode=1777 + networks: + public_network: + ipv4_address: ${NXMETA_IP} # Static IP + mac_address: ${NXMETA_MAC} # Static MAC + local_network: + stack_network: + labels: + - traefik.enable=true # Traefik SSL proxy + # Two-hostname pattern (DNS records configured externally, e.g. + # in your LAN DNS or hosts file): an A/AAAA `nxmeta -> ${NXMETA_IP}` + # points at the dedicated macvlan IP above for direct access, + # and a CNAME `nxmeta-web -> ` fronts the service + # with SSL termination. Traefik routes only the `-web` hostname; + # the bare hostname bypasses Traefik entirely. + - traefik.http.routers.nxmeta.rule=HostRegexp(`^nxmeta-web${DOMAIN_REGEX}$$`) + - traefik.http.services.nxmeta.loadbalancer.server.scheme=https + - traefik.http.services.nxmeta.loadbalancer.server.port=7001 +``` + +## Table of Contents + +- [Build and Distribution](#build-and-distribution) + - [Build Status](#build-status) + - [Release Notes](#release-notes) +- [Getting Started](#getting-started) +- [Table of Contents](#table-of-contents) +- [Products](#products) +- [Releases](#releases) +- [Overview](#overview) + - [Introduction](#introduction) + - [Base Images](#base-images) + - [LinuxServer](#linuxserver) +- [Configuration](#configuration) + - [LSIO Volumes](#lsio-volumes) + - [Non-LSIO Volumes](#non-lsio-volumes) + - [Ports](#ports) + - [Environment Variables](#environment-variables) + - [Network Mode](#network-mode) +- [Examples](#examples) + - [LSIO Docker Create](#lsio-docker-create) + - [LSIO Docker Compose](#lsio-docker-compose) + - [Non-LSIO Docker Compose](#non-lsio-docker-compose) + - [Unraid Template](#unraid-template) +- [Product Information](#product-information) + - [Release Information](#release-information) + - [Advanced Configuration](#advanced-configuration) +- [Build Process](#build-process) +- [Known Issues](#known-issues) +- [Troubleshooting](#troubleshooting) + - [Missing Storage](#missing-storage) +- [License](#license) + +## Products + +The project supports the following product variants: + +- [Network Optix][networkoptix-link] [Nx Witness VMS][nxwitness-link] (not available for purchase in the US) +- [Network Optix][networkoptix-link] [Nx Meta VMS][nxmeta-link] (developer and early access version of Nx Witness) +- [Network Optix][networkoptix-link] [Nx Go VMS][nxgo-link] (version of Nx Witness targeted at transportation sector) +- [Digital Watchdog][digitalwatchdog-link] [DW Spectrum IPVMS][dwspectrum-link] (US licensed and OEM branded version of Nx Witness) +- [Hanwha Vision][hanwhavision-link] [Wisenet WAVE VMS][dwspectrum-link] (US licensed and OEM branded version of Nx Witness) + +## Releases + +Images are published on [Docker Hub][hub-link]: + +- [NxWitness][hubnxwitness-link]: `docker pull docker.io/ptr727/nxwitness` +- [NxWitness-LSIO][hubnxwitnesslsio-link]: `docker pull docker.io/ptr727/nxwitness-lsio` +- [NxMeta][hubnxmeta-link]: `docker pull docker.io/ptr727/nxmeta` +- [NxMeta-LSIO][hubnxmetalsio-link]: `docker pull docker.io/ptr727/nxmeta-lsio` +- [NxGo][hubnxgo-link]: `docker pull docker.io/ptr727/nxgo` +- [NxGo-LSIO][hubnxwitnesslsio-link]: `docker pull docker.io/ptr727/nxgo-lsio` +- [DWSpectrum][hubdwspectrum-link]: `docker pull docker.io/ptr727/dwspectrum` +- [DWSpectrum-LSIO][hubdwspectrumlsio-link]: `docker pull docker.io/ptr727/dwspectrum-lsio` +- [WisenetWAVE][hubwisenetwave-link]: `docker pull docker.io/ptr727/wisenetwave` +- [WisenetWAVE-LSIO][hubwisenetwavelsio-link]: `docker pull docker.io/ptr727/wisenetwave-lsio` + +Images are tagged as follows: + +- `latest`: Latest published version, e.g. `docker pull docker.io/ptr727/nxmeta:latest`. +- `stable`: Latest released version, e.g. `docker pull docker.io/ptr727/nxmeta:stable`. +- `rc`: Latest RC version, e.g. `docker pull docker.io/ptr727/nxmeta:rc`. +- `beta`: Latest Beta version, e.g. `docker pull docker.io/ptr727/nxmeta:beta` +- `develop`: Builds created from the develop branch, e.g. `docker pull docker.io/ptr727/nxmeta:develop`. +- `[version]`: Release version number, e.g. `docker pull docker.io/ptr727/nxmeta:5.2.2.37996`. + +Notes: + +- `latest` and `stable` may be the same version if all builds are released builds. +- `rc` and `beta` tags are only built when RC and Beta builds are published by Nx, and may be older than current `latest` or `stable` builds. +- Images are published once a week on a schedule (and on-demand via manual trigger), picking up the latest upstream Ubuntu updates and newly released Nx product versions. A single scheduled run publishes both the `main` tags (`latest`, `stable`, version numbers) and the `develop` tags. Merging code or dependency updates does not republish images, so the published images only change when there is an actual content change. +- See [Build Process](#build-process) for more details. + +**Docker releases**: + +[NxWitness][hubnxwitness-link]:\ +[![NxWitness Stable][hubnxwitnessstable-shield]][hubnxwitness-link] +[![NxWitness Latest][hubnxwitnesslatest-shield]][hubnxwitness-link] +[![NxWitness RC][hubnxwitnessrc-shield]][hubnxwitness-link] +[![NxWitness Beta][hubnxwitnessbeta-shield]][hubnxwitness-link] + +[NxWitness-LSIO][hubnxwitnesslsio-link]:\ +[![NxWitness-LSIO Stable][hubnxwitnesslsiostable-shield]][hubnxwitnesslsio-link] +[![NxWitness-LSIO Latest][hubnxwitnesslsiolatest-shield]][hubnxwitnesslsio-link] +[![NxWitness-LSIO RC][hubnxwitnesslsiorc-shield]][hubnxwitnesslsio-link] +[![NxWitness-LSIO Beta][hubnxwitnesslsiobeta-shield]][hubnxwitnesslsio-link] + +[NxMeta][hubnxmeta-link]:\ +[![NxMeta Stable][hubnxmetastable-shield]][hubnxmeta-link] +[![NxMeta Latest][hubnxmetalatest-shield]][hubnxmeta-link] +[![NxMeta RC][hubnxmetarc-shield]][hubnxmeta-link] +[![NxMeta Beta][hubnxmetabeta-shield]][hubnxmeta-link] + +[NxMeta-LSIO][hubnxmetalsio-link]:\ +[![NxMeta-LSIO Stable][hubnxmetalsiostable-shield]][hubnxmetalsio-link] +[![NxMeta-LSIO Latest][hubnxmetalsiolatest-shield]][hubnxmetalsio-link] +[![NxMeta-LSIO RC][hubnxmetalsiorc-shield]][hubnxmetalsio-link] +[![NxMeta-LSIO Beta][hubnxmetalsiobeta-shield]][hubnxmetalsio-link] + +[NxGo][hubnxgo-link]:\ +[![NxGo Stable][hubnxgostable-shield]][hubnxgo-link] +[![NxGo Latest][hubnxgolatest-shield]][hubnxgo-link] +[![NxGo RC][hubnxgorc-shield]][hubnxgo-link] +[![NxGo Beta][hubnxgobeta-shield]][hubnxgo-link] + +[NxGo-LSIO][hubnxgolsio-link]:\ +[![NxGo-LSIO Stable][hubnxgolsiostable-shield]][hubnxgolsio-link] +[![NxGo-LSIO Latest][hubnxgolsiolatest-shield]][hubnxgolsio-link] +[![NxGo-LSIO RC][hubnxgolsiorc-shield]][hubnxgolsio-link] +[![NxGo-LSIO Beta][hubnxgolsiobeta-shield]][hubnxgolsio-link] + +[DWSpectrum][hubdwspectrum-link]:\ +[![DWSpectrum Stable][hubdwspectrumstable-shield]][hubdwspectrum-link] +[![DWSpectrum Latest][hubdwspectrumlatest-shield]][hubdwspectrum-link] +[![DWSpectrum RC][hubdwspectrumrc-shield]][hubdwspectrum-link] +[![DWSpectrum Beta][hubdwspectrumbeta-shield]][hubdwspectrum-link] + +[DWSpectrum-LSIO][hubdwspectrumlsio-link]:\ +[![DWSpectrum-LSIO Stable][hubdwspectrumlsiostable-shield]][hubdwspectrumlsio-link] +[![DWSpectrum-LSIO Latest][hubdwspectrumlsiolatest-shield]][hubdwspectrumlsio-link] +[![DWSpectrum-LSIO RC][hubdwspectrumlsiorc-shield]][hubdwspectrumlsio-link] +[![DWSpectrum-LSIO Beta][hubdwspectrumlsiobeta-shield]][hubdwspectrumlsio-link] + +[WisenetWAVE][hubwisenetwave-link]:\ +[![WisenetWAVE Stable][hubwisenetwavestable-shield]][hubwisenetwave-link] +[![WisenetWAVE Latest][hubwisenetwavelatest-shield]][hubwisenetwave-link] +[![WisenetWAVE RC][hubwisenetwaverc-shield]][hubwisenetwave-link] +[![WisenetWAVE Beta][hubwisenetwavebeta-shield]][hubwisenetwave-link] + +[WisenetWAVE-LSIO][hubwisenetwavelsio-link]:\ +[![WisenetWAVE-LSIO Stable][hubwisenetwavelsiostable-shield]][hubwisenetwavelsio-link] +[![WisenetWAVE-LSIO Latest][hubwisenetwavelsiolatest-shield]][hubwisenetwavelsio-link] +[![WisenetWAVE-LSIO RC][hubwisenetwavelsiorc-shield]][hubwisenetwavelsio-link] +[![WisenetWAVE-LSIO Beta][hubwisenetwavelsiobeta-shield]][hubwisenetwavelsio-link] + +## Overview + +### Introduction + +I ran DW Spectrum in my home lab on an Ubuntu Virtual Machine, and was looking for a way to run it in Docker. At the time Network Optix provided no support for Docker, but I did find the [The Home Repot NxWitness][thehomegithub-link] project, that inspired me to create this project.\ +I started with individual repositories for Nx Witness, Nx Meta, and DW Spectrum, but that soon became cumbersome with lots of duplication, and I combined all product flavors into this one project. + +Today Network Optix supports [Docker][nxdocker-link], and they publish [build scripts][nxgithubdocker-link], but they do not publish container images. + +### Base Images + +The project creates two variants of each product using different base images: + +- [Ubuntu][ubuntu-link] using [ubuntu:noble][ubuntudocker-link] base image. +- [LinuxServer][lsio-link] using [lsiobase/ubuntu:noble][ubuntulsiodocker-link] base image. + +The build pipeline also publishes internal base images (`ptr727/nx-base:ubuntu-noble` and +`ptr727/nx-base-lsio:ubuntu-noble`) built from `ubuntu:noble` and `lsiobase/ubuntu:noble` +to reduce CI cache churn. These base images are not intended for end-user deployments. + +Note that smaller base images like [Alpine][alpine-link] are not [supported][nxossupport-link] by the mediaserver. + +### LinuxServer + +The [LinuxServer (LSIO)][lsio-link] base images provide valuable container functionality: + +- The LSIO images are based on [s6-overlay][s6overlay-link], are updated weekly, and LSIO [produces][lsiofleet-link] containers for many popular open source applications. +- LSIO allows us to [specify][lsiopuid-link] the user account to use when running the mediaserver, while still running the `root-tool` as `root` (required for license enforcement). +- Running as non-root is a [best practice][dockernonroot-link], and required if we need user specific permissions when accessing mapped volumes. +- The [nxvms-docker][nxgithubcompose-link] project takes a different approach running a compose stack that runs the mediaserver in one instance under the `${COMPANY_NAME}` account, and the root-tool in a second instance under the `root` account, using a shared `/tmp` volume for socket IPC between the mediaserver and root-tool, but the user account `${COMPANY_NAME}` does not readily map to a user on the host system. + +## Configuration + +User accounts and directory names are based on the product variant exposed by the `${COMPANY_NAME}` variable: + +- NxWitness: `networkoptix` +- DWSpectrum: `digitalwatchdog` +- NxMeta: `networkoptix-metavms` +- WisenetWAVE: `hanwha` + +### LSIO Volumes + +The LSIO images [re-link](./LSIO/etc/s6-overlay/s6-rc.d/init-nx-relocate/run) various internal paths to `/config`. + +- `/config` : Configuration files: + - `/opt/${COMPANY_NAME}/mediaserver/etc` links to `/config/etc` : Configuration. + - `/root/.config/nx_ini` links to `/config/ini` : Additional configuration. + - `/opt/${COMPANY_NAME}/mediaserver/var` links to `/config/var` : State and logs. +- `/media` : Recording files. + +### Non-LSIO Volumes + +The non-LSIO images must be mapped directly to the installed paths, refer to the [nxvms-docker][nxgithubvolumes-link] page for details. + +- `/opt/${COMPANY_NAME}/mediaserver/etc` : Configuration. +- `/home/${COMPANY_NAME}/.config/nx_ini` : Additional configuration. +- `/opt/${COMPANY_NAME}/mediaserver/var` : State and logs. +- `/media` : Recording files. + +### Ports + +- `7001` : Default server port. + +### Environment Variables + +- `PUID` : User Id, LSIO only, optional. +- `PGID` : Group Id, LSIO only, optional. +- `TZ` : Timezone, e.g. `America/Los_Angeles`. + +See [LSIO docs][lsiopuid-link] for usage of `PUID` and `PGID` that allow the mediaserver to run under a user account and the root-tool to run as root. + +### Network Mode + +Any network mode can be used, but due to the hardware bound licensing, `host` mode is [recommended][nxgithubnetworking-link]. + +## Examples + +### LSIO Docker Create + +```shell +docker create \ + --name=nxwitness-lsio-test-container \ + --hostname=nxwitness-lsio-test-host \ + --domainname=foo.bar.net \ + --restart=unless-stopped \ + --network=host \ + --env TZ=America/Los_Angeles \ + --volume /mnt/nxwitness/config:/config:rw \ + --volume /mnt/nxwitness/media:/media:rw \ + docker.io/ptr727/nxwitness-lsio:stable + +docker start nxwitness-lsio-test-container +``` + +### LSIO Docker Compose + +```yaml +services: + nxwitness: + image: docker.io/ptr727/nxwitness-lsio:stable + container_name: nxwitness-lsio-test-container + restart: unless-stopped + network_mode: host + environment: + # - PUID=65534 # id $user + # - PGID=65534 # id $group + - TZ=America/Los_Angeles + volumes: + - /mnt/nxwitness/config:/config + - /mnt/nxwitness/media:/media + tmpfs: + # Keep mediaserver's Unix socket and tmp files in RAM + - /tmp:size=1g,mode=1777 +``` + +### Non-LSIO Docker Compose + +```yaml +services: + nxwitness: + image: docker.io/ptr727/nxwitness:stable + container_name: nxwitness-test-container + restart: unless-stopped + network_mode: host + volumes: + - /mnt/nxwitness/config/etc:/opt/networkoptix/mediaserver/etc + - /mnt/nxwitness/config/nx_ini:/home/networkoptix/.config/nx_ini + - /mnt/nxwitness/config/var:/opt/networkoptix/mediaserver/var + - /mnt/nxwitness/media:/media + tmpfs: + # Keep mediaserver's Unix socket and tmp files in RAM + - /tmp:size=1g,mode=1777 +``` + +### Unraid Template + +- Add the template [URL](./Unraid) `https://github.com/ptr727/NxWitness/tree/main/Unraid` to the "Template Repositories" section, at the bottom of the "Docker" configuration tab, and click "Save". +- Create a new container by clicking the "Add Container" button, select the desired product template from the dropdown. +- If using Unassigned Devices for media storage, use `RW/Slave` access mode. +- Use `nobody` and `users` identifiers, `PUID=99` and `PGID=100`. +- Register the Unraid filesystems in the `additionalLocalFsTypes` advanced settings, see the [Missing Storage](#missing-storage) section for help. + +## Product Information + +### Release Information + +- Nx Witness: + - [Releases JSON API][nxwitnessreleases-link] + - [Downloads][nxwitnessdownload-link] + - [Beta Downloads][nxwitnessbetadownload-link] + - [Release Notes][nxwitnessreleasenotes-link] +- Nx Meta: + - [Releases JSON API][nxmetareleases-link] + - [Signup for Nx Meta][getstartedwithmeta-link] + - [Request Developer Licenses][getalicense-link] + - [Downloads][nxmetadownload-link] + - [Beta Downloads][nxmetabetadownload-link] +- Nx Go: + - [Releases JSON API][nxgoreleases-link] + - [Downloads][nxgodownload-link] + - [Beta Downloads][nxgobetadownload-link] + - [Release Notes][nxgoreleasenotes-link] +- DW Spectrum: + - [Releases JSON API][dwspectrumreleases-link] + - [Downloads][dwspectrumdownload-link] + - [Release Notes][dwspectrumreleasenotes-link] +- Wisenet WAVE: + - [Releases JSON API][wisenetwavereleases-link] + - [Downloads][wisenetwavedownload-link] + - [Release Notes][wisenetwavereleasenotes-link] + +### Advanced Configuration + +- `mediaserver.conf` [Configuration][configoptions-link]: `https://[hostname]:[port]/#/server-documentation` +- `nx_vms_server.ini` [Configuration][iniconfig-link]: `https://[hostname]:[port]/api/iniConfig/` +- Advanced Server Configuration: `https://[hostname]:[port]/#/settings/advanced` +- Storage Reporting: `https://[hostname]:[port]/#/health/storages` + +## Build Process + +**Build overview**: + +- [`CreateMatrix`](./CreateMatrix/) is used to update available product versions, and to create Docker files for all product permutations. +- [`Version.json`](./Make/Version.json) is updated using the mediaserver [Releases JSON API][nxwitnessreleases-link] and [Packages API][packages-link]. +- The logic follows the same pattern as used by the [Nx Open][releaseinfo-link] desktop client logic. +- The "released" status of a build follows the same method as Nx uses in [`isBuildPublished()`][isbuildpublished-link] where `release_date` and `release_delivery_days` from the [Releases JSON API][nxwitnessreleases-link] must be greater than `0` +- [`Matrix.json`](./Make/Matrix.json) is created from the `Version.json` file and is used during pipeline builds using a [Matrix][matrix-link] strategy. +- Automated builds use [GitHub Actions](https://docs.github.com/en/actions): + - Pull requests run unit tests, and when image files change, a fast representative amd64 smoke build of `NxMeta` and `NxMeta-LSIO` ([`test-pull-request.yml`](./.github/workflows/test-pull-request.yml)) -- the full matrix is not built on every PR. + - Publishing happens only on a weekly schedule or manual trigger ([`publish-release.yml`](./.github/workflows/publish-release.yml)), which builds and pushes the full matrix for both the `main` and `develop` branches. Merges to `main`/`develop` (including auto-merged Dependabot and codegen updates) do not publish; the next scheduled run picks them up. +- Version history is maintained and used by `CreateMatrix` such that generic tags, e.g. `latest`, will never result in a lesser version number, i.e. break-fix-forward only, see [Issue #62](https://github.com/ptr727/NxWitness/issues/62) for details on Nx re-publishing "released" builds using an older version breaking already upgraded systems. + +**Local testing**: + +- Run `cd ./Make` and [`./Test.sh`](./Make/Test.sh), the following will be executed: + - [`Create.sh`](./Make/Create.sh): Create `Dockerfile`'s and update the latest version information using `CreateMatrix`. + - [`Build.sh`](./Make/Build.sh): Builds the `Dockerfile`'s using `docker buildx build`. + - [`Up.sh`](./Make/Up.sh): Launch a docker compose stack [`Test.yaml`](./Make/Test.yml) to run all product variants. +- Ctrl-Click on the links to launch the web UI for each of the product variants. +- Run [`Clean.sh`](./Make/Clean.sh) to shutdown the compose stack and cleanup images. + +## Known Issues + +- Licensing: + - Camera recording license keys are activated and bound to hardware attributes of the host server collected by the `root-tool` that is required to run as `root`. + - Requiring the `root-tool` to run as root overly complicates running the `mediaserver` as a non-root user, and requires the container to run using `host` networking to not break the hardware license checks. + - Docker containers are supposed to be portable, and moving containers between hosts will break license activation. + - Nx's own [`nxvms-docker`][nxgithubcompose-link] reference image [disabled `root-tool` in late 2025](https://github.com/networkoptix/nxvms-docker/commit/4285f93) by setting `ignoreRootTool=true` in `mediaserver.conf` and dropping their separate `root-tool` container. This trades hardware-ID license enforcement for a simpler unprivileged container. NxWitness does **not** follow this change - licensed deployments would lose activation - and will revisit only if Nx publishes a clearer official position on Docker licensing without `root-tool`. + - Nx to fix: Associate licenses with the [Cloud Account][nxcloud-link] not the local hardware. +- Storage Management: + - The mediaserver attempts to automatically decide what storage to use. + - Filesystem types are filtered out if not on the [supported list][nxgithubstorage-link]. + - Mounted volumes are ignored if backed by the same physical storage, even if logically separate. + - Unwanted `Nx MetaVMS Media` directories are created on any discoverable writable storage. + - Nx to fix: Eliminate the elaborate filesystem filter logic and use only the admin specified storage locations. +- Configuration Files: + - `.conf` configuration files are located in a static `mediaserver/etc` location while `.ini` configuration files are in a user-account dependent location, e.g. `/home/networkoptix/.config/nx_ini` or `/root/.config/nx_ini`. + - There is no value in having a server use per-user configuration directories, and it is inconsistent to mix configuration file locations. + - Nx to fix: Store all configuration files in `mediaserver/etc`. +- External Plugins: + - Custom or [Marketplace][nxmarketplace-link] plugins are installed in the `mediaserver/bin/plugins` directory. + - The `mediaserver/bin/plugins` directory is already pre-populated with Nx installed plugins. + - It is not possible to use external plugins from a mounted volume as the directory is already in-use. + - Nx to fix: Load plugins from `mediaserver/var/plugins` or from sub-directories mounted below `mediaserver/bin/plugins`, e.g. `mediaserver/bin/plugins/external` +- Lifetime Upgrades: + - Nx is a cloud product, free to view, free upgrades, comes with ongoing costs of hosting, maintenance, and support, it is [unfeasible][nxcrunchbase-link] to sustain a business with ongoing costs using perpetual one-off licenses. + - My personal experience with [Digital Watchdog][digitalwatchdog-link] and their [Lifetime Upgrades and No Annual Agreements][dwupgrades-link] is an inflexible policy of three activations per license and you have to buy a new license, thus the "license lifetime" is a multiplier of the "hardware lifetime". + - Nx to fix: Yearly camera license renewals covering the cost of support and upgrades. +- Archiving: + - Nx makes no distinction between recording and archiving storage, archive is basically just a recording mirror without any capacity or retention benefit. + - Recording storage is typically high speed low latency high cost low capacity SSD/NVMe arrays, while archival playback storage is very high capacity low cost magnetic media arrays. + - Nx to fix: Implement something akin to archiving in [Milestone XProtect VMS][milestone-link] where recording storage is separate from long term archival storage. +- Image Publication: + - Nx relies on end-users or projects like this one to create and publish docker images. + - Nx to fix: Publish up-to-date images for all product variants and release channels. +- Break-Fix-Version-Forward: + - Nx product versions published via their releases API occasionally go backwards, e.g. `release`: v4.3 -> v5.0 -> v4.3. + - Nx supports forward-only in-place upgrades, e.g. v4.3 to v5.0, but not v5.0 to v4.3. + - Publishing generic tags, e.g. `latest`, using a version that regresses, e.g. v4.3 -> v5.0 -> v4.3 breaks deployments, see [Issue #62](https://github.com/ptr727/NxWitness/issues/62) for details. + - `CreateMatrix` tooling keeps track of published versions, and prevents version regression of generic `latest`, `rc` and `beta` tags. + - Nx to fix: Release break-fix-version-forward only via release API's. + +## Troubleshooting + +I am not affiliated with Network Optix, I cannot provide support for their products, please contact [Network Optix Support][nxsupport-link] for product support issues.\ +If there are issues with the docker build scripts used in this project, please create a [GitHub Issue][issues-link].\ +Note that I only test and run `nxmeta-lsio:stable` in my home lab, other images get very little to no testing, please test accordingly. + +### Missing Storage + +The following section will help troubleshoot common problems with missing storage.\ +If this does not help, please contact [Network Optix Support][nxsupport-link].\ +Please do not open a GitHub issue unless you are positive the issue is with the `Dockerfile`. + +Confirm that all the mounted volumes are listed in the available storage locations in the [web admin][nxwebadmin-link] portal. + +Enable [debug logging][nxdebuglogging-link] in the mediaserver:\ +Edit `mediaserver.conf`, set `logLevel=verbose`, restart the server.\ +Look for clues in `/config/var/log/log_file.log`. + +E.g. + +```log +VERBOSE nx::vms::server::fs: shfs /media fuse.shfs - duplicate +VERBOSE nx::vms::server::fs: /dev/sdb8 /media btrfs - duplicate +DEBUG QnStorageSpaceRestHandler(0x7f85043b0b00): Return 0 storages and 1 protocols +``` + +Get a list of the mapped volume mounts in the running container, and verify that `/config` and `/media` are listed in the `Mounts` section: + +```shell +docker ps --no-trunc +docker container inspect [containername] +``` + +Launch a shell in the running container and get a list of filesystems mounts: + +```shell +docker ps --no-trunc +docker exec --interactive --tty [containername] /bin/bash +cat /proc/mounts +exit +``` + +Example output for ZFS (note that ZFS support [was added][nxreleasenotes-link] in v5.0): + +```shell +ssdpool/appdata /config zfs rw,noatime,xattr,posixacl 0 0 +nvrpool/nvr /media zfs rw,noatime,xattr,posixacl 0 0 +ssdpool/docker /archive zfs rw,noatime,xattr,posixacl 0 0 +``` + +Mount `/config` is on device `ssdpool/appdata` and filesystem is `zfs`.\ +Mount `/media` is on device `nvrpool/nvr` and filesystem is `zfs`.\ +Mount `/archive` is on device `ssdpool/docker` and filesystem is `zfs`. + +In this case the devices are unique and will not be filtered, but `zfs` is not supported and needs to be registered. + +Example output for UnRaid FUSE: + +```shell +shfs /config fuse.shfs rw,nosuid,nodev,noatime,user_id=0,group_id=0,allow_other 0 0 +shfs /media fuse.shfs rw,nosuid,nodev,noatime,user_id=0,group_id=0,allow_other 0 0 +shfs /archive fuse.shfs rw,nosuid,nodev,noatime,user_id=0,group_id=0,allow_other 0 0 +``` + +In this case there are two issues, the device is `/shfs` for all three mounts and will be filtered, and the filesystem type is `fuse.shfs` that is not supported and needs to be registered. + +Log file output for Unraid FUSE: + +```log +VERBOSE nx::vms::server::fs: shfs /config fuse.shfs - added +VERBOSE nx::vms::server::fs: shfs /media fuse.shfs - added +VERBOSE nx::vms::server::fs: shfs /archive fuse.shfs - duplicate +``` + +The `/archive` mount is classified as a duplicate and ignored, map just `/media`, do not map `/archive`.\ +Alternative use the "Unassigned Devices" plugin and dedicate e.g. a XFS formatted SSD drive to `/media` and/or `/config`. + +Example output for Unraid BTRFS: + +```shell +/dev/sdb8 /test btrfs rw,relatime,space_cache,subvolid=5,subvol=/test 0 0 +/dev/sdb8 /config btrfs rw,relatime,space_cache,subvolid=5,subvol=/config 0 0 +/dev/sdb8 /media btrfs rw,relatime,space_cache,subvolid=5,subvol=/media 0 0 +/dev/sdb8 /archive btrfs rw,relatime,space_cache,subvolid=5,subvol=/archive 0 0 +``` + +```log +VERBOSE nx::vms::server::fs: /dev/sdb8 /test btrfs - added +VERBOSE nx::vms::server::fs: /dev/sdb8 /config btrfs - duplicate +VERBOSE nx::vms::server::fs: /dev/sdb8 /media btrfs - duplicate +VERBOSE nx::vms::server::fs: /dev/sdb8 /archive btrfs - duplicate +``` + +In this example the `/test` volume was accepted, but all other volumes on `/dev/sdb8` was ignored as duplicates. + +Add the required filesystem types in the [advanced configuration](#advanced-configuration) menu. +Edit the `additionalLocalFsTypes` option and add the required filesystem types, e.g. `fuse.shfs,btrfs,zfs`, restart the server. + +Alternatively call the configuration API directly:\ +`wget --no-check-certificate --user=[username] --password=[password] https://[hostname]:[port]/api/systemSettings?additionalLocalFsTypes=fuse.shfs,btrfs,zfs`. + +To my knowledge there is no solution to duplicate devices being filtered, please contact [Network Optix Support][nxsupport-link] and ask them to stop filtering filesystem types and devices. + +## License + +Licensed under the [MIT License][license-link]\ +![GitHub License][license-shield] + +[actions-link]: https://github.com/ptr727/NxWitness/actions +[alpine-link]: https://alpinelinux.org/ +[configoptions-link]: https://support.networkoptix.com/hc/en-us/articles/360036389693-How-to-access-Nx-Server-configuration-options +[digitalwatchdog-link]: https://digital-watchdog.com/ +[dockernonroot-link]: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user +[dwspectrum-link]: https://dwspectrum.com/ +[dwspectrumdownload-link]: https://dwspectrum.digital-watchdog.com/download/linux +[dwspectrumreleasenotes-link]: https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html +[dwspectrumreleases-link]: https://updates.vmsproxy.com/digitalwatchdog/releases.json +[dwupgrades-link]: https://dwspectrum.com/upgrades/ +[getalicense-link]: https://support.networkoptix.com/hc/en-us/articles/8693698259607-Get-a-License-for-Developers +[getstartedwithmeta-link]: https://www.networkoptix.com/nx-meta/get-started-with-meta +[github-link]: https://github.com/ptr727/NxWitness +[hanwhavision-link]: https://hanwhavisionamerica.com/ +[hub-link]: https://hub.docker.com/u/ptr727 +[hubdwspectrum-link]: https://hub.docker.com/r/ptr727/dwspectrum +[hubdwspectrumbeta-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum/beta?label=beta&logo=docker +[hubdwspectrumlatest-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum/latest?label=latest&logo=docker +[hubdwspectrumlsio-link]: https://hub.docker.com/r/ptr727/dwspectrum-lsio +[hubdwspectrumlsiobeta-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum-lsio/beta?label=beta&logo=docker +[hubdwspectrumlsiolatest-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum-lsio/latest?label=latest&logo=docker +[hubdwspectrumlsiorc-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum-lsio/rc?label=rc&logo=docker +[hubdwspectrumlsiostable-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum-lsio/stable?label=stable&logo=docker +[hubdwspectrumrc-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum/rc?label=rc&logo=docker +[hubdwspectrumstable-shield]: https://img.shields.io/docker/v/ptr727/dwspectrum/stable?label=stable&logo=docker +[hubnxgo-link]: https://hub.docker.com/r/ptr727/nxgo +[hubnxgobeta-shield]: https://img.shields.io/docker/v/ptr727/nxgo/beta?label=beta&logo=docker +[hubnxgolatest-shield]: https://img.shields.io/docker/v/ptr727/nxgo/latest?label=latest&logo=docker +[hubnxgolsio-link]: https://hub.docker.com/r/ptr727/nxgo-lsio +[hubnxgolsiobeta-shield]: https://img.shields.io/docker/v/ptr727/nxgo-lsio/beta?label=beta&logo=docker +[hubnxgolsiolatest-shield]: https://img.shields.io/docker/v/ptr727/nxgo-lsio/latest?label=latest&logo=docker +[hubnxgolsiorc-shield]: https://img.shields.io/docker/v/ptr727/nxgo-lsio/rc?label=rc&logo=docker +[hubnxgolsiostable-shield]: https://img.shields.io/docker/v/ptr727/nxgo-lsio/stable?label=stable&logo=docker +[hubnxgorc-shield]: https://img.shields.io/docker/v/ptr727/nxgo/rc?label=rc&logo=docker +[hubnxgostable-shield]: https://img.shields.io/docker/v/ptr727/nxgo/stable?label=stable&logo=docker +[hubnxmeta-link]: https://hub.docker.com/r/ptr727/nxmeta +[hubnxmetabeta-shield]: https://img.shields.io/docker/v/ptr727/nxmeta/beta?label=beta&logo=docker +[hubnxmetalatest-shield]: https://img.shields.io/docker/v/ptr727/nxmeta/latest?label=latest&logo=docker +[hubnxmetalsio-link]: https://hub.docker.com/r/ptr727/nxmeta-lsio +[hubnxmetalsiobeta-shield]: https://img.shields.io/docker/v/ptr727/nxmeta-lsio/beta?label=beta&logo=docker +[hubnxmetalsiolatest-shield]: https://img.shields.io/docker/v/ptr727/nxmeta-lsio/latest?label=latest&logo=docker +[hubnxmetalsiorc-shield]: https://img.shields.io/docker/v/ptr727/nxmeta-lsio/rc?label=rc&logo=docker +[hubnxmetalsiostable-shield]: https://img.shields.io/docker/v/ptr727/nxmeta-lsio/stable?label=stable&logo=docker +[hubnxmetarc-shield]: https://img.shields.io/docker/v/ptr727/nxmeta/rc?label=rc&logo=docker +[hubnxmetastable-shield]: https://img.shields.io/docker/v/ptr727/nxmeta/stable?label=stable&logo=docker +[hubnxwitness-link]: https://hub.docker.com/r/ptr727/nxwitness +[hubnxwitnessbeta-shield]: https://img.shields.io/docker/v/ptr727/nxwitness/beta?label=beta&logo=docker +[hubnxwitnesslatest-shield]: https://img.shields.io/docker/v/ptr727/nxwitness/latest?label=latest&logo=docker +[hubnxwitnesslsio-link]: https://hub.docker.com/r/ptr727/nxwitness-lsio +[hubnxwitnesslsiobeta-shield]: https://img.shields.io/docker/v/ptr727/nxwitness-lsio/beta?label=beta&logo=docker +[hubnxwitnesslsiolatest-shield]: https://img.shields.io/docker/v/ptr727/nxwitness-lsio/latest?label=latest&logo=docker +[hubnxwitnesslsiorc-shield]: https://img.shields.io/docker/v/ptr727/nxwitness-lsio/rc?label=rc&logo=docker +[hubnxwitnesslsiostable-shield]: https://img.shields.io/docker/v/ptr727/nxwitness-lsio/stable?label=stable&logo=docker +[hubnxwitnessrc-shield]: https://img.shields.io/docker/v/ptr727/nxwitness/rc?label=rc&logo=docker +[hubnxwitnessstable-shield]: https://img.shields.io/docker/v/ptr727/nxwitness/stable?label=stable&logo=docker +[hubwisenetwave-link]: https://hub.docker.com/r/ptr727/wisenetwave +[hubwisenetwavebeta-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave/beta?label=beta&logo=docker +[hubwisenetwavelatest-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave/latest?label=latest&logo=docker +[hubwisenetwavelsio-link]: https://hub.docker.com/r/ptr727/wisenetwave-lsio +[hubwisenetwavelsiobeta-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave-lsio/beta?label=beta&logo=docker +[hubwisenetwavelsiolatest-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave-lsio/latest?label=latest&logo=docker +[hubwisenetwavelsiorc-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave-lsio/rc?label=rc&logo=docker +[hubwisenetwavelsiostable-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave-lsio/stable?label=stable&logo=docker +[hubwisenetwaverc-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave/rc?label=rc&logo=docker +[hubwisenetwavestable-shield]: https://img.shields.io/docker/v/ptr727/wisenetwave/stable?label=stable&logo=docker +[iniconfig-link]: https://meta.nxvms.com/docs/developers/knowledgebase/241-configuring-via-ini-files--iniconfig +[isbuildpublished-link]: https://github.com/networkoptix/nx_open/blob/526967920636d3119c92a5220290ecc10957bf12/vms/libs/nx_vms_update/src/nx/vms/update/releases_info.cpp#L31 +[issues-link]: https://github.com/ptr727/NxWitness/issues +[lastcommit-shield]: https://img.shields.io/github/last-commit/ptr727/NxWitness?logo=github&label=Last%20Commit +[license-link]: ./LICENSE +[license-shield]: https://img.shields.io/github/license/ptr727/NxWitness +[lsio-link]: https://www.linuxserver.io/ +[lsiofleet-link]: https://fleet.linuxserver.io/ +[lsiopuid-link]: https://docs.linuxserver.io/general/understanding-puid-and-pgid +[matrix-link]: https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs +[milestone-link]: https://doc.milestonesys.com/latest/en-US/standard_features/sf_mc/sf_systemoverview/mc_storageandarchivingexplained.htm +[networkoptix-link]: https://www.networkoptix.com/ +[nxcloud-link]: https://www.networkoptix.com/nx-witness/nx-witness-cloud/ +[nxcrunchbase-link]: https://www.crunchbase.com/organization/network-optix +[nxdebuglogging-link]: https://support.networkoptix.com/hc/en-us/articles/236033688-How-to-change-software-logging-level-and-how-to-get-logs +[nxdocker-link]: https://support.networkoptix.com/hc/en-us/articles/360037973573-Docker +[nxgithubcompose-link]: https://github.com/networkoptix/nxvms-docker/blob/master/docker-compose.yaml +[nxgithubdocker-link]: https://github.com/networkoptix/nxvms-docker +[nxgithubnetworking-link]: https://github.com/networkoptix/nxvms-docker#networking +[nxgithubstorage-link]: https://github.com/networkoptix/nxvms-docker#notes-about-storage +[nxgithubvolumes-link]: https://github.com/networkoptix/nxvms-docker#volumes-description +[nxgo-link]: https://updates.networkoptix.com/nxgo +[nxgobetadownload-link]: https://cloud.nxgo.io/download/betas/linux +[nxgodownload-link]: https://cloud.nxgo.io/download/releases/linux +[nxgoreleasenotes-link]: https://updates.networkoptix.com/nxgo/#releases_list +[nxgoreleases-link]: https://updates.networkoptix.com/nxgo/releases.json +[nxmarketplace-link]: https://www.networkoptix.com/nx-meta/nx-integrations-marketplace/ +[nxmeta-link]: https://meta.nxvms.com/ +[nxmetabetadownload-link]: https://meta.nxvms.com/downloads/betas +[nxmetadownload-link]: https://meta.nxvms.com/download/linux +[nxmetareleases-link]: https://updates.vmsproxy.com/metavms/releases.json +[nxossupport-link]: https://support.networkoptix.com/hc/en-us/articles/205313168-Nx-Witness-Operating-System-Support +[nxreleasenotes-link]: https://support.networkoptix.com/hc/en-us/articles/360042751193-Current-and-Past-Releases-Downloads-Release-Notes +[nxsupport-link]: https://support.networkoptix.com/hc/en-us/community/topics +[nxwebadmin-link]: https://support.networkoptix.com/hc/en-us/articles/115012831028-Nx-Server-Web-Admin +[nxwitness-link]: https://www.networkoptix.com/nx-witness/ +[nxwitnessbetadownload-link]: https://beta.networkoptix.com/beta-builds/default +[nxwitnessdownload-link]: https://nxvms.com/download/linux +[nxwitnessreleasenotes-link]: https://www.networkoptix.com/all-nx-witness-release-notes +[nxwitnessreleases-link]: https://updates.vmsproxy.com/default/releases.json +[packages-link]: https://updates.networkoptix.com/default/38363/packages.json +[releasebuildstatus-shield]: https://img.shields.io/github/actions/workflow/status/ptr727/NxWitness/publish-release.yml?branch=main&logo=github&label=Build%20Status +[releaseinfo-link]: https://github.com/networkoptix/nx_open/blob/master/vms/libs/nx_vms_update/src/nx/vms/update/releases_info.cpp +[s6overlay-link]: https://github.com/just-containers/s6-overlay +[thehomegithub-link]: https://github.com/thehomerepot/nxwitness +[ubuntu-link]: https://ubuntu.com/ +[ubuntudocker-link]: https://hub.docker.com/_/ubuntu +[ubuntulsiodocker-link]: https://hub.docker.com/r/lsiobase/ubuntu +[wisenetwavedownload-link]: https://wavevms.com/download/linux +[wisenetwavereleasenotes-link]: https://wavevms.com/release-notes/ +[wisenetwavereleases-link]: https://updates.vmsproxy.com/hanwha/releases.json diff --git a/Samples/Downloads/DWSpectrum.1.json b/Samples/Downloads/DWSpectrum.1.json index 2fbeb151..5daac522 100644 --- a/Samples/Downloads/DWSpectrum.1.json +++ b/Samples/Downloads/DWSpectrum.1.json @@ -1,350 +1,350 @@ -{ - "version": "5.0.0.35271", - "releaseNotes": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", - "product": "dwspectrum", - "productDescription": "Video Management System", - "date": "26 Jul 2022", - "buildNumber": "35271", - "password": "lqdo89", - "type": "release", - "installers": [ - { - "platform": "linux_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-linux_x64.deb", - "path": "linux/dwspectrum-client-5.0.0.35271-linux_x64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-linux_x64.deb", - "path": "linux/dwspectrum-server-5.0.0.35271-linux_x64.deb", - "niceName": "Ubuntu x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-bundle-5.0.0.35271-windows_x64.exe", - "path": "windows/dwspectrum-bundle-5.0.0.35271-windows_x64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "windows_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-windows_x64.exe", - "path": "windows/dwspectrum-client-5.0.0.35271-windows_x64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-5.0.0.35271-Net2v5.exe", - "path": "windows/dwspectrum-paxton_plugin-5.0.0.35271-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-5.0.0.35271-Net2v6.exe", - "path": "windows/dwspectrum-paxton_plugin-5.0.0.35271-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "windows_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-windows_x64.exe", - "path": "windows/dwspectrum-server-5.0.0.35271-windows_x64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-testcamera-5.0.0.35271-windows_x64.zip", - "path": "windows/dwspectrum-testcamera-5.0.0.35271-windows_x64.zip", - "niceName": "Windows x64 - Test camera" - }, - { - "platform": "macos_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-macos_x64.dmg", - "path": "macos/dwspectrum-client-5.0.0.35271-macos_x64.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "macos_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-macos_arm64.dmg", - "path": "macos/dwspectrum-client-5.0.0.35271-macos_arm64.dmg", - "niceName": "Mac OS ARM64 - Client installer" - }, - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-edge1.zip", - "path": "arm/dwspectrum-server-5.0.0.35271-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-linux_arm64.deb", - "path": "arm/dwspectrum-server-5.0.0.35271-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-linux_arm64.deb", - "path": "arm/dwspectrum-client-5.0.0.35271-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-linux_arm32.deb", - "path": "arm/dwspectrum-server-5.0.0.35271-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-metadata_sdk-5.0.0.35271-universal.zip", - "path": "sdk/dwspectrum-metadata_sdk-5.0.0.35271-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-storage_sdk-5.0.0.35271-universal.zip", - "path": "sdk/dwspectrum-storage_sdk-5.0.0.35271-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - }, - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-video_source_sdk-5.0.0.35271-universal.zip", - "path": "sdk/dwspectrum-video_source_sdk-5.0.0.35271-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - } - ], - "platforms": [ - { - "name": "linux", - "files": [ - { - "platform": "linux_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-linux_x64.deb", - "path": "linux/dwspectrum-client-5.0.0.35271-linux_x64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-linux_x64.deb", - "path": "linux/dwspectrum-server-5.0.0.35271-linux_x64.deb", - "niceName": "Ubuntu x64 - Server installer" - } - ] - }, - { - "name": "windows", - "files": [ - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-5.0.0.35271-Net2v5.exe", - "path": "windows/dwspectrum-paxton_plugin-5.0.0.35271-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-5.0.0.35271-Net2v6.exe", - "path": "windows/dwspectrum-paxton_plugin-5.0.0.35271-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "windows_x64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-bundle-5.0.0.35271-windows_x64.exe", - "path": "windows/dwspectrum-bundle-5.0.0.35271-windows_x64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "windows_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-windows_x64.exe", - "path": "windows/dwspectrum-client-5.0.0.35271-windows_x64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "windows_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-windows_x64.exe", - "path": "windows/dwspectrum-server-5.0.0.35271-windows_x64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-testcamera-5.0.0.35271-windows_x64.zip", - "path": "windows/dwspectrum-testcamera-5.0.0.35271-windows_x64.zip", - "niceName": "Windows x64 - Test camera" - } - ] - }, - { - "name": "macos", - "files": [ - { - "platform": "macos_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-macos_x64.dmg", - "path": "macos/dwspectrum-client-5.0.0.35271-macos_x64.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "macos_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-macos_arm64.dmg", - "path": "macos/dwspectrum-client-5.0.0.35271-macos_arm64.dmg", - "niceName": "Mac OS ARM64 - Client installer" - } - ] - }, - { - "name": "arm", - "files": [ - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-edge1.zip", - "path": "arm/dwspectrum-server-5.0.0.35271-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-5.0.0.35271-linux_arm64.deb", - "path": "arm/dwspectrum-client-5.0.0.35271-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-linux_arm64.deb", - "path": "arm/dwspectrum-server-5.0.0.35271-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-5.0.0.35271-linux_arm32.deb", - "path": "arm/dwspectrum-server-5.0.0.35271-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - } - ] - }, - { - "name": "sdk", - "files": [ - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-video_source_sdk-5.0.0.35271-universal.zip", - "path": "sdk/dwspectrum-video_source_sdk-5.0.0.35271-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-metadata_sdk-5.0.0.35271-universal.zip", - "path": "sdk/dwspectrum-metadata_sdk-5.0.0.35271-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-storage_sdk-5.0.0.35271-universal.zip", - "path": "sdk/dwspectrum-storage_sdk-5.0.0.35271-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - } - ] - } - ], - "backwardsCompatible": true, - "cloudGroup": "prod", - "beta": false, - "dismissed": false, - "releaseUrl": "https://updates.networkoptix.com/digitalwatchdog/35271/" +{ + "version": "5.0.0.35271", + "releaseNotes": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", + "product": "dwspectrum", + "productDescription": "Video Management System", + "date": "26 Jul 2022", + "buildNumber": "35271", + "password": "lqdo89", + "type": "release", + "installers": [ + { + "platform": "linux_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-linux_x64.deb", + "path": "linux/dwspectrum-client-5.0.0.35271-linux_x64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-linux_x64.deb", + "path": "linux/dwspectrum-server-5.0.0.35271-linux_x64.deb", + "niceName": "Ubuntu x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-bundle-5.0.0.35271-windows_x64.exe", + "path": "windows/dwspectrum-bundle-5.0.0.35271-windows_x64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "windows_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-windows_x64.exe", + "path": "windows/dwspectrum-client-5.0.0.35271-windows_x64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-5.0.0.35271-Net2v5.exe", + "path": "windows/dwspectrum-paxton_plugin-5.0.0.35271-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-5.0.0.35271-Net2v6.exe", + "path": "windows/dwspectrum-paxton_plugin-5.0.0.35271-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "windows_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-windows_x64.exe", + "path": "windows/dwspectrum-server-5.0.0.35271-windows_x64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-testcamera-5.0.0.35271-windows_x64.zip", + "path": "windows/dwspectrum-testcamera-5.0.0.35271-windows_x64.zip", + "niceName": "Windows x64 - Test camera" + }, + { + "platform": "macos_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-macos_x64.dmg", + "path": "macos/dwspectrum-client-5.0.0.35271-macos_x64.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "macos_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-macos_arm64.dmg", + "path": "macos/dwspectrum-client-5.0.0.35271-macos_arm64.dmg", + "niceName": "Mac OS ARM64 - Client installer" + }, + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-edge1.zip", + "path": "arm/dwspectrum-server-5.0.0.35271-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-linux_arm64.deb", + "path": "arm/dwspectrum-server-5.0.0.35271-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-linux_arm64.deb", + "path": "arm/dwspectrum-client-5.0.0.35271-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-linux_arm32.deb", + "path": "arm/dwspectrum-server-5.0.0.35271-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-metadata_sdk-5.0.0.35271-universal.zip", + "path": "sdk/dwspectrum-metadata_sdk-5.0.0.35271-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-storage_sdk-5.0.0.35271-universal.zip", + "path": "sdk/dwspectrum-storage_sdk-5.0.0.35271-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + }, + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-video_source_sdk-5.0.0.35271-universal.zip", + "path": "sdk/dwspectrum-video_source_sdk-5.0.0.35271-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + } + ], + "platforms": [ + { + "name": "linux", + "files": [ + { + "platform": "linux_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-linux_x64.deb", + "path": "linux/dwspectrum-client-5.0.0.35271-linux_x64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-linux_x64.deb", + "path": "linux/dwspectrum-server-5.0.0.35271-linux_x64.deb", + "niceName": "Ubuntu x64 - Server installer" + } + ] + }, + { + "name": "windows", + "files": [ + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-5.0.0.35271-Net2v5.exe", + "path": "windows/dwspectrum-paxton_plugin-5.0.0.35271-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-5.0.0.35271-Net2v6.exe", + "path": "windows/dwspectrum-paxton_plugin-5.0.0.35271-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "windows_x64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-bundle-5.0.0.35271-windows_x64.exe", + "path": "windows/dwspectrum-bundle-5.0.0.35271-windows_x64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "windows_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-windows_x64.exe", + "path": "windows/dwspectrum-client-5.0.0.35271-windows_x64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "windows_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-windows_x64.exe", + "path": "windows/dwspectrum-server-5.0.0.35271-windows_x64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-testcamera-5.0.0.35271-windows_x64.zip", + "path": "windows/dwspectrum-testcamera-5.0.0.35271-windows_x64.zip", + "niceName": "Windows x64 - Test camera" + } + ] + }, + { + "name": "macos", + "files": [ + { + "platform": "macos_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-macos_x64.dmg", + "path": "macos/dwspectrum-client-5.0.0.35271-macos_x64.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "macos_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-macos_arm64.dmg", + "path": "macos/dwspectrum-client-5.0.0.35271-macos_arm64.dmg", + "niceName": "Mac OS ARM64 - Client installer" + } + ] + }, + { + "name": "arm", + "files": [ + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-edge1.zip", + "path": "arm/dwspectrum-server-5.0.0.35271-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-5.0.0.35271-linux_arm64.deb", + "path": "arm/dwspectrum-client-5.0.0.35271-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-linux_arm64.deb", + "path": "arm/dwspectrum-server-5.0.0.35271-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-5.0.0.35271-linux_arm32.deb", + "path": "arm/dwspectrum-server-5.0.0.35271-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + } + ] + }, + { + "name": "sdk", + "files": [ + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-video_source_sdk-5.0.0.35271-universal.zip", + "path": "sdk/dwspectrum-video_source_sdk-5.0.0.35271-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-metadata_sdk-5.0.0.35271-universal.zip", + "path": "sdk/dwspectrum-metadata_sdk-5.0.0.35271-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-storage_sdk-5.0.0.35271-universal.zip", + "path": "sdk/dwspectrum-storage_sdk-5.0.0.35271-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + } + ] + } + ], + "backwardsCompatible": true, + "cloudGroup": "prod", + "beta": false, + "dismissed": false, + "releaseUrl": "https://updates.networkoptix.com/digitalwatchdog/35271/" } \ No newline at end of file diff --git a/Samples/Downloads/DWSpectrum.2.json b/Samples/Downloads/DWSpectrum.2.json index 47e0e1e0..597cb744 100644 --- a/Samples/Downloads/DWSpectrum.2.json +++ b/Samples/Downloads/DWSpectrum.2.json @@ -1,332 +1,332 @@ -{ - "version": "4.2.0.32842", - "releaseNotes": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", - "product": "dwspectrum", - "productDescription": "Video Management System", - "date": "27 Apr 2021", - "buildNumber": "32842", - "password": "hkh1td", - "type": "release", - "installers": [ - { - "platform": "linux64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Server installer" - }, - { - "platform": "win64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "win64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "win64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "win64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", - "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", - "niceName": "Windows x64 - Test camera" - }, - { - "platform": "mac", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", - "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", - "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - }, - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - } - ], - "platforms": [ - { - "name": "linux", - "files": [ - { - "platform": "linux64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Server installer" - } - ] - }, - { - "name": "windows", - "files": [ - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "win64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "win64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "win64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "win64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", - "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", - "niceName": "Windows x64 - Test camera" - } - ] - }, - { - "name": "macos", - "files": [ - { - "platform": "mac", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", - "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", - "niceName": "Mac OS - Client installer" - } - ] - }, - { - "name": "arm", - "files": [ - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", - "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - } - ] - }, - { - "name": "sdk", - "files": [ - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - } - ] - } - ], - "backwardsCompatible": true, - "cloudGroup": "prod", - "beta": false, - "dismissed": false, - "releaseUrl": "https://updates.networkoptix.com/digitalwatchdog/32842/" +{ + "version": "4.2.0.32842", + "releaseNotes": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", + "product": "dwspectrum", + "productDescription": "Video Management System", + "date": "27 Apr 2021", + "buildNumber": "32842", + "password": "hkh1td", + "type": "release", + "installers": [ + { + "platform": "linux64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Server installer" + }, + { + "platform": "win64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "win64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "win64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "win64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", + "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", + "niceName": "Windows x64 - Test camera" + }, + { + "platform": "mac", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", + "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", + "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + }, + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + } + ], + "platforms": [ + { + "name": "linux", + "files": [ + { + "platform": "linux64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Server installer" + } + ] + }, + { + "name": "windows", + "files": [ + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "win64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "win64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "win64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "win64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", + "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", + "niceName": "Windows x64 - Test camera" + } + ] + }, + { + "name": "macos", + "files": [ + { + "platform": "mac", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", + "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", + "niceName": "Mac OS - Client installer" + } + ] + }, + { + "name": "arm", + "files": [ + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", + "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + } + ] + }, + { + "name": "sdk", + "files": [ + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + } + ] + } + ], + "backwardsCompatible": true, + "cloudGroup": "prod", + "beta": false, + "dismissed": false, + "releaseUrl": "https://updates.networkoptix.com/digitalwatchdog/32842/" } \ No newline at end of file diff --git a/Samples/Downloads/DWSpectrum.3.json b/Samples/Downloads/DWSpectrum.3.json index 47e0e1e0..597cb744 100644 --- a/Samples/Downloads/DWSpectrum.3.json +++ b/Samples/Downloads/DWSpectrum.3.json @@ -1,332 +1,332 @@ -{ - "version": "4.2.0.32842", - "releaseNotes": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", - "product": "dwspectrum", - "productDescription": "Video Management System", - "date": "27 Apr 2021", - "buildNumber": "32842", - "password": "hkh1td", - "type": "release", - "installers": [ - { - "platform": "linux64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Server installer" - }, - { - "platform": "win64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "win64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "win64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "win64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", - "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", - "niceName": "Windows x64 - Test camera" - }, - { - "platform": "mac", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", - "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", - "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - }, - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - } - ], - "platforms": [ - { - "name": "linux", - "files": [ - { - "platform": "linux64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Server installer" - } - ] - }, - { - "name": "windows", - "files": [ - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "win64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "win64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "win64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "win64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", - "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", - "niceName": "Windows x64 - Test camera" - } - ] - }, - { - "name": "macos", - "files": [ - { - "platform": "mac", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", - "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", - "niceName": "Mac OS - Client installer" - } - ] - }, - { - "name": "arm", - "files": [ - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", - "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - } - ] - }, - { - "name": "sdk", - "files": [ - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - } - ] - } - ], - "backwardsCompatible": true, - "cloudGroup": "prod", - "beta": false, - "dismissed": false, - "releaseUrl": "https://updates.networkoptix.com/digitalwatchdog/32842/" +{ + "version": "4.2.0.32842", + "releaseNotes": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", + "product": "dwspectrum", + "productDescription": "Video Management System", + "date": "27 Apr 2021", + "buildNumber": "32842", + "password": "hkh1td", + "type": "release", + "installers": [ + { + "platform": "linux64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Server installer" + }, + { + "platform": "win64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "win64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "win64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "win64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", + "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", + "niceName": "Windows x64 - Test camera" + }, + { + "platform": "mac", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", + "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", + "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + }, + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + } + ], + "platforms": [ + { + "name": "linux", + "files": [ + { + "platform": "linux64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Server installer" + } + ] + }, + { + "name": "windows", + "files": [ + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "win64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "win64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "win64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "win64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", + "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", + "niceName": "Windows x64 - Test camera" + } + ] + }, + { + "name": "macos", + "files": [ + { + "platform": "mac", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", + "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", + "niceName": "Mac OS - Client installer" + } + ] + }, + { + "name": "arm", + "files": [ + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", + "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + } + ] + }, + { + "name": "sdk", + "files": [ + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + } + ] + } + ], + "backwardsCompatible": true, + "cloudGroup": "prod", + "beta": false, + "dismissed": false, + "releaseUrl": "https://updates.networkoptix.com/digitalwatchdog/32842/" } \ No newline at end of file diff --git a/Samples/Downloads/DWspectrum.json b/Samples/Downloads/DWspectrum.json index 9b61b3bb..061c9e3f 100644 --- a/Samples/Downloads/DWspectrum.json +++ b/Samples/Downloads/DWspectrum.json @@ -1,291 +1,291 @@ -{ - "version": "4.2.0.32842", - "releaseNotes": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", - "product": "dwspectrum", - "productDescription": "Video Management System", - "date": "27 Apr 2021", - "buildNumber": "32842", - "password": "hkh1td", - "type": "release", - "installers": [ - { - "platform": "linux64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Server installer" - }, - { - "platform": "win64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "win64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "win64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "win64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", - "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", - "niceName": "Windows x64 - Test camera" - }, - { - "platform": "mac", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", - "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", - "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - }, - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - } - ], - "platforms": [ - { - "name": "linux", - "files": [ - { - "platform": "linux64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", - "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", - "niceName": "Ubuntu x64 - Server installer" - } - ] - }, - { - "name": "windows", - "files": [ - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "win64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "win64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "win64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", - "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "win64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", - "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", - "niceName": "Windows x64 - Test camera" - } - ] - }, - { - "name": "macos", - "files": [ - { - "platform": "mac", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", - "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", - "niceName": "Mac OS - Client installer" - } - ] - }, - { - "name": "sdk", - "files": [ - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - } - ] - } - ], - "backwardsCompatible": true, - "cloudGroup": "prod", - "beta": false, - "dismissed": false, - "releaseUrl": "https://updates.networkoptix.com/digitalwatchdog/32842/" +{ + "version": "4.2.0.32842", + "releaseNotes": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", + "product": "dwspectrum", + "productDescription": "Video Management System", + "date": "27 Apr 2021", + "buildNumber": "32842", + "password": "hkh1td", + "type": "release", + "installers": [ + { + "platform": "linux64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Server installer" + }, + { + "platform": "win64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "win64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "win64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "win64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", + "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", + "niceName": "Windows x64 - Test camera" + }, + { + "platform": "mac", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", + "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-edge1.zip", + "path": "arm/dwspectrum-server-4.2.0.32842-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-client-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "path": "arm/dwspectrum-server-4.2.0.32842-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + }, + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + } + ], + "platforms": [ + { + "name": "linux", + "files": [ + { + "platform": "linux64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-client-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-linux64.deb", + "path": "linux/dwspectrum-server-4.2.0.32842-linux64.deb", + "niceName": "Ubuntu x64 - Server installer" + } + ] + }, + { + "name": "windows", + "files": [ + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "path": "windows/dwspectrum-paxton_plugin-4.2.0.32842-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "win64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-bundle-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-bundle-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "win64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-client-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "win64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-server-4.2.0.32842-win64.exe", + "path": "windows/dwspectrum-server-4.2.0.32842-win64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "win64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-testcamera-4.2.0.32842-win64.zip", + "path": "windows/dwspectrum-testcamera-4.2.0.32842-win64.zip", + "niceName": "Windows x64 - Test camera" + } + ] + }, + { + "name": "macos", + "files": [ + { + "platform": "mac", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-client-4.2.0.32842-mac.dmg", + "path": "macos/dwspectrum-client-4.2.0.32842-mac.dmg", + "niceName": "Mac OS - Client installer" + } + ] + }, + { + "name": "sdk", + "files": [ + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-video_source_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-metadata_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "path": "sdk/dwspectrum-storage_sdk-4.2.0.32842-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + } + ] + } + ], + "backwardsCompatible": true, + "cloudGroup": "prod", + "beta": false, + "dismissed": false, + "releaseUrl": "https://updates.networkoptix.com/digitalwatchdog/32842/" } \ No newline at end of file diff --git a/Samples/Downloads/NxMeta.json b/Samples/Downloads/NxMeta.json index a5d142df..62f208bd 100644 --- a/Samples/Downloads/NxMeta.json +++ b/Samples/Downloads/NxMeta.json @@ -1,332 +1,332 @@ -{ - "version": "5.0.0.35269 R11", - "releaseNotes": "https://meta.nxvms.com/", - "product": "metavms", - "productDescription": "Video Management System", - "date": "26 Jul 2022", - "buildNumber": "35269", - "password": "pvj899", - "type": "release", - "installers": [ - { - "platform": "linux_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-linux_x64.deb", - "path": "linux/metavms-client-5.0.0.35269-linux_x64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-server-5.0.0.35269-linux_x64.deb", - "path": "linux/metavms-server-5.0.0.35269-linux_x64.deb", - "niceName": "Ubuntu x64 - Server installer" - }, - { - "platform": "linux_x64", - "appType": "vms_benchmark", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-vms_benchmark-5.0.0.35269-linux_x64.zip", - "path": "linux/metavms-vms_benchmark-5.0.0.35269-linux_x64.zip", - "niceName": "Ubuntu x64 - VMS Benchmark Tool" - }, - { - "platform": "windows_x64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-bundle-5.0.0.35269-windows_x64.exe", - "path": "windows/metavms-bundle-5.0.0.35269-windows_x64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "windows_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-windows_x64.exe", - "path": "windows/metavms-client-5.0.0.35269-windows_x64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "windows_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-server-5.0.0.35269-windows_x64.exe", - "path": "windows/metavms-server-5.0.0.35269-windows_x64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-testcamera-5.0.0.35269-windows_x64.zip", - "path": "windows/metavms-testcamera-5.0.0.35269-windows_x64.zip", - "niceName": "Windows x64 - Test camera" - }, - { - "platform": "windows_x64", - "appType": "vms_benchmark", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-vms_benchmark-5.0.0.35269-windows_x64.zip", - "path": "windows/metavms-vms_benchmark-5.0.0.35269-windows_x64.zip", - "niceName": "Windows x64 - VMS Benchmark Tool" - }, - { - "platform": "macos_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-macos_x64.dmg", - "path": "macos/metavms-client-5.0.0.35269-macos_x64.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "macos_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-macos_arm64.dmg", - "path": "macos/metavms-client-5.0.0.35269-macos_arm64.dmg", - "niceName": "Mac OS ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-server-5.0.0.35269-linux_arm64.deb", - "path": "arm/metavms-server-5.0.0.35269-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-linux_arm64.deb", - "path": "arm/metavms-client-5.0.0.35269-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-server-5.0.0.35269-linux_arm32.deb", - "path": "arm/metavms-server-5.0.0.35269-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-metadata_sdk-5.0.0.35269-universal.zip", - "path": "sdk/metavms-metadata_sdk-5.0.0.35269-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-storage_sdk-5.0.0.35269-universal.zip", - "path": "sdk/metavms-storage_sdk-5.0.0.35269-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - }, - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-video_source_sdk-5.0.0.35269-universal.zip", - "path": "sdk/metavms-video_source_sdk-5.0.0.35269-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - } - ], - "platforms": [ - { - "name": "linux", - "files": [ - { - "platform": "linux_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-linux_x64.deb", - "path": "linux/metavms-client-5.0.0.35269-linux_x64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-server-5.0.0.35269-linux_x64.deb", - "path": "linux/metavms-server-5.0.0.35269-linux_x64.deb", - "niceName": "Ubuntu x64 - Server installer" - }, - { - "platform": "linux_x64", - "appType": "vms_benchmark", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-vms_benchmark-5.0.0.35269-linux_x64.zip", - "path": "linux/metavms-vms_benchmark-5.0.0.35269-linux_x64.zip", - "niceName": "Ubuntu x64 - VMS Benchmark Tool" - } - ] - }, - { - "name": "windows", - "files": [ - { - "platform": "windows_x64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-bundle-5.0.0.35269-windows_x64.exe", - "path": "windows/metavms-bundle-5.0.0.35269-windows_x64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "windows_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-windows_x64.exe", - "path": "windows/metavms-client-5.0.0.35269-windows_x64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "windows_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-server-5.0.0.35269-windows_x64.exe", - "path": "windows/metavms-server-5.0.0.35269-windows_x64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-testcamera-5.0.0.35269-windows_x64.zip", - "path": "windows/metavms-testcamera-5.0.0.35269-windows_x64.zip", - "niceName": "Windows x64 - Test camera" - }, - { - "platform": "windows_x64", - "appType": "vms_benchmark", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-vms_benchmark-5.0.0.35269-windows_x64.zip", - "path": "windows/metavms-vms_benchmark-5.0.0.35269-windows_x64.zip", - "niceName": "Windows x64 - VMS Benchmark Tool" - } - ] - }, - { - "name": "macos", - "files": [ - { - "platform": "macos_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-macos_x64.dmg", - "path": "macos/metavms-client-5.0.0.35269-macos_x64.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "macos_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-macos_arm64.dmg", - "path": "macos/metavms-client-5.0.0.35269-macos_arm64.dmg", - "niceName": "Mac OS ARM64 - Client installer" - } - ] - }, - { - "name": "arm", - "files": [ - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-client-5.0.0.35269-linux_arm64.deb", - "path": "arm/metavms-client-5.0.0.35269-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-server-5.0.0.35269-linux_arm64.deb", - "path": "arm/metavms-server-5.0.0.35269-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-server-5.0.0.35269-linux_arm32.deb", - "path": "arm/metavms-server-5.0.0.35269-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - } - ] - }, - { - "name": "sdk", - "files": [ - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-video_source_sdk-5.0.0.35269-universal.zip", - "path": "sdk/metavms-video_source_sdk-5.0.0.35269-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-metadata_sdk-5.0.0.35269-universal.zip", - "path": "sdk/metavms-metadata_sdk-5.0.0.35269-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "metavms-storage_sdk-5.0.0.35269-universal.zip", - "path": "sdk/metavms-storage_sdk-5.0.0.35269-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - } - ] - } - ], - "backwardsCompatible": true, - "cloudGroup": "prod", - "beta": false, - "dismissed": false, - "releaseUrl": "https://updates.networkoptix.com/metavms/35269/" +{ + "version": "5.0.0.35269 R11", + "releaseNotes": "https://meta.nxvms.com/", + "product": "metavms", + "productDescription": "Video Management System", + "date": "26 Jul 2022", + "buildNumber": "35269", + "password": "pvj899", + "type": "release", + "installers": [ + { + "platform": "linux_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-linux_x64.deb", + "path": "linux/metavms-client-5.0.0.35269-linux_x64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-server-5.0.0.35269-linux_x64.deb", + "path": "linux/metavms-server-5.0.0.35269-linux_x64.deb", + "niceName": "Ubuntu x64 - Server installer" + }, + { + "platform": "linux_x64", + "appType": "vms_benchmark", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-vms_benchmark-5.0.0.35269-linux_x64.zip", + "path": "linux/metavms-vms_benchmark-5.0.0.35269-linux_x64.zip", + "niceName": "Ubuntu x64 - VMS Benchmark Tool" + }, + { + "platform": "windows_x64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-bundle-5.0.0.35269-windows_x64.exe", + "path": "windows/metavms-bundle-5.0.0.35269-windows_x64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "windows_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-windows_x64.exe", + "path": "windows/metavms-client-5.0.0.35269-windows_x64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "windows_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-server-5.0.0.35269-windows_x64.exe", + "path": "windows/metavms-server-5.0.0.35269-windows_x64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-testcamera-5.0.0.35269-windows_x64.zip", + "path": "windows/metavms-testcamera-5.0.0.35269-windows_x64.zip", + "niceName": "Windows x64 - Test camera" + }, + { + "platform": "windows_x64", + "appType": "vms_benchmark", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-vms_benchmark-5.0.0.35269-windows_x64.zip", + "path": "windows/metavms-vms_benchmark-5.0.0.35269-windows_x64.zip", + "niceName": "Windows x64 - VMS Benchmark Tool" + }, + { + "platform": "macos_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-macos_x64.dmg", + "path": "macos/metavms-client-5.0.0.35269-macos_x64.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "macos_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-macos_arm64.dmg", + "path": "macos/metavms-client-5.0.0.35269-macos_arm64.dmg", + "niceName": "Mac OS ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-server-5.0.0.35269-linux_arm64.deb", + "path": "arm/metavms-server-5.0.0.35269-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-linux_arm64.deb", + "path": "arm/metavms-client-5.0.0.35269-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-server-5.0.0.35269-linux_arm32.deb", + "path": "arm/metavms-server-5.0.0.35269-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-metadata_sdk-5.0.0.35269-universal.zip", + "path": "sdk/metavms-metadata_sdk-5.0.0.35269-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-storage_sdk-5.0.0.35269-universal.zip", + "path": "sdk/metavms-storage_sdk-5.0.0.35269-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + }, + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-video_source_sdk-5.0.0.35269-universal.zip", + "path": "sdk/metavms-video_source_sdk-5.0.0.35269-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + } + ], + "platforms": [ + { + "name": "linux", + "files": [ + { + "platform": "linux_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-linux_x64.deb", + "path": "linux/metavms-client-5.0.0.35269-linux_x64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-server-5.0.0.35269-linux_x64.deb", + "path": "linux/metavms-server-5.0.0.35269-linux_x64.deb", + "niceName": "Ubuntu x64 - Server installer" + }, + { + "platform": "linux_x64", + "appType": "vms_benchmark", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-vms_benchmark-5.0.0.35269-linux_x64.zip", + "path": "linux/metavms-vms_benchmark-5.0.0.35269-linux_x64.zip", + "niceName": "Ubuntu x64 - VMS Benchmark Tool" + } + ] + }, + { + "name": "windows", + "files": [ + { + "platform": "windows_x64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-bundle-5.0.0.35269-windows_x64.exe", + "path": "windows/metavms-bundle-5.0.0.35269-windows_x64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "windows_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-windows_x64.exe", + "path": "windows/metavms-client-5.0.0.35269-windows_x64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "windows_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-server-5.0.0.35269-windows_x64.exe", + "path": "windows/metavms-server-5.0.0.35269-windows_x64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-testcamera-5.0.0.35269-windows_x64.zip", + "path": "windows/metavms-testcamera-5.0.0.35269-windows_x64.zip", + "niceName": "Windows x64 - Test camera" + }, + { + "platform": "windows_x64", + "appType": "vms_benchmark", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-vms_benchmark-5.0.0.35269-windows_x64.zip", + "path": "windows/metavms-vms_benchmark-5.0.0.35269-windows_x64.zip", + "niceName": "Windows x64 - VMS Benchmark Tool" + } + ] + }, + { + "name": "macos", + "files": [ + { + "platform": "macos_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-macos_x64.dmg", + "path": "macos/metavms-client-5.0.0.35269-macos_x64.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "macos_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-macos_arm64.dmg", + "path": "macos/metavms-client-5.0.0.35269-macos_arm64.dmg", + "niceName": "Mac OS ARM64 - Client installer" + } + ] + }, + { + "name": "arm", + "files": [ + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-client-5.0.0.35269-linux_arm64.deb", + "path": "arm/metavms-client-5.0.0.35269-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-server-5.0.0.35269-linux_arm64.deb", + "path": "arm/metavms-server-5.0.0.35269-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-server-5.0.0.35269-linux_arm32.deb", + "path": "arm/metavms-server-5.0.0.35269-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + } + ] + }, + { + "name": "sdk", + "files": [ + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-video_source_sdk-5.0.0.35269-universal.zip", + "path": "sdk/metavms-video_source_sdk-5.0.0.35269-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-metadata_sdk-5.0.0.35269-universal.zip", + "path": "sdk/metavms-metadata_sdk-5.0.0.35269-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "metavms-storage_sdk-5.0.0.35269-universal.zip", + "path": "sdk/metavms-storage_sdk-5.0.0.35269-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + } + ] + } + ], + "backwardsCompatible": true, + "cloudGroup": "prod", + "beta": false, + "dismissed": false, + "releaseUrl": "https://updates.networkoptix.com/metavms/35269/" } \ No newline at end of file diff --git a/Samples/Downloads/NxWitness.json b/Samples/Downloads/NxWitness.json index 41246ff7..4c85d2b8 100644 --- a/Samples/Downloads/NxWitness.json +++ b/Samples/Downloads/NxWitness.json @@ -1,350 +1,350 @@ -{ - "version": "5.0.0.35270", - "releaseNotes": "https://www.networkoptix.com/all-nx-witness-release-notes", - "product": "nxwitness", - "productDescription": "Video Management System", - "date": "26 Jul 2022", - "buildNumber": "35270", - "password": "xk3y79", - "type": "release", - "installers": [ - { - "platform": "linux_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-linux_x64.deb", - "path": "linux/nxwitness-client-5.0.0.35270-linux_x64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-linux_x64.deb", - "path": "linux/nxwitness-server-5.0.0.35270-linux_x64.deb", - "niceName": "Ubuntu x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-bundle-5.0.0.35270-windows_x64.exe", - "path": "windows/nxwitness-bundle-5.0.0.35270-windows_x64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "windows_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-windows_x64.exe", - "path": "windows/nxwitness-client-5.0.0.35270-windows_x64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.35270-Net2v5.exe", - "path": "windows/nxwitness-paxton_plugin-5.0.0.35270-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.35270-Net2v6.exe", - "path": "windows/nxwitness-paxton_plugin-5.0.0.35270-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "windows_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-windows_x64.exe", - "path": "windows/nxwitness-server-5.0.0.35270-windows_x64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-testcamera-5.0.0.35270-windows_x64.zip", - "path": "windows/nxwitness-testcamera-5.0.0.35270-windows_x64.zip", - "niceName": "Windows x64 - Test camera" - }, - { - "platform": "macos_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-macos_x64.dmg", - "path": "macos/nxwitness-client-5.0.0.35270-macos_x64.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "macos_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-macos_arm64.dmg", - "path": "macos/nxwitness-client-5.0.0.35270-macos_arm64.dmg", - "niceName": "Mac OS ARM64 - Client installer" - }, - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-edge1.zip", - "path": "arm/nxwitness-server-5.0.0.35270-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-linux_arm64.deb", - "path": "arm/nxwitness-server-5.0.0.35270-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-linux_arm64.deb", - "path": "arm/nxwitness-client-5.0.0.35270-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-linux_arm32.deb", - "path": "arm/nxwitness-server-5.0.0.35270-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-metadata_sdk-5.0.0.35270-universal.zip", - "path": "sdk/nxwitness-metadata_sdk-5.0.0.35270-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-storage_sdk-5.0.0.35270-universal.zip", - "path": "sdk/nxwitness-storage_sdk-5.0.0.35270-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - }, - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-video_source_sdk-5.0.0.35270-universal.zip", - "path": "sdk/nxwitness-video_source_sdk-5.0.0.35270-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - } - ], - "platforms": [ - { - "name": "linux", - "files": [ - { - "platform": "linux_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-linux_x64.deb", - "path": "linux/nxwitness-client-5.0.0.35270-linux_x64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-linux_x64.deb", - "path": "linux/nxwitness-server-5.0.0.35270-linux_x64.deb", - "niceName": "Ubuntu x64 - Server installer" - } - ] - }, - { - "name": "windows", - "files": [ - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.35270-Net2v5.exe", - "path": "windows/nxwitness-paxton_plugin-5.0.0.35270-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.35270-Net2v6.exe", - "path": "windows/nxwitness-paxton_plugin-5.0.0.35270-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "windows_x64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-bundle-5.0.0.35270-windows_x64.exe", - "path": "windows/nxwitness-bundle-5.0.0.35270-windows_x64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "windows_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-windows_x64.exe", - "path": "windows/nxwitness-client-5.0.0.35270-windows_x64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "windows_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-windows_x64.exe", - "path": "windows/nxwitness-server-5.0.0.35270-windows_x64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-testcamera-5.0.0.35270-windows_x64.zip", - "path": "windows/nxwitness-testcamera-5.0.0.35270-windows_x64.zip", - "niceName": "Windows x64 - Test camera" - } - ] - }, - { - "name": "macos", - "files": [ - { - "platform": "macos_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-macos_x64.dmg", - "path": "macos/nxwitness-client-5.0.0.35270-macos_x64.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "macos_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-macos_arm64.dmg", - "path": "macos/nxwitness-client-5.0.0.35270-macos_arm64.dmg", - "niceName": "Mac OS ARM64 - Client installer" - } - ] - }, - { - "name": "arm", - "files": [ - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-edge1.zip", - "path": "arm/nxwitness-server-5.0.0.35270-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.35270-linux_arm64.deb", - "path": "arm/nxwitness-client-5.0.0.35270-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-linux_arm64.deb", - "path": "arm/nxwitness-server-5.0.0.35270-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.35270-linux_arm32.deb", - "path": "arm/nxwitness-server-5.0.0.35270-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - } - ] - }, - { - "name": "sdk", - "files": [ - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-video_source_sdk-5.0.0.35270-universal.zip", - "path": "sdk/nxwitness-video_source_sdk-5.0.0.35270-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-metadata_sdk-5.0.0.35270-universal.zip", - "path": "sdk/nxwitness-metadata_sdk-5.0.0.35270-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-storage_sdk-5.0.0.35270-universal.zip", - "path": "sdk/nxwitness-storage_sdk-5.0.0.35270-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - } - ] - } - ], - "backwardsCompatible": true, - "cloudGroup": "prod", - "beta": false, - "dismissed": false, - "releaseUrl": "https://updates.networkoptix.com/default/35270/" +{ + "version": "5.0.0.35270", + "releaseNotes": "https://www.networkoptix.com/all-nx-witness-release-notes", + "product": "nxwitness", + "productDescription": "Video Management System", + "date": "26 Jul 2022", + "buildNumber": "35270", + "password": "xk3y79", + "type": "release", + "installers": [ + { + "platform": "linux_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-linux_x64.deb", + "path": "linux/nxwitness-client-5.0.0.35270-linux_x64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-linux_x64.deb", + "path": "linux/nxwitness-server-5.0.0.35270-linux_x64.deb", + "niceName": "Ubuntu x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-bundle-5.0.0.35270-windows_x64.exe", + "path": "windows/nxwitness-bundle-5.0.0.35270-windows_x64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "windows_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-windows_x64.exe", + "path": "windows/nxwitness-client-5.0.0.35270-windows_x64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.35270-Net2v5.exe", + "path": "windows/nxwitness-paxton_plugin-5.0.0.35270-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.35270-Net2v6.exe", + "path": "windows/nxwitness-paxton_plugin-5.0.0.35270-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "windows_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-windows_x64.exe", + "path": "windows/nxwitness-server-5.0.0.35270-windows_x64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-testcamera-5.0.0.35270-windows_x64.zip", + "path": "windows/nxwitness-testcamera-5.0.0.35270-windows_x64.zip", + "niceName": "Windows x64 - Test camera" + }, + { + "platform": "macos_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-macos_x64.dmg", + "path": "macos/nxwitness-client-5.0.0.35270-macos_x64.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "macos_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-macos_arm64.dmg", + "path": "macos/nxwitness-client-5.0.0.35270-macos_arm64.dmg", + "niceName": "Mac OS ARM64 - Client installer" + }, + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-edge1.zip", + "path": "arm/nxwitness-server-5.0.0.35270-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-linux_arm64.deb", + "path": "arm/nxwitness-server-5.0.0.35270-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-linux_arm64.deb", + "path": "arm/nxwitness-client-5.0.0.35270-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-linux_arm32.deb", + "path": "arm/nxwitness-server-5.0.0.35270-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-metadata_sdk-5.0.0.35270-universal.zip", + "path": "sdk/nxwitness-metadata_sdk-5.0.0.35270-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-storage_sdk-5.0.0.35270-universal.zip", + "path": "sdk/nxwitness-storage_sdk-5.0.0.35270-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + }, + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-video_source_sdk-5.0.0.35270-universal.zip", + "path": "sdk/nxwitness-video_source_sdk-5.0.0.35270-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + } + ], + "platforms": [ + { + "name": "linux", + "files": [ + { + "platform": "linux_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-linux_x64.deb", + "path": "linux/nxwitness-client-5.0.0.35270-linux_x64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-linux_x64.deb", + "path": "linux/nxwitness-server-5.0.0.35270-linux_x64.deb", + "niceName": "Ubuntu x64 - Server installer" + } + ] + }, + { + "name": "windows", + "files": [ + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.35270-Net2v5.exe", + "path": "windows/nxwitness-paxton_plugin-5.0.0.35270-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.35270-Net2v6.exe", + "path": "windows/nxwitness-paxton_plugin-5.0.0.35270-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "windows_x64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-bundle-5.0.0.35270-windows_x64.exe", + "path": "windows/nxwitness-bundle-5.0.0.35270-windows_x64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "windows_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-windows_x64.exe", + "path": "windows/nxwitness-client-5.0.0.35270-windows_x64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "windows_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-windows_x64.exe", + "path": "windows/nxwitness-server-5.0.0.35270-windows_x64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-testcamera-5.0.0.35270-windows_x64.zip", + "path": "windows/nxwitness-testcamera-5.0.0.35270-windows_x64.zip", + "niceName": "Windows x64 - Test camera" + } + ] + }, + { + "name": "macos", + "files": [ + { + "platform": "macos_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-macos_x64.dmg", + "path": "macos/nxwitness-client-5.0.0.35270-macos_x64.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "macos_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-macos_arm64.dmg", + "path": "macos/nxwitness-client-5.0.0.35270-macos_arm64.dmg", + "niceName": "Mac OS ARM64 - Client installer" + } + ] + }, + { + "name": "arm", + "files": [ + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-edge1.zip", + "path": "arm/nxwitness-server-5.0.0.35270-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.35270-linux_arm64.deb", + "path": "arm/nxwitness-client-5.0.0.35270-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-linux_arm64.deb", + "path": "arm/nxwitness-server-5.0.0.35270-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.35270-linux_arm32.deb", + "path": "arm/nxwitness-server-5.0.0.35270-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + } + ] + }, + { + "name": "sdk", + "files": [ + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-video_source_sdk-5.0.0.35270-universal.zip", + "path": "sdk/nxwitness-video_source_sdk-5.0.0.35270-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-metadata_sdk-5.0.0.35270-universal.zip", + "path": "sdk/nxwitness-metadata_sdk-5.0.0.35270-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-storage_sdk-5.0.0.35270-universal.zip", + "path": "sdk/nxwitness-storage_sdk-5.0.0.35270-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + } + ] + } + ], + "backwardsCompatible": true, + "cloudGroup": "prod", + "beta": false, + "dismissed": false, + "releaseUrl": "https://updates.networkoptix.com/default/35270/" } \ No newline at end of file diff --git a/Samples/Downloads/Nxwitness.1.json b/Samples/Downloads/Nxwitness.1.json index 6f3ccabf..907239d6 100644 --- a/Samples/Downloads/Nxwitness.1.json +++ b/Samples/Downloads/Nxwitness.1.json @@ -1,927 +1,927 @@ -{ - "version": "5.0.0.36634", - "meta_version": "P1", - "releaseNotes": "https://www.networkoptix.com/all-nx-witness-release-notes", - "product": "nxwitness", - "productDescription": "Video Management System", - "date": "04 Apr 2023", - "buildNumber": "36634", - "password": "thn04r", - "type": "release", - "backwardCompatible": true, - "cloudGroup": "prod", - "beta": false, - "dismissed": false, - "installers": [ - { - "platform": "edge1", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-edge1.txt", - "path": "arm/nxwitness-conan_refs-5.0.0.36634-edge1.txt", - "niceName": "Edge - conan_refs" - }, - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-edge1.zip", - "path": "arm/nxwitness-server-5.0.0.36634-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "edge1", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-edge1.zip", - "path": "nxwitness-server_update-5.0.0.36634-edge1.zip", - "niceName": "Edge - Update package - server" - }, - { - "platform": "linux_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-linux_x64.deb", - "path": "linux/nxwitness-client-5.0.0.36634-linux_x64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux_x64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-linux_x64.zip", - "path": "nxwitness-client_update-5.0.0.36634-linux_x64.zip", - "niceName": "Ubuntu x64 - Update package - client" - }, - { - "platform": "linux_x64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_x64.txt", - "path": "linux/nxwitness-conan_refs-5.0.0.36634-linux_x64.txt", - "niceName": "Ubuntu x64 - conan_refs" - }, - { - "platform": "linux_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-linux_x64.deb", - "path": "linux/nxwitness-server-5.0.0.36634-linux_x64.deb", - "niceName": "Ubuntu x64 - Server installer" - }, - { - "platform": "linux_x64", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-linux_x64.zip", - "path": "nxwitness-server_update-5.0.0.36634-linux_x64.zip", - "niceName": "Ubuntu x64 - Update package - server" - }, - { - "platform": "linux_arm32", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_arm32.txt", - "path": "arm/nxwitness-conan_refs-5.0.0.36634-linux_arm32.txt", - "niceName": "Raspberry Pi / ARM32 - conan_refs" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-linux_arm32.deb", - "path": "arm/nxwitness-server-5.0.0.36634-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - }, - { - "platform": "bananapi", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-bananapi.zip", - "path": "nxwitness-server_update-5.0.0.36634-bananapi.zip", - "niceName": "Banana Pi - Update package - server" - }, - { - "platform": "linux_arm32", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-linux_arm32.zip", - "path": "nxwitness-server_update-5.0.0.36634-linux_arm32.zip", - "niceName": "Raspberry Pi / ARM32 - Update package - server" - }, - { - "platform": "rpi", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-rpi.zip", - "path": "nxwitness-server_update-5.0.0.36634-rpi.zip", - "niceName": "Raspberry Pi - Update package - server" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-linux_arm64.deb", - "path": "arm/nxwitness-client-5.0.0.36634-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-linux_arm64.zip", - "path": "nxwitness-client_update-5.0.0.36634-linux_arm64.zip", - "niceName": "NVIDIA Jetson / ARM64 - Update package - client" - }, - { - "platform": "linux_arm64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_arm64.txt", - "path": "arm/nxwitness-conan_refs-5.0.0.36634-linux_arm64.txt", - "niceName": "NVIDIA Jetson / ARM64 - conan_refs" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-linux_arm64.deb", - "path": "arm/nxwitness-server-5.0.0.36634-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-linux_arm64.zip", - "path": "nxwitness-server_update-5.0.0.36634-linux_arm64.zip", - "niceName": "NVIDIA Jetson / ARM64 - Update package - server" - }, - { - "platform": "macos_x64", - "appType": "client_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_debug-5.0.0.36634-macos_x64.zip", - "path": "macos/nxwitness-client_debug-5.0.0.36634-macos_x64.zip", - "niceName": "Mac OS - ~ PDB - client debug files" - }, - { - "platform": "macos_x64", - "appType": "libs_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-libs_debug-5.0.0.36634-macos_x64.zip", - "path": "macos/nxwitness-libs_debug-5.0.0.36634-macos_x64.zip", - "niceName": "Mac OS - ~ PDB - libraries debug files" - }, - { - "platform": "macos_x64", - "appType": "misc_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-misc_debug-5.0.0.36634-macos_x64.zip", - "path": "macos/nxwitness-misc_debug-5.0.0.36634-macos_x64.zip", - "niceName": "Mac OS - ~ PDB - Applauncher, traytool & other debug files" - }, - { - "platform": "macos_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-macos_x64.dmg", - "path": "macos/nxwitness-client-5.0.0.36634-macos_x64.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "macos", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-macos.zip", - "path": "nxwitness-client_update-5.0.0.36634-macos.zip", - "niceName": "Mac OS - Update package - client" - }, - { - "platform": "macos_x64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-macos_x64.zip", - "path": "nxwitness-client_update-5.0.0.36634-macos_x64.zip", - "niceName": "Mac OS - Update package - client" - }, - { - "platform": "macos_x64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-macos_x64.txt", - "path": "macos/nxwitness-conan_refs-5.0.0.36634-macos_x64.txt", - "niceName": "Mac OS - conan_refs" - }, - { - "platform": "macos_arm64", - "appType": "client_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_debug-5.0.0.36634-macos_arm64.zip", - "path": "macos/nxwitness-client_debug-5.0.0.36634-macos_arm64.zip", - "niceName": "Mac OS ARM64 - ~ PDB - client debug files" - }, - { - "platform": "macos_arm64", - "appType": "libs_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-libs_debug-5.0.0.36634-macos_arm64.zip", - "path": "macos/nxwitness-libs_debug-5.0.0.36634-macos_arm64.zip", - "niceName": "Mac OS ARM64 - ~ PDB - libraries debug files" - }, - { - "platform": "macos_arm64", - "appType": "misc_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-misc_debug-5.0.0.36634-macos_arm64.zip", - "path": "macos/nxwitness-misc_debug-5.0.0.36634-macos_arm64.zip", - "niceName": "Mac OS ARM64 - ~ PDB - Applauncher, traytool & other debug files" - }, - { - "platform": "macos_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-macos_arm64.dmg", - "path": "macos/nxwitness-client-5.0.0.36634-macos_arm64.dmg", - "niceName": "Mac OS ARM64 - Client installer" - }, - { - "platform": "macos_arm64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-macos_arm64.zip", - "path": "nxwitness-client_update-5.0.0.36634-macos_arm64.zip", - "niceName": "Mac OS ARM64 - Update package - client" - }, - { - "platform": "macos_arm64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-macos_arm64.txt", - "path": "macos/nxwitness-conan_refs-5.0.0.36634-macos_arm64.txt", - "niceName": "Mac OS ARM64 - conan_refs" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-metadata_sdk-5.0.0.36634-universal.zip", - "path": "sdk/nxwitness-metadata_sdk-5.0.0.36634-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-storage_sdk-5.0.0.36634-universal.zip", - "path": "sdk/nxwitness-storage_sdk-5.0.0.36634-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - }, - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-video_source_sdk-5.0.0.36634-universal.zip", - "path": "sdk/nxwitness-video_source_sdk-5.0.0.36634-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - }, - { - "platform": "windows_x64", - "appType": "client_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_debug-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-client_debug-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - ~ PDB - client debug files" - }, - { - "platform": "windows_x64", - "appType": "libs_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-libs_debug-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-libs_debug-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - ~ PDB - libraries debug files" - }, - { - "platform": "windows_x64", - "appType": "misc_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-misc_debug-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-misc_debug-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - ~ PDB - Applauncher, traytool & other debug files" - }, - { - "platform": "windows_x64", - "appType": "server_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_debug-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-server_debug-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - ~ PDB - server debug files" - }, - { - "platform": "windows_x64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-bundle-5.0.0.36634-windows_x64.exe", - "path": "windows/nxwitness-bundle-5.0.0.36634-windows_x64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "windows_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-windows_x64.exe", - "path": "windows/nxwitness-client-5.0.0.36634-windows_x64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "windows_x64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-windows_x64.zip", - "path": "nxwitness-client_update-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - Update package - client" - }, - { - "platform": "windows_x64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-windows_x64.txt", - "path": "windows/nxwitness-conan_refs-5.0.0.36634-windows_x64.txt", - "niceName": "Windows x64 - conan_refs" - }, - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v5.exe", - "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v5.msi", - "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v5.msi", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v6.exe", - "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v6.msi", - "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v6.msi", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "windows_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-windows_x64.exe", - "path": "windows/nxwitness-server-5.0.0.36634-windows_x64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-windows_x64.zip", - "path": "nxwitness-server_update-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - Update package - server" - }, - { - "platform": "windows_x64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-testcamera-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-testcamera-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - Test camera" - } - ], - "platforms": [ - { - "name": "sdk", - "files": [ - { - "platform": "universal", - "appType": "video_source_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-video_source_sdk-5.0.0.36634-universal.zip", - "path": "sdk/nxwitness-video_source_sdk-5.0.0.36634-universal.zip", - "niceName": "Cross-platform - SDK: Camera" - }, - { - "platform": "universal", - "appType": "metadata_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-metadata_sdk-5.0.0.36634-universal.zip", - "path": "sdk/nxwitness-metadata_sdk-5.0.0.36634-universal.zip", - "niceName": "Cross-platform - SDK: Metadata" - }, - { - "platform": "universal", - "appType": "storage_sdk", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-storage_sdk-5.0.0.36634-universal.zip", - "path": "sdk/nxwitness-storage_sdk-5.0.0.36634-universal.zip", - "niceName": "Cross-platform - SDK: Storage" - } - ] - }, - { - "name": "arm", - "files": [ - { - "platform": "bananapi", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-bananapi.zip", - "path": "nxwitness-server_update-5.0.0.36634-bananapi.zip", - "niceName": "Banana Pi - Update package - server" - }, - { - "platform": "edge1", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-edge1.zip", - "path": "arm/nxwitness-server-5.0.0.36634-edge1.zip", - "niceName": "Edge - Server installer" - }, - { - "platform": "edge1", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-edge1.zip", - "path": "nxwitness-server_update-5.0.0.36634-edge1.zip", - "niceName": "Edge - Update package - server" - }, - { - "platform": "edge1", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-edge1.txt", - "path": "arm/nxwitness-conan_refs-5.0.0.36634-edge1.txt", - "niceName": "Edge - conan_refs" - }, - { - "platform": "linux_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-linux_arm64.deb", - "path": "arm/nxwitness-client-5.0.0.36634-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Client installer" - }, - { - "platform": "linux_arm64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-linux_arm64.deb", - "path": "arm/nxwitness-server-5.0.0.36634-linux_arm64.deb", - "niceName": "NVIDIA Jetson / ARM64 - Server installer" - }, - { - "platform": "linux_arm64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-linux_arm64.zip", - "path": "nxwitness-client_update-5.0.0.36634-linux_arm64.zip", - "niceName": "NVIDIA Jetson / ARM64 - Update package - client" - }, - { - "platform": "linux_arm64", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-linux_arm64.zip", - "path": "nxwitness-server_update-5.0.0.36634-linux_arm64.zip", - "niceName": "NVIDIA Jetson / ARM64 - Update package - server" - }, - { - "platform": "linux_arm64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_arm64.txt", - "path": "arm/nxwitness-conan_refs-5.0.0.36634-linux_arm64.txt", - "niceName": "NVIDIA Jetson / ARM64 - conan_refs" - }, - { - "platform": "rpi", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-rpi.zip", - "path": "nxwitness-server_update-5.0.0.36634-rpi.zip", - "niceName": "Raspberry Pi - Update package - server" - }, - { - "platform": "linux_arm32", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-linux_arm32.deb", - "path": "arm/nxwitness-server-5.0.0.36634-linux_arm32.deb", - "niceName": "Raspberry Pi / ARM32 - Server installer" - }, - { - "platform": "linux_arm32", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-linux_arm32.zip", - "path": "nxwitness-server_update-5.0.0.36634-linux_arm32.zip", - "niceName": "Raspberry Pi / ARM32 - Update package - server" - }, - { - "platform": "linux_arm32", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_arm32.txt", - "path": "arm/nxwitness-conan_refs-5.0.0.36634-linux_arm32.txt", - "niceName": "Raspberry Pi / ARM32 - conan_refs" - } - ] - }, - { - "name": "linux", - "files": [ - { - "platform": "linux_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-linux_x64.deb", - "path": "linux/nxwitness-client-5.0.0.36634-linux_x64.deb", - "niceName": "Ubuntu x64 - Client installer" - }, - { - "platform": "linux_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-linux_x64.deb", - "path": "linux/nxwitness-server-5.0.0.36634-linux_x64.deb", - "niceName": "Ubuntu x64 - Server installer" - }, - { - "platform": "linux_x64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-linux_x64.zip", - "path": "nxwitness-client_update-5.0.0.36634-linux_x64.zip", - "niceName": "Ubuntu x64 - Update package - client" - }, - { - "platform": "linux_x64", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-linux_x64.zip", - "path": "nxwitness-server_update-5.0.0.36634-linux_x64.zip", - "niceName": "Ubuntu x64 - Update package - server" - }, - { - "platform": "linux_x64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_x64.txt", - "path": "linux/nxwitness-conan_refs-5.0.0.36634-linux_x64.txt", - "niceName": "Ubuntu x64 - conan_refs" - } - ] - }, - { - "name": "macos", - "files": [ - { - "platform": "macos_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-macos_x64.dmg", - "path": "macos/nxwitness-client-5.0.0.36634-macos_x64.dmg", - "niceName": "Mac OS - Client installer" - }, - { - "platform": "macos", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-macos.zip", - "path": "nxwitness-client_update-5.0.0.36634-macos.zip", - "niceName": "Mac OS - Update package - client" - }, - { - "platform": "macos_x64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-macos_x64.zip", - "path": "nxwitness-client_update-5.0.0.36634-macos_x64.zip", - "niceName": "Mac OS - Update package - client" - }, - { - "platform": "macos_x64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-macos_x64.txt", - "path": "macos/nxwitness-conan_refs-5.0.0.36634-macos_x64.txt", - "niceName": "Mac OS - conan_refs" - }, - { - "platform": "macos_x64", - "appType": "misc_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-misc_debug-5.0.0.36634-macos_x64.zip", - "path": "macos/nxwitness-misc_debug-5.0.0.36634-macos_x64.zip", - "niceName": "Mac OS - ~ PDB - Applauncher, traytool & other debug files" - }, - { - "platform": "macos_x64", - "appType": "client_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_debug-5.0.0.36634-macos_x64.zip", - "path": "macos/nxwitness-client_debug-5.0.0.36634-macos_x64.zip", - "niceName": "Mac OS - ~ PDB - client debug files" - }, - { - "platform": "macos_x64", - "appType": "libs_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-libs_debug-5.0.0.36634-macos_x64.zip", - "path": "macos/nxwitness-libs_debug-5.0.0.36634-macos_x64.zip", - "niceName": "Mac OS - ~ PDB - libraries debug files" - }, - { - "platform": "macos_arm64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-macos_arm64.dmg", - "path": "macos/nxwitness-client-5.0.0.36634-macos_arm64.dmg", - "niceName": "Mac OS ARM64 - Client installer" - }, - { - "platform": "macos_arm64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-macos_arm64.zip", - "path": "nxwitness-client_update-5.0.0.36634-macos_arm64.zip", - "niceName": "Mac OS ARM64 - Update package - client" - }, - { - "platform": "macos_arm64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-macos_arm64.txt", - "path": "macos/nxwitness-conan_refs-5.0.0.36634-macos_arm64.txt", - "niceName": "Mac OS ARM64 - conan_refs" - }, - { - "platform": "macos_arm64", - "appType": "misc_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-misc_debug-5.0.0.36634-macos_arm64.zip", - "path": "macos/nxwitness-misc_debug-5.0.0.36634-macos_arm64.zip", - "niceName": "Mac OS ARM64 - ~ PDB - Applauncher, traytool & other debug files" - }, - { - "platform": "macos_arm64", - "appType": "client_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_debug-5.0.0.36634-macos_arm64.zip", - "path": "macos/nxwitness-client_debug-5.0.0.36634-macos_arm64.zip", - "niceName": "Mac OS ARM64 - ~ PDB - client debug files" - }, - { - "platform": "macos_arm64", - "appType": "libs_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-libs_debug-5.0.0.36634-macos_arm64.zip", - "path": "macos/nxwitness-libs_debug-5.0.0.36634-macos_arm64.zip", - "niceName": "Mac OS ARM64 - ~ PDB - libraries debug files" - } - ] - }, - { - "name": "windows", - "files": [ - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v5.exe", - "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v5.exe", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v5", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v5.msi", - "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v5.msi", - "niceName": "Paxton Net2 Pro v5 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v6.exe", - "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v6.exe", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "Net2v6", - "appType": "paxton_plugin", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v6.msi", - "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v6.msi", - "niceName": "Paxton Net2 Pro v6 - plugin installer" - }, - { - "platform": "windows_x64", - "appType": "bundle", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-bundle-5.0.0.36634-windows_x64.exe", - "path": "windows/nxwitness-bundle-5.0.0.36634-windows_x64.exe", - "niceName": "Windows x64 - Client & Server" - }, - { - "platform": "windows_x64", - "appType": "client", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client-5.0.0.36634-windows_x64.exe", - "path": "windows/nxwitness-client-5.0.0.36634-windows_x64.exe", - "niceName": "Windows x64 - Client installer" - }, - { - "platform": "windows_x64", - "appType": "server", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server-5.0.0.36634-windows_x64.exe", - "path": "windows/nxwitness-server-5.0.0.36634-windows_x64.exe", - "niceName": "Windows x64 - Server installer" - }, - { - "platform": "windows_x64", - "appType": "testcamera", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-testcamera-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-testcamera-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - Test camera" - }, - { - "platform": "windows_x64", - "appType": "client_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_update-5.0.0.36634-windows_x64.zip", - "path": "nxwitness-client_update-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - Update package - client" - }, - { - "platform": "windows_x64", - "appType": "server_update", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_update-5.0.0.36634-windows_x64.zip", - "path": "nxwitness-server_update-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - Update package - server" - }, - { - "platform": "windows_x64", - "appType": "conan_refs", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-conan_refs-5.0.0.36634-windows_x64.txt", - "path": "windows/nxwitness-conan_refs-5.0.0.36634-windows_x64.txt", - "niceName": "Windows x64 - conan_refs" - }, - { - "platform": "windows_x64", - "appType": "misc_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-misc_debug-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-misc_debug-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - ~ PDB - Applauncher, traytool & other debug files" - }, - { - "platform": "windows_x64", - "appType": "client_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-client_debug-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-client_debug-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - ~ PDB - client debug files" - }, - { - "platform": "windows_x64", - "appType": "libs_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-libs_debug-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-libs_debug-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - ~ PDB - libraries debug files" - }, - { - "platform": "windows_x64", - "appType": "server_debug", - "beta": false, - "cloudGroup": "prod", - "fileName": "nxwitness-server_debug-5.0.0.36634-windows_x64.zip", - "path": "windows/nxwitness-server_debug-5.0.0.36634-windows_x64.zip", - "niceName": "Windows x64 - ~ PDB - server debug files" - } - ] - } - ], - "releaseUrl": "https://updates.networkoptix.com/default/36634/" +{ + "version": "5.0.0.36634", + "meta_version": "P1", + "releaseNotes": "https://www.networkoptix.com/all-nx-witness-release-notes", + "product": "nxwitness", + "productDescription": "Video Management System", + "date": "04 Apr 2023", + "buildNumber": "36634", + "password": "thn04r", + "type": "release", + "backwardCompatible": true, + "cloudGroup": "prod", + "beta": false, + "dismissed": false, + "installers": [ + { + "platform": "edge1", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-edge1.txt", + "path": "arm/nxwitness-conan_refs-5.0.0.36634-edge1.txt", + "niceName": "Edge - conan_refs" + }, + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-edge1.zip", + "path": "arm/nxwitness-server-5.0.0.36634-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "edge1", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-edge1.zip", + "path": "nxwitness-server_update-5.0.0.36634-edge1.zip", + "niceName": "Edge - Update package - server" + }, + { + "platform": "linux_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-linux_x64.deb", + "path": "linux/nxwitness-client-5.0.0.36634-linux_x64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux_x64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-linux_x64.zip", + "path": "nxwitness-client_update-5.0.0.36634-linux_x64.zip", + "niceName": "Ubuntu x64 - Update package - client" + }, + { + "platform": "linux_x64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_x64.txt", + "path": "linux/nxwitness-conan_refs-5.0.0.36634-linux_x64.txt", + "niceName": "Ubuntu x64 - conan_refs" + }, + { + "platform": "linux_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-linux_x64.deb", + "path": "linux/nxwitness-server-5.0.0.36634-linux_x64.deb", + "niceName": "Ubuntu x64 - Server installer" + }, + { + "platform": "linux_x64", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-linux_x64.zip", + "path": "nxwitness-server_update-5.0.0.36634-linux_x64.zip", + "niceName": "Ubuntu x64 - Update package - server" + }, + { + "platform": "linux_arm32", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_arm32.txt", + "path": "arm/nxwitness-conan_refs-5.0.0.36634-linux_arm32.txt", + "niceName": "Raspberry Pi / ARM32 - conan_refs" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-linux_arm32.deb", + "path": "arm/nxwitness-server-5.0.0.36634-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + }, + { + "platform": "bananapi", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-bananapi.zip", + "path": "nxwitness-server_update-5.0.0.36634-bananapi.zip", + "niceName": "Banana Pi - Update package - server" + }, + { + "platform": "linux_arm32", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-linux_arm32.zip", + "path": "nxwitness-server_update-5.0.0.36634-linux_arm32.zip", + "niceName": "Raspberry Pi / ARM32 - Update package - server" + }, + { + "platform": "rpi", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-rpi.zip", + "path": "nxwitness-server_update-5.0.0.36634-rpi.zip", + "niceName": "Raspberry Pi - Update package - server" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-linux_arm64.deb", + "path": "arm/nxwitness-client-5.0.0.36634-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-linux_arm64.zip", + "path": "nxwitness-client_update-5.0.0.36634-linux_arm64.zip", + "niceName": "NVIDIA Jetson / ARM64 - Update package - client" + }, + { + "platform": "linux_arm64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_arm64.txt", + "path": "arm/nxwitness-conan_refs-5.0.0.36634-linux_arm64.txt", + "niceName": "NVIDIA Jetson / ARM64 - conan_refs" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-linux_arm64.deb", + "path": "arm/nxwitness-server-5.0.0.36634-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-linux_arm64.zip", + "path": "nxwitness-server_update-5.0.0.36634-linux_arm64.zip", + "niceName": "NVIDIA Jetson / ARM64 - Update package - server" + }, + { + "platform": "macos_x64", + "appType": "client_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_debug-5.0.0.36634-macos_x64.zip", + "path": "macos/nxwitness-client_debug-5.0.0.36634-macos_x64.zip", + "niceName": "Mac OS - ~ PDB - client debug files" + }, + { + "platform": "macos_x64", + "appType": "libs_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-libs_debug-5.0.0.36634-macos_x64.zip", + "path": "macos/nxwitness-libs_debug-5.0.0.36634-macos_x64.zip", + "niceName": "Mac OS - ~ PDB - libraries debug files" + }, + { + "platform": "macos_x64", + "appType": "misc_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-misc_debug-5.0.0.36634-macos_x64.zip", + "path": "macos/nxwitness-misc_debug-5.0.0.36634-macos_x64.zip", + "niceName": "Mac OS - ~ PDB - Applauncher, traytool & other debug files" + }, + { + "platform": "macos_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-macos_x64.dmg", + "path": "macos/nxwitness-client-5.0.0.36634-macos_x64.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "macos", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-macos.zip", + "path": "nxwitness-client_update-5.0.0.36634-macos.zip", + "niceName": "Mac OS - Update package - client" + }, + { + "platform": "macos_x64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-macos_x64.zip", + "path": "nxwitness-client_update-5.0.0.36634-macos_x64.zip", + "niceName": "Mac OS - Update package - client" + }, + { + "platform": "macos_x64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-macos_x64.txt", + "path": "macos/nxwitness-conan_refs-5.0.0.36634-macos_x64.txt", + "niceName": "Mac OS - conan_refs" + }, + { + "platform": "macos_arm64", + "appType": "client_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_debug-5.0.0.36634-macos_arm64.zip", + "path": "macos/nxwitness-client_debug-5.0.0.36634-macos_arm64.zip", + "niceName": "Mac OS ARM64 - ~ PDB - client debug files" + }, + { + "platform": "macos_arm64", + "appType": "libs_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-libs_debug-5.0.0.36634-macos_arm64.zip", + "path": "macos/nxwitness-libs_debug-5.0.0.36634-macos_arm64.zip", + "niceName": "Mac OS ARM64 - ~ PDB - libraries debug files" + }, + { + "platform": "macos_arm64", + "appType": "misc_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-misc_debug-5.0.0.36634-macos_arm64.zip", + "path": "macos/nxwitness-misc_debug-5.0.0.36634-macos_arm64.zip", + "niceName": "Mac OS ARM64 - ~ PDB - Applauncher, traytool & other debug files" + }, + { + "platform": "macos_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-macos_arm64.dmg", + "path": "macos/nxwitness-client-5.0.0.36634-macos_arm64.dmg", + "niceName": "Mac OS ARM64 - Client installer" + }, + { + "platform": "macos_arm64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-macos_arm64.zip", + "path": "nxwitness-client_update-5.0.0.36634-macos_arm64.zip", + "niceName": "Mac OS ARM64 - Update package - client" + }, + { + "platform": "macos_arm64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-macos_arm64.txt", + "path": "macos/nxwitness-conan_refs-5.0.0.36634-macos_arm64.txt", + "niceName": "Mac OS ARM64 - conan_refs" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-metadata_sdk-5.0.0.36634-universal.zip", + "path": "sdk/nxwitness-metadata_sdk-5.0.0.36634-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-storage_sdk-5.0.0.36634-universal.zip", + "path": "sdk/nxwitness-storage_sdk-5.0.0.36634-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + }, + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-video_source_sdk-5.0.0.36634-universal.zip", + "path": "sdk/nxwitness-video_source_sdk-5.0.0.36634-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + }, + { + "platform": "windows_x64", + "appType": "client_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_debug-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-client_debug-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - ~ PDB - client debug files" + }, + { + "platform": "windows_x64", + "appType": "libs_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-libs_debug-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-libs_debug-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - ~ PDB - libraries debug files" + }, + { + "platform": "windows_x64", + "appType": "misc_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-misc_debug-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-misc_debug-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - ~ PDB - Applauncher, traytool & other debug files" + }, + { + "platform": "windows_x64", + "appType": "server_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_debug-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-server_debug-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - ~ PDB - server debug files" + }, + { + "platform": "windows_x64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-bundle-5.0.0.36634-windows_x64.exe", + "path": "windows/nxwitness-bundle-5.0.0.36634-windows_x64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "windows_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-windows_x64.exe", + "path": "windows/nxwitness-client-5.0.0.36634-windows_x64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "windows_x64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-windows_x64.zip", + "path": "nxwitness-client_update-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - Update package - client" + }, + { + "platform": "windows_x64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-windows_x64.txt", + "path": "windows/nxwitness-conan_refs-5.0.0.36634-windows_x64.txt", + "niceName": "Windows x64 - conan_refs" + }, + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v5.exe", + "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v5.msi", + "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v5.msi", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v6.exe", + "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v6.msi", + "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v6.msi", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "windows_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-windows_x64.exe", + "path": "windows/nxwitness-server-5.0.0.36634-windows_x64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-windows_x64.zip", + "path": "nxwitness-server_update-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - Update package - server" + }, + { + "platform": "windows_x64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-testcamera-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-testcamera-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - Test camera" + } + ], + "platforms": [ + { + "name": "sdk", + "files": [ + { + "platform": "universal", + "appType": "video_source_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-video_source_sdk-5.0.0.36634-universal.zip", + "path": "sdk/nxwitness-video_source_sdk-5.0.0.36634-universal.zip", + "niceName": "Cross-platform - SDK: Camera" + }, + { + "platform": "universal", + "appType": "metadata_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-metadata_sdk-5.0.0.36634-universal.zip", + "path": "sdk/nxwitness-metadata_sdk-5.0.0.36634-universal.zip", + "niceName": "Cross-platform - SDK: Metadata" + }, + { + "platform": "universal", + "appType": "storage_sdk", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-storage_sdk-5.0.0.36634-universal.zip", + "path": "sdk/nxwitness-storage_sdk-5.0.0.36634-universal.zip", + "niceName": "Cross-platform - SDK: Storage" + } + ] + }, + { + "name": "arm", + "files": [ + { + "platform": "bananapi", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-bananapi.zip", + "path": "nxwitness-server_update-5.0.0.36634-bananapi.zip", + "niceName": "Banana Pi - Update package - server" + }, + { + "platform": "edge1", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-edge1.zip", + "path": "arm/nxwitness-server-5.0.0.36634-edge1.zip", + "niceName": "Edge - Server installer" + }, + { + "platform": "edge1", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-edge1.zip", + "path": "nxwitness-server_update-5.0.0.36634-edge1.zip", + "niceName": "Edge - Update package - server" + }, + { + "platform": "edge1", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-edge1.txt", + "path": "arm/nxwitness-conan_refs-5.0.0.36634-edge1.txt", + "niceName": "Edge - conan_refs" + }, + { + "platform": "linux_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-linux_arm64.deb", + "path": "arm/nxwitness-client-5.0.0.36634-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Client installer" + }, + { + "platform": "linux_arm64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-linux_arm64.deb", + "path": "arm/nxwitness-server-5.0.0.36634-linux_arm64.deb", + "niceName": "NVIDIA Jetson / ARM64 - Server installer" + }, + { + "platform": "linux_arm64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-linux_arm64.zip", + "path": "nxwitness-client_update-5.0.0.36634-linux_arm64.zip", + "niceName": "NVIDIA Jetson / ARM64 - Update package - client" + }, + { + "platform": "linux_arm64", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-linux_arm64.zip", + "path": "nxwitness-server_update-5.0.0.36634-linux_arm64.zip", + "niceName": "NVIDIA Jetson / ARM64 - Update package - server" + }, + { + "platform": "linux_arm64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_arm64.txt", + "path": "arm/nxwitness-conan_refs-5.0.0.36634-linux_arm64.txt", + "niceName": "NVIDIA Jetson / ARM64 - conan_refs" + }, + { + "platform": "rpi", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-rpi.zip", + "path": "nxwitness-server_update-5.0.0.36634-rpi.zip", + "niceName": "Raspberry Pi - Update package - server" + }, + { + "platform": "linux_arm32", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-linux_arm32.deb", + "path": "arm/nxwitness-server-5.0.0.36634-linux_arm32.deb", + "niceName": "Raspberry Pi / ARM32 - Server installer" + }, + { + "platform": "linux_arm32", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-linux_arm32.zip", + "path": "nxwitness-server_update-5.0.0.36634-linux_arm32.zip", + "niceName": "Raspberry Pi / ARM32 - Update package - server" + }, + { + "platform": "linux_arm32", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_arm32.txt", + "path": "arm/nxwitness-conan_refs-5.0.0.36634-linux_arm32.txt", + "niceName": "Raspberry Pi / ARM32 - conan_refs" + } + ] + }, + { + "name": "linux", + "files": [ + { + "platform": "linux_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-linux_x64.deb", + "path": "linux/nxwitness-client-5.0.0.36634-linux_x64.deb", + "niceName": "Ubuntu x64 - Client installer" + }, + { + "platform": "linux_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-linux_x64.deb", + "path": "linux/nxwitness-server-5.0.0.36634-linux_x64.deb", + "niceName": "Ubuntu x64 - Server installer" + }, + { + "platform": "linux_x64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-linux_x64.zip", + "path": "nxwitness-client_update-5.0.0.36634-linux_x64.zip", + "niceName": "Ubuntu x64 - Update package - client" + }, + { + "platform": "linux_x64", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-linux_x64.zip", + "path": "nxwitness-server_update-5.0.0.36634-linux_x64.zip", + "niceName": "Ubuntu x64 - Update package - server" + }, + { + "platform": "linux_x64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-linux_x64.txt", + "path": "linux/nxwitness-conan_refs-5.0.0.36634-linux_x64.txt", + "niceName": "Ubuntu x64 - conan_refs" + } + ] + }, + { + "name": "macos", + "files": [ + { + "platform": "macos_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-macos_x64.dmg", + "path": "macos/nxwitness-client-5.0.0.36634-macos_x64.dmg", + "niceName": "Mac OS - Client installer" + }, + { + "platform": "macos", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-macos.zip", + "path": "nxwitness-client_update-5.0.0.36634-macos.zip", + "niceName": "Mac OS - Update package - client" + }, + { + "platform": "macos_x64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-macos_x64.zip", + "path": "nxwitness-client_update-5.0.0.36634-macos_x64.zip", + "niceName": "Mac OS - Update package - client" + }, + { + "platform": "macos_x64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-macos_x64.txt", + "path": "macos/nxwitness-conan_refs-5.0.0.36634-macos_x64.txt", + "niceName": "Mac OS - conan_refs" + }, + { + "platform": "macos_x64", + "appType": "misc_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-misc_debug-5.0.0.36634-macos_x64.zip", + "path": "macos/nxwitness-misc_debug-5.0.0.36634-macos_x64.zip", + "niceName": "Mac OS - ~ PDB - Applauncher, traytool & other debug files" + }, + { + "platform": "macos_x64", + "appType": "client_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_debug-5.0.0.36634-macos_x64.zip", + "path": "macos/nxwitness-client_debug-5.0.0.36634-macos_x64.zip", + "niceName": "Mac OS - ~ PDB - client debug files" + }, + { + "platform": "macos_x64", + "appType": "libs_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-libs_debug-5.0.0.36634-macos_x64.zip", + "path": "macos/nxwitness-libs_debug-5.0.0.36634-macos_x64.zip", + "niceName": "Mac OS - ~ PDB - libraries debug files" + }, + { + "platform": "macos_arm64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-macos_arm64.dmg", + "path": "macos/nxwitness-client-5.0.0.36634-macos_arm64.dmg", + "niceName": "Mac OS ARM64 - Client installer" + }, + { + "platform": "macos_arm64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-macos_arm64.zip", + "path": "nxwitness-client_update-5.0.0.36634-macos_arm64.zip", + "niceName": "Mac OS ARM64 - Update package - client" + }, + { + "platform": "macos_arm64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-macos_arm64.txt", + "path": "macos/nxwitness-conan_refs-5.0.0.36634-macos_arm64.txt", + "niceName": "Mac OS ARM64 - conan_refs" + }, + { + "platform": "macos_arm64", + "appType": "misc_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-misc_debug-5.0.0.36634-macos_arm64.zip", + "path": "macos/nxwitness-misc_debug-5.0.0.36634-macos_arm64.zip", + "niceName": "Mac OS ARM64 - ~ PDB - Applauncher, traytool & other debug files" + }, + { + "platform": "macos_arm64", + "appType": "client_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_debug-5.0.0.36634-macos_arm64.zip", + "path": "macos/nxwitness-client_debug-5.0.0.36634-macos_arm64.zip", + "niceName": "Mac OS ARM64 - ~ PDB - client debug files" + }, + { + "platform": "macos_arm64", + "appType": "libs_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-libs_debug-5.0.0.36634-macos_arm64.zip", + "path": "macos/nxwitness-libs_debug-5.0.0.36634-macos_arm64.zip", + "niceName": "Mac OS ARM64 - ~ PDB - libraries debug files" + } + ] + }, + { + "name": "windows", + "files": [ + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v5.exe", + "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v5.exe", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v5", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v5.msi", + "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v5.msi", + "niceName": "Paxton Net2 Pro v5 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v6.exe", + "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v6.exe", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "Net2v6", + "appType": "paxton_plugin", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-paxton_plugin-5.0.0.36634-Net2v6.msi", + "path": "windows/nxwitness-paxton_plugin-5.0.0.36634-Net2v6.msi", + "niceName": "Paxton Net2 Pro v6 - plugin installer" + }, + { + "platform": "windows_x64", + "appType": "bundle", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-bundle-5.0.0.36634-windows_x64.exe", + "path": "windows/nxwitness-bundle-5.0.0.36634-windows_x64.exe", + "niceName": "Windows x64 - Client & Server" + }, + { + "platform": "windows_x64", + "appType": "client", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client-5.0.0.36634-windows_x64.exe", + "path": "windows/nxwitness-client-5.0.0.36634-windows_x64.exe", + "niceName": "Windows x64 - Client installer" + }, + { + "platform": "windows_x64", + "appType": "server", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server-5.0.0.36634-windows_x64.exe", + "path": "windows/nxwitness-server-5.0.0.36634-windows_x64.exe", + "niceName": "Windows x64 - Server installer" + }, + { + "platform": "windows_x64", + "appType": "testcamera", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-testcamera-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-testcamera-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - Test camera" + }, + { + "platform": "windows_x64", + "appType": "client_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_update-5.0.0.36634-windows_x64.zip", + "path": "nxwitness-client_update-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - Update package - client" + }, + { + "platform": "windows_x64", + "appType": "server_update", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_update-5.0.0.36634-windows_x64.zip", + "path": "nxwitness-server_update-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - Update package - server" + }, + { + "platform": "windows_x64", + "appType": "conan_refs", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-conan_refs-5.0.0.36634-windows_x64.txt", + "path": "windows/nxwitness-conan_refs-5.0.0.36634-windows_x64.txt", + "niceName": "Windows x64 - conan_refs" + }, + { + "platform": "windows_x64", + "appType": "misc_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-misc_debug-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-misc_debug-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - ~ PDB - Applauncher, traytool & other debug files" + }, + { + "platform": "windows_x64", + "appType": "client_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-client_debug-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-client_debug-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - ~ PDB - client debug files" + }, + { + "platform": "windows_x64", + "appType": "libs_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-libs_debug-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-libs_debug-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - ~ PDB - libraries debug files" + }, + { + "platform": "windows_x64", + "appType": "server_debug", + "beta": false, + "cloudGroup": "prod", + "fileName": "nxwitness-server_debug-5.0.0.36634-windows_x64.zip", + "path": "windows/nxwitness-server_debug-5.0.0.36634-windows_x64.zip", + "niceName": "Windows x64 - ~ PDB - server debug files" + } + ] + } + ], + "releaseUrl": "https://updates.networkoptix.com/default/36634/" } \ No newline at end of file diff --git a/Samples/Packages/DWSpectrum.json b/Samples/Packages/DWSpectrum.json index 656c1620..1bec1555 100644 --- a/Samples/Packages/DWSpectrum.json +++ b/Samples/Packages/DWSpectrum.json @@ -1,155 +1,155 @@ -{ - "version": "5.0.0.35271", - "cloudHost": "dwspectrum.digital-watchdog.com", - "releaseNotesUrl": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", - "description": "Support for Ubuntu 16.04 will be discontinued in the next release version and Systems on Ubuntu 16.04 will be blocked from updating to the next release version.", - "eula": "\n\n\n \n DW Spectrum IPVMS EULA\n\n\n\t

DW SPECTRUM IPVMS END USER LICENSE AGREEMENT

\n

IMPORTANT – PLEASE READ CAREFULLY: This End User License Agreement (the “EULA”) is a legally enforceable contract between You (either an individual or an entity) and Digital Watchdog. (the “Company”) regarding the use of the Company’s software programs accompanying or provided under this Agreement together with the SDK made available by the Company, associated media, corresponding Documentation, technical configurations, and technical data (collectively, the “Software”). “Documentation” means the user’s guide and other documentation (including print and online), if any, provided to you with the Software.

\n

BY CLICKING “I ACCEPT” BUTTON BELOW OR BY DOWNLOADING, INSTALLING OR OTHERWISE USING THE SOFTWARE, YOU CONSENT TO BE LEGALLY BOUND BY ALL THE TERMS AND CONDITIONS OF THE AGREEMENT. IF YOU ARE ENTERING INTO THE AGREEMENT ON BEHALF OF A COMPANY OR OTHER LEGAL ENTITY, YOU REPRESENT THAT YOU HAVE THE AUTHORITY TO BIND SUCH ENTITY AND ITS AFFILIATES TO THE AGREEMENT, IN WHICH CASE THE TERMS \"YOU\" OR \"YOUR\" SHALL REFER TO SUCH ENTITY AND ITS AFFILIATES. IF YOU DO NOT HAVE SUCH AUTHORITY, OR IF YOU DO NOT AGREE WITH ANY OF THE TERMS OF THE AGREEMENT, YOU MUST NOT ACCEPT THE AGREEMENT AND MAY NOT USE THE SOFTWARE.

\n

In addition, certain third party programs or code (“Third Party Software”) are being provided to you under the terms and conditions of a separate end user license accompanying such Third Party Software (“Third Party Software Licenses”), which will be provided to you by the Company as part of the Documentation or may be made available for viewing by the Company at the website or webpage at https://dwspectrum.digital-watchdog.com/content/libraries(the “Third Party Site”). If required by any Third Party Software License, the Companywill make such Third Party Software, and the Company’s modifications to that Third Party Software, available to you on the Third Party Site. The Company may be required or wish to provide you with certain notices or information about Third Party Software or Third Party Software Licenses and you acknowledge that such notices and information may be provided to you by the posting of such notices and information on the Third Party Site. BY INSTALLING, COPYING OR OTHERWISE USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THE TERMS OF THIS AGREEMENT AS WELL AS THE TERMS OF THIRD PARTY SOFTWARE LICENSES. IF YOU DO NOT AGREE TO ALL SUCH TERMS, DO NOT INSTALL OR USE THE SOFTWARE.

\n
    \n
  1. License Grant.Subject to the terms and conditions of this Agreement, the Company grants you a non-exclusive, non-transferable, non-sublicensable, limited license to install and use the Software, in object code version only, for your own use subject to the restrictions contained in this Agreement.
  2. \n
  3. Backup Copies.You may make one backup copy of the Software, provided your backup copy is not installed or used on any computer and further provided that all such copies shall bear the original and unmodified copyright, patent and other intellectual property markings that appear on or in the Software. You may not transfer the rights to a backup copy unless you transfer all rights in the Software as provided in this Agreement.
  4. \n
  5. Ownership.You acknowledge that the Software and any copies that you are authorized by the Company to make are the intellectual property of, and are owned by, the Company and its licensors. The structure, sequence and organization of the Software are the valuable trade secrets and confidential information of the Company and its licensors. The Software is protected by copyright, including without limitation by United States copyright law, international treaty provisions and applicable laws in the country in which it is used. You acknowledge that the Company, or its licensors, retain the ownership of all patents, copyrights, trade secrets, trademarks and other intellectual property rights pertaining to the Software. You will take no actions which adversely affect the Company’s intellectual property rights in the Software. This Agreement is a license and not an agreement for sale. No title to, or ownership of, the Software, or any intellectual property rights subsisting therein, is transferred to you.
  6. \n
  7. Restrictions.The Software is licensed solely for your own use. The Company and its licensors reserve all rights not expressly granted to you in this Agreement. Without limiting the generality of the foregoing, you shall not nor shall you permit any other party to: (i) make copies of the Software except as expressly set forth in this Agreement, or (ii) disassemble, decompile or translate any part of the Software, or otherwise attempt to reconstruct or discover the source code of the Software, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation, or (iii) modify or create derivative works based upon the Software, or (iv) externally distribute, sublicense, resell, encumber or otherwise transfer the Software, or (v) rent, lease, lend, or use the Software, or any part thereof, for timesharing or bureau use, or (vi) allow a third party to copy, access, or use the Software (except as expressly provided in this Agreement), or (vii) alter or remove any copyright, trademark or other proprietary notice which may appear on the Software, (viii) take any action that would cause the Software to be placed in the public domain or become subject to open source license agreement, or (ix) use the Software in any manner that violates any statute, law, rule, regulation, directive, guideline, bylaw whether presently in force or may be implemented by federal, state or local authorities.
  8. \n
  9. Paid Features.The Software is made available to you at no charge or fees. However, the Company may offer certain premium features to you upon payment of a license fees determined by the Company in its sole discretion (“Paid Features”). In order to use Paid Features, you must agree to pay the license fees plus any applicable taxes through one of the payment methods accepted by the Company. Upon receipt of the license fees, the Company will either provide you a license key to activate the Paid Features or allow you to download the Software with the Paid Features. Activation of Paid Features may also be regulated through the use of transferrable license keys, and such license keys may only be transferred within your organization subject to the limitations listed in your purchase order or other documentation describing the Software and/or license rights obtained in the Software.You expressly acknowledge and agree that this Agreement will govern your use of the Paid Features.
  10. \n
  11. Updates.Except as otherwise expressly provided herein, this Agreement will govern any updates and upgrades to the Software that may be provided to you in accordance with the Company’s then-current maintenance and support policies, unless such updates and upgrades are provided under a separate license agreement. You may use such updates or upgrades only in conjunction with your then-existing the Software licensed under this Agreement. The Software and all updates and upgrades are licensed as a single product and such updates and upgrades may not be separated from the Software to exceed the scope of your original license. Any other software that may be provided with the Software that is associated with a separate end-user license agreement is licensed to you under the terms of that separate license agreement.
  12. \n
  13. No Support.Except as expressly required by applicable law, nothing in this Agreement entitles you to any support, maintenance or new versions of the Software. You may contact the Company to determine the availability of support, maintenance, and new versions, and the fees terms and conditions that would apply.
  14. \n
  15. Disclaimer of Warranties.THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OR REPRESENTATION OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COMPANY NOR ITS LICENSORS REPRESENT OR WARRANT THAT THE SOFTWARE WILL SATISFY YOUR REQUIREMENTS OR THAT IT IS WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE UNINTERRUPTED OR ERROR FREE. The Company does not warrant that functions contained in the Software will meet your requirements or that the operation of the Software will be error-free or uninterrupted. The Company is not responsible for problems caused by changes in, or modifications to, the operating characteristics of any computer hardware or operating system for which the Software or any upgrade or update is procured, nor is the Company responsible for problems which occur as a result of the use of the Software in conjunction with software of third parties or with hardware, which does not meet the Company’s specific requirements. Some jurisdictions do not allow certain disclaimers or limitations of warranties, so some of these may not apply to you.
  16. \n
  17. Limitation of Liability.TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW EVEN IF A REMEDY FAILS ITS ESSENTIAL PURPOSE, IN NO EVENT SHALL THE COMPANY OR ITS LICENSORS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE OR THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF THE COMPANY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, THE COMPANY’S ENTIRE LIABILITY TO YOU OR ANY OTHER PARTY FOR LOSS OR DAMAGE RESULTING FROM ANY CLAIMS, DEMANDS OR ACTIONS ARISING OUT OF THIS AGREEMENT SHALL NOT EXCEED THE FEE ACTUALLY PAID BY YOU TO THE COMPANY FOR THE SOFTWARE, IF ANY, NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. Because some states and jurisdictions do not allow the exclusion or limitation of liability, the above limitation may not apply to you. In such states and jurisdictions, the Company’s liability shall be limited to the greatest extent permitted by law.
  18. \n
  19. Sole Remedy and Allocation of Risk.YOUR SOLE AND EXCLUSIVE REMEDY AND THE COMPANY’S SOLE AND EXCLUSIVE LIABILITY ARE SET FORTH IN THIS AGREEMENT. This Agreement defines a mutually agreed-upon allocation of risk.
  20. \n
  21. Third Party Software Licenses. Third Party Software included with the Software is licensed under the terms of Third Party Software Licenses. You understand and agree that, although provided to you by the Company with the Software, your use of Third Party Software shall be and is governed by such Third Party Software Licenses. You understand and acknowledge that Third Party Software is being provided “AS IS” WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. If there is a conflict between this Agreement and any Third Party Software Licensewith respect to Third Party Software, the provisions of such Third Party Software Licenseshall prevail.
  22. \n
  23. Export Restrictions.You agree to comply with all export and re-export restrictions and regulations (“Export Restrictions”) imposed by the United States or any other country in which you conduct business. Without limiting the generality of the foregoing, and regardless of any disclosure made by you to the Company regarding an ultimate destination of the Software, you represent and warrant that you understand the U.S. law currently prohibits the export or re-export, directly or indirectly (including via remote access) of U.S. origin products and technology to certain proscribed countries (including Cuba, Iran, Sudan, North Korea and Syria and/or any other country that may become subject to an embargo by the United States), entities, organizations and individuals, without prior authorization from the U.S. Government. You will not commit any act or omission that will result in a breach of any such Export Restrictions. Your breach of this clause shall constitute cause for immediate termination of this Agreement.
  24. \n
  25. High Risk Activities.The Software is not designed, manufactured or intended for use or resale as on-line control equipment in hazardous environments requiring fail-safe performance, such as in the operation of nuclear facilities, aircraft navigation or communication systems, air traffic control, direct life support machines, or weapons systems, in which the failure of the Software could lead directly to death, personal injury, or severe physical or environmental damage (“High Risk Activities”). The Company specifically disclaims any express or implied warranty of fitness for High Risk Activities.
  26. \n
  27. Consent to Collect Data.You consent Company to collect and use technical data and information from the installed application anonymously. The collected information include but not limited to the version of the Software, Software configuration, devices added or connected to the Software, total session time, Internet/network usage, and hardware specification. The information is gathered to provision the Software usage, updates, devices and configuration so developers can enhance and improve the Software.
  28. \n
\n

The data collection can be disabled from the Software if you do not want the data to be sent.

\n
    \n
  1. Termination.The licenses granted herein shall automatically terminate without notice if you fail to comply with any material provision of this Agreement. In such event, you must immediately uninstall and return the Software, including all Documentation and original media, to the Company. This shall not limit or affect any remedy available to the Company for your breach of this Agreement.
  2. \n
  3. Miscellaneous.
  4. \n
\n

(a) If you are a unit or agency of the United States Government, the following applies: The Software is provided with RESTRICTED RIGHTS. Use, duplication or disclosure by the Government is subject to restrictions as set forth in Subparagraphs (a) through (d) of the Commercial Computer-Restricted Rights clause at FAR 52.227-19 when applicable, or in Subparagraph 252.227-7013 (c)(1)(ii) of the Rights in Technical Data and Computer Software at DFARS, and in similar clauses in the NASA FAR Supplement. Contractor/manufacturer is Digital Watchdog.

\n

(b) This Agreement shall be governed by the laws of the State of California, exclusive of its choice of law rules. Any dispute between you and the Company regarding this Agreement will be subject to the exclusive jurisdiction of the state and federal courts sitting in the County of Los Angeles in the State of California. In any such action, the prevailing party will be entitled to costs and attorneys’ fees.

\n

(c) You may not assign, sublicense, or transfer this Agreement, the Software, or any rights or obligations hereunder without prior written consent of the Company. Any such attempted assignment, sublicense, or transfer will be null and void. The Company may terminate this Agreement in the event of any such attempted assignment, sublicense, or transfer.

\n

(d) This Agreement is in the English language only, and will only be provided in the English language, which language will be controlling in all respects, and all versions hereof in any other language will not be binding on the parties hereto. All communications and notices to be made or given pursuant to this Agreement must be in the English language.

\n

(e) The Agreement constitutes the final and complete understanding between you and the Company with respect to the subject matter of this Agreement. Any modifications or waivers of this Agreement must be in writing and signed by both parties hereto.

\n

(f) If any provision in this Agreement is invalid or unenforceable or contrary to applicable law, such provision shall be construed, limited, or altered, as necessary, to eliminate the invalidity or unenforceability or the conflict with applicable law, and all other provisions of this Agreement shall remain in effect.

\n

(g) Failure or delay on the part of the Company to exercise any right, power, privilege, or remedy will not constitute a waiver of, or bar the later exercise of, that or any other right, power, privilege, or remedy of the Company.

\n

(h) No rule of strict construction shall apply against or in favor of either party in the construction and interpretation of this Agreement.

\n\n\n", - "eulaVersion": 1, - "packages": [ - { - "component": "server", - "platform": "linux_x64", - "file": "dwspectrum-server_update-5.0.0.35271-linux_x64.zip", - "size": 88614643, - "md5": "e027ae6b5f4420ac77dfd76f4e502f99", - "signature": "DF54oM/oYcBZ6h6gamUt/QfGoV+yxA/yfkrDZyqqhhXdg3uALI5kRYQy7A32EvmThB1DQ2pi/5Y7s4EHAg4kJWOgPZiwaO1cQ8++gai4YVes2duzLFqkUHCFAvoD61QQSps+Wbtq/agcH9nR0RrnhvYdvG0kRkcuOrESWN3XkuC0g+LxczUpuDXYtZ3SMjuoQ60UTkrQDcMsb/kcfFEzyIL+aRlV2u1tBbdYjADP1opQjPAtbVjrfzlTXTz75Hn1HldtXdO8vByIm9ljhKFETs0FFjXj6YUn0TdzLffw0Bs/IFyrZ7KcvQW7M62rKN8z+1u0KvwQKatrh8+ZFCEZAFx8MH+gUy5sTcyRrvInrmvDnL1r7WVmXv7p3gWFLSewtMDlcBOK7X+rW4+c5pk5ltQWM5G2V50lYsVOOPjjFvoDSpkdAOksXRWg06unS3tU2jDRlFEEE46HLLueuFH7a2fTG/IX8LcRE/Pcfn0kDHEzjB3lZnwDahGwH42CynzaZUXRW6KH73MwcwYDPO/fjBzB7YrXhBicMCHxASPjDMD3ml/7lbOdqGPeeknbNhnfVkgze+S0nr9OIaTQ2CcVfIHRmmjtbMG2cIo7X+3wgdUaSfSBOGoZA/8yIyAJehiz9XlpjOEpFD2P+ndGQsIsIIExXhNfOxUmdjsFLye31IA=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "16.04" - } - ] - }, - { - "component": "server", - "platform": "windows_x64", - "file": "dwspectrum-server_update-5.0.0.35271-windows_x64.zip", - "size": 97987920, - "md5": "c809fdd6cd162e368a8593b211c35e5f", - "signature": "AmCSSckelnzgMzganq9H/m8Ekwg+fmbVGByxGDXIvQljFWw2D/B17nnEVdfH8vE5NVPO4Co7qW3xdJMSGJrJCaHEf5ijs5SJe3LEGDAiNjZEsGH9MFMJNnOPyS3W9PysLDL/5SFSHYcXkgeug+erlnuC8MAkVtb/GDq9s/8nqIY/KWKQ3hNn67uq5lJe+8i4A9GK5jailev355JnGvGrs3+9Hu47+dSWMl2dYq2IYsNsO2bDbknaUpIctkTxhQ/RDBuyazOHT/67P5IwedTemzWvo5/oJqPaU7khx82qKqEgifmZXLD8eZX3aPwTq3NtXbdBmrqcHEmdpG0s+pVhWHYUUjlPpyQ0p+VqeJKqC1PvecGdZpMVkj7Ix8/FlJbu5TtO+0SY6bg/h1hgQHD2Vtf8PqapeyrKigprPONz6JVYIvmkOTLIrfaEOrFhsIF1cNzYXxuQI1T/U/q6Okisr4P88Wkcd/ZDAbj3Z95fyRxFEozsWOa2s6WA27+0NRKoNR08GzhR0LIc1HqKOL9pwgNAwcDnqPca0R35tqDDGOUj+Czt5mE6dgnTSG4XNPr0G1tsU9Xt5+4dhYRIfkqFYyML42kq9SVfZLCL3GbLbL1Lpo09RKbUwu0zJllCk8itRb6FASevcZPpDNOYhWo08ijABEiYIdhUcOdKhyt9IWw=", - "variants": [] - }, - { - "component": "server", - "platform": "edge1", - "file": "dwspectrum-server_update-5.0.0.35271-edge1.zip", - "size": 85437501, - "md5": "018f57ac6160f849c7eaf51ceea7d258", - "signature": "mkWJRgn+t1e+5811FaPMSSokpwRrtc4L0UcIBDvdpVBTEzhtBcl7FzJBJHP9ljhHW71eEnev4j269C/S4FNn59m01+WsGdiiqd29ReH7zw8mL+YUjjitlzpCNSnSh3fwcvAQ5piLyNcMgdEXFkoZE/A8T8BzKgw6dSfkLhM/5LFftdB8BS1QG4poA83fl1XJqCCDzXt0mgDcCQGwj6s9fEnCe56eB8dDFwoA8M4LpAdojdbKuvxXSi/VRa7CYwvyvwQQaeqSJ44PLD/NrNjL68vl8Mm2HW95goIovE1MpycUCoB7Hb9anfxnP5diWsA/537FhWUSABEjml4L2j6ncGZC3ZatXBpC4Jr2ewPuUBaBJQMFY64fYMPy5jcL2GdiX4Kjl8S0XDUDHmywxl7eFTjuB9LPOjLUWWHd9D3w8vi40OefwIbHfGpE2gNKCb6pkvmNgRvwn6a47hNJ1iTNGuOigq0b/pug/8WxZDSdHkMuq3M61l0MPeucydD3AGWbpMv+teceYTd3DH/gYksM8++jRccErGPH5LOpzlHwZnHorHfyp1/iVoKVL77I5aOzU2vgpiv+sJNHvEj2PIPg7pyUjAZbHvFMwusfm2w1fCGla9Df/lmiDAEogVG4qqBJuKn8lYe1/xZwYH/L4oQ3+zSHc/+JeXwmo2ynaekZzYk=", - "variants": [] - }, - { - "component": "server", - "platform": "linux_arm64", - "file": "dwspectrum-server_update-5.0.0.35271-linux_arm64.zip", - "size": 143427373, - "md5": "589df20cab8d54a1eb1c909cfd703f75", - "signature": "RuCn/6jOR5z5wf6JEspzPB1HyDMTYauUKxqBkIrxbS8YEO8MVpd2jrRd5hkd0u58fZ/9CZIXHFplc9887K3MPkjgn1YoDSO6lv5sieLxO8lM7LYtaFQs6H8/YIv7QZnooyOv1cgLLkRvb/LcLmEkbIBPHKLQ8ChDUJD6bmFyolxcGRjFfiAyFRzEsqBIwiK/gr6JmyC5DBqFbz5MyBafMt/BwWXrH4e0e9zgDSulBJC2FLoUKnewEtCN6TUDD248d2hGrUz4Q8JXIzcGjSxt0jwnc3c7TNpkVxfuy0e5Ob7KCTWnjX/LNzvD6bimMBWzxPQXIlvn1OmLsv4fMm5Qzz4ZX0FWTOxZsZO8B5VWfVBYJaGtwPhXOZhNt3IsS9ffTKG8K9C0taTQTi8JQVbZHgwjzRY87fuC9NxGYXYJ3HXriaQ/VB8s6mvy7GOee+cqux145B09pkndJIy46QF0VTypmDZC6G8fCCY8tT9tOtJcksgizh0QchwNNik+KInx1wgxds4vTN8ld9CmtWoJEfB0ydmBGHL0/vZksj+o9+ds8nD7gfTfdo+4RuMiDmzQX5sOehg8u1fAHuGVUOydqOGzgHa9WPKdtQ3CpzipKLnu4BRJRqpE5LJ0KXAM1yTxmskINIzODrlZPRkeCG63uarbJSlQlhEHajLGONyRJNk=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "16.04" - } - ] - }, - { - "component": "server", - "platform": "linux_arm32", - "file": "dwspectrum-server_update-5.0.0.35271-linux_arm32.zip", - "size": 169064419, - "md5": "39e11cb1c489b0993380dd8b2a2e99a4", - "signature": "Kd2gdCR7oFSNLfrqb8XZPf0NmuNZ6LpHznAtNoDL6DqpnWBXsZ2YdqtAvYmpWX0EoqJZ7TRoyjyEKiu06kalgOXE+xFyEhAYTaQp50MIpkq3kutZ8l5rnkum99WvCW0NnHPBIF1OlO7xBFEQMWWDSSmooVaR9EqxhajIsYBj+8mzBTvOnNCn5cDgv1/4V+GCSIjHAeDiLE3CB0hr8yprzKKhKfdZQMTYfHziVspjDLTgeSx07cehEhYqn9uJb35QZN4ily4e3htbXPPLbPNMP2/Sn97mJYFRm7tMs8xzUImUE5gGV5Fc6SFmiYK/otpYtxUyuexRTZU5w7dHQXuRksIE3b8BQQ8CRFhRd0zN534hm9whNRI0IYnBPOG2oaEA1mWsTEvAj+9Gzp8WsIKCHXRE1Ybicqa8D7tdO781sBgrW5ltiEZEmZWOo/p5Z/boC87UTib1s3aQG10lccW8srL4skirydboipFPE3QYAjcnOc3eocRN4A9LHtDbgCTmME2cRAAAvIYDCwiVSfnbuWWrkOSGDXv+A06OwCB1l+OS2qUecaHg+QoiFYGXffBStNCU0RyVg8QWbeOjxBD2xcpFquypyo6+jxXO/ZPZ5AUXepBB1iS5iIWpqpGc0RHtlDQSRdse1xFVshc6CCOvk1H0EO9RqDq1oxmv+PATo7g=", - "variants": [ - { - "name": "debian", - "minimumVersion": "9" - }, - { - "name": "ubuntu", - "minimumVersion": "16.04" - }, - { - "name": "raspbian", - "minimumVersion": "9" - } - ] - }, - { - "component": "client", - "platform": "linux_x64", - "file": "dwspectrum-client_update-5.0.0.35271-linux_x64.zip", - "size": 268837878, - "md5": "265a473af3a8bda35984e924d3783e05", - "signature": "MLGEe7AcSZbch2+Bvh2RUhdydCbuFXOnRjpm9rJWsp/eOYVq2ZXsyOwoEV6sZexCAxfQS3LZg8xPnGTklJE6V1FP2Qc7GnxW/jPAhRKoWeP931+m//ZRfvkg4adLTLId9fVgvtX95btqrqdqLUUBSoWHAZoO/XrliHh0JsnSACbF1umv1l6byhxJAEHVKWEFzc7aTX/ibsfnZljDI1qLONtRWIOE8Gg+gct65B0uq/FLr8FcLfocD1v4nkRQkAtBB4mk1TjOsIyKMweOhnuvwNE5XUnx6IjBUDuxMFYAkyRfsZ1IbydMUzwcZHUnCAwbo6XbloJsu1yH55jYwnJNlHGJnPC+xCyThJiWvXfypgp/1WqS35yMFH2e3TSZxKK7SUiF7f8Hxkjc7iYM/Ba71KvsoU07Q+nE7if1EEUOiHqeUCrwYZg0R/cSUiMfqfx0A3OuuVeQTQKTPZ35VI/Wgry7I0buV9kpugHNshQ2U2ZTZYG8ipMC0HdoXaS//5Tfh+iK0xqhdTjzWjZeNwDPs9F+ap4ICzkWK27qgtszNM0XgFczkGE6g+I/V5oJH3f32qn+fi2YGJOLGer2samoy8lnLzNamLszUSuUojLvlEIAdR5cjsErwln/srjIFQb1Wuy8+sMqaV2DYHSexuM7oQKK5LMaNdlACZ89O+XXZzs=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "16.04" - } - ] - }, - { - "component": "client", - "platform": "windows_x64", - "file": "dwspectrum-client_update-5.0.0.35271-windows_x64.zip", - "size": 153542461, - "md5": "2ce20c6bb040a39c2c76d63b5e072c08", - "signature": "GNdBfjjZKscX1+wya6AgEkCTfM9fg0H/T+MW6QtAINhGI+iaAyJfAKF9/6KZ2KiotkUud9mnmr3gi2Bkq/GdxpTbv6EOidkzgcvbSJTEATf6CDI7F1aJH19wQFzor05Y31y9TyMJWYHIO8Yue9DH5xoVUAZptbOxHyil3gXuQVk8LAZZWd6pNoA8ABQS5V8WBf2zzt4cSvxMlc1aKf2PPiimyk4Sklq/ZEt0N5TqWkxTnReA/32IoZGAbYFeaWAhpuZhP+BixYuDEdLoR/3WZ/SRPZei4hg7/g4gUPB3+isIpiE+3LWY3O/BkBQm1ZQuayCbeJwHiUGlUfQ+gQwVbPJAEUbJ6TaOYgUeIao+pBXPFa0LQtG/ISJcbsG0xKbGcOox1NRT81WGMynjrfxLGiNPGkU+YurDF3CN8Cctmd1wkqbY32g4XWw4BsaHwTCRKRdJdaoowbYBSsjiCYQ/X/FJChcVFcKcQNhLVc/aVSdy9PTp4kfWWpAdPzaXI7TgSSEWBGatKkE0av6xshWCMZT0lbk1aQr8iyq/LjamUJwPVDsxtTas4ppfJDV5VgqVfvEazf4TyMlbHyi4Db776caZWFSQto6szh2JTMIfq35oaJTL+nCXnD8AkTS3jot/qLAUFBY4hCV0FM2W5U7G4+kM4dotVxbTdaK4o1UhnwI=", - "variants": [] - }, - { - "component": "client", - "platform": "macos_x64", - "file": "dwspectrum-client_update-5.0.0.35271-macos_x64.zip", - "size": 158000043, - "md5": "4bb2e9498267f51fc7057c2695bc417d", - "signature": "TCxNIKAlgbVKXXUessdjx0manmIFgYuKPjbnLMhe05MQ1Gg7nhUtfthQipJ8R+ewiy487L1KxSfu5uUCBAu7zcMnckjtM2t/owYv7PLb00HnqLzXfY6ZhfGQ9gVHJbHhANZDtsRlzAdFX+263+l3O3Uh/5Ci7rkIsjW7KmiIlC5yePipK3Pnp7R9VR8G4Bp2ZVlPoseRD4ovg+HGN4xABCZk7A784KE66/nEnXGUuUFofij6O30+YZYvgk4GTzQ5CX4iwko2y/eXFSUm7ftCN+FV55YQuShwgocV/uTmVD2git+mR4cBELBIfVOkTkwKiwMij+h3DhJPZnzvUC3qcQbiejRN+6sVA48z6+RcI0nmG+HVvkOesceqNQ+UeG7kRFsgBQXM9f2twy2f5ui8u7eh4/YXenCvzk7tOzjHeAcTjuorWJsL5f7dd4bl7EZVwo7itS0jmKitSmQvaUBxEAfMEjRTpgmZnk0PH3wHT75WyE9Bu9mVG0rFA5E1HUcOmQg5qyzv9C9mPohUSMfZSRbBNcrBr2kcikzdZS3bj/TsGhP6c10SFts5M0npdtNZiBMIrTInRKaPVfihG3wYaCNAjJO0yR7PpXet5qkAP0bUWh0E4yEWdm3vKyFrLMfkui7Xr8r108n4TlZflYOmY6hK7+o9rIJU+LqvFZyo8hc=", - "variants": [] - }, - { - "component": "client", - "platform": "macos", - "file": "dwspectrum-client_update-5.0.0.35271-macos.zip", - "size": 158000038, - "md5": "c3b126cb6d4558f5c38b52e423269dd0", - "signature": "bHJUbQtwFm/PkkFpAAbIjnw/iWPDZX8GMkmUmJSXB8VGkxy5kITi9iV9P1iHc2GhBM7hXV2ihYuEhDoRfI2p79oe/a4Aa1lT/iIS7x1EA6hQ3VeD5DwWVfP3OvDFv98h0F/6QAvxbCDAdF12YTMLWn/DBQ1kgL4dZz0wgkxrrvDwSuaRp2Z2RGJGnX85A4CyNrgujWYRMtAhZ9TusVqF5LSVE2BZoBqGa/p26v3SbKy7b5KpHu94xyKtFvL0JTSzL0LBZj3BfupSXJMuGKdaLnlbZlN5L+aoEO1+uKuht7qMVKI3fiXVlAoH0ZjadBAcyb+NEsYnz970TGq2tUm0RXpQkGdF5f/QW8xE2sEDZEDCER66jRceIqig6e2OChECYRJ4m8s93hwcNm0lc9IfqkfsN/V2c/S4D9pOCx0L2fwGpEVawqTAFAwJnUqiOgDwWut3iTXair22ani67d6wreulqQyEBoh9wkNph21toaFq3FX9+ChRGXW8oe63m4WdgZD/n9zdlgyhbg03HbO8CUJI5OyiMdrQxhkyyPlz+7r3y2RXL+GrnKlX3mvXtDvC4cFQ4Fz+9mKZ72A6yX/NN72C+ca73Or4XWOPrFwrR0z98oWLozoAaFiexYz2+gIibNW4uPoVUk9f62Zdb7mMJsnmTcMouRjAun9xOF1SAig=", - "variants": [] - }, - { - "component": "client", - "platform": "macos_arm64", - "file": "dwspectrum-client_update-5.0.0.35271-macos_arm64.zip", - "size": 144560236, - "md5": "c2cc87a5915d02d9b74354838236a083", - "signature": "zBV4SXYDfpq9+veBn3wV4NyzSQtt6b7h0kIKgNOyUYXxilQEnjqxkAoqEe6YZPBdifQrJT7OSzLUD11zxWg2tHpsQB/jsX2QaItZO7AWYaMxRotbZ25yzcmCW/Tx+cHDyG0xNP5R6tE/3MQR+8FepnoLP+DgAFFyrCl+U3kuW74H6hYSS3RlsTVoz4/41wxc/jkleYKEQHO/LRd2j3+T+DQTGAPaBBTeRPR4GcOpdTzSMtNm6QA6bZQmj3Wh5wuKQc8fw5k5Ixjcc+TAj7gYMLBvf4jWgD+4soYqv/XftVbr9FL+WwBTYjKTjJA89n/MHXuYTV4rsU8xYGMiBloWZQwO/4kVLEJcbSKy7NpEP61qcgGj19TUK/aEhD3a12sgcaUzvP3sOc8c65yy66CkqKZWIUcPiGYjvweltvWBmpSBG2dH92GDBO9tU9Ivoi9PPrnoxxN2YcVghDZVmirXi+WlIJy9NDH2xONN87im4bsMZB1V4dMrbhQSTXIq0rKzVKWU37JrgXml1qsjl2zqUt4xl30DxPwYoH9AIxk++uhMlGmH16kgVywlOBUjyV6/NEc1MM0ac3zwqTYgy6edpGufLRjttg6oKLMZ6Xc55XMvILkzU7V3AQqW1L0qJJSHuwUWhERhQzLE7sIKgMQBNFtte0MxjpseEJ0xxg/4wSY=", - "variants": [] - }, - { - "component": "client", - "platform": "linux_arm64", - "file": "dwspectrum-client_update-5.0.0.35271-linux_arm64.zip", - "size": 273433878, - "md5": "e6b7170ff2557c6b34f76761d16a6794", - "signature": "m1KZMp1DTuD7vwKhnUzMlL827EhQHYs1Y5Hmg/ufvfw0fgCCES+b2j4XWUGv2Y1L+MZWVc5lhrydcTCooVL57pESub8SSaKd8BIqjrjv4ZitOWzSPlN/kaOylV84NM8Zf6PTPl+/hxkjwFGqlI3RPp6nw1AmUIJOJLNM1YxfnRE8COBCKqfey5kWLclGnqGvfRcyQ8BSAiL4o1rnewm0773faeX7JKoQTJ5xwPxqVyinIjZxzXZK5DEuC9QHj+b/uwiATAzhcObij3xmq5BR8xTg3EboaKHeAMu8zDQhTHLhdnVwyeRZtLo961GbuavaMYJ+nxEd6Nb8/WiM8dUUAL0eVo6ngHJFxJ9ChtFe6Bz55cZNsCxZdkhSzmrVk3uH9puWJ98HBQYlCe2GEx/9W+Lm10qOhpQ1B3BdCYqK6JcMvtnfZMYtvYlaJ2UuDm+tFWCJhidjVbGDRJ9DQ/q+BoiknslPndfYZvARtOP8/CxB9gUhD820yAzjKhjmx2NeiO/TEg37w8oWjdaaXegmWq2fmq3Hue13RQSh52pgwLqHEeikunxo36TgKe0aY+3lpANzSgzIzyO7ngbiJgTgsd9kCnXRuyLfGVmXGZcpFeK5ptIAU5bU5nmcQm628JH/Cjeln+WhQFFThM0w3fP0DrQ84KbuCYvp5keuue8j+D8=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "16.04" - } - ] - }, - { - "component": "server", - "platform": "nx1", - "file": "", - "size": 0, - "md5": "", - "variants": [ - { - "name": "", - "minimumVersion": "3000" - } - ] - } - ] +{ + "version": "5.0.0.35271", + "cloudHost": "dwspectrum.digital-watchdog.com", + "releaseNotesUrl": "https://digital-watchdog.com/DWSpectrum-Releasenote/DWSpectrum.html", + "description": "Support for Ubuntu 16.04 will be discontinued in the next release version and Systems on Ubuntu 16.04 will be blocked from updating to the next release version.", + "eula": "\n\n\n \n DW Spectrum IPVMS EULA\n\n\n\t

DW SPECTRUM IPVMS END USER LICENSE AGREEMENT

\n

IMPORTANT – PLEASE READ CAREFULLY: This End User License Agreement (the “EULA”) is a legally enforceable contract between You (either an individual or an entity) and Digital Watchdog. (the “Company”) regarding the use of the Company’s software programs accompanying or provided under this Agreement together with the SDK made available by the Company, associated media, corresponding Documentation, technical configurations, and technical data (collectively, the “Software”). “Documentation” means the user’s guide and other documentation (including print and online), if any, provided to you with the Software.

\n

BY CLICKING “I ACCEPT” BUTTON BELOW OR BY DOWNLOADING, INSTALLING OR OTHERWISE USING THE SOFTWARE, YOU CONSENT TO BE LEGALLY BOUND BY ALL THE TERMS AND CONDITIONS OF THE AGREEMENT. IF YOU ARE ENTERING INTO THE AGREEMENT ON BEHALF OF A COMPANY OR OTHER LEGAL ENTITY, YOU REPRESENT THAT YOU HAVE THE AUTHORITY TO BIND SUCH ENTITY AND ITS AFFILIATES TO THE AGREEMENT, IN WHICH CASE THE TERMS \"YOU\" OR \"YOUR\" SHALL REFER TO SUCH ENTITY AND ITS AFFILIATES. IF YOU DO NOT HAVE SUCH AUTHORITY, OR IF YOU DO NOT AGREE WITH ANY OF THE TERMS OF THE AGREEMENT, YOU MUST NOT ACCEPT THE AGREEMENT AND MAY NOT USE THE SOFTWARE.

\n

In addition, certain third party programs or code (“Third Party Software”) are being provided to you under the terms and conditions of a separate end user license accompanying such Third Party Software (“Third Party Software Licenses”), which will be provided to you by the Company as part of the Documentation or may be made available for viewing by the Company at the website or webpage at https://dwspectrum.digital-watchdog.com/content/libraries(the “Third Party Site”). If required by any Third Party Software License, the Companywill make such Third Party Software, and the Company’s modifications to that Third Party Software, available to you on the Third Party Site. The Company may be required or wish to provide you with certain notices or information about Third Party Software or Third Party Software Licenses and you acknowledge that such notices and information may be provided to you by the posting of such notices and information on the Third Party Site. BY INSTALLING, COPYING OR OTHERWISE USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THE TERMS OF THIS AGREEMENT AS WELL AS THE TERMS OF THIRD PARTY SOFTWARE LICENSES. IF YOU DO NOT AGREE TO ALL SUCH TERMS, DO NOT INSTALL OR USE THE SOFTWARE.

\n
    \n
  1. License Grant.Subject to the terms and conditions of this Agreement, the Company grants you a non-exclusive, non-transferable, non-sublicensable, limited license to install and use the Software, in object code version only, for your own use subject to the restrictions contained in this Agreement.
  2. \n
  3. Backup Copies.You may make one backup copy of the Software, provided your backup copy is not installed or used on any computer and further provided that all such copies shall bear the original and unmodified copyright, patent and other intellectual property markings that appear on or in the Software. You may not transfer the rights to a backup copy unless you transfer all rights in the Software as provided in this Agreement.
  4. \n
  5. Ownership.You acknowledge that the Software and any copies that you are authorized by the Company to make are the intellectual property of, and are owned by, the Company and its licensors. The structure, sequence and organization of the Software are the valuable trade secrets and confidential information of the Company and its licensors. The Software is protected by copyright, including without limitation by United States copyright law, international treaty provisions and applicable laws in the country in which it is used. You acknowledge that the Company, or its licensors, retain the ownership of all patents, copyrights, trade secrets, trademarks and other intellectual property rights pertaining to the Software. You will take no actions which adversely affect the Company’s intellectual property rights in the Software. This Agreement is a license and not an agreement for sale. No title to, or ownership of, the Software, or any intellectual property rights subsisting therein, is transferred to you.
  6. \n
  7. Restrictions.The Software is licensed solely for your own use. The Company and its licensors reserve all rights not expressly granted to you in this Agreement. Without limiting the generality of the foregoing, you shall not nor shall you permit any other party to: (i) make copies of the Software except as expressly set forth in this Agreement, or (ii) disassemble, decompile or translate any part of the Software, or otherwise attempt to reconstruct or discover the source code of the Software, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation, or (iii) modify or create derivative works based upon the Software, or (iv) externally distribute, sublicense, resell, encumber or otherwise transfer the Software, or (v) rent, lease, lend, or use the Software, or any part thereof, for timesharing or bureau use, or (vi) allow a third party to copy, access, or use the Software (except as expressly provided in this Agreement), or (vii) alter or remove any copyright, trademark or other proprietary notice which may appear on the Software, (viii) take any action that would cause the Software to be placed in the public domain or become subject to open source license agreement, or (ix) use the Software in any manner that violates any statute, law, rule, regulation, directive, guideline, bylaw whether presently in force or may be implemented by federal, state or local authorities.
  8. \n
  9. Paid Features.The Software is made available to you at no charge or fees. However, the Company may offer certain premium features to you upon payment of a license fees determined by the Company in its sole discretion (“Paid Features”). In order to use Paid Features, you must agree to pay the license fees plus any applicable taxes through one of the payment methods accepted by the Company. Upon receipt of the license fees, the Company will either provide you a license key to activate the Paid Features or allow you to download the Software with the Paid Features. Activation of Paid Features may also be regulated through the use of transferrable license keys, and such license keys may only be transferred within your organization subject to the limitations listed in your purchase order or other documentation describing the Software and/or license rights obtained in the Software.You expressly acknowledge and agree that this Agreement will govern your use of the Paid Features.
  10. \n
  11. Updates.Except as otherwise expressly provided herein, this Agreement will govern any updates and upgrades to the Software that may be provided to you in accordance with the Company’s then-current maintenance and support policies, unless such updates and upgrades are provided under a separate license agreement. You may use such updates or upgrades only in conjunction with your then-existing the Software licensed under this Agreement. The Software and all updates and upgrades are licensed as a single product and such updates and upgrades may not be separated from the Software to exceed the scope of your original license. Any other software that may be provided with the Software that is associated with a separate end-user license agreement is licensed to you under the terms of that separate license agreement.
  12. \n
  13. No Support.Except as expressly required by applicable law, nothing in this Agreement entitles you to any support, maintenance or new versions of the Software. You may contact the Company to determine the availability of support, maintenance, and new versions, and the fees terms and conditions that would apply.
  14. \n
  15. Disclaimer of Warranties.THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OR REPRESENTATION OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COMPANY NOR ITS LICENSORS REPRESENT OR WARRANT THAT THE SOFTWARE WILL SATISFY YOUR REQUIREMENTS OR THAT IT IS WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE UNINTERRUPTED OR ERROR FREE. The Company does not warrant that functions contained in the Software will meet your requirements or that the operation of the Software will be error-free or uninterrupted. The Company is not responsible for problems caused by changes in, or modifications to, the operating characteristics of any computer hardware or operating system for which the Software or any upgrade or update is procured, nor is the Company responsible for problems which occur as a result of the use of the Software in conjunction with software of third parties or with hardware, which does not meet the Company’s specific requirements. Some jurisdictions do not allow certain disclaimers or limitations of warranties, so some of these may not apply to you.
  16. \n
  17. Limitation of Liability.TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW EVEN IF A REMEDY FAILS ITS ESSENTIAL PURPOSE, IN NO EVENT SHALL THE COMPANY OR ITS LICENSORS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE OR THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF THE COMPANY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, THE COMPANY’S ENTIRE LIABILITY TO YOU OR ANY OTHER PARTY FOR LOSS OR DAMAGE RESULTING FROM ANY CLAIMS, DEMANDS OR ACTIONS ARISING OUT OF THIS AGREEMENT SHALL NOT EXCEED THE FEE ACTUALLY PAID BY YOU TO THE COMPANY FOR THE SOFTWARE, IF ANY, NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. Because some states and jurisdictions do not allow the exclusion or limitation of liability, the above limitation may not apply to you. In such states and jurisdictions, the Company’s liability shall be limited to the greatest extent permitted by law.
  18. \n
  19. Sole Remedy and Allocation of Risk.YOUR SOLE AND EXCLUSIVE REMEDY AND THE COMPANY’S SOLE AND EXCLUSIVE LIABILITY ARE SET FORTH IN THIS AGREEMENT. This Agreement defines a mutually agreed-upon allocation of risk.
  20. \n
  21. Third Party Software Licenses. Third Party Software included with the Software is licensed under the terms of Third Party Software Licenses. You understand and agree that, although provided to you by the Company with the Software, your use of Third Party Software shall be and is governed by such Third Party Software Licenses. You understand and acknowledge that Third Party Software is being provided “AS IS” WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. If there is a conflict between this Agreement and any Third Party Software Licensewith respect to Third Party Software, the provisions of such Third Party Software Licenseshall prevail.
  22. \n
  23. Export Restrictions.You agree to comply with all export and re-export restrictions and regulations (“Export Restrictions”) imposed by the United States or any other country in which you conduct business. Without limiting the generality of the foregoing, and regardless of any disclosure made by you to the Company regarding an ultimate destination of the Software, you represent and warrant that you understand the U.S. law currently prohibits the export or re-export, directly or indirectly (including via remote access) of U.S. origin products and technology to certain proscribed countries (including Cuba, Iran, Sudan, North Korea and Syria and/or any other country that may become subject to an embargo by the United States), entities, organizations and individuals, without prior authorization from the U.S. Government. You will not commit any act or omission that will result in a breach of any such Export Restrictions. Your breach of this clause shall constitute cause for immediate termination of this Agreement.
  24. \n
  25. High Risk Activities.The Software is not designed, manufactured or intended for use or resale as on-line control equipment in hazardous environments requiring fail-safe performance, such as in the operation of nuclear facilities, aircraft navigation or communication systems, air traffic control, direct life support machines, or weapons systems, in which the failure of the Software could lead directly to death, personal injury, or severe physical or environmental damage (“High Risk Activities”). The Company specifically disclaims any express or implied warranty of fitness for High Risk Activities.
  26. \n
  27. Consent to Collect Data.You consent Company to collect and use technical data and information from the installed application anonymously. The collected information include but not limited to the version of the Software, Software configuration, devices added or connected to the Software, total session time, Internet/network usage, and hardware specification. The information is gathered to provision the Software usage, updates, devices and configuration so developers can enhance and improve the Software.
  28. \n
\n

The data collection can be disabled from the Software if you do not want the data to be sent.

\n
    \n
  1. Termination.The licenses granted herein shall automatically terminate without notice if you fail to comply with any material provision of this Agreement. In such event, you must immediately uninstall and return the Software, including all Documentation and original media, to the Company. This shall not limit or affect any remedy available to the Company for your breach of this Agreement.
  2. \n
  3. Miscellaneous.
  4. \n
\n

(a) If you are a unit or agency of the United States Government, the following applies: The Software is provided with RESTRICTED RIGHTS. Use, duplication or disclosure by the Government is subject to restrictions as set forth in Subparagraphs (a) through (d) of the Commercial Computer-Restricted Rights clause at FAR 52.227-19 when applicable, or in Subparagraph 252.227-7013 (c)(1)(ii) of the Rights in Technical Data and Computer Software at DFARS, and in similar clauses in the NASA FAR Supplement. Contractor/manufacturer is Digital Watchdog.

\n

(b) This Agreement shall be governed by the laws of the State of California, exclusive of its choice of law rules. Any dispute between you and the Company regarding this Agreement will be subject to the exclusive jurisdiction of the state and federal courts sitting in the County of Los Angeles in the State of California. In any such action, the prevailing party will be entitled to costs and attorneys’ fees.

\n

(c) You may not assign, sublicense, or transfer this Agreement, the Software, or any rights or obligations hereunder without prior written consent of the Company. Any such attempted assignment, sublicense, or transfer will be null and void. The Company may terminate this Agreement in the event of any such attempted assignment, sublicense, or transfer.

\n

(d) This Agreement is in the English language only, and will only be provided in the English language, which language will be controlling in all respects, and all versions hereof in any other language will not be binding on the parties hereto. All communications and notices to be made or given pursuant to this Agreement must be in the English language.

\n

(e) The Agreement constitutes the final and complete understanding between you and the Company with respect to the subject matter of this Agreement. Any modifications or waivers of this Agreement must be in writing and signed by both parties hereto.

\n

(f) If any provision in this Agreement is invalid or unenforceable or contrary to applicable law, such provision shall be construed, limited, or altered, as necessary, to eliminate the invalidity or unenforceability or the conflict with applicable law, and all other provisions of this Agreement shall remain in effect.

\n

(g) Failure or delay on the part of the Company to exercise any right, power, privilege, or remedy will not constitute a waiver of, or bar the later exercise of, that or any other right, power, privilege, or remedy of the Company.

\n

(h) No rule of strict construction shall apply against or in favor of either party in the construction and interpretation of this Agreement.

\n\n\n", + "eulaVersion": 1, + "packages": [ + { + "component": "server", + "platform": "linux_x64", + "file": "dwspectrum-server_update-5.0.0.35271-linux_x64.zip", + "size": 88614643, + "md5": "e027ae6b5f4420ac77dfd76f4e502f99", + "signature": "DF54oM/oYcBZ6h6gamUt/QfGoV+yxA/yfkrDZyqqhhXdg3uALI5kRYQy7A32EvmThB1DQ2pi/5Y7s4EHAg4kJWOgPZiwaO1cQ8++gai4YVes2duzLFqkUHCFAvoD61QQSps+Wbtq/agcH9nR0RrnhvYdvG0kRkcuOrESWN3XkuC0g+LxczUpuDXYtZ3SMjuoQ60UTkrQDcMsb/kcfFEzyIL+aRlV2u1tBbdYjADP1opQjPAtbVjrfzlTXTz75Hn1HldtXdO8vByIm9ljhKFETs0FFjXj6YUn0TdzLffw0Bs/IFyrZ7KcvQW7M62rKN8z+1u0KvwQKatrh8+ZFCEZAFx8MH+gUy5sTcyRrvInrmvDnL1r7WVmXv7p3gWFLSewtMDlcBOK7X+rW4+c5pk5ltQWM5G2V50lYsVOOPjjFvoDSpkdAOksXRWg06unS3tU2jDRlFEEE46HLLueuFH7a2fTG/IX8LcRE/Pcfn0kDHEzjB3lZnwDahGwH42CynzaZUXRW6KH73MwcwYDPO/fjBzB7YrXhBicMCHxASPjDMD3ml/7lbOdqGPeeknbNhnfVkgze+S0nr9OIaTQ2CcVfIHRmmjtbMG2cIo7X+3wgdUaSfSBOGoZA/8yIyAJehiz9XlpjOEpFD2P+ndGQsIsIIExXhNfOxUmdjsFLye31IA=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "16.04" + } + ] + }, + { + "component": "server", + "platform": "windows_x64", + "file": "dwspectrum-server_update-5.0.0.35271-windows_x64.zip", + "size": 97987920, + "md5": "c809fdd6cd162e368a8593b211c35e5f", + "signature": "AmCSSckelnzgMzganq9H/m8Ekwg+fmbVGByxGDXIvQljFWw2D/B17nnEVdfH8vE5NVPO4Co7qW3xdJMSGJrJCaHEf5ijs5SJe3LEGDAiNjZEsGH9MFMJNnOPyS3W9PysLDL/5SFSHYcXkgeug+erlnuC8MAkVtb/GDq9s/8nqIY/KWKQ3hNn67uq5lJe+8i4A9GK5jailev355JnGvGrs3+9Hu47+dSWMl2dYq2IYsNsO2bDbknaUpIctkTxhQ/RDBuyazOHT/67P5IwedTemzWvo5/oJqPaU7khx82qKqEgifmZXLD8eZX3aPwTq3NtXbdBmrqcHEmdpG0s+pVhWHYUUjlPpyQ0p+VqeJKqC1PvecGdZpMVkj7Ix8/FlJbu5TtO+0SY6bg/h1hgQHD2Vtf8PqapeyrKigprPONz6JVYIvmkOTLIrfaEOrFhsIF1cNzYXxuQI1T/U/q6Okisr4P88Wkcd/ZDAbj3Z95fyRxFEozsWOa2s6WA27+0NRKoNR08GzhR0LIc1HqKOL9pwgNAwcDnqPca0R35tqDDGOUj+Czt5mE6dgnTSG4XNPr0G1tsU9Xt5+4dhYRIfkqFYyML42kq9SVfZLCL3GbLbL1Lpo09RKbUwu0zJllCk8itRb6FASevcZPpDNOYhWo08ijABEiYIdhUcOdKhyt9IWw=", + "variants": [] + }, + { + "component": "server", + "platform": "edge1", + "file": "dwspectrum-server_update-5.0.0.35271-edge1.zip", + "size": 85437501, + "md5": "018f57ac6160f849c7eaf51ceea7d258", + "signature": "mkWJRgn+t1e+5811FaPMSSokpwRrtc4L0UcIBDvdpVBTEzhtBcl7FzJBJHP9ljhHW71eEnev4j269C/S4FNn59m01+WsGdiiqd29ReH7zw8mL+YUjjitlzpCNSnSh3fwcvAQ5piLyNcMgdEXFkoZE/A8T8BzKgw6dSfkLhM/5LFftdB8BS1QG4poA83fl1XJqCCDzXt0mgDcCQGwj6s9fEnCe56eB8dDFwoA8M4LpAdojdbKuvxXSi/VRa7CYwvyvwQQaeqSJ44PLD/NrNjL68vl8Mm2HW95goIovE1MpycUCoB7Hb9anfxnP5diWsA/537FhWUSABEjml4L2j6ncGZC3ZatXBpC4Jr2ewPuUBaBJQMFY64fYMPy5jcL2GdiX4Kjl8S0XDUDHmywxl7eFTjuB9LPOjLUWWHd9D3w8vi40OefwIbHfGpE2gNKCb6pkvmNgRvwn6a47hNJ1iTNGuOigq0b/pug/8WxZDSdHkMuq3M61l0MPeucydD3AGWbpMv+teceYTd3DH/gYksM8++jRccErGPH5LOpzlHwZnHorHfyp1/iVoKVL77I5aOzU2vgpiv+sJNHvEj2PIPg7pyUjAZbHvFMwusfm2w1fCGla9Df/lmiDAEogVG4qqBJuKn8lYe1/xZwYH/L4oQ3+zSHc/+JeXwmo2ynaekZzYk=", + "variants": [] + }, + { + "component": "server", + "platform": "linux_arm64", + "file": "dwspectrum-server_update-5.0.0.35271-linux_arm64.zip", + "size": 143427373, + "md5": "589df20cab8d54a1eb1c909cfd703f75", + "signature": "RuCn/6jOR5z5wf6JEspzPB1HyDMTYauUKxqBkIrxbS8YEO8MVpd2jrRd5hkd0u58fZ/9CZIXHFplc9887K3MPkjgn1YoDSO6lv5sieLxO8lM7LYtaFQs6H8/YIv7QZnooyOv1cgLLkRvb/LcLmEkbIBPHKLQ8ChDUJD6bmFyolxcGRjFfiAyFRzEsqBIwiK/gr6JmyC5DBqFbz5MyBafMt/BwWXrH4e0e9zgDSulBJC2FLoUKnewEtCN6TUDD248d2hGrUz4Q8JXIzcGjSxt0jwnc3c7TNpkVxfuy0e5Ob7KCTWnjX/LNzvD6bimMBWzxPQXIlvn1OmLsv4fMm5Qzz4ZX0FWTOxZsZO8B5VWfVBYJaGtwPhXOZhNt3IsS9ffTKG8K9C0taTQTi8JQVbZHgwjzRY87fuC9NxGYXYJ3HXriaQ/VB8s6mvy7GOee+cqux145B09pkndJIy46QF0VTypmDZC6G8fCCY8tT9tOtJcksgizh0QchwNNik+KInx1wgxds4vTN8ld9CmtWoJEfB0ydmBGHL0/vZksj+o9+ds8nD7gfTfdo+4RuMiDmzQX5sOehg8u1fAHuGVUOydqOGzgHa9WPKdtQ3CpzipKLnu4BRJRqpE5LJ0KXAM1yTxmskINIzODrlZPRkeCG63uarbJSlQlhEHajLGONyRJNk=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "16.04" + } + ] + }, + { + "component": "server", + "platform": "linux_arm32", + "file": "dwspectrum-server_update-5.0.0.35271-linux_arm32.zip", + "size": 169064419, + "md5": "39e11cb1c489b0993380dd8b2a2e99a4", + "signature": "Kd2gdCR7oFSNLfrqb8XZPf0NmuNZ6LpHznAtNoDL6DqpnWBXsZ2YdqtAvYmpWX0EoqJZ7TRoyjyEKiu06kalgOXE+xFyEhAYTaQp50MIpkq3kutZ8l5rnkum99WvCW0NnHPBIF1OlO7xBFEQMWWDSSmooVaR9EqxhajIsYBj+8mzBTvOnNCn5cDgv1/4V+GCSIjHAeDiLE3CB0hr8yprzKKhKfdZQMTYfHziVspjDLTgeSx07cehEhYqn9uJb35QZN4ily4e3htbXPPLbPNMP2/Sn97mJYFRm7tMs8xzUImUE5gGV5Fc6SFmiYK/otpYtxUyuexRTZU5w7dHQXuRksIE3b8BQQ8CRFhRd0zN534hm9whNRI0IYnBPOG2oaEA1mWsTEvAj+9Gzp8WsIKCHXRE1Ybicqa8D7tdO781sBgrW5ltiEZEmZWOo/p5Z/boC87UTib1s3aQG10lccW8srL4skirydboipFPE3QYAjcnOc3eocRN4A9LHtDbgCTmME2cRAAAvIYDCwiVSfnbuWWrkOSGDXv+A06OwCB1l+OS2qUecaHg+QoiFYGXffBStNCU0RyVg8QWbeOjxBD2xcpFquypyo6+jxXO/ZPZ5AUXepBB1iS5iIWpqpGc0RHtlDQSRdse1xFVshc6CCOvk1H0EO9RqDq1oxmv+PATo7g=", + "variants": [ + { + "name": "debian", + "minimumVersion": "9" + }, + { + "name": "ubuntu", + "minimumVersion": "16.04" + }, + { + "name": "raspbian", + "minimumVersion": "9" + } + ] + }, + { + "component": "client", + "platform": "linux_x64", + "file": "dwspectrum-client_update-5.0.0.35271-linux_x64.zip", + "size": 268837878, + "md5": "265a473af3a8bda35984e924d3783e05", + "signature": "MLGEe7AcSZbch2+Bvh2RUhdydCbuFXOnRjpm9rJWsp/eOYVq2ZXsyOwoEV6sZexCAxfQS3LZg8xPnGTklJE6V1FP2Qc7GnxW/jPAhRKoWeP931+m//ZRfvkg4adLTLId9fVgvtX95btqrqdqLUUBSoWHAZoO/XrliHh0JsnSACbF1umv1l6byhxJAEHVKWEFzc7aTX/ibsfnZljDI1qLONtRWIOE8Gg+gct65B0uq/FLr8FcLfocD1v4nkRQkAtBB4mk1TjOsIyKMweOhnuvwNE5XUnx6IjBUDuxMFYAkyRfsZ1IbydMUzwcZHUnCAwbo6XbloJsu1yH55jYwnJNlHGJnPC+xCyThJiWvXfypgp/1WqS35yMFH2e3TSZxKK7SUiF7f8Hxkjc7iYM/Ba71KvsoU07Q+nE7if1EEUOiHqeUCrwYZg0R/cSUiMfqfx0A3OuuVeQTQKTPZ35VI/Wgry7I0buV9kpugHNshQ2U2ZTZYG8ipMC0HdoXaS//5Tfh+iK0xqhdTjzWjZeNwDPs9F+ap4ICzkWK27qgtszNM0XgFczkGE6g+I/V5oJH3f32qn+fi2YGJOLGer2samoy8lnLzNamLszUSuUojLvlEIAdR5cjsErwln/srjIFQb1Wuy8+sMqaV2DYHSexuM7oQKK5LMaNdlACZ89O+XXZzs=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "16.04" + } + ] + }, + { + "component": "client", + "platform": "windows_x64", + "file": "dwspectrum-client_update-5.0.0.35271-windows_x64.zip", + "size": 153542461, + "md5": "2ce20c6bb040a39c2c76d63b5e072c08", + "signature": "GNdBfjjZKscX1+wya6AgEkCTfM9fg0H/T+MW6QtAINhGI+iaAyJfAKF9/6KZ2KiotkUud9mnmr3gi2Bkq/GdxpTbv6EOidkzgcvbSJTEATf6CDI7F1aJH19wQFzor05Y31y9TyMJWYHIO8Yue9DH5xoVUAZptbOxHyil3gXuQVk8LAZZWd6pNoA8ABQS5V8WBf2zzt4cSvxMlc1aKf2PPiimyk4Sklq/ZEt0N5TqWkxTnReA/32IoZGAbYFeaWAhpuZhP+BixYuDEdLoR/3WZ/SRPZei4hg7/g4gUPB3+isIpiE+3LWY3O/BkBQm1ZQuayCbeJwHiUGlUfQ+gQwVbPJAEUbJ6TaOYgUeIao+pBXPFa0LQtG/ISJcbsG0xKbGcOox1NRT81WGMynjrfxLGiNPGkU+YurDF3CN8Cctmd1wkqbY32g4XWw4BsaHwTCRKRdJdaoowbYBSsjiCYQ/X/FJChcVFcKcQNhLVc/aVSdy9PTp4kfWWpAdPzaXI7TgSSEWBGatKkE0av6xshWCMZT0lbk1aQr8iyq/LjamUJwPVDsxtTas4ppfJDV5VgqVfvEazf4TyMlbHyi4Db776caZWFSQto6szh2JTMIfq35oaJTL+nCXnD8AkTS3jot/qLAUFBY4hCV0FM2W5U7G4+kM4dotVxbTdaK4o1UhnwI=", + "variants": [] + }, + { + "component": "client", + "platform": "macos_x64", + "file": "dwspectrum-client_update-5.0.0.35271-macos_x64.zip", + "size": 158000043, + "md5": "4bb2e9498267f51fc7057c2695bc417d", + "signature": "TCxNIKAlgbVKXXUessdjx0manmIFgYuKPjbnLMhe05MQ1Gg7nhUtfthQipJ8R+ewiy487L1KxSfu5uUCBAu7zcMnckjtM2t/owYv7PLb00HnqLzXfY6ZhfGQ9gVHJbHhANZDtsRlzAdFX+263+l3O3Uh/5Ci7rkIsjW7KmiIlC5yePipK3Pnp7R9VR8G4Bp2ZVlPoseRD4ovg+HGN4xABCZk7A784KE66/nEnXGUuUFofij6O30+YZYvgk4GTzQ5CX4iwko2y/eXFSUm7ftCN+FV55YQuShwgocV/uTmVD2git+mR4cBELBIfVOkTkwKiwMij+h3DhJPZnzvUC3qcQbiejRN+6sVA48z6+RcI0nmG+HVvkOesceqNQ+UeG7kRFsgBQXM9f2twy2f5ui8u7eh4/YXenCvzk7tOzjHeAcTjuorWJsL5f7dd4bl7EZVwo7itS0jmKitSmQvaUBxEAfMEjRTpgmZnk0PH3wHT75WyE9Bu9mVG0rFA5E1HUcOmQg5qyzv9C9mPohUSMfZSRbBNcrBr2kcikzdZS3bj/TsGhP6c10SFts5M0npdtNZiBMIrTInRKaPVfihG3wYaCNAjJO0yR7PpXet5qkAP0bUWh0E4yEWdm3vKyFrLMfkui7Xr8r108n4TlZflYOmY6hK7+o9rIJU+LqvFZyo8hc=", + "variants": [] + }, + { + "component": "client", + "platform": "macos", + "file": "dwspectrum-client_update-5.0.0.35271-macos.zip", + "size": 158000038, + "md5": "c3b126cb6d4558f5c38b52e423269dd0", + "signature": "bHJUbQtwFm/PkkFpAAbIjnw/iWPDZX8GMkmUmJSXB8VGkxy5kITi9iV9P1iHc2GhBM7hXV2ihYuEhDoRfI2p79oe/a4Aa1lT/iIS7x1EA6hQ3VeD5DwWVfP3OvDFv98h0F/6QAvxbCDAdF12YTMLWn/DBQ1kgL4dZz0wgkxrrvDwSuaRp2Z2RGJGnX85A4CyNrgujWYRMtAhZ9TusVqF5LSVE2BZoBqGa/p26v3SbKy7b5KpHu94xyKtFvL0JTSzL0LBZj3BfupSXJMuGKdaLnlbZlN5L+aoEO1+uKuht7qMVKI3fiXVlAoH0ZjadBAcyb+NEsYnz970TGq2tUm0RXpQkGdF5f/QW8xE2sEDZEDCER66jRceIqig6e2OChECYRJ4m8s93hwcNm0lc9IfqkfsN/V2c/S4D9pOCx0L2fwGpEVawqTAFAwJnUqiOgDwWut3iTXair22ani67d6wreulqQyEBoh9wkNph21toaFq3FX9+ChRGXW8oe63m4WdgZD/n9zdlgyhbg03HbO8CUJI5OyiMdrQxhkyyPlz+7r3y2RXL+GrnKlX3mvXtDvC4cFQ4Fz+9mKZ72A6yX/NN72C+ca73Or4XWOPrFwrR0z98oWLozoAaFiexYz2+gIibNW4uPoVUk9f62Zdb7mMJsnmTcMouRjAun9xOF1SAig=", + "variants": [] + }, + { + "component": "client", + "platform": "macos_arm64", + "file": "dwspectrum-client_update-5.0.0.35271-macos_arm64.zip", + "size": 144560236, + "md5": "c2cc87a5915d02d9b74354838236a083", + "signature": "zBV4SXYDfpq9+veBn3wV4NyzSQtt6b7h0kIKgNOyUYXxilQEnjqxkAoqEe6YZPBdifQrJT7OSzLUD11zxWg2tHpsQB/jsX2QaItZO7AWYaMxRotbZ25yzcmCW/Tx+cHDyG0xNP5R6tE/3MQR+8FepnoLP+DgAFFyrCl+U3kuW74H6hYSS3RlsTVoz4/41wxc/jkleYKEQHO/LRd2j3+T+DQTGAPaBBTeRPR4GcOpdTzSMtNm6QA6bZQmj3Wh5wuKQc8fw5k5Ixjcc+TAj7gYMLBvf4jWgD+4soYqv/XftVbr9FL+WwBTYjKTjJA89n/MHXuYTV4rsU8xYGMiBloWZQwO/4kVLEJcbSKy7NpEP61qcgGj19TUK/aEhD3a12sgcaUzvP3sOc8c65yy66CkqKZWIUcPiGYjvweltvWBmpSBG2dH92GDBO9tU9Ivoi9PPrnoxxN2YcVghDZVmirXi+WlIJy9NDH2xONN87im4bsMZB1V4dMrbhQSTXIq0rKzVKWU37JrgXml1qsjl2zqUt4xl30DxPwYoH9AIxk++uhMlGmH16kgVywlOBUjyV6/NEc1MM0ac3zwqTYgy6edpGufLRjttg6oKLMZ6Xc55XMvILkzU7V3AQqW1L0qJJSHuwUWhERhQzLE7sIKgMQBNFtte0MxjpseEJ0xxg/4wSY=", + "variants": [] + }, + { + "component": "client", + "platform": "linux_arm64", + "file": "dwspectrum-client_update-5.0.0.35271-linux_arm64.zip", + "size": 273433878, + "md5": "e6b7170ff2557c6b34f76761d16a6794", + "signature": "m1KZMp1DTuD7vwKhnUzMlL827EhQHYs1Y5Hmg/ufvfw0fgCCES+b2j4XWUGv2Y1L+MZWVc5lhrydcTCooVL57pESub8SSaKd8BIqjrjv4ZitOWzSPlN/kaOylV84NM8Zf6PTPl+/hxkjwFGqlI3RPp6nw1AmUIJOJLNM1YxfnRE8COBCKqfey5kWLclGnqGvfRcyQ8BSAiL4o1rnewm0773faeX7JKoQTJ5xwPxqVyinIjZxzXZK5DEuC9QHj+b/uwiATAzhcObij3xmq5BR8xTg3EboaKHeAMu8zDQhTHLhdnVwyeRZtLo961GbuavaMYJ+nxEd6Nb8/WiM8dUUAL0eVo6ngHJFxJ9ChtFe6Bz55cZNsCxZdkhSzmrVk3uH9puWJ98HBQYlCe2GEx/9W+Lm10qOhpQ1B3BdCYqK6JcMvtnfZMYtvYlaJ2UuDm+tFWCJhidjVbGDRJ9DQ/q+BoiknslPndfYZvARtOP8/CxB9gUhD820yAzjKhjmx2NeiO/TEg37w8oWjdaaXegmWq2fmq3Hue13RQSh52pgwLqHEeikunxo36TgKe0aY+3lpANzSgzIzyO7ngbiJgTgsd9kCnXRuyLfGVmXGZcpFeK5ptIAU5bU5nmcQm628JH/Cjeln+WhQFFThM0w3fP0DrQ84KbuCYvp5keuue8j+D8=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "16.04" + } + ] + }, + { + "component": "server", + "platform": "nx1", + "file": "", + "size": 0, + "md5": "", + "variants": [ + { + "name": "", + "minimumVersion": "3000" + } + ] + } + ] } \ No newline at end of file diff --git a/Samples/Packages/NxMeta.json b/Samples/Packages/NxMeta.json index 0c6717c8..f50ff01a 100644 --- a/Samples/Packages/NxMeta.json +++ b/Samples/Packages/NxMeta.json @@ -1,166 +1,166 @@ -{ - "version": "6.0.0.38086", - "cloudHost": "meta.nxvms.com", - "releaseNotesUrl": "https://meta.nxvms.com/downloads/releases", - "description": "", - "eula": "\n\n\n \n Nx Witness EULA\n\n\n\t

NETWORK OPTIX, INC.

\n

END USER LICENSE AGREEMENT

\n

IMPORTANT – PLEASE READ CAREFULLY: This End User License Agreement (the “Agreement”) is a legally enforceable contract between You (either an individual or an entity) and Network Optix, Inc. (the “Company”) regarding the use of the Company’s software programs accompanying or provided under this Agreement together with the SDK made available by the Company, associated media, corresponding Documentation, technical configurations, and technical data (collectively, the “Software”). “Documentation” means the user’s guide and other documentation (including print and online), if any, provided to you with the Software.

\n

BY CLICKING “I ACCEPT” BUTTON BELOW OR BY DOWNLOADING, INSTALLING OR OTHERWISE USING THE SOFTWARE, YOU CONSENT TO BE LEGALLY BOUND BY ALL THE TERMS AND CONDITIONS OF THE AGREEMENT. IF YOU ARE ENTERING INTO THE AGREEMENT ON BEHALF OF A COMPANY OR OTHER LEGAL ENTITY, YOU REPRESENT THAT YOU HAVE THE AUTHORITY TO BIND SUCH ENTITY AND ITS AFFILIATES TO THE AGREEMENT, IN WHICH CASE THE TERMS \"YOU\" OR \"YOUR\" SHALL REFER TO SUCH ENTITY AND ITS AFFILIATES. IF YOU DO NOT HAVE SUCH AUTHORITY, OR IF YOU DO NOT AGREE WITH ANY OF THE TERMS OF THE AGREEMENT, YOU MUST NOT ACCEPT THE AGREEMENT AND MAY NOT USE THE SOFTWARE.

\n

In addition, certain third party programs or code (“Third Party Software”) are being provided to you under the terms and conditions of a separate end user license accompanying such Third Party Software (“Third Party Software Licenses”), which will be provided to you by the Company as part of the Documentation or may be made available for viewing by the Company at the website or webpage at https://meta.nxvms.com/content/libraries (the “Third Party Site”). If required by any Third Party Software License, the Company will make such Third Party Software, and the Company’s modifications to that Third Party Software, available to you on the Third Party Site. The Company may be required or wish to provide you with certain notices or information about Third Party Software or Third Party Software Licenses and you acknowledge that such notices and information may be provided to you by the posting of such notices and information on the Third Party Site.. BY INSTALLING, COPYING OR OTHERWISE USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THE TERMS OF THIS AGREEMENT AS WELL AS THE TERMS OF THIRD PARTY SOFTWARE LICENSES. IF YOU DO NOT AGREE TO ALL SUCH TERMS, DO NOT INSTALL OR USE THE SOFTWARE.

\n

1. License Grant. Subject to the terms and conditions of this Agreement, the Company grants you a non-exclusive, non-transferable, non-sublicensable, limited license to install and use the Software, in object code version only, for your own use subject to the restrictions contained in this Agreement.

\n

2. Backup Copies. You may make one backup copy of the Software, provided your backup copy is not installed or used on any computer and further provided that all such copies shall bear the original and unmodified copyright, patent and other intellectual property markings that appear on or in the Software. You may not transfer the rights to a backup copy unless you transfer all rights in the Software as provided in this Agreement.

\n

3. Ownership. You acknowledge that the Software and any copies that you are authorized by the Company to make are the intellectual property of, and are owned by, the Company and its licensors. The structure, sequence and organization of the Software are the valuable trade secrets and confidential information of the Company and its licensors. The Software is protected by copyright, including without limitation by United States copyright law, international treaty provisions and applicable laws in the country in which it is used. You acknowledge that the Company, or its licensors, retain the ownership of all patents, copyrights, trade secrets, trademarks and other intellectual property rights pertaining to the Software. You will take no actions which adversely affect the Company’s intellectual property rights in the Software. This Agreement is a license and not an agreement for sale. No title to, or ownership of, the Software, or any intellectual property rights subsisting therein, is transferred to you.

\n

4. Restrictions. The Software is licensed solely for your own use. The Company and its licensors reserve all rights not expressly granted to you in this Agreement. Without limiting the generality of the foregoing, you shall not nor shall you permit any other party to: (i) make copies of the Software except as expressly set forth in this Agreement, or (ii) disassemble, decompile or translate any part of the Software, or otherwise attempt to reconstruct or discover the source code of the Software, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation, or (iii) modify or create derivative works based upon the Software, or (iv) externally distribute, sublicense, resell, encumber or otherwise transfer the Software, or (v) rent, lease, lend, or use the Software, or any part thereof, for timesharing or bureau use, or (vi) allow a third party to copy, access, or use the Software (except as expressly provided in this Agreement), or (vi) alter or remove any copyright, trademark or other proprietary notice which may appear on the Software, (vii) take any action that would cause the Software to be placed in the public domain or become subject to open source license agreement, or (viii) use the Software in any manner that violates any statute, law, rule, regulation, directive, guideline, bylaw whether presently in force or may be implemented by federal, state or local authorities.

\n

5. Paid Features. The Software is made available to you at no charge or fees. However, the Company may offer certain premium features to you upon payment of a license fees determined by the Company in its sole discretion (“Paid Features”). In order to use Paid Features, you must agree to pay the license fees plus any applicable taxes through one of the payment methods accepted by the Company. Upon receipt of the license fees, the Company will either provide you a license key to activate the Paid Features or allow you to download the Software with the Paid Features. Activation of Paid Features may also be regulated through the use of transferrable license keys, and such license keys may only be transferred within your organization subject to the limitations listed in your purchase order or other documentation describing the Software and/or license rights obtained in the Software. You expressly acknowledge and agree that this Agreement will govern your use of the Paid Features.

\n

6. Updates. Except as otherwise expressly provided herein, this Agreement will govern any updates and upgrades to the Software that may be provided to you in accordance with the Company’s then-current maintenance and support policies, unless such updates and upgrades are provided under a separate license agreement. You may use such updates or upgrades only in conjunction with your then-existing the Software licensed under this Agreement. The Software and all updates and upgrades are licensed as a single product and such updates and upgrades may not be separated from the Software to exceed the scope of your original license. Any other software that may be provided with the Software that is associated with a separate end-user license agreement is licensed to you under the terms of that separate license agreement.

\n

7. No Support. Except as expressly required by applicable law, nothing in this Agreement entitles you to any support, maintenance or new versions of the Software. You may contact the Company to determine the availability of support, maintenance, and new versions, and the fees terms and conditions that would apply.

\n

8. Disclaimer of Warranties. THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OR REPRESENTATION OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COMPANY NOR ITS LICENSORS REPRESENT OR WARRANT THAT THE SOFTWARE WILL SATISFY YOUR REQUIREMENTS OR THAT IT IS WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE UNINTERRUPTED OR ERROR FREE. The Company does not warrant that functions contained in the Software will meet your requirements or that the operation of the Software will be error-free or uninterrupted. The Company is not responsible for problems caused by changes in, or modifications to, the operating characteristics of any computer hardware or operating system for which the Software or any upgrade or update is procured, nor is the Company responsible for problems which occur as a result of the use of the Software in conjunction with software of third parties or with hardware, which does not meet the Company’s specific requirements. Some jurisdictions do not allow certain disclaimers or limitations of warranties, so some of these may not apply to you.

\n

9. Limitation Of Liability. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW EVEN IF A REMEDY FAILS ITS ESSENTIAL PURPOSE, IN NO EVENT SHALL THE COMPANY OR ITS LICENSORS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE OR THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF THE COMPANY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, THE COMPANY’S ENTIRE LIABILITY TO YOU OR ANY OTHER PARTY FOR LOSS OR DAMAGE RESULTING FROM ANY CLAIMS, DEMANDS OR ACTIONS ARISING OUT OF THIS AGREEMENT SHALL NOT EXCEED THE FEE ACTUALLY PAID BY YOU TO THE COMPANY FOR THE SOFTWARE, IF ANY, NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. Because some states and jurisdictions do not allow the exclusion or limitation of liability, the above limitation may not apply to you. In such states and jurisdictions, the Company’s liability shall be limited to the greatest extent permitted by law.

\n

10. Sole Remedy and Allocation of Risk. YOUR SOLE AND EXCLUSIVE REMEDY AND THE COMPANY’S SOLE AND EXCLUSIVE LIABILITY ARE SET FORTH IN THIS AGREEMENT. This Agreement defines a mutually agreed-upon allocation of risk.

\n

11. Third Party Software Licenses. Third Party Software included with the Software is licensed under the terms of Third Party Software Licenses. You understand and agree that, although provided to you by the Company with the Software, your use of Third Party Software shall be and is governed by such Third Party Software Licenses. You understand and acknowledge that Third Party Software is being provided “AS IS” WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. If there is a conflict between this Agreement and any Third Party Software License with respect to Third Party Software, the provisions of such Third Party Software License shall prevail.

\n

12. Export Restrictions. You agree to comply with all export and re-export restrictions and regulations (“Export Restrictions”) imposed by the United States or any other country in which you conduct business. Without limiting the generality of the foregoing, and regardless of any disclosure made by you to the Company regarding an ultimate destination of the Software, you represent and warrant that you understand the U.S. law currently prohibits the export or re-export, directly or indirectly (including via remote access) of U.S. origin products and technology to certain proscribed countries (including Cuba, Iran, Sudan, North Korea and Syria and/or any other country that may become subject to an embargo by the United States), entities, organizations and individuals, without prior authorization from the U.S. Government. You will not commit any act or omission that will result in a breach of any such Export Restrictions. Your breach of this clause shall constitute cause for immediate termination of this Agreement.

\n

13. High Risk Activities. The Software is not designed, manufactured or intended for use or resale as on-line control equipment in hazardous environments requiring fail-safe performance, such as in the operation of nuclear facilities, aircraft navigation or communication systems, air traffic control, direct life support machines, or weapons systems, in which the failure of the Software could lead directly to death, personal injury, or severe physical or environmental damage (“High Risk Activities”). The Company specifically disclaims any express or implied warranty of fitness for High Risk Activities.

\n

14. Termination. The licenses granted herein shall automatically terminate without notice if you fail to comply with any material provision of this Agreement. In such event, you must immediately uninstall and return the Software, including all Documentation and original media, to the Company. This shall not limit or affect any remedy available to the Company for your breach of this Agreement.

\n

15. Miscellaneous.

\n

(a) If you are a unit or agency of the United States Government, the following applies: The Software is provided with RESTRICTED RIGHTS. Use, duplication or disclosure by the Government is subject to restrictions as set forth in Subparagraphs (a) through (d) of the Commercial Computer-Restricted Rights clause at FAR 52.227-19 when applicable, or in Subparagraph 252.227-7013 (c)(1)(ii) of the Rights in Technical Data and Computer Software at DFARS, and in similar clauses in the NASA FAR Supplement. Contractor/manufacturer is Network Optix, Inc.

\n

(b) This Agreement shall be governed by the laws of the State of California, exclusive of its choice of law rules. Any dispute between you and the Company regarding this Agreement will be subject to the exclusive jurisdiction of the state and federal courts sitting in the County of Los Angeles in the State of California. In any such action, the prevailing party will be entitled to costs and attorneys’ fees.

\n

(c) You may not assign, sublicense, or transfer this Agreement, the Software, or any rights or obligations hereunder without prior written consent of the Company. Any such attempted assignment, sublicense, or transfer will be null and void. The Company may terminate this Agreement in the event of any such attempted assignment, sublicense, or transfer.

\n

(d) This Agreement is in the English language only, and will only be provided in the English language, which language will be controlling in all respects, and all versions hereof in any other language will not be binding on the parties hereto. All communications and notices to be made or given pursuant to this Agreement must be in the English language.

\n

(e) The Agreement constitutes the final and complete understanding between you and the Company with respect to the subject matter of this Agreement. Any modifications or waivers of this Agreement must be in writing and signed by both parties hereto.

\n

(f) If any provision in this Agreement is invalid or unenforceable or contrary to applicable law, such provision shall be construed, limited, or altered, as necessary, to eliminate the invalidity or unenforceability or the conflict with applicable law, and all other provisions of this Agreement shall remain in effect.

\n

(g) Failure or delay on the part of the Company to exercise any right, power, privilege, or remedy will not constitute a waiver of, or bar the later exercise of, that or any other right, power, privilege, or remedy of the Company.

\n

(h) No rule of strict construction shall apply against or in favor of either party in the construction and interpretation of this Agreement.

\n

(i) This document was originally written in the English language, and the English text of this document shall prevail over any translation hereof.

\n\n", - "eulaVersion": 1, - "packages": [ - { - "component": "client", - "platform": "linux_arm64", - "file": "metavms-client_update-6.0.0.38086-linux_arm64-beta.zip", - "size": 282726618, - "md5": "79fb0e0d413f76fd888b9f1b69018b46", - "signature": "g7V7D7N7K09qTq0f1h2KRPK3El+HGUbYD7sTw0cw5ZcSPfWqchjFoqmQwseJTvyvjsmefW8dL/XeQTp6zrKJf/6+fA9j7BQoXz6IXWme8m5reob9ZnEhOuCrHNi2zf3JO9+PCwrVAkV2AVlIMMTr49gmAPvVn9c0O/jilqz6RtaZm5CWYjrdXUyPIbqer/aNxKpNNLfHJ4GkBEospoRpLqofxBXM1C2V9/R9tzqMoDO9R81fJjnztrqyoBRl1BdOPxTojNQ7D7AgAxOnsE8nSaq0HIn2ZvBmTY9q7OCv28jk2/6zUwKYoebw9SAbKcMa8O/nqZtQwU7EtGI0GKOhMJ1wUzKb2ymuhPrVrfeMhoB4FPjTfy2aUIfVDr7FgHePaxSpywowYcApdd406bW4kUoyp24dcz5NgTKoZJiMbubRWVpeB5DKcSFKgWjPE8Emaw5LCwJ+urlEpO8kcwaUDeBJAeB1VHYbknfoHVyWZtld98hzkhqkHQDCVl+gfHlx2BPEkAf4HdOIYN6FNz9DdTe6HT0mOulO5xbto+FWn1Z33lCFGa86EEsUmp1SCdlQ0Rb0DYh8eC26m0gVPIb0iTL4F7+q3+CQzyLgJsugURMCKZOvJiG/+mOnYWJK1JZcOPdQrTNjH5R3ZotqSPfYI+gdlyy1JOEcFOZ6Z84bzCA=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "18.04" - } - ] - }, - { - "component": "client", - "platform": "linux_x64", - "file": "metavms-client_update-6.0.0.38086-linux_x64-beta.zip", - "size": 281203560, - "md5": "c3be5b242d9b87574b2eb568cd43203f", - "signature": "TBLhJpLEn96Luf5ok56vbzXjOFF8ToBPlS88rGi3Wr7+Jg4oz24K0AxNSCgRcHEAwQdvja7EP1Ixgq+xvGKmIRHNIk316U7bz3ieuMOrQ36k49KsEdDlBgM+JsuUC9YqX9jx0HTVN+w97fM8iyzn7m4J3ImBIUmnUlaXgcX3T2I2Hhw5YAeQYqSiDDkOBD/GSYsHBpEiKQCL0TrBszGIMl94ADIbjZ3Dp2QPdQ7e/40GiQu9+CUAQlugGwVXf5ix0dT0K5+DS6uIKFFzIVCmX3O3JX5pf5gmC4qH35GJJRpx0IwjUk+8N55YK8uL/gGpFWOMaYelVu9Gx94rCfWrfPiww0Wfq35yhxZ7qLWPzFQmoVT4f6PZYhCjwl2lqEv8Gt5R1KpzgY3g1ZZVgBFNAVn4c08cVssUlbf2jzYS3CCEEEox747sSn3fiJRDO9lM6B2qOQVCCm4nbvG96axLUt4wpv878Jt5TGIFuitKQA7DbIKZDtFaWF0z97LnUoQo5v/oom18JzCYpxTIyw/V4CYoBwQAqpsdQrESOTr0TtnQUz07jZ8KyBkDwFEWn0DlbCPv++ZdMeHeUUlcpOhF/Pbi5RJE2PkXOV7Bv7lwo4S1hJ5/19Q39eLu2Wtj/B8ygJG8WhNPmYKs+hah5pZhyzW5fBQ7sLyrcklmkuzl+PA=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "18.04" - } - ] - }, - { - "component": "client", - "platform": "macos", - "file": "metavms-client_update-6.0.0.38086-macos-beta.zip", - "size": 219372732, - "md5": "c76a78dc11421786b0169b76c1485cf1", - "signature": "ZWk4138Gmh8qfapMFQ8nKnAZDOXlqzuhO4vZmuAYM3EeKM5s5RzmUrzoBK4n6qG/GuhjgvRP8p93LVhlgTn33Kpj7ErDUJ+f1j8Z4FPGvOO/LVBE4B7yVxbugIeatnSwcRlyhTy7s2w6llIfqPYkshAG6o344jj4i0iZsG4wEYm0jbRnpDQ545Xnl/wnJetynYRN0xGO9FfPKkuGL2ahlY9bDLOcssnPYijf7KLiAKiJ0uycVouZGnmCN/RYoID8x7WCHgjq3pFGVJ5Bx9AybOKbWndDOgWXGJSuZ8QLapM1uxM7ibsx320oBgTv+LibtqLcK5Lo+6xR5AjTSpyJj7VTkt0MNTxWiddtXKq2Vox9J/O0G6eT9SvyV0K1NPn0hFyNf2S4somSdsUko2Cs98KIhQMZLqYGQxTiS90lCzVPWor8jAagZSGWRR/0RLdstBs3eeEcdDq9pWRQpuI/E/oPbr35WZ0uNig+pw9HzhtV99UkpnJwBmytNaWOpqVEvwS3YGpHxUjsRh3pRe4JYLfeN/ytm9qCVUEjT9QrhOzrgs2ImkKuENrIoa8PLfWIG6izkTd6cIevBUR1xcbNw48cwbGfK5Ybzf9OpVWHZPvhAMTo29Lvc5P75XuhMGEezLdI18Jy1zvaYYSR7cVP0zUnyzm6frlGfg1lJSZ/K8g=", - "variants": [ - { - "minimumVersion": "10.16" - } - ] - }, - { - "component": "client", - "platform": "macos_arm64", - "file": "metavms-client_update-6.0.0.38086-macos_arm64-beta.zip", - "size": 201973015, - "md5": "ae8c852cead52283d44895cfdac4ddfa", - "signature": "GlVWMhGiC3e30/B40+yvdK8witVx9Gs3q9PgfANTg4GXYI8JvEyuKWTHnwWTZz8DRQcvUxuSBiMm8fgWucmSumXoTYMioYQPcmsiAa7GX9ZiJ93XSHEyh+2xhPSLML1U2TKKFXGjLqzTTOb/u9Fpf5j/FCqP/sHl+WtuCYxROZWpFmWRRwt9sci1l+vhlhXzNVgJ5xz76lIPfE2EVeSyQZo2u5+WEKc+Zhi/4klU3KXMrMMYXfWK6aJRTVlAYss/Aue/PMunZxAlSl5fikelTsL1aT9JOeQl7kpWNoj8GVnNTlDrtkriWYHIZIzNLCG6je3395AXYeGNjzwExwc0ufo6mLa1ITxEB6Y5LiRNP3Vo7RQstHMJLxgROt4AJgii5sU3cS3i2AJVRIxBdMJvcxWkMG9zlBKWc0WrhoJm6WhlRA3qYxbKqmulvJXG28IWiBxvr4xhxPXN5fDvrLKg03nIhclPdTRdwOxxnCIGmm6XUP5QnsoNt2N/CDbrcftNX3GaFvND4CRujKjriy8GBNSGXecLjtP4NXK1eUCewCSMVGicq4wkzc5JQuwCZf504smG4tZAioK8/bvWM6+AUnyd/xjuyjYV6tca5s+oQjMxFAvGpSR14quE1wnLRHYKj1fKrqtduZ77tWOf2qvZK3QlDgYJKtBagOULgYm8cfk=", - "variants": [ - { - "minimumVersion": "10.16" - } - ] - }, - { - "component": "client", - "platform": "macos_x64", - "file": "metavms-client_update-6.0.0.38086-macos_x64-beta.zip", - "size": 219372736, - "md5": "d646cca0af7ce95aa4dd7f90547a4438", - "signature": "ye0VzNZAdqAxD/Z66C59+XDcTL1pdZeqA+pALPZ4XXTHwhvaBDK8LG0XpAOQofwbHhmyaomSYyj4OUVmXU9OLAaJUDJjjw9/m3zXg5rlD8GnSeuThEVuDB9V4EdXLErK/QJcm9sOJd6JOjaylsXXlth3QcQrSrzKFl4VvGJDrz3uRXho3/chemd9PPLiBfXtNk9FImFP/LgayJtrGNTSXDdeKzelmO+5oH0PucHU0EuAnCV/ynvFEGfmRCyCxNQsy6fEr9+eZzFYEoWkAQkaMIOMSf1wwbQzlrfXkpqn0WO06ESuIxkhEjfkHHjM2uxMPVzhDoQkmHZ9cVEnUdjf6217lom6sq91bCki98MpzLcjwqcEwC1O/zHQg7oTAlNkwbWqWoJUedPHaFEsK1hFBBlQWn4ayWsLJpDkaKxy6nWbrpqX7kJnnTXs0kNWSm267BmtrbqqvGL/qomSOaYeJeRPwyUT8NdZ4UHk4YsANJcp2Zw4CRdpuPbzqvsUw+LYgGUzAZZ/fTJbAW36LWRjdO0r0EKaeSR0OV29lpu1P/Ed8uKKzTau8K1Ey5l+y8ErLt0B+dFL6IQDb1OSbh0ckJthH6EE/SIzEs2yLNQWzlvojHY+JwT7nsDt7b/3EMxYuvBih1Plms2fizd6ZysLkAFTcr+0+ueM18ZenVgIB0Y=", - "variants": [ - { - "minimumVersion": "10.16" - } - ] - }, - { - "component": "client", - "platform": "windows_x64", - "file": "metavms-client_update-6.0.0.38086-windows_x64-beta.zip", - "size": 199831192, - "md5": "4bcd9723ec9eb960d7afed675ede1098", - "signature": "VERbzAL23xdKFOXTs2+z8gYQPaFCoXeFX4u2DLektmGGABHd2HLvlav5KN7u6aGbkj8vPfiVr/qoA+0JyJhKoi3Y5N/fj6OUNjDDZt0FXHDPZNjxCTkCrt4k4kwVQ+OROETS8CsmtIIptz46SIaDMEvy67alUdnPcoPsy4ddcrMyJ73utwgAzCkZMBAeAfsx7g2B3xM7lX27UhhwRa+ABsZW1TciF0UgFD1WTvyKaSI3eaOLAPnZXOQlpeej2awcBN6p0Sf2HLjdHbJEishiNfHgG1MGjD5k8ZcTVItSg3hRbHWEVZ3b7dWVJIrdkwfiN+ISuUWWtqONZ10ou61Po1La08j+ro+OV/BEXsU5xP0ZT1vuH3ZW9y78Uz+bhxrDVD7zHZ+WPovGhwbt+EpiKfsZbS3jh22o6U8jhA/EL25VNwDeo19HJlwfMADrbOwoBmcPDJvyJZj5hadsfC3UBfiicfeMNH5HMP0ZEPn9rElkrlnslAU9gxcDOoL2U6uzCfNsF8OSu/DN7tqiYm7RuhbKynUJ5siGZhV+hr/Nx2WhsEdMVN8CY9yNq7FL2ysFl36eamX5qzZtnSAWkFjy1iLmqXO1Zc8HLQ/f3Rqx0w50YDML20ceK21Yus1jHpZ1jksujjDSzYsgxlGmELfXvMB4YFtrlHOV8OUglrvXad8=", - "variants": [ - { - "minimumVersion": "10.0" - } - ] - }, - { - "component": "server", - "platform": "linux_arm32", - "file": "metavms-server_update-6.0.0.38086-linux_arm32-beta.zip", - "size": 153105322, - "md5": "e985edbcd2e2f3ba3855ac87914b8a06", - "signature": "BR3SY6C3LqzM0v/Qc3RGl/SNthjU+X0bBhxEJqRzHnSh8GolOde+ttrc+5gaQGZCFjKxRVUmqXiUtHX0cmiR+qlhp5VZkjh3qG8g8TUJoL6L7dR/k5y4IPY51a517QJrGXPw+8s8HIAS4N/9HHtadzoDl/F7Z78fbkXgww4qR0vxeDDKBq8wmPN/fgnqN46CF70T75+1rIyLr6VjJ6obiBZ0vY81ZevtCOKMzqPeYgOSy+9/pOH/OuSaDdeph3i7KxaNdSr2gt883E1IBFykC524cw182+GtBvdlWefNgsHQ/2qikaxjWgx9BRr2NaoCX3qq4SrRbiGUEJ4whuRBnx1+k+iZwABghvVDnVpPsoBZc5z1wlsjoRrnWUVmFeeapdVl9sPmiRyYHOOHQqpitD2DjiOylQ/2CLOHOQlyTgLdCNOklDCvlKWI1DkR0XVeeR0y+pmOcEq97ewIRfw+A7h46+xqV4pWgxmWDnNl5ff2ociNoglqO6gh35Gc08VFE0qJFey3m/xzBLQ+iiKPTY1h2t18RFHdTt38yPtTLoFDjeUwVhFlEOS9Bn1T3Z5FOpYTkDAS3fNy+atCvK01TTChsZW69jyOpPRfCg89W88aOQ2LVHv64jUpvKE8PH9V0Ej1FzClrZO260S23SHJGjYpnMj5kvQ2fcO9lJ1J2hY=", - "variants": [ - { - "name": "debian", - "minimumVersion": "9" - }, - { - "name": "ubuntu", - "minimumVersion": "18.04" - }, - { - "name": "raspbian", - "minimumVersion": "9" - } - ] - }, - { - "component": "server", - "platform": "linux_arm64", - "file": "metavms-server_update-6.0.0.38086-linux_arm64-beta.zip", - "size": 128927202, - "md5": "e9728af6304a1380a706afa9d16eab21", - "signature": "dbQVDOUDfD8d01XNHP0N/X3XlUaO2/LWGPy/LQys9ZTMS+2XGABOfDDKWsnwSKGuprBasbJKwQxu7bsGhkvmijqhRK0/fVWc8Rcdjo4cle16YiVX6kVm1O+zWH7cI2kqySSaGwsP1HsbYl6+4NzUm3eh0RjXb2IaErZHIRfZKvhNmQmihBAFXjEYMtO5yUDnsa6WjAVswqbqoLacjSEN2DgFDLnE8HcQZQBQF4mEN0tLidFF2TEWiRp6w5EDobO8ljhi9MvCtubqHjvQJJCkayZ1k68u75jjFWV4mn2oCK6PHbL45BDT123c80xfh/EIoLbfq08MHcb0W7yvFZp5EBsxqOOOPgmgxBsEMp4tkxcjEngXSiYuyqtC9aeuKYVD2obx5KXn/7wDD2y5a8KnPk97nGw+4bXSnqYmai8t1oXVbWb0VUrVgql4H/onejvROqKuE5EV5Yh4pvxXSbv32odvTL/0dd7o/o3YSuSpyYCAyPeGfT6CJ6TuBipm0QVgFLTRWtYvAEo6O8jU/bLEwdcDIxbVw/b0SXJieQF9buTIEoEIdXKqG7P/yZjJmyw8cLZuwgZftZYi7kI+JxfARsqYXoWGNTUo/X+DuPYDlS92dELHNCR55oMYimwK2Dn4FOIBJ76IEhTEeaihESamOqNnao0ujhyZuafLJhJIQG4=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "18.04" - } - ] - }, - { - "component": "server", - "platform": "linux_arm64", - "file": "metavms-server_update-6.0.0.38086-linux_arm64.vivotek_edge1-beta.zip", - "size": 178921404, - "md5": "5e670d1ae52ed6e1c06974698d6e3117", - "signature": "I/6i8dac7k5QBOKJS6+qhIJDPFD1SU/6igv7T6gWuzWgWBD7EtUJOUqG5PkagnJT698AUYV3fitFgcikxOoqq/31tdiZq3Ig94NHVrwqKKmjnMkWoLVWpGLS89MnDXeJXMqXYTCBEkYyU7OyD7VynjyRW+RcC1AK5gfrB1OXH4tRFJ7USxG8ZTcagMjTX9dibSWOy1Lhb1fypkYU92sgIg0+oH/TqucS7IdA9EcGzgYEmbQB8RcDnI8pmGdqaXSRzSeOTmjIY3xHKxhzHQKwz76jphb/J7dy+b14N4ogLwexG1S8TdVbkVPKVZzHo3PRLHlC1A9AyDC0NGKo/MOVcrqdPz39vRFnI8Xt+NqJAYGdIejjiuZjupBv7BLX1yHl8z/5OryX6Idhs2w7c77//MVXlpLoVVTzAQH069ECy6ZX4olia323R/3s/PTRIOzlvZfWXe7oPqBVQYA/CcuikEgHPvTd/1780bKLExQ4uE+JFvSU1NZ6+dfMX5uKIGaMfpKSo/fj6D9WtHUpxIiSmgE8urHrkEqimXLuTIc9OO3OtQufNxNiQtcGvSdf+MXsWwoGXAW4IwwO1mF+wSkxQUXHWBJO5iHHuAAlJDqIlCVwbEVZD6Zb+5L3xP0yeNSqY5tLC/ak3mfwbRb1V37gJvLJ0Xip1u+erXrtDM9pwvU=", - "variants": [ - { - "name": "flavor:vivotek_edge1" - } - ] - }, - { - "component": "server", - "platform": "linux_x64", - "file": "metavms-server_update-6.0.0.38086-linux_x64-beta.zip", - "size": 114637378, - "md5": "3cbce22ff5a59849b4faf23c2112090c", - "signature": "fORSN39H2h6QQVnlAIOy2kjBPazWoK+sHA523yb4gNFOoLFQsQa6E/O+kqC8jjpPJMlu00Cg8elHO83rAKXvD1klSym2ByAvHNjBU4xH18S76FsmtZrhRjG0MovL/D8M2Z3g/50Y7YPIBAKn/ughjAuYd27zznWK3REbkkkKXYMROXE1dMKGAzAbeVGIvOOZRe5iGMGQI2L9u8mtFsZoGFlZerB8bkYOF5rdN+KBrNPslChW66579t0KyqDo7CjmE8x3imc0HW0hMd5gdDQ8DIRsNXzbjLRIqtuiDzCCfIb8eNFTMftKfhp7EKtBAdR2yLJz09PkdSMr8ri2SxZTClb77zl59ewZE2lnjg8Z8AJINTe3q22zdzO4TkGrYLvk8l3LvLFCYts44IjfgEQuCLmYJe+4HVn/lT5/XReqSUBHbmiqbcnvteljt/Q7e2TbW7YH7K3mfCFojsQJbQTIrLQKqTSvasMsayLZrLoIa9ZZBpShj54PPYCvCiSE93fhIh8kXaNXKQK1QR9c/c+GUwd5DHbFVUzrWYpnaiJs0btmA8MKvxFx27AGX7eTlHzHGmNSxflK7mDmycGkM7FAJJfGM8Q0AeLLt9qTq+eaqWwo4QJRlJYvxAGTvGSNOfj6cY/Wwz7hRljfaF4/gwodudT34UWkDFy36xh6utVLNuI=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "18.04" - } - ] - }, - { - "component": "server", - "platform": "windows_x64", - "file": "metavms-server_update-6.0.0.38086-windows_x64-beta.zip", - "size": 109881758, - "md5": "efabdca9e0d37bb8fc00475c1333b4ac", - "signature": "ACffywPrAIX8hzdA62P+giSEoiB+Q/0nOe59pLn6ivoG3osrTzpv7YLKPiB9w5GZ0DPyExLHRkp/98wAUw7rGkYsezFjzynA0/r5hYURj26ew7dGVz0sg+NI3adSj45o+d6rhsMao39A2cU8eTjcLm9+cgjaJFtE7EmJHv5TymNNDHKpteNIsGzNgIiZrRyUA7YM/9IUMEWEkzAe3LBygHx76V/3lBZU50Wb/pxUxA0+WuImT9uwLQWDCtG/xdHgCt9+FfjpWLCN18G9vP4QGVnEEOgXqfv6nS2FID6ylal8oCbcbmz3P6iNJHMIryztQDXkZTQ9bjCrCro+5RPKJ9NgxcxeKzuL7SglgW/uiBQ6mwLRAHQRsQidw0LLfwLKWnKfk5LjIMrOBIVfCZfQHqntDSBd0B24lq3RRILlUthrULaqn0wXv7N8BkOafSNeKICCKJb7SL4OTbpmnemc7R7y/bpVGXjTkCBq2ROn2Ank3czq2X/RdkpVWFzjC6U+eKZ2pDU5kV3EvYWQMcydTumASHo461so9axTxRlhmVx86RM7aEBVFpGgs3Jmh5+pDdLwAFVgoq6QKqLGONcaIO3GtAsD28VZL8uXZOmNf0Zd8bh1IpSoSyptQnNvIKefyVZyyaXzCKGHHbLGa66g42VKY9kdBVUM9N03mQT9Zyo=", - "variants": [ - { - "minimumVersion": "10.0" - } - ] - } - ] +{ + "version": "6.0.0.38086", + "cloudHost": "meta.nxvms.com", + "releaseNotesUrl": "https://meta.nxvms.com/downloads/releases", + "description": "", + "eula": "\n\n\n \n Nx Witness EULA\n\n\n\t

NETWORK OPTIX, INC.

\n

END USER LICENSE AGREEMENT

\n

IMPORTANT – PLEASE READ CAREFULLY: This End User License Agreement (the “Agreement”) is a legally enforceable contract between You (either an individual or an entity) and Network Optix, Inc. (the “Company”) regarding the use of the Company’s software programs accompanying or provided under this Agreement together with the SDK made available by the Company, associated media, corresponding Documentation, technical configurations, and technical data (collectively, the “Software”). “Documentation” means the user’s guide and other documentation (including print and online), if any, provided to you with the Software.

\n

BY CLICKING “I ACCEPT” BUTTON BELOW OR BY DOWNLOADING, INSTALLING OR OTHERWISE USING THE SOFTWARE, YOU CONSENT TO BE LEGALLY BOUND BY ALL THE TERMS AND CONDITIONS OF THE AGREEMENT. IF YOU ARE ENTERING INTO THE AGREEMENT ON BEHALF OF A COMPANY OR OTHER LEGAL ENTITY, YOU REPRESENT THAT YOU HAVE THE AUTHORITY TO BIND SUCH ENTITY AND ITS AFFILIATES TO THE AGREEMENT, IN WHICH CASE THE TERMS \"YOU\" OR \"YOUR\" SHALL REFER TO SUCH ENTITY AND ITS AFFILIATES. IF YOU DO NOT HAVE SUCH AUTHORITY, OR IF YOU DO NOT AGREE WITH ANY OF THE TERMS OF THE AGREEMENT, YOU MUST NOT ACCEPT THE AGREEMENT AND MAY NOT USE THE SOFTWARE.

\n

In addition, certain third party programs or code (“Third Party Software”) are being provided to you under the terms and conditions of a separate end user license accompanying such Third Party Software (“Third Party Software Licenses”), which will be provided to you by the Company as part of the Documentation or may be made available for viewing by the Company at the website or webpage at https://meta.nxvms.com/content/libraries (the “Third Party Site”). If required by any Third Party Software License, the Company will make such Third Party Software, and the Company’s modifications to that Third Party Software, available to you on the Third Party Site. The Company may be required or wish to provide you with certain notices or information about Third Party Software or Third Party Software Licenses and you acknowledge that such notices and information may be provided to you by the posting of such notices and information on the Third Party Site.. BY INSTALLING, COPYING OR OTHERWISE USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THE TERMS OF THIS AGREEMENT AS WELL AS THE TERMS OF THIRD PARTY SOFTWARE LICENSES. IF YOU DO NOT AGREE TO ALL SUCH TERMS, DO NOT INSTALL OR USE THE SOFTWARE.

\n

1. License Grant. Subject to the terms and conditions of this Agreement, the Company grants you a non-exclusive, non-transferable, non-sublicensable, limited license to install and use the Software, in object code version only, for your own use subject to the restrictions contained in this Agreement.

\n

2. Backup Copies. You may make one backup copy of the Software, provided your backup copy is not installed or used on any computer and further provided that all such copies shall bear the original and unmodified copyright, patent and other intellectual property markings that appear on or in the Software. You may not transfer the rights to a backup copy unless you transfer all rights in the Software as provided in this Agreement.

\n

3. Ownership. You acknowledge that the Software and any copies that you are authorized by the Company to make are the intellectual property of, and are owned by, the Company and its licensors. The structure, sequence and organization of the Software are the valuable trade secrets and confidential information of the Company and its licensors. The Software is protected by copyright, including without limitation by United States copyright law, international treaty provisions and applicable laws in the country in which it is used. You acknowledge that the Company, or its licensors, retain the ownership of all patents, copyrights, trade secrets, trademarks and other intellectual property rights pertaining to the Software. You will take no actions which adversely affect the Company’s intellectual property rights in the Software. This Agreement is a license and not an agreement for sale. No title to, or ownership of, the Software, or any intellectual property rights subsisting therein, is transferred to you.

\n

4. Restrictions. The Software is licensed solely for your own use. The Company and its licensors reserve all rights not expressly granted to you in this Agreement. Without limiting the generality of the foregoing, you shall not nor shall you permit any other party to: (i) make copies of the Software except as expressly set forth in this Agreement, or (ii) disassemble, decompile or translate any part of the Software, or otherwise attempt to reconstruct or discover the source code of the Software, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation, or (iii) modify or create derivative works based upon the Software, or (iv) externally distribute, sublicense, resell, encumber or otherwise transfer the Software, or (v) rent, lease, lend, or use the Software, or any part thereof, for timesharing or bureau use, or (vi) allow a third party to copy, access, or use the Software (except as expressly provided in this Agreement), or (vi) alter or remove any copyright, trademark or other proprietary notice which may appear on the Software, (vii) take any action that would cause the Software to be placed in the public domain or become subject to open source license agreement, or (viii) use the Software in any manner that violates any statute, law, rule, regulation, directive, guideline, bylaw whether presently in force or may be implemented by federal, state or local authorities.

\n

5. Paid Features. The Software is made available to you at no charge or fees. However, the Company may offer certain premium features to you upon payment of a license fees determined by the Company in its sole discretion (“Paid Features”). In order to use Paid Features, you must agree to pay the license fees plus any applicable taxes through one of the payment methods accepted by the Company. Upon receipt of the license fees, the Company will either provide you a license key to activate the Paid Features or allow you to download the Software with the Paid Features. Activation of Paid Features may also be regulated through the use of transferrable license keys, and such license keys may only be transferred within your organization subject to the limitations listed in your purchase order or other documentation describing the Software and/or license rights obtained in the Software. You expressly acknowledge and agree that this Agreement will govern your use of the Paid Features.

\n

6. Updates. Except as otherwise expressly provided herein, this Agreement will govern any updates and upgrades to the Software that may be provided to you in accordance with the Company’s then-current maintenance and support policies, unless such updates and upgrades are provided under a separate license agreement. You may use such updates or upgrades only in conjunction with your then-existing the Software licensed under this Agreement. The Software and all updates and upgrades are licensed as a single product and such updates and upgrades may not be separated from the Software to exceed the scope of your original license. Any other software that may be provided with the Software that is associated with a separate end-user license agreement is licensed to you under the terms of that separate license agreement.

\n

7. No Support. Except as expressly required by applicable law, nothing in this Agreement entitles you to any support, maintenance or new versions of the Software. You may contact the Company to determine the availability of support, maintenance, and new versions, and the fees terms and conditions that would apply.

\n

8. Disclaimer of Warranties. THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OR REPRESENTATION OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COMPANY NOR ITS LICENSORS REPRESENT OR WARRANT THAT THE SOFTWARE WILL SATISFY YOUR REQUIREMENTS OR THAT IT IS WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE UNINTERRUPTED OR ERROR FREE. The Company does not warrant that functions contained in the Software will meet your requirements or that the operation of the Software will be error-free or uninterrupted. The Company is not responsible for problems caused by changes in, or modifications to, the operating characteristics of any computer hardware or operating system for which the Software or any upgrade or update is procured, nor is the Company responsible for problems which occur as a result of the use of the Software in conjunction with software of third parties or with hardware, which does not meet the Company’s specific requirements. Some jurisdictions do not allow certain disclaimers or limitations of warranties, so some of these may not apply to you.

\n

9. Limitation Of Liability. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW EVEN IF A REMEDY FAILS ITS ESSENTIAL PURPOSE, IN NO EVENT SHALL THE COMPANY OR ITS LICENSORS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE OR THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF THE COMPANY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, THE COMPANY’S ENTIRE LIABILITY TO YOU OR ANY OTHER PARTY FOR LOSS OR DAMAGE RESULTING FROM ANY CLAIMS, DEMANDS OR ACTIONS ARISING OUT OF THIS AGREEMENT SHALL NOT EXCEED THE FEE ACTUALLY PAID BY YOU TO THE COMPANY FOR THE SOFTWARE, IF ANY, NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. Because some states and jurisdictions do not allow the exclusion or limitation of liability, the above limitation may not apply to you. In such states and jurisdictions, the Company’s liability shall be limited to the greatest extent permitted by law.

\n

10. Sole Remedy and Allocation of Risk. YOUR SOLE AND EXCLUSIVE REMEDY AND THE COMPANY’S SOLE AND EXCLUSIVE LIABILITY ARE SET FORTH IN THIS AGREEMENT. This Agreement defines a mutually agreed-upon allocation of risk.

\n

11. Third Party Software Licenses. Third Party Software included with the Software is licensed under the terms of Third Party Software Licenses. You understand and agree that, although provided to you by the Company with the Software, your use of Third Party Software shall be and is governed by such Third Party Software Licenses. You understand and acknowledge that Third Party Software is being provided “AS IS” WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. If there is a conflict between this Agreement and any Third Party Software License with respect to Third Party Software, the provisions of such Third Party Software License shall prevail.

\n

12. Export Restrictions. You agree to comply with all export and re-export restrictions and regulations (“Export Restrictions”) imposed by the United States or any other country in which you conduct business. Without limiting the generality of the foregoing, and regardless of any disclosure made by you to the Company regarding an ultimate destination of the Software, you represent and warrant that you understand the U.S. law currently prohibits the export or re-export, directly or indirectly (including via remote access) of U.S. origin products and technology to certain proscribed countries (including Cuba, Iran, Sudan, North Korea and Syria and/or any other country that may become subject to an embargo by the United States), entities, organizations and individuals, without prior authorization from the U.S. Government. You will not commit any act or omission that will result in a breach of any such Export Restrictions. Your breach of this clause shall constitute cause for immediate termination of this Agreement.

\n

13. High Risk Activities. The Software is not designed, manufactured or intended for use or resale as on-line control equipment in hazardous environments requiring fail-safe performance, such as in the operation of nuclear facilities, aircraft navigation or communication systems, air traffic control, direct life support machines, or weapons systems, in which the failure of the Software could lead directly to death, personal injury, or severe physical or environmental damage (“High Risk Activities”). The Company specifically disclaims any express or implied warranty of fitness for High Risk Activities.

\n

14. Termination. The licenses granted herein shall automatically terminate without notice if you fail to comply with any material provision of this Agreement. In such event, you must immediately uninstall and return the Software, including all Documentation and original media, to the Company. This shall not limit or affect any remedy available to the Company for your breach of this Agreement.

\n

15. Miscellaneous.

\n

(a) If you are a unit or agency of the United States Government, the following applies: The Software is provided with RESTRICTED RIGHTS. Use, duplication or disclosure by the Government is subject to restrictions as set forth in Subparagraphs (a) through (d) of the Commercial Computer-Restricted Rights clause at FAR 52.227-19 when applicable, or in Subparagraph 252.227-7013 (c)(1)(ii) of the Rights in Technical Data and Computer Software at DFARS, and in similar clauses in the NASA FAR Supplement. Contractor/manufacturer is Network Optix, Inc.

\n

(b) This Agreement shall be governed by the laws of the State of California, exclusive of its choice of law rules. Any dispute between you and the Company regarding this Agreement will be subject to the exclusive jurisdiction of the state and federal courts sitting in the County of Los Angeles in the State of California. In any such action, the prevailing party will be entitled to costs and attorneys’ fees.

\n

(c) You may not assign, sublicense, or transfer this Agreement, the Software, or any rights or obligations hereunder without prior written consent of the Company. Any such attempted assignment, sublicense, or transfer will be null and void. The Company may terminate this Agreement in the event of any such attempted assignment, sublicense, or transfer.

\n

(d) This Agreement is in the English language only, and will only be provided in the English language, which language will be controlling in all respects, and all versions hereof in any other language will not be binding on the parties hereto. All communications and notices to be made or given pursuant to this Agreement must be in the English language.

\n

(e) The Agreement constitutes the final and complete understanding between you and the Company with respect to the subject matter of this Agreement. Any modifications or waivers of this Agreement must be in writing and signed by both parties hereto.

\n

(f) If any provision in this Agreement is invalid or unenforceable or contrary to applicable law, such provision shall be construed, limited, or altered, as necessary, to eliminate the invalidity or unenforceability or the conflict with applicable law, and all other provisions of this Agreement shall remain in effect.

\n

(g) Failure or delay on the part of the Company to exercise any right, power, privilege, or remedy will not constitute a waiver of, or bar the later exercise of, that or any other right, power, privilege, or remedy of the Company.

\n

(h) No rule of strict construction shall apply against or in favor of either party in the construction and interpretation of this Agreement.

\n

(i) This document was originally written in the English language, and the English text of this document shall prevail over any translation hereof.

\n\n", + "eulaVersion": 1, + "packages": [ + { + "component": "client", + "platform": "linux_arm64", + "file": "metavms-client_update-6.0.0.38086-linux_arm64-beta.zip", + "size": 282726618, + "md5": "79fb0e0d413f76fd888b9f1b69018b46", + "signature": "g7V7D7N7K09qTq0f1h2KRPK3El+HGUbYD7sTw0cw5ZcSPfWqchjFoqmQwseJTvyvjsmefW8dL/XeQTp6zrKJf/6+fA9j7BQoXz6IXWme8m5reob9ZnEhOuCrHNi2zf3JO9+PCwrVAkV2AVlIMMTr49gmAPvVn9c0O/jilqz6RtaZm5CWYjrdXUyPIbqer/aNxKpNNLfHJ4GkBEospoRpLqofxBXM1C2V9/R9tzqMoDO9R81fJjnztrqyoBRl1BdOPxTojNQ7D7AgAxOnsE8nSaq0HIn2ZvBmTY9q7OCv28jk2/6zUwKYoebw9SAbKcMa8O/nqZtQwU7EtGI0GKOhMJ1wUzKb2ymuhPrVrfeMhoB4FPjTfy2aUIfVDr7FgHePaxSpywowYcApdd406bW4kUoyp24dcz5NgTKoZJiMbubRWVpeB5DKcSFKgWjPE8Emaw5LCwJ+urlEpO8kcwaUDeBJAeB1VHYbknfoHVyWZtld98hzkhqkHQDCVl+gfHlx2BPEkAf4HdOIYN6FNz9DdTe6HT0mOulO5xbto+FWn1Z33lCFGa86EEsUmp1SCdlQ0Rb0DYh8eC26m0gVPIb0iTL4F7+q3+CQzyLgJsugURMCKZOvJiG/+mOnYWJK1JZcOPdQrTNjH5R3ZotqSPfYI+gdlyy1JOEcFOZ6Z84bzCA=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "18.04" + } + ] + }, + { + "component": "client", + "platform": "linux_x64", + "file": "metavms-client_update-6.0.0.38086-linux_x64-beta.zip", + "size": 281203560, + "md5": "c3be5b242d9b87574b2eb568cd43203f", + "signature": "TBLhJpLEn96Luf5ok56vbzXjOFF8ToBPlS88rGi3Wr7+Jg4oz24K0AxNSCgRcHEAwQdvja7EP1Ixgq+xvGKmIRHNIk316U7bz3ieuMOrQ36k49KsEdDlBgM+JsuUC9YqX9jx0HTVN+w97fM8iyzn7m4J3ImBIUmnUlaXgcX3T2I2Hhw5YAeQYqSiDDkOBD/GSYsHBpEiKQCL0TrBszGIMl94ADIbjZ3Dp2QPdQ7e/40GiQu9+CUAQlugGwVXf5ix0dT0K5+DS6uIKFFzIVCmX3O3JX5pf5gmC4qH35GJJRpx0IwjUk+8N55YK8uL/gGpFWOMaYelVu9Gx94rCfWrfPiww0Wfq35yhxZ7qLWPzFQmoVT4f6PZYhCjwl2lqEv8Gt5R1KpzgY3g1ZZVgBFNAVn4c08cVssUlbf2jzYS3CCEEEox747sSn3fiJRDO9lM6B2qOQVCCm4nbvG96axLUt4wpv878Jt5TGIFuitKQA7DbIKZDtFaWF0z97LnUoQo5v/oom18JzCYpxTIyw/V4CYoBwQAqpsdQrESOTr0TtnQUz07jZ8KyBkDwFEWn0DlbCPv++ZdMeHeUUlcpOhF/Pbi5RJE2PkXOV7Bv7lwo4S1hJ5/19Q39eLu2Wtj/B8ygJG8WhNPmYKs+hah5pZhyzW5fBQ7sLyrcklmkuzl+PA=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "18.04" + } + ] + }, + { + "component": "client", + "platform": "macos", + "file": "metavms-client_update-6.0.0.38086-macos-beta.zip", + "size": 219372732, + "md5": "c76a78dc11421786b0169b76c1485cf1", + "signature": "ZWk4138Gmh8qfapMFQ8nKnAZDOXlqzuhO4vZmuAYM3EeKM5s5RzmUrzoBK4n6qG/GuhjgvRP8p93LVhlgTn33Kpj7ErDUJ+f1j8Z4FPGvOO/LVBE4B7yVxbugIeatnSwcRlyhTy7s2w6llIfqPYkshAG6o344jj4i0iZsG4wEYm0jbRnpDQ545Xnl/wnJetynYRN0xGO9FfPKkuGL2ahlY9bDLOcssnPYijf7KLiAKiJ0uycVouZGnmCN/RYoID8x7WCHgjq3pFGVJ5Bx9AybOKbWndDOgWXGJSuZ8QLapM1uxM7ibsx320oBgTv+LibtqLcK5Lo+6xR5AjTSpyJj7VTkt0MNTxWiddtXKq2Vox9J/O0G6eT9SvyV0K1NPn0hFyNf2S4somSdsUko2Cs98KIhQMZLqYGQxTiS90lCzVPWor8jAagZSGWRR/0RLdstBs3eeEcdDq9pWRQpuI/E/oPbr35WZ0uNig+pw9HzhtV99UkpnJwBmytNaWOpqVEvwS3YGpHxUjsRh3pRe4JYLfeN/ytm9qCVUEjT9QrhOzrgs2ImkKuENrIoa8PLfWIG6izkTd6cIevBUR1xcbNw48cwbGfK5Ybzf9OpVWHZPvhAMTo29Lvc5P75XuhMGEezLdI18Jy1zvaYYSR7cVP0zUnyzm6frlGfg1lJSZ/K8g=", + "variants": [ + { + "minimumVersion": "10.16" + } + ] + }, + { + "component": "client", + "platform": "macos_arm64", + "file": "metavms-client_update-6.0.0.38086-macos_arm64-beta.zip", + "size": 201973015, + "md5": "ae8c852cead52283d44895cfdac4ddfa", + "signature": "GlVWMhGiC3e30/B40+yvdK8witVx9Gs3q9PgfANTg4GXYI8JvEyuKWTHnwWTZz8DRQcvUxuSBiMm8fgWucmSumXoTYMioYQPcmsiAa7GX9ZiJ93XSHEyh+2xhPSLML1U2TKKFXGjLqzTTOb/u9Fpf5j/FCqP/sHl+WtuCYxROZWpFmWRRwt9sci1l+vhlhXzNVgJ5xz76lIPfE2EVeSyQZo2u5+WEKc+Zhi/4klU3KXMrMMYXfWK6aJRTVlAYss/Aue/PMunZxAlSl5fikelTsL1aT9JOeQl7kpWNoj8GVnNTlDrtkriWYHIZIzNLCG6je3395AXYeGNjzwExwc0ufo6mLa1ITxEB6Y5LiRNP3Vo7RQstHMJLxgROt4AJgii5sU3cS3i2AJVRIxBdMJvcxWkMG9zlBKWc0WrhoJm6WhlRA3qYxbKqmulvJXG28IWiBxvr4xhxPXN5fDvrLKg03nIhclPdTRdwOxxnCIGmm6XUP5QnsoNt2N/CDbrcftNX3GaFvND4CRujKjriy8GBNSGXecLjtP4NXK1eUCewCSMVGicq4wkzc5JQuwCZf504smG4tZAioK8/bvWM6+AUnyd/xjuyjYV6tca5s+oQjMxFAvGpSR14quE1wnLRHYKj1fKrqtduZ77tWOf2qvZK3QlDgYJKtBagOULgYm8cfk=", + "variants": [ + { + "minimumVersion": "10.16" + } + ] + }, + { + "component": "client", + "platform": "macos_x64", + "file": "metavms-client_update-6.0.0.38086-macos_x64-beta.zip", + "size": 219372736, + "md5": "d646cca0af7ce95aa4dd7f90547a4438", + "signature": "ye0VzNZAdqAxD/Z66C59+XDcTL1pdZeqA+pALPZ4XXTHwhvaBDK8LG0XpAOQofwbHhmyaomSYyj4OUVmXU9OLAaJUDJjjw9/m3zXg5rlD8GnSeuThEVuDB9V4EdXLErK/QJcm9sOJd6JOjaylsXXlth3QcQrSrzKFl4VvGJDrz3uRXho3/chemd9PPLiBfXtNk9FImFP/LgayJtrGNTSXDdeKzelmO+5oH0PucHU0EuAnCV/ynvFEGfmRCyCxNQsy6fEr9+eZzFYEoWkAQkaMIOMSf1wwbQzlrfXkpqn0WO06ESuIxkhEjfkHHjM2uxMPVzhDoQkmHZ9cVEnUdjf6217lom6sq91bCki98MpzLcjwqcEwC1O/zHQg7oTAlNkwbWqWoJUedPHaFEsK1hFBBlQWn4ayWsLJpDkaKxy6nWbrpqX7kJnnTXs0kNWSm267BmtrbqqvGL/qomSOaYeJeRPwyUT8NdZ4UHk4YsANJcp2Zw4CRdpuPbzqvsUw+LYgGUzAZZ/fTJbAW36LWRjdO0r0EKaeSR0OV29lpu1P/Ed8uKKzTau8K1Ey5l+y8ErLt0B+dFL6IQDb1OSbh0ckJthH6EE/SIzEs2yLNQWzlvojHY+JwT7nsDt7b/3EMxYuvBih1Plms2fizd6ZysLkAFTcr+0+ueM18ZenVgIB0Y=", + "variants": [ + { + "minimumVersion": "10.16" + } + ] + }, + { + "component": "client", + "platform": "windows_x64", + "file": "metavms-client_update-6.0.0.38086-windows_x64-beta.zip", + "size": 199831192, + "md5": "4bcd9723ec9eb960d7afed675ede1098", + "signature": "VERbzAL23xdKFOXTs2+z8gYQPaFCoXeFX4u2DLektmGGABHd2HLvlav5KN7u6aGbkj8vPfiVr/qoA+0JyJhKoi3Y5N/fj6OUNjDDZt0FXHDPZNjxCTkCrt4k4kwVQ+OROETS8CsmtIIptz46SIaDMEvy67alUdnPcoPsy4ddcrMyJ73utwgAzCkZMBAeAfsx7g2B3xM7lX27UhhwRa+ABsZW1TciF0UgFD1WTvyKaSI3eaOLAPnZXOQlpeej2awcBN6p0Sf2HLjdHbJEishiNfHgG1MGjD5k8ZcTVItSg3hRbHWEVZ3b7dWVJIrdkwfiN+ISuUWWtqONZ10ou61Po1La08j+ro+OV/BEXsU5xP0ZT1vuH3ZW9y78Uz+bhxrDVD7zHZ+WPovGhwbt+EpiKfsZbS3jh22o6U8jhA/EL25VNwDeo19HJlwfMADrbOwoBmcPDJvyJZj5hadsfC3UBfiicfeMNH5HMP0ZEPn9rElkrlnslAU9gxcDOoL2U6uzCfNsF8OSu/DN7tqiYm7RuhbKynUJ5siGZhV+hr/Nx2WhsEdMVN8CY9yNq7FL2ysFl36eamX5qzZtnSAWkFjy1iLmqXO1Zc8HLQ/f3Rqx0w50YDML20ceK21Yus1jHpZ1jksujjDSzYsgxlGmELfXvMB4YFtrlHOV8OUglrvXad8=", + "variants": [ + { + "minimumVersion": "10.0" + } + ] + }, + { + "component": "server", + "platform": "linux_arm32", + "file": "metavms-server_update-6.0.0.38086-linux_arm32-beta.zip", + "size": 153105322, + "md5": "e985edbcd2e2f3ba3855ac87914b8a06", + "signature": "BR3SY6C3LqzM0v/Qc3RGl/SNthjU+X0bBhxEJqRzHnSh8GolOde+ttrc+5gaQGZCFjKxRVUmqXiUtHX0cmiR+qlhp5VZkjh3qG8g8TUJoL6L7dR/k5y4IPY51a517QJrGXPw+8s8HIAS4N/9HHtadzoDl/F7Z78fbkXgww4qR0vxeDDKBq8wmPN/fgnqN46CF70T75+1rIyLr6VjJ6obiBZ0vY81ZevtCOKMzqPeYgOSy+9/pOH/OuSaDdeph3i7KxaNdSr2gt883E1IBFykC524cw182+GtBvdlWefNgsHQ/2qikaxjWgx9BRr2NaoCX3qq4SrRbiGUEJ4whuRBnx1+k+iZwABghvVDnVpPsoBZc5z1wlsjoRrnWUVmFeeapdVl9sPmiRyYHOOHQqpitD2DjiOylQ/2CLOHOQlyTgLdCNOklDCvlKWI1DkR0XVeeR0y+pmOcEq97ewIRfw+A7h46+xqV4pWgxmWDnNl5ff2ociNoglqO6gh35Gc08VFE0qJFey3m/xzBLQ+iiKPTY1h2t18RFHdTt38yPtTLoFDjeUwVhFlEOS9Bn1T3Z5FOpYTkDAS3fNy+atCvK01TTChsZW69jyOpPRfCg89W88aOQ2LVHv64jUpvKE8PH9V0Ej1FzClrZO260S23SHJGjYpnMj5kvQ2fcO9lJ1J2hY=", + "variants": [ + { + "name": "debian", + "minimumVersion": "9" + }, + { + "name": "ubuntu", + "minimumVersion": "18.04" + }, + { + "name": "raspbian", + "minimumVersion": "9" + } + ] + }, + { + "component": "server", + "platform": "linux_arm64", + "file": "metavms-server_update-6.0.0.38086-linux_arm64-beta.zip", + "size": 128927202, + "md5": "e9728af6304a1380a706afa9d16eab21", + "signature": "dbQVDOUDfD8d01XNHP0N/X3XlUaO2/LWGPy/LQys9ZTMS+2XGABOfDDKWsnwSKGuprBasbJKwQxu7bsGhkvmijqhRK0/fVWc8Rcdjo4cle16YiVX6kVm1O+zWH7cI2kqySSaGwsP1HsbYl6+4NzUm3eh0RjXb2IaErZHIRfZKvhNmQmihBAFXjEYMtO5yUDnsa6WjAVswqbqoLacjSEN2DgFDLnE8HcQZQBQF4mEN0tLidFF2TEWiRp6w5EDobO8ljhi9MvCtubqHjvQJJCkayZ1k68u75jjFWV4mn2oCK6PHbL45BDT123c80xfh/EIoLbfq08MHcb0W7yvFZp5EBsxqOOOPgmgxBsEMp4tkxcjEngXSiYuyqtC9aeuKYVD2obx5KXn/7wDD2y5a8KnPk97nGw+4bXSnqYmai8t1oXVbWb0VUrVgql4H/onejvROqKuE5EV5Yh4pvxXSbv32odvTL/0dd7o/o3YSuSpyYCAyPeGfT6CJ6TuBipm0QVgFLTRWtYvAEo6O8jU/bLEwdcDIxbVw/b0SXJieQF9buTIEoEIdXKqG7P/yZjJmyw8cLZuwgZftZYi7kI+JxfARsqYXoWGNTUo/X+DuPYDlS92dELHNCR55oMYimwK2Dn4FOIBJ76IEhTEeaihESamOqNnao0ujhyZuafLJhJIQG4=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "18.04" + } + ] + }, + { + "component": "server", + "platform": "linux_arm64", + "file": "metavms-server_update-6.0.0.38086-linux_arm64.vivotek_edge1-beta.zip", + "size": 178921404, + "md5": "5e670d1ae52ed6e1c06974698d6e3117", + "signature": "I/6i8dac7k5QBOKJS6+qhIJDPFD1SU/6igv7T6gWuzWgWBD7EtUJOUqG5PkagnJT698AUYV3fitFgcikxOoqq/31tdiZq3Ig94NHVrwqKKmjnMkWoLVWpGLS89MnDXeJXMqXYTCBEkYyU7OyD7VynjyRW+RcC1AK5gfrB1OXH4tRFJ7USxG8ZTcagMjTX9dibSWOy1Lhb1fypkYU92sgIg0+oH/TqucS7IdA9EcGzgYEmbQB8RcDnI8pmGdqaXSRzSeOTmjIY3xHKxhzHQKwz76jphb/J7dy+b14N4ogLwexG1S8TdVbkVPKVZzHo3PRLHlC1A9AyDC0NGKo/MOVcrqdPz39vRFnI8Xt+NqJAYGdIejjiuZjupBv7BLX1yHl8z/5OryX6Idhs2w7c77//MVXlpLoVVTzAQH069ECy6ZX4olia323R/3s/PTRIOzlvZfWXe7oPqBVQYA/CcuikEgHPvTd/1780bKLExQ4uE+JFvSU1NZ6+dfMX5uKIGaMfpKSo/fj6D9WtHUpxIiSmgE8urHrkEqimXLuTIc9OO3OtQufNxNiQtcGvSdf+MXsWwoGXAW4IwwO1mF+wSkxQUXHWBJO5iHHuAAlJDqIlCVwbEVZD6Zb+5L3xP0yeNSqY5tLC/ak3mfwbRb1V37gJvLJ0Xip1u+erXrtDM9pwvU=", + "variants": [ + { + "name": "flavor:vivotek_edge1" + } + ] + }, + { + "component": "server", + "platform": "linux_x64", + "file": "metavms-server_update-6.0.0.38086-linux_x64-beta.zip", + "size": 114637378, + "md5": "3cbce22ff5a59849b4faf23c2112090c", + "signature": "fORSN39H2h6QQVnlAIOy2kjBPazWoK+sHA523yb4gNFOoLFQsQa6E/O+kqC8jjpPJMlu00Cg8elHO83rAKXvD1klSym2ByAvHNjBU4xH18S76FsmtZrhRjG0MovL/D8M2Z3g/50Y7YPIBAKn/ughjAuYd27zznWK3REbkkkKXYMROXE1dMKGAzAbeVGIvOOZRe5iGMGQI2L9u8mtFsZoGFlZerB8bkYOF5rdN+KBrNPslChW66579t0KyqDo7CjmE8x3imc0HW0hMd5gdDQ8DIRsNXzbjLRIqtuiDzCCfIb8eNFTMftKfhp7EKtBAdR2yLJz09PkdSMr8ri2SxZTClb77zl59ewZE2lnjg8Z8AJINTe3q22zdzO4TkGrYLvk8l3LvLFCYts44IjfgEQuCLmYJe+4HVn/lT5/XReqSUBHbmiqbcnvteljt/Q7e2TbW7YH7K3mfCFojsQJbQTIrLQKqTSvasMsayLZrLoIa9ZZBpShj54PPYCvCiSE93fhIh8kXaNXKQK1QR9c/c+GUwd5DHbFVUzrWYpnaiJs0btmA8MKvxFx27AGX7eTlHzHGmNSxflK7mDmycGkM7FAJJfGM8Q0AeLLt9qTq+eaqWwo4QJRlJYvxAGTvGSNOfj6cY/Wwz7hRljfaF4/gwodudT34UWkDFy36xh6utVLNuI=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "18.04" + } + ] + }, + { + "component": "server", + "platform": "windows_x64", + "file": "metavms-server_update-6.0.0.38086-windows_x64-beta.zip", + "size": 109881758, + "md5": "efabdca9e0d37bb8fc00475c1333b4ac", + "signature": "ACffywPrAIX8hzdA62P+giSEoiB+Q/0nOe59pLn6ivoG3osrTzpv7YLKPiB9w5GZ0DPyExLHRkp/98wAUw7rGkYsezFjzynA0/r5hYURj26ew7dGVz0sg+NI3adSj45o+d6rhsMao39A2cU8eTjcLm9+cgjaJFtE7EmJHv5TymNNDHKpteNIsGzNgIiZrRyUA7YM/9IUMEWEkzAe3LBygHx76V/3lBZU50Wb/pxUxA0+WuImT9uwLQWDCtG/xdHgCt9+FfjpWLCN18G9vP4QGVnEEOgXqfv6nS2FID6ylal8oCbcbmz3P6iNJHMIryztQDXkZTQ9bjCrCro+5RPKJ9NgxcxeKzuL7SglgW/uiBQ6mwLRAHQRsQidw0LLfwLKWnKfk5LjIMrOBIVfCZfQHqntDSBd0B24lq3RRILlUthrULaqn0wXv7N8BkOafSNeKICCKJb7SL4OTbpmnemc7R7y/bpVGXjTkCBq2ROn2Ank3czq2X/RdkpVWFzjC6U+eKZ2pDU5kV3EvYWQMcydTumASHo461so9axTxRlhmVx86RM7aEBVFpGgs3Jmh5+pDdLwAFVgoq6QKqLGONcaIO3GtAsD28VZL8uXZOmNf0Zd8bh1IpSoSyptQnNvIKefyVZyyaXzCKGHHbLGa66g42VKY9kdBVUM9N03mQT9Zyo=", + "variants": [ + { + "minimumVersion": "10.0" + } + ] + } + ] } \ No newline at end of file diff --git a/Samples/Packages/NxWitness.json b/Samples/Packages/NxWitness.json index 3009a25e..874161be 100644 --- a/Samples/Packages/NxWitness.json +++ b/Samples/Packages/NxWitness.json @@ -1,155 +1,155 @@ -{ - "version": "5.0.0.35270", - "cloudHost": "nxvms.com", - "releaseNotesUrl": "http://www.networkoptix.com/all-nx-witness-release-notes", - "description": "Support for Ubuntu 16.04 will be discontinued in the next release version and Systems on Ubuntu 16.04 will be blocked from updating to the next release version.", - "eula": "\n\n\n \n Nx Witness EULA\n\n\n\t

NETWORK OPTIX, INC.

\n

END USER LICENSE AGREEMENT

\n

IMPORTANT – PLEASE READ CAREFULLY: This End User License Agreement (the “Agreement”) is a legally enforceable contract between You (either an individual or an entity) and Network Optix, Inc. (the “Company”) regarding the use of the Company’s software programs accompanying or provided under this Agreement together with the SDK made available by the Company, associated media, corresponding Documentation, technical configurations, and technical data (collectively, the “Software”). “Documentation” means the user’s guide and other documentation (including print and online), if any, provided to you with the Software.

\n

BY CLICKING “I ACCEPT” BUTTON BELOW OR BY DOWNLOADING, INSTALLING OR OTHERWISE USING THE SOFTWARE, YOU CONSENT TO BE LEGALLY BOUND BY ALL THE TERMS AND CONDITIONS OF THE AGREEMENT. IF YOU ARE ENTERING INTO THE AGREEMENT ON BEHALF OF A COMPANY OR OTHER LEGAL ENTITY, YOU REPRESENT THAT YOU HAVE THE AUTHORITY TO BIND SUCH ENTITY AND ITS AFFILIATES TO THE AGREEMENT, IN WHICH CASE THE TERMS \"YOU\" OR \"YOUR\" SHALL REFER TO SUCH ENTITY AND ITS AFFILIATES. IF YOU DO NOT HAVE SUCH AUTHORITY, OR IF YOU DO NOT AGREE WITH ANY OF THE TERMS OF THE AGREEMENT, YOU MUST NOT ACCEPT THE AGREEMENT AND MAY NOT USE THE SOFTWARE.

\n

In addition, certain third party programs or code (“Third Party Software”) are being provided to you under the terms and conditions of a separate end user license accompanying such Third Party Software (“Third Party Software Licenses”), which will be provided to you by the Company as part of the Documentation or may be made available for viewing by the Company at the website or webpage at https://nxvms.com/content/libraries (the “Third Party Site”). If required by any Third Party Software License, the Company will make such Third Party Software, and the Company’s modifications to that Third Party Software, available to you on the Third Party Site. The Company may be required or wish to provide you with certain notices or information about Third Party Software or Third Party Software Licenses and you acknowledge that such notices and information may be provided to you by the posting of such notices and information on the Third Party Site.. BY INSTALLING, COPYING OR OTHERWISE USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THE TERMS OF THIS AGREEMENT AS WELL AS THE TERMS OF THIRD PARTY SOFTWARE LICENSES. IF YOU DO NOT AGREE TO ALL SUCH TERMS, DO NOT INSTALL OR USE THE SOFTWARE.

\n

1. License Grant. Subject to the terms and conditions of this Agreement, the Company grants you a non-exclusive, non-transferable, non-sublicensable, limited license to install and use the Software, in object code version only, for your own use subject to the restrictions contained in this Agreement.

\n

2. Backup Copies. You may make one backup copy of the Software, provided your backup copy is not installed or used on any computer and further provided that all such copies shall bear the original and unmodified copyright, patent and other intellectual property markings that appear on or in the Software. You may not transfer the rights to a backup copy unless you transfer all rights in the Software as provided in this Agreement.

\n

3. Ownership. You acknowledge that the Software and any copies that you are authorized by the Company to make are the intellectual property of, and are owned by, the Company and its licensors. The structure, sequence and organization of the Software are the valuable trade secrets and confidential information of the Company and its licensors. The Software is protected by copyright, including without limitation by United States copyright law, international treaty provisions and applicable laws in the country in which it is used. You acknowledge that the Company, or its licensors, retain the ownership of all patents, copyrights, trade secrets, trademarks and other intellectual property rights pertaining to the Software. You will take no actions which adversely affect the Company’s intellectual property rights in the Software. This Agreement is a license and not an agreement for sale. No title to, or ownership of, the Software, or any intellectual property rights subsisting therein, is transferred to you.

\n

4. Restrictions. The Software is licensed solely for your own use. The Company and its licensors reserve all rights not expressly granted to you in this Agreement. Without limiting the generality of the foregoing, you shall not nor shall you permit any other party to: (i) make copies of the Software except as expressly set forth in this Agreement, or (ii) disassemble, decompile or translate any part of the Software, or otherwise attempt to reconstruct or discover the source code of the Software, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation, or (iii) modify or create derivative works based upon the Software, or (iv) externally distribute, sublicense, resell, encumber or otherwise transfer the Software, or (v) rent, lease, lend, or use the Software, or any part thereof, for timesharing or bureau use, or (vi) allow a third party to copy, access, or use the Software (except as expressly provided in this Agreement), or (vii) alter or remove any copyright, trademark or other proprietary notice which may appear on the Software, (viii) take any action that would cause the Software to be placed in the public domain or become subject to open source license agreement, or (ix) use the Software in any manner that violates any statute, law, rule, regulation, directive, guideline, bylaw whether presently in force or may be implemented by federal, state or local authorities.

\n

5. Paid Features. The Software is made available to you at no charge or fees. However, the Company may offer certain premium features to you upon payment of a license fees determined by the Company in its sole discretion (“Paid Features”). In order to use Paid Features, you must agree to pay the license fees plus any applicable taxes through one of the payment methods accepted by the Company. Upon receipt of the license fees, the Company will either provide you a license key to activate the Paid Features or allow you to download the Software with the Paid Features. Activation of Paid Features may also be regulated through the use of transferrable license keys, and such license keys may only be transferred within your organization subject to the limitations listed in your purchase order or other documentation describing the Software and/or license rights obtained in the Software. You expressly acknowledge and agree that this Agreement will govern your use of the Paid Features.

\n

6. Updates. Except as otherwise expressly provided herein, this Agreement will govern any updates and upgrades to the Software that may be provided to you in accordance with the Company’s then-current maintenance and support policies, unless such updates and upgrades are provided under a separate license agreement. You may use such updates or upgrades only in conjunction with your then-existing the Software licensed under this Agreement. The Software and all updates and upgrades are licensed as a single product and such updates and upgrades may not be separated from the Software to exceed the scope of your original license. Any other software that may be provided with the Software that is associated with a separate end-user license agreement is licensed to you under the terms of that separate license agreement.

\n

7. No Support. Except as expressly required by applicable law, nothing in this Agreement entitles you to any support, maintenance or new versions of the Software. You may contact the Company to determine the availability of support, maintenance, and new versions, and the fees terms and conditions that would apply.

\n

8. Disclaimer of Warranties. THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OR REPRESENTATION OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COMPANY NOR ITS LICENSORS REPRESENT OR WARRANT THAT THE SOFTWARE WILL SATISFY YOUR REQUIREMENTS OR THAT IT IS WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE UNINTERRUPTED OR ERROR FREE. The Company does not warrant that functions contained in the Software will meet your requirements or that the operation of the Software will be error-free or uninterrupted. The Company is not responsible for problems caused by changes in, or modifications to, the operating characteristics of any computer hardware or operating system for which the Software or any upgrade or update is procured, nor is the Company responsible for problems which occur as a result of the use of the Software in conjunction with software of third parties or with hardware, which does not meet the Company’s specific requirements. Some jurisdictions do not allow certain disclaimers or limitations of warranties, so some of these may not apply to you.

\n

9. Limitation Of Liability. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW EVEN IF A REMEDY FAILS ITS ESSENTIAL PURPOSE, IN NO EVENT SHALL THE COMPANY OR ITS LICENSORS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE OR THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF THE COMPANY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, THE COMPANY’S ENTIRE LIABILITY TO YOU OR ANY OTHER PARTY FOR LOSS OR DAMAGE RESULTING FROM ANY CLAIMS, DEMANDS OR ACTIONS ARISING OUT OF THIS AGREEMENT SHALL NOT EXCEED THE FEE ACTUALLY PAID BY YOU TO THE COMPANY FOR THE SOFTWARE, IF ANY, NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. Because some states and jurisdictions do not allow the exclusion or limitation of liability, the above limitation may not apply to you. In such states and jurisdictions, the Company’s liability shall be limited to the greatest extent permitted by law.

\n

10. Sole Remedy and Allocation of Risk. YOUR SOLE AND EXCLUSIVE REMEDY AND THE COMPANY’S SOLE AND EXCLUSIVE LIABILITY ARE SET FORTH IN THIS AGREEMENT. This Agreement defines a mutually agreed-upon allocation of risk.

\n

11. Third Party Software Licenses. Third Party Software included with the Software is licensed under the terms of Third Party Software Licenses. You understand and agree that, although provided to you by the Company with the Software, your use of Third Party Software shall be and is governed by such Third Party Software Licenses. You understand and acknowledge that Third Party Software is being provided “AS IS” WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. If there is a conflict between this Agreement and any Third Party Software License with respect to Third Party Software, the provisions of such Third Party Software License shall prevail.

\n

12. Export Restrictions. You agree to comply with all export and re-export restrictions and regulations (“Export Restrictions”) imposed by the United States or any other country in which you conduct business. Without limiting the generality of the foregoing, and regardless of any disclosure made by you to the Company regarding an ultimate destination of the Software, you represent and warrant that you understand the U.S. law currently prohibits the export or re-export, directly or indirectly (including via remote access) of U.S. origin products and technology to certain proscribed countries (including Cuba, Iran, Sudan, North Korea and Syria and/or any other country that may become subject to an embargo by the United States), entities, organizations and individuals, without prior authorization from the U.S. Government. You will not commit any act or omission that will result in a breach of any such Export Restrictions. Your breach of this clause shall constitute cause for immediate termination of this Agreement.

\n

13. High Risk Activities. The Software is not designed, manufactured or intended for use or resale as on-line control equipment in hazardous environments requiring fail-safe performance, such as in the operation of nuclear facilities, aircraft navigation or communication systems, air traffic control, direct life support machines, or weapons systems, in which the failure of the Software could lead directly to death, personal injury, or severe physical or environmental damage (“High Risk Activities”). The Company specifically disclaims any express or implied warranty of fitness for High Risk Activities.

\n

14. Termination. The licenses granted herein shall automatically terminate without notice if you fail to comply with any material provision of this Agreement. In such event, you must immediately uninstall and return the Software, including all Documentation and original media, to the Company. This shall not limit or affect any remedy available to the Company for your breach of this Agreement.

\n

15. Miscellaneous.

\n

(a) If you are a unit or agency of the United States Government, the following applies: The Software is provided with RESTRICTED RIGHTS. Use, duplication or disclosure by the Government is subject to restrictions as set forth in Subparagraphs (a) through (d) of the Commercial Computer-Restricted Rights clause at FAR 52.227-19 when applicable, or in Subparagraph 252.227-7013 (c)(1)(ii) of the Rights in Technical Data and Computer Software at DFARS, and in similar clauses in the NASA FAR Supplement. Contractor/manufacturer is Network Optix, Inc.

\n

(b) This Agreement shall be governed by the laws of the State of California, exclusive of its choice of law rules. Any dispute between you and the Company regarding this Agreement will be subject to the exclusive jurisdiction of the state and federal courts sitting in the County of Los Angeles in the State of California. In any such action, the prevailing party will be entitled to costs and attorneys’ fees.

\n

(c) You may not assign, sublicense, or transfer this Agreement, the Software, or any rights or obligations hereunder without prior written consent of the Company. Any such attempted assignment, sublicense, or transfer will be null and void. The Company may terminate this Agreement in the event of any such attempted assignment, sublicense, or transfer.

\n

(d) This Agreement is in the English language only, and will only be provided in the English language, which language will be controlling in all respects, and all versions hereof in any other language will not be binding on the parties hereto. All communications and notices to be made or given pursuant to this Agreement must be in the English language.

\n

(e) The Agreement constitutes the final and complete understanding between you and the Company with respect to the subject matter of this Agreement. Any modifications or waivers of this Agreement must be in writing and signed by both parties hereto.

\n

(f) If any provision in this Agreement is invalid or unenforceable or contrary to applicable law, such provision shall be construed, limited, or altered, as necessary, to eliminate the invalidity or unenforceability or the conflict with applicable law, and all other provisions of this Agreement shall remain in effect.

\n

(g) Failure or delay on the part of the Company to exercise any right, power, privilege, or remedy will not constitute a waiver of, or bar the later exercise of, that or any other right, power, privilege, or remedy of the Company.

\n

(h) No rule of strict construction shall apply against or in favor of either party in the construction and interpretation of this Agreement.

\n

(i) This document was originally written in the English language, and the English text of this document shall prevail over any translation hereof.

\n

 

\n\n\n", - "eulaVersion": 1, - "packages": [ - { - "component": "server", - "platform": "linux_x64", - "file": "nxwitness-server_update-5.0.0.35270-linux_x64.zip", - "size": 89567537, - "md5": "2c3ef4243b96c804495641659957bf74", - "signature": "n/mHdUgx8uEwSnLsUXPmUKLriXNSeKNODq6d8ANMzNkvrE0UDXqpzbhPXvoOyt4pgdT0EPTphYDnY7KVQAs4aZxhu47aq1kAGAvoEtRpPs1BbhwHOdxhUZIEZroZJhJiI4bbXuzCZsDGstfX+Ti0tN9aGJehIxLLU5qHVEfUlVuMlrYqMyElWqBgj68Z6xcM4QS8K7q2MI5Uo5qkNzcnV+Il5LdgIJDLs9WIDlj5eLO7ul6Cn4mBGdH6ffLJtD5uXuelfzGvSgh6iO/GKV/Nzs+5lp4cscqb5/2mn2e10aYDlGmJ5k6eI5dcXdxHksxQv/42cnYWRq2GA+hYOk+hMIZn6svSrsOzVKktvA0bIOC7IYc4pWEFs5X1FGEy95fUyanwQLv6LPUE5xq+/IILSPX/dQFpKOVOvkYwtLmqPzfUkrTX0PA6bG02eOItQlJ+xXulGcTlEMc4s2TPsSeabyNLuLX7hex3Ew3+BuNZ0GTB1M0lYV2ho+BPpkJYt47IpfLViE1IFxPmjyZu2w5X4hDYZbYukfPrETsaGincWljBCgTeJ4ZMeGHujBqjQkrfauWLw4akvvduy50AVKeX+4dbf3d7urkA0xJFyWdOmXVFJkDsdTThYQK0pOcpxEyH30Yls3eLkPtC3cDlWTLMEeZXvn6Mf1ScojqYRoFhKxc=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "16.04" - } - ] - }, - { - "component": "server", - "platform": "windows_x64", - "file": "nxwitness-server_update-5.0.0.35270-windows_x64.zip", - "size": 112246778, - "md5": "a915fecbe66e2a10eb54b98e50e98f86", - "signature": "wbJrtCZnHWv6v6TBbQNiKc7r6vJ3A2hRLih7oyPvfryATVPgliOJ2/y9h0Gf4l4+8HDxdwCh0pOwN8LDi3CauEs2h4cwEnz1ysa+Z9ZiguQ/6+AMYOW4NgAJd1J6kv5x1NegS0oZ9X2X+a6R9DTWJFuY0aVAtuf/9d/srR8zJnMAYH1jJUk1NP6DqQ3/DCDsaK1lazqR+OjWkCDs2Y3dCEroW5Q2IM1H8gR4nXQO9XTMbo9IpvV1ENp8ogGlJa7xTCOh9drMfNXrex92NZdnrSlNsx/9b9M73tMuH3+0ZfNytL0dPOzCSkYQthVZN5wmtgTzjW2UOtBiCGmbeGPO5/ZlBJbnmV3iwDpv1XiUvWLhJfdhsKk6VM9gXgcC8UExHpxeT88uhxG9F4QSKOKbgvhrh2imqRQiD64hvThBA0I98UjiddtCvDDUMm2v3ScAPUjl8SSiCzKYsfBLfnfgFYOa7VscK/+8Y3v8gOSHRWTCVb+lWm+nuAnBGk52IQT7kHV8U5L3b6L+r+nEVVHPKyNrpkhmCpM4+ld9e887oUIecCZrbOrpsYEdqCOjZyNyQh/A98cYDZdAOiztxJOnGr8tIwDFLryzRHz9tQ9AIAoadOT+NvAOzEDM+u2G6nSu7OsdQXzPU6vyFM3ItIqclfaiu7g8+9/58pqDh/NX8ds=", - "variants": [] - }, - { - "component": "server", - "platform": "edge1", - "file": "nxwitness-server_update-5.0.0.35270-edge1.zip", - "size": 85894365, - "md5": "b222e3008273335f17a421a58e8392ae", - "signature": "MlAJf3klqKYCVYR9Jf74bK5/ekicsqP56O5rqxBFzZ/g99ZZpFkI/2jTDSM7VcrY1fpjVTsyTSf0Tci7Mk+quB1bzYlU7UKLslmmsKwPxv1Ho3bYZBL2ufcyv/aljZNxg1OS9EihMmit3O/7w8KEnGRLWsToIB6VN737W/6wuAqfzBv6gBEjHyEDk3Vwi90BYXFgAP7I5NrN6ugcy94yzIFS7RuWsY6NWwW4O7NS8o/vr6N4szT3lYIC+rrMa0xoyB7yAxATTp/NIhWUZXV9cmAS0xulPt9bgXIcz8w7LtyHAOmdQbOnWcN11QIpwRrtTIykVmY5Cz2dfREa94IN1Rgu+/XrChhyvIIGfoY+qrG5co0sdAJQZEnvrjr3iVmyp/U4bx6ZL7VRLheHT/GGUvny7jW5jryS/cDQST+isXBSc8QOEK0dtgVhVwlLFT5cwvQ1E1Fa+sNYw7A8VQzRdjhvfZVUSIyEbrWOpy8mA2L2axrhXoKUOhvQ5d5/+vyjQumHUCDFuVXPabX6cr0hLzPV+Riz7u+R/yZncVV9V7OeIFtOw2rtgQ5NJb/mer4Z9230gzu/uFu6Gm1WGi31G4rxMUq2QklpEQRWrMGeOfzrDszQuVJc+XOWrBjLYDideqScDy728NGBWGomtsLePyF7d9ABMcmwa4U5yoVIfNY=", - "variants": [] - }, - { - "component": "server", - "platform": "linux_arm64", - "file": "nxwitness-server_update-5.0.0.35270-linux_arm64.zip", - "size": 146000167, - "md5": "af4dcd0463070803191c759bb9c2cc24", - "signature": "jmiKRBpvkEdPPOzQhp1efSY4l1vjx8hZEJrO9Ariyp1c8kgdERAnDmNQZzRcdekcQKJtNwi757UOZI/UcxWJp+TNyDQwfERs1Fk1JB/cSX0flYzcBMIdVe4LAFBoBDE/3Asu+hYYOZFP1QYqB2Am7uRRMMUtvhe59U384ykdiM+ueJ35MKV7FnzF4uj1/b80UBJTb3CmFocxvgwK1+aZjoFpSWMJj1DqJ7WRBKL+wwdfN11DprRw6eMUqc4mL7ddRnIjCWAJr8vXGOZnMT/+rXxizhcQTJqrxL/OUGa3kUqNzyhyDyOZfO1vxyg86hUZ+ztRmu0ekuizbAXjQEq6zmQN5teXyBf6t4UJrq7kjaZWSaS4czelYQyLe6ikFjUMM/ROIagwpjACfhR/NLo313hEzIrd/r3cnA/uWdKfjCs22kP7U+c99fno/LW4Z1LgX5/O34iHGveHs30Gigv33Iv0u8+CEqTZsWSUTcbflVwpk4JCUf0XzpC48n+rqP4wAbcauPQTWVXC+O+EUjS4O+zjJL5Vl1f4IGTwegwBYi4OceOJiZYFgIxV6JTgDVvtLRvn3+u7ec2KvOS0v4U6cBhJPOs66Qf3O0Wvm9/NaYpNr183F9r1n88rPekFBgGrAycSgJszvnibtCq//3S7nTKBSAtf9zwYMsYLaGeMxkA=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "16.04" - } - ] - }, - { - "component": "server", - "platform": "linux_arm32", - "file": "nxwitness-server_update-5.0.0.35270-linux_arm32.zip", - "size": 169700173, - "md5": "e591ad3c572bb08c077e1189817ef9fb", - "signature": "M6u0J+rxn0+D/J7sA6sT8cPMvhxynRMLY+IX1CZKne1gQSY1VwsZAYjev25L0HGULM7Pyvuzzwykgbn4rsSieps/mNays0RAjRUVOTKMs1ZndZ7iQy6oitDdpxF5iU8Zy1mYyf3idFJmDRUlu7YlmzLHkVmIoAFCReEISPceNA/r1IWQRzC5povzUPk5JcQA0Kv3KL4Hbo6dgNCk3gZR/KaGCC6mPygQTkVjeXOQn3Ci8tH6DS8lzlwOR5heLGjm7+akQ8ETCksbC2zrHpnSQCQVJGfjwkCrG/LhBcjDOST6ucga8Q1HGqHseXYF7xi6s7/3OwpKKydVvQ6mtkxXHL8PVlqjRCdcQCHyJpB38S2V3n4dXagfzROS4GNvgFS3mq6Br87HBZDoQSta0yH9lwscwnIh6V3SRSpY1e8/4kzERXTkYKqAeoy7naMKqGSVzHEUb9wRolvl6oZhiLkjntk8txyj2YkpLb8ZZjTfU99PwenIVwn8nfNvsso02UoBFqZXRWOu84qmSWf7W48f6A38KaXVvdjtEwFsBeTQe9wRc+lHH4kLNGQ3RQCyTm7eC8QcJsCjNAk4SJDCeBCH2i77mno3Mug38CmqzZQLmjjbY5dMLgepowQpjqXKm6f5UbyTkreaShVu4YMWTGNuk41o4LDYkafavSfFKxl1Hh0=", - "variants": [ - { - "name": "debian", - "minimumVersion": "9" - }, - { - "name": "ubuntu", - "minimumVersion": "16.04" - }, - { - "name": "raspbian", - "minimumVersion": "9" - } - ] - }, - { - "component": "client", - "platform": "linux_x64", - "file": "nxwitness-client_update-5.0.0.35270-linux_x64.zip", - "size": 271453787, - "md5": "d1276fd672b9c32a1b0ed7b7529763ac", - "signature": "eGpTGO4Gg+WCgqOAcPBbuSdCM1Hfv46AlYWI1qZIa2KHax9B47VEdo3wSHdhcyMjjXxn3mavU5zIVNOcx7b5ovr0qQKMBC3OBV/ZWnp2iInB8LKd8VjvMk7sBiegaBTgdDqxHIeXNTlE2bvIVdhPalWHZeUPILzfWdtdOhE4sO4Csej8ZGrT8TkcleVG12/gZMjXa3Wof8FpXt95994+0RpxP/Ec4Z8IHxZP0nN0e3zLF7/m6z305wgrTEUfpMlYOQbb9d/7O5KPEcK6IjIOHQ+9VlczdYAzuXpz2WqR6UOzbtG+cKLTuiAEjLjvRpGRowYmVGpL3R2vCp47EsFrWveFpYUaBgsO+/Wz2X+E4veN3GY3uM/MZmNncOsROYxJ1MDV2AFkMjB/6lfzwcND+/q6IHLL3APPoYLXicvH29A+NBZhudy6AqZ2M6t3Uhj8VHvYz/vU+uN8CDsvozv/eid+do9oVzFd0iZcMfXHjRCbg2YjXdtTdrfkYLkCip0Yx3PbP8GZWs7Z8G6jmr8iZZxmt03aotraXWGvjtFcKQOKM+r+d9irDNNsYiuf2AvQGCOkwl4EnQSoxyWpGpqHISOFX8VbOYPXmhbdEr+8aJmArMzcVj0QVXdaSWEZsqbJ4yfRXFfx0ipaTSptlcBTl5M6kEBEWcTRFCRlPEzNCKs=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "16.04" - } - ] - }, - { - "component": "client", - "platform": "windows_x64", - "file": "nxwitness-client_update-5.0.0.35270-windows_x64.zip", - "size": 156180611, - "md5": "2462fb805a75275244ebac1b1f349472", - "signature": "CJsqytA3QieL/yQJ7fQEQXdgPI+CnbkHTjVArcm+kXOCtzLsehx/j9z4vsjhOilXQTY2mYPe+44OkziNq6GwPoj3Vd3CxmWep8pdySFPu5CJdM3aDPByvTbz2On+Ps+/HUXReroMmB0Jx/KHaU0bNBhXz2GjauYp+XmAbY2eCQrPc6W0l7LO7/nD0Rs7oM8YDtqZ5m/2p4UgaXzeencETyNUFgYc3Vixj+RRI8lHj29/oLS/f8l6/dnXKi3M5G9iuqBRvHvZc7CY+iTImPxNO6QbPbktnZEKNCtgh9kTTjp5ekGokk75R90kheoOrl+Z8U6T9G4mlmpK6SbSb3N8TCNNhesXucD0m2O0akWIER/m6B/hFdZ6AbjvwJZNIL1YMMh9wWluwsfCaVfgyihPVQHS25EkeZ8iGMjvfxWhZA4ADhk89Nbx0K32+CSBZjT70Jk8v6A3U5wTMFQHVFYFeH/nfCN1rHcJU6YYZr70iKGOG7qcN6QMvWn8/oadHGO7BanK9HXfy2o8KSNm0joT2XVwDBZwyfNdrTDGEDp3JVz5vW+dXVtywbBvNVm2l1OmZeKGHx7n9BhPhRga3VlntZESNUtrQQE91VtNb7bGn4YB7sHYXCg1M4OwmZ7cpuBuwbVL4adJ4JAYy3AhtqPCLi0NZhcguVJBz1+obbcHEAs=", - "variants": [] - }, - { - "component": "client", - "platform": "macos_x64", - "file": "nxwitness-client_update-5.0.0.35270-macos_x64.zip", - "size": 160431892, - "md5": "27ccd9decc6e12bb278d2445eef7a96a", - "signature": "aaj7K0xoWA5QsebnWtOQZnZw47Vh5GGXJlhazaCpkl1gnyaQ/EybuQg2m+v19KqoM+MtqFrbkh13fQVFvKNZ7ynPVz+EDQ6nTvblkKt01IFo9jzO4Gfvnxycq7prMl6kf8abg2Tf3Krhnrkm1Pcv+qYVkte1fgbF7umrD2sfSXbs7KxjKJV2Zeh2NJfKLrmshLU0YVjjEHmjwpasVzX6hDcQ7xavWxGIOf6uvZKgbENH2x8AiOlhbumkl1NLhD/PmWIOndE8buD8Erhy5cNlbO0JCxserF/ly6+NqwhneRXRWfMCHD/acdtrkK6GYWr+JfTmSdkmqwqGLRbOPDKeSneLrbc+RXse30XzlyaD/hzM2X3mvtaPtlfuaj5lnKg0AsYaxNL5QZ68lQDDA6mW/rJnMjl2nz7K/gKdmTlgd2sAjjfGQILaH+TiYKhTGv4gjurdfmrhUxY89w873XbLUZYsb1D/aoINXczpp8gmu7cEFw8BVgEjkNbvoME98OEGxllH8X1vtpB5/7jqVvhKtiHUJ8Y/TgfvMWwtD+KEVV6C2cH3+RsJpDIIDlux8/GNXHzmUUqdRS832nL4jgbSbePXmvJI4yGHZbbCZ1UM6TePtsDGvpAb83I1Rj+KjsBkPZppgwVdMY5jDXkFyDso/MF5QAZHzD5zTySZtu8fzIs=", - "variants": [] - }, - { - "component": "client", - "platform": "macos", - "file": "nxwitness-client_update-5.0.0.35270-macos.zip", - "size": 160431888, - "md5": "a0a7ec56f0efdec004491379354130b6", - "signature": "brtTWDXlH2OLY5D51vus8XEvyWuJqaL+rw4CVdiD18ms3w/OXTO+QHtHmRwbiNFm9zLzgiEM1snhQkIyLFJEC24XnYjwt2BxbWtdK1BOUzwJdNiglIkHtQQpYeVM5NK1teu78AMtKnuF5cKJNZ9J4fYuQqPhOYqLnXNaa9Sy2gq/B/P9kp21r58ov4hAYxSjrOTALIoyKLGQlUE452NXj1ng4t4ueujKrwT19gwq17hx4G5N5DEwrMmrbh2VLANl7I0FyyjwWo6I65Cn+AzAV/wXadoflguCEUMeS12mDzmZrd31l7yVd4SSvgbfdNNOaPWW5RgmjsJ9B+e5NQqB6JH99T8Y0CCvF1x+K9mtz4xtyQ7xBvZZudZSVQBACvMfzKgd/YgLPdEao72MwOpE2KZj5Rke9l+hDImcDPD/TEold26QMZHO3qgspKwmgTLVUrijSThuCCFvc57o1N8w8rruPC0pFyqJbexOTrnrGBMcEFFQ9XaIA5uc3/BN99TwOt2GddPpK5aXOxdTymMjB3kCxrw8sVO9zKk4P7sFYTIT1STuB9/2mCKy/s3DdmPENbiuzypE8LXmChIaecJK4R+XJuwx2fPgICg6Yp6M9QRiqXeFf/XevSXugZnbcyW0lpWwbzBVXPP/BftNp8+BTojsQb1TppZxxByb6k7j9vE=", - "variants": [] - }, - { - "component": "client", - "platform": "macos_arm64", - "file": "nxwitness-client_update-5.0.0.35270-macos_arm64.zip", - "size": 146991991, - "md5": "0f6741c39c00d5c7af8d9b13a25f8fcf", - "signature": "J66YBALqFUTKGYcHMe3FoDzvgcePvO1+uWY2963e1HMJMgSZ8gow9uqN2aV1wxN+Cja6vF0RHJ6JPmyIT2nH/TgGbDo4wG5N3xN2Dpt6CdVY9cF0ClMlnZYbZ8+o/NtR9bX78cEt0EK4BEHfKP1djYnjaBjBlH2s1ihciCwhxNt1vst9MpPiZK2k2+SFp2W1DnnvKXuwntECOaPfNBgBpzdap3EmZnRCQ8oOTGbgbz8pfn9YM4Rg7ZNVf8XWzSldMpuSHmS17dmnNobGF87T7WDV5VgShHrMmMoRsx7yvSS3zN1OD7fL0oEIREuBa191QXvc22T1icN6AXr03MESyy0HD8N2L6SVpoStq+dfRtRSO4NJX+26bcsxoMI4Ddhl/XAz88tGfW2o1vQiS3vx6UaXt/Uaw8OZoYsJREqtYsHSf2rUSMhbR76eD3P3apSp01rAzCcI4ZCwuJAJDlzWNr1oRNGzOSQFGhaRebqJNZINN5xswSbbPxyMSOD7x0MkFHP4Du77I6MvxROwzqCv+jB18En/KblazOGOhS4WP8NAbtV5s21nxerU5CbuMQl8CuuMi8DgwmuveYOsN0XXdk4F/B5SArJszEewqXj1BVfKYmTgJlbSJoF5XS8Hq77S+YispsHR/IaBHxe5M14X4pYK4yCmJA/IN6lBm18QOKk=", - "variants": [] - }, - { - "component": "client", - "platform": "linux_arm64", - "file": "nxwitness-client_update-5.0.0.35270-linux_arm64.zip", - "size": 276050812, - "md5": "83bac919b14dbf30d471b0746f666e30", - "signature": "HXGNqsprAgu+nzs2EJ/NB6ymjsTlphXXyw/ZWnEXQgghtoRqwkPuK2aOfV++gfN+SyzDFfDsC78ELfVD8ExoPlS7FHRpMCBuN3cKcVopFVRoaVuzNwObzsrkxE4A36ZwDqIE9c+lfDNsI8VMuBsEDm3kVscP4du2aqtGeFnc277JMKJIHzPfdWNO2Z5dptGiou/+86EGJAIUrQwon4vWkrZxSC4dg0nOXtQ9rhHhLQg1ArJ4+n9IyNdSbyzSBfGBX6HB1n7GNCWv5JgHRzXJyiS6CGr2HyE6SL8x1FmMV1s2UqNzjX5Co4sp61RYREatVtKvBIDwEIb+G1IsWKriaX7tW/DCKE9QeA4TtzMDPKmhg8LvxBiNmTsY7ZlUDwbZ2nD9lo/0DJmvMEUXxAMAYXSQPX74qu24YSMQWOgBODeypNsqQrRLlpCTnC4t1VS+cYdD8dlwm6Q5485TraJ3lZzTlISUTjuI1tLiJGZmnQklFTcW+hOHjoyo/AdKgG/MmK34jEtKoRbY0d6jRQiAryEHa70M7dYu038Ulz7Hls/nsDAo4RdCAx85LJZeunH4hifnqt/Z5dcFBuP0FNuKLghy+8cBG585HqhKj0qs7PSNWUa1azEPLJq1AoaaOaWTHBlJm81HocY42XHYx13rLfNK46eqIqhAvcsreClg18U=", - "variants": [ - { - "name": "ubuntu", - "minimumVersion": "16.04" - } - ] - }, - { - "component": "server", - "platform": "nx1", - "file": "", - "size": 0, - "md5": "", - "variants": [ - { - "name": "", - "minimumVersion": "3000" - } - ] - } - ] +{ + "version": "5.0.0.35270", + "cloudHost": "nxvms.com", + "releaseNotesUrl": "http://www.networkoptix.com/all-nx-witness-release-notes", + "description": "Support for Ubuntu 16.04 will be discontinued in the next release version and Systems on Ubuntu 16.04 will be blocked from updating to the next release version.", + "eula": "\n\n\n \n Nx Witness EULA\n\n\n\t

NETWORK OPTIX, INC.

\n

END USER LICENSE AGREEMENT

\n

IMPORTANT – PLEASE READ CAREFULLY: This End User License Agreement (the “Agreement”) is a legally enforceable contract between You (either an individual or an entity) and Network Optix, Inc. (the “Company”) regarding the use of the Company’s software programs accompanying or provided under this Agreement together with the SDK made available by the Company, associated media, corresponding Documentation, technical configurations, and technical data (collectively, the “Software”). “Documentation” means the user’s guide and other documentation (including print and online), if any, provided to you with the Software.

\n

BY CLICKING “I ACCEPT” BUTTON BELOW OR BY DOWNLOADING, INSTALLING OR OTHERWISE USING THE SOFTWARE, YOU CONSENT TO BE LEGALLY BOUND BY ALL THE TERMS AND CONDITIONS OF THE AGREEMENT. IF YOU ARE ENTERING INTO THE AGREEMENT ON BEHALF OF A COMPANY OR OTHER LEGAL ENTITY, YOU REPRESENT THAT YOU HAVE THE AUTHORITY TO BIND SUCH ENTITY AND ITS AFFILIATES TO THE AGREEMENT, IN WHICH CASE THE TERMS \"YOU\" OR \"YOUR\" SHALL REFER TO SUCH ENTITY AND ITS AFFILIATES. IF YOU DO NOT HAVE SUCH AUTHORITY, OR IF YOU DO NOT AGREE WITH ANY OF THE TERMS OF THE AGREEMENT, YOU MUST NOT ACCEPT THE AGREEMENT AND MAY NOT USE THE SOFTWARE.

\n

In addition, certain third party programs or code (“Third Party Software”) are being provided to you under the terms and conditions of a separate end user license accompanying such Third Party Software (“Third Party Software Licenses”), which will be provided to you by the Company as part of the Documentation or may be made available for viewing by the Company at the website or webpage at https://nxvms.com/content/libraries (the “Third Party Site”). If required by any Third Party Software License, the Company will make such Third Party Software, and the Company’s modifications to that Third Party Software, available to you on the Third Party Site. The Company may be required or wish to provide you with certain notices or information about Third Party Software or Third Party Software Licenses and you acknowledge that such notices and information may be provided to you by the posting of such notices and information on the Third Party Site.. BY INSTALLING, COPYING OR OTHERWISE USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THE TERMS OF THIS AGREEMENT AS WELL AS THE TERMS OF THIRD PARTY SOFTWARE LICENSES. IF YOU DO NOT AGREE TO ALL SUCH TERMS, DO NOT INSTALL OR USE THE SOFTWARE.

\n

1. License Grant. Subject to the terms and conditions of this Agreement, the Company grants you a non-exclusive, non-transferable, non-sublicensable, limited license to install and use the Software, in object code version only, for your own use subject to the restrictions contained in this Agreement.

\n

2. Backup Copies. You may make one backup copy of the Software, provided your backup copy is not installed or used on any computer and further provided that all such copies shall bear the original and unmodified copyright, patent and other intellectual property markings that appear on or in the Software. You may not transfer the rights to a backup copy unless you transfer all rights in the Software as provided in this Agreement.

\n

3. Ownership. You acknowledge that the Software and any copies that you are authorized by the Company to make are the intellectual property of, and are owned by, the Company and its licensors. The structure, sequence and organization of the Software are the valuable trade secrets and confidential information of the Company and its licensors. The Software is protected by copyright, including without limitation by United States copyright law, international treaty provisions and applicable laws in the country in which it is used. You acknowledge that the Company, or its licensors, retain the ownership of all patents, copyrights, trade secrets, trademarks and other intellectual property rights pertaining to the Software. You will take no actions which adversely affect the Company’s intellectual property rights in the Software. This Agreement is a license and not an agreement for sale. No title to, or ownership of, the Software, or any intellectual property rights subsisting therein, is transferred to you.

\n

4. Restrictions. The Software is licensed solely for your own use. The Company and its licensors reserve all rights not expressly granted to you in this Agreement. Without limiting the generality of the foregoing, you shall not nor shall you permit any other party to: (i) make copies of the Software except as expressly set forth in this Agreement, or (ii) disassemble, decompile or translate any part of the Software, or otherwise attempt to reconstruct or discover the source code of the Software, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation, or (iii) modify or create derivative works based upon the Software, or (iv) externally distribute, sublicense, resell, encumber or otherwise transfer the Software, or (v) rent, lease, lend, or use the Software, or any part thereof, for timesharing or bureau use, or (vi) allow a third party to copy, access, or use the Software (except as expressly provided in this Agreement), or (vii) alter or remove any copyright, trademark or other proprietary notice which may appear on the Software, (viii) take any action that would cause the Software to be placed in the public domain or become subject to open source license agreement, or (ix) use the Software in any manner that violates any statute, law, rule, regulation, directive, guideline, bylaw whether presently in force or may be implemented by federal, state or local authorities.

\n

5. Paid Features. The Software is made available to you at no charge or fees. However, the Company may offer certain premium features to you upon payment of a license fees determined by the Company in its sole discretion (“Paid Features”). In order to use Paid Features, you must agree to pay the license fees plus any applicable taxes through one of the payment methods accepted by the Company. Upon receipt of the license fees, the Company will either provide you a license key to activate the Paid Features or allow you to download the Software with the Paid Features. Activation of Paid Features may also be regulated through the use of transferrable license keys, and such license keys may only be transferred within your organization subject to the limitations listed in your purchase order or other documentation describing the Software and/or license rights obtained in the Software. You expressly acknowledge and agree that this Agreement will govern your use of the Paid Features.

\n

6. Updates. Except as otherwise expressly provided herein, this Agreement will govern any updates and upgrades to the Software that may be provided to you in accordance with the Company’s then-current maintenance and support policies, unless such updates and upgrades are provided under a separate license agreement. You may use such updates or upgrades only in conjunction with your then-existing the Software licensed under this Agreement. The Software and all updates and upgrades are licensed as a single product and such updates and upgrades may not be separated from the Software to exceed the scope of your original license. Any other software that may be provided with the Software that is associated with a separate end-user license agreement is licensed to you under the terms of that separate license agreement.

\n

7. No Support. Except as expressly required by applicable law, nothing in this Agreement entitles you to any support, maintenance or new versions of the Software. You may contact the Company to determine the availability of support, maintenance, and new versions, and the fees terms and conditions that would apply.

\n

8. Disclaimer of Warranties. THE SOFTWARE IS PROVIDED “AS IS,” WITHOUT WARRANTY OR REPRESENTATION OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE COMPANY NOR ITS LICENSORS REPRESENT OR WARRANT THAT THE SOFTWARE WILL SATISFY YOUR REQUIREMENTS OR THAT IT IS WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE UNINTERRUPTED OR ERROR FREE. The Company does not warrant that functions contained in the Software will meet your requirements or that the operation of the Software will be error-free or uninterrupted. The Company is not responsible for problems caused by changes in, or modifications to, the operating characteristics of any computer hardware or operating system for which the Software or any upgrade or update is procured, nor is the Company responsible for problems which occur as a result of the use of the Software in conjunction with software of third parties or with hardware, which does not meet the Company’s specific requirements. Some jurisdictions do not allow certain disclaimers or limitations of warranties, so some of these may not apply to you.

\n

9. Limitation Of Liability. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW EVEN IF A REMEDY FAILS ITS ESSENTIAL PURPOSE, IN NO EVENT SHALL THE COMPANY OR ITS LICENSORS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE OR THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT SERVICES, EVEN IF THE COMPANY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, THE COMPANY’S ENTIRE LIABILITY TO YOU OR ANY OTHER PARTY FOR LOSS OR DAMAGE RESULTING FROM ANY CLAIMS, DEMANDS OR ACTIONS ARISING OUT OF THIS AGREEMENT SHALL NOT EXCEED THE FEE ACTUALLY PAID BY YOU TO THE COMPANY FOR THE SOFTWARE, IF ANY, NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. Because some states and jurisdictions do not allow the exclusion or limitation of liability, the above limitation may not apply to you. In such states and jurisdictions, the Company’s liability shall be limited to the greatest extent permitted by law.

\n

10. Sole Remedy and Allocation of Risk. YOUR SOLE AND EXCLUSIVE REMEDY AND THE COMPANY’S SOLE AND EXCLUSIVE LIABILITY ARE SET FORTH IN THIS AGREEMENT. This Agreement defines a mutually agreed-upon allocation of risk.

\n

11. Third Party Software Licenses. Third Party Software included with the Software is licensed under the terms of Third Party Software Licenses. You understand and agree that, although provided to you by the Company with the Software, your use of Third Party Software shall be and is governed by such Third Party Software Licenses. You understand and acknowledge that Third Party Software is being provided “AS IS” WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. If there is a conflict between this Agreement and any Third Party Software License with respect to Third Party Software, the provisions of such Third Party Software License shall prevail.

\n

12. Export Restrictions. You agree to comply with all export and re-export restrictions and regulations (“Export Restrictions”) imposed by the United States or any other country in which you conduct business. Without limiting the generality of the foregoing, and regardless of any disclosure made by you to the Company regarding an ultimate destination of the Software, you represent and warrant that you understand the U.S. law currently prohibits the export or re-export, directly or indirectly (including via remote access) of U.S. origin products and technology to certain proscribed countries (including Cuba, Iran, Sudan, North Korea and Syria and/or any other country that may become subject to an embargo by the United States), entities, organizations and individuals, without prior authorization from the U.S. Government. You will not commit any act or omission that will result in a breach of any such Export Restrictions. Your breach of this clause shall constitute cause for immediate termination of this Agreement.

\n

13. High Risk Activities. The Software is not designed, manufactured or intended for use or resale as on-line control equipment in hazardous environments requiring fail-safe performance, such as in the operation of nuclear facilities, aircraft navigation or communication systems, air traffic control, direct life support machines, or weapons systems, in which the failure of the Software could lead directly to death, personal injury, or severe physical or environmental damage (“High Risk Activities”). The Company specifically disclaims any express or implied warranty of fitness for High Risk Activities.

\n

14. Termination. The licenses granted herein shall automatically terminate without notice if you fail to comply with any material provision of this Agreement. In such event, you must immediately uninstall and return the Software, including all Documentation and original media, to the Company. This shall not limit or affect any remedy available to the Company for your breach of this Agreement.

\n

15. Miscellaneous.

\n

(a) If you are a unit or agency of the United States Government, the following applies: The Software is provided with RESTRICTED RIGHTS. Use, duplication or disclosure by the Government is subject to restrictions as set forth in Subparagraphs (a) through (d) of the Commercial Computer-Restricted Rights clause at FAR 52.227-19 when applicable, or in Subparagraph 252.227-7013 (c)(1)(ii) of the Rights in Technical Data and Computer Software at DFARS, and in similar clauses in the NASA FAR Supplement. Contractor/manufacturer is Network Optix, Inc.

\n

(b) This Agreement shall be governed by the laws of the State of California, exclusive of its choice of law rules. Any dispute between you and the Company regarding this Agreement will be subject to the exclusive jurisdiction of the state and federal courts sitting in the County of Los Angeles in the State of California. In any such action, the prevailing party will be entitled to costs and attorneys’ fees.

\n

(c) You may not assign, sublicense, or transfer this Agreement, the Software, or any rights or obligations hereunder without prior written consent of the Company. Any such attempted assignment, sublicense, or transfer will be null and void. The Company may terminate this Agreement in the event of any such attempted assignment, sublicense, or transfer.

\n

(d) This Agreement is in the English language only, and will only be provided in the English language, which language will be controlling in all respects, and all versions hereof in any other language will not be binding on the parties hereto. All communications and notices to be made or given pursuant to this Agreement must be in the English language.

\n

(e) The Agreement constitutes the final and complete understanding between you and the Company with respect to the subject matter of this Agreement. Any modifications or waivers of this Agreement must be in writing and signed by both parties hereto.

\n

(f) If any provision in this Agreement is invalid or unenforceable or contrary to applicable law, such provision shall be construed, limited, or altered, as necessary, to eliminate the invalidity or unenforceability or the conflict with applicable law, and all other provisions of this Agreement shall remain in effect.

\n

(g) Failure or delay on the part of the Company to exercise any right, power, privilege, or remedy will not constitute a waiver of, or bar the later exercise of, that or any other right, power, privilege, or remedy of the Company.

\n

(h) No rule of strict construction shall apply against or in favor of either party in the construction and interpretation of this Agreement.

\n

(i) This document was originally written in the English language, and the English text of this document shall prevail over any translation hereof.

\n

 

\n\n\n", + "eulaVersion": 1, + "packages": [ + { + "component": "server", + "platform": "linux_x64", + "file": "nxwitness-server_update-5.0.0.35270-linux_x64.zip", + "size": 89567537, + "md5": "2c3ef4243b96c804495641659957bf74", + "signature": "n/mHdUgx8uEwSnLsUXPmUKLriXNSeKNODq6d8ANMzNkvrE0UDXqpzbhPXvoOyt4pgdT0EPTphYDnY7KVQAs4aZxhu47aq1kAGAvoEtRpPs1BbhwHOdxhUZIEZroZJhJiI4bbXuzCZsDGstfX+Ti0tN9aGJehIxLLU5qHVEfUlVuMlrYqMyElWqBgj68Z6xcM4QS8K7q2MI5Uo5qkNzcnV+Il5LdgIJDLs9WIDlj5eLO7ul6Cn4mBGdH6ffLJtD5uXuelfzGvSgh6iO/GKV/Nzs+5lp4cscqb5/2mn2e10aYDlGmJ5k6eI5dcXdxHksxQv/42cnYWRq2GA+hYOk+hMIZn6svSrsOzVKktvA0bIOC7IYc4pWEFs5X1FGEy95fUyanwQLv6LPUE5xq+/IILSPX/dQFpKOVOvkYwtLmqPzfUkrTX0PA6bG02eOItQlJ+xXulGcTlEMc4s2TPsSeabyNLuLX7hex3Ew3+BuNZ0GTB1M0lYV2ho+BPpkJYt47IpfLViE1IFxPmjyZu2w5X4hDYZbYukfPrETsaGincWljBCgTeJ4ZMeGHujBqjQkrfauWLw4akvvduy50AVKeX+4dbf3d7urkA0xJFyWdOmXVFJkDsdTThYQK0pOcpxEyH30Yls3eLkPtC3cDlWTLMEeZXvn6Mf1ScojqYRoFhKxc=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "16.04" + } + ] + }, + { + "component": "server", + "platform": "windows_x64", + "file": "nxwitness-server_update-5.0.0.35270-windows_x64.zip", + "size": 112246778, + "md5": "a915fecbe66e2a10eb54b98e50e98f86", + "signature": "wbJrtCZnHWv6v6TBbQNiKc7r6vJ3A2hRLih7oyPvfryATVPgliOJ2/y9h0Gf4l4+8HDxdwCh0pOwN8LDi3CauEs2h4cwEnz1ysa+Z9ZiguQ/6+AMYOW4NgAJd1J6kv5x1NegS0oZ9X2X+a6R9DTWJFuY0aVAtuf/9d/srR8zJnMAYH1jJUk1NP6DqQ3/DCDsaK1lazqR+OjWkCDs2Y3dCEroW5Q2IM1H8gR4nXQO9XTMbo9IpvV1ENp8ogGlJa7xTCOh9drMfNXrex92NZdnrSlNsx/9b9M73tMuH3+0ZfNytL0dPOzCSkYQthVZN5wmtgTzjW2UOtBiCGmbeGPO5/ZlBJbnmV3iwDpv1XiUvWLhJfdhsKk6VM9gXgcC8UExHpxeT88uhxG9F4QSKOKbgvhrh2imqRQiD64hvThBA0I98UjiddtCvDDUMm2v3ScAPUjl8SSiCzKYsfBLfnfgFYOa7VscK/+8Y3v8gOSHRWTCVb+lWm+nuAnBGk52IQT7kHV8U5L3b6L+r+nEVVHPKyNrpkhmCpM4+ld9e887oUIecCZrbOrpsYEdqCOjZyNyQh/A98cYDZdAOiztxJOnGr8tIwDFLryzRHz9tQ9AIAoadOT+NvAOzEDM+u2G6nSu7OsdQXzPU6vyFM3ItIqclfaiu7g8+9/58pqDh/NX8ds=", + "variants": [] + }, + { + "component": "server", + "platform": "edge1", + "file": "nxwitness-server_update-5.0.0.35270-edge1.zip", + "size": 85894365, + "md5": "b222e3008273335f17a421a58e8392ae", + "signature": "MlAJf3klqKYCVYR9Jf74bK5/ekicsqP56O5rqxBFzZ/g99ZZpFkI/2jTDSM7VcrY1fpjVTsyTSf0Tci7Mk+quB1bzYlU7UKLslmmsKwPxv1Ho3bYZBL2ufcyv/aljZNxg1OS9EihMmit3O/7w8KEnGRLWsToIB6VN737W/6wuAqfzBv6gBEjHyEDk3Vwi90BYXFgAP7I5NrN6ugcy94yzIFS7RuWsY6NWwW4O7NS8o/vr6N4szT3lYIC+rrMa0xoyB7yAxATTp/NIhWUZXV9cmAS0xulPt9bgXIcz8w7LtyHAOmdQbOnWcN11QIpwRrtTIykVmY5Cz2dfREa94IN1Rgu+/XrChhyvIIGfoY+qrG5co0sdAJQZEnvrjr3iVmyp/U4bx6ZL7VRLheHT/GGUvny7jW5jryS/cDQST+isXBSc8QOEK0dtgVhVwlLFT5cwvQ1E1Fa+sNYw7A8VQzRdjhvfZVUSIyEbrWOpy8mA2L2axrhXoKUOhvQ5d5/+vyjQumHUCDFuVXPabX6cr0hLzPV+Riz7u+R/yZncVV9V7OeIFtOw2rtgQ5NJb/mer4Z9230gzu/uFu6Gm1WGi31G4rxMUq2QklpEQRWrMGeOfzrDszQuVJc+XOWrBjLYDideqScDy728NGBWGomtsLePyF7d9ABMcmwa4U5yoVIfNY=", + "variants": [] + }, + { + "component": "server", + "platform": "linux_arm64", + "file": "nxwitness-server_update-5.0.0.35270-linux_arm64.zip", + "size": 146000167, + "md5": "af4dcd0463070803191c759bb9c2cc24", + "signature": "jmiKRBpvkEdPPOzQhp1efSY4l1vjx8hZEJrO9Ariyp1c8kgdERAnDmNQZzRcdekcQKJtNwi757UOZI/UcxWJp+TNyDQwfERs1Fk1JB/cSX0flYzcBMIdVe4LAFBoBDE/3Asu+hYYOZFP1QYqB2Am7uRRMMUtvhe59U384ykdiM+ueJ35MKV7FnzF4uj1/b80UBJTb3CmFocxvgwK1+aZjoFpSWMJj1DqJ7WRBKL+wwdfN11DprRw6eMUqc4mL7ddRnIjCWAJr8vXGOZnMT/+rXxizhcQTJqrxL/OUGa3kUqNzyhyDyOZfO1vxyg86hUZ+ztRmu0ekuizbAXjQEq6zmQN5teXyBf6t4UJrq7kjaZWSaS4czelYQyLe6ikFjUMM/ROIagwpjACfhR/NLo313hEzIrd/r3cnA/uWdKfjCs22kP7U+c99fno/LW4Z1LgX5/O34iHGveHs30Gigv33Iv0u8+CEqTZsWSUTcbflVwpk4JCUf0XzpC48n+rqP4wAbcauPQTWVXC+O+EUjS4O+zjJL5Vl1f4IGTwegwBYi4OceOJiZYFgIxV6JTgDVvtLRvn3+u7ec2KvOS0v4U6cBhJPOs66Qf3O0Wvm9/NaYpNr183F9r1n88rPekFBgGrAycSgJszvnibtCq//3S7nTKBSAtf9zwYMsYLaGeMxkA=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "16.04" + } + ] + }, + { + "component": "server", + "platform": "linux_arm32", + "file": "nxwitness-server_update-5.0.0.35270-linux_arm32.zip", + "size": 169700173, + "md5": "e591ad3c572bb08c077e1189817ef9fb", + "signature": "M6u0J+rxn0+D/J7sA6sT8cPMvhxynRMLY+IX1CZKne1gQSY1VwsZAYjev25L0HGULM7Pyvuzzwykgbn4rsSieps/mNays0RAjRUVOTKMs1ZndZ7iQy6oitDdpxF5iU8Zy1mYyf3idFJmDRUlu7YlmzLHkVmIoAFCReEISPceNA/r1IWQRzC5povzUPk5JcQA0Kv3KL4Hbo6dgNCk3gZR/KaGCC6mPygQTkVjeXOQn3Ci8tH6DS8lzlwOR5heLGjm7+akQ8ETCksbC2zrHpnSQCQVJGfjwkCrG/LhBcjDOST6ucga8Q1HGqHseXYF7xi6s7/3OwpKKydVvQ6mtkxXHL8PVlqjRCdcQCHyJpB38S2V3n4dXagfzROS4GNvgFS3mq6Br87HBZDoQSta0yH9lwscwnIh6V3SRSpY1e8/4kzERXTkYKqAeoy7naMKqGSVzHEUb9wRolvl6oZhiLkjntk8txyj2YkpLb8ZZjTfU99PwenIVwn8nfNvsso02UoBFqZXRWOu84qmSWf7W48f6A38KaXVvdjtEwFsBeTQe9wRc+lHH4kLNGQ3RQCyTm7eC8QcJsCjNAk4SJDCeBCH2i77mno3Mug38CmqzZQLmjjbY5dMLgepowQpjqXKm6f5UbyTkreaShVu4YMWTGNuk41o4LDYkafavSfFKxl1Hh0=", + "variants": [ + { + "name": "debian", + "minimumVersion": "9" + }, + { + "name": "ubuntu", + "minimumVersion": "16.04" + }, + { + "name": "raspbian", + "minimumVersion": "9" + } + ] + }, + { + "component": "client", + "platform": "linux_x64", + "file": "nxwitness-client_update-5.0.0.35270-linux_x64.zip", + "size": 271453787, + "md5": "d1276fd672b9c32a1b0ed7b7529763ac", + "signature": "eGpTGO4Gg+WCgqOAcPBbuSdCM1Hfv46AlYWI1qZIa2KHax9B47VEdo3wSHdhcyMjjXxn3mavU5zIVNOcx7b5ovr0qQKMBC3OBV/ZWnp2iInB8LKd8VjvMk7sBiegaBTgdDqxHIeXNTlE2bvIVdhPalWHZeUPILzfWdtdOhE4sO4Csej8ZGrT8TkcleVG12/gZMjXa3Wof8FpXt95994+0RpxP/Ec4Z8IHxZP0nN0e3zLF7/m6z305wgrTEUfpMlYOQbb9d/7O5KPEcK6IjIOHQ+9VlczdYAzuXpz2WqR6UOzbtG+cKLTuiAEjLjvRpGRowYmVGpL3R2vCp47EsFrWveFpYUaBgsO+/Wz2X+E4veN3GY3uM/MZmNncOsROYxJ1MDV2AFkMjB/6lfzwcND+/q6IHLL3APPoYLXicvH29A+NBZhudy6AqZ2M6t3Uhj8VHvYz/vU+uN8CDsvozv/eid+do9oVzFd0iZcMfXHjRCbg2YjXdtTdrfkYLkCip0Yx3PbP8GZWs7Z8G6jmr8iZZxmt03aotraXWGvjtFcKQOKM+r+d9irDNNsYiuf2AvQGCOkwl4EnQSoxyWpGpqHISOFX8VbOYPXmhbdEr+8aJmArMzcVj0QVXdaSWEZsqbJ4yfRXFfx0ipaTSptlcBTl5M6kEBEWcTRFCRlPEzNCKs=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "16.04" + } + ] + }, + { + "component": "client", + "platform": "windows_x64", + "file": "nxwitness-client_update-5.0.0.35270-windows_x64.zip", + "size": 156180611, + "md5": "2462fb805a75275244ebac1b1f349472", + "signature": "CJsqytA3QieL/yQJ7fQEQXdgPI+CnbkHTjVArcm+kXOCtzLsehx/j9z4vsjhOilXQTY2mYPe+44OkziNq6GwPoj3Vd3CxmWep8pdySFPu5CJdM3aDPByvTbz2On+Ps+/HUXReroMmB0Jx/KHaU0bNBhXz2GjauYp+XmAbY2eCQrPc6W0l7LO7/nD0Rs7oM8YDtqZ5m/2p4UgaXzeencETyNUFgYc3Vixj+RRI8lHj29/oLS/f8l6/dnXKi3M5G9iuqBRvHvZc7CY+iTImPxNO6QbPbktnZEKNCtgh9kTTjp5ekGokk75R90kheoOrl+Z8U6T9G4mlmpK6SbSb3N8TCNNhesXucD0m2O0akWIER/m6B/hFdZ6AbjvwJZNIL1YMMh9wWluwsfCaVfgyihPVQHS25EkeZ8iGMjvfxWhZA4ADhk89Nbx0K32+CSBZjT70Jk8v6A3U5wTMFQHVFYFeH/nfCN1rHcJU6YYZr70iKGOG7qcN6QMvWn8/oadHGO7BanK9HXfy2o8KSNm0joT2XVwDBZwyfNdrTDGEDp3JVz5vW+dXVtywbBvNVm2l1OmZeKGHx7n9BhPhRga3VlntZESNUtrQQE91VtNb7bGn4YB7sHYXCg1M4OwmZ7cpuBuwbVL4adJ4JAYy3AhtqPCLi0NZhcguVJBz1+obbcHEAs=", + "variants": [] + }, + { + "component": "client", + "platform": "macos_x64", + "file": "nxwitness-client_update-5.0.0.35270-macos_x64.zip", + "size": 160431892, + "md5": "27ccd9decc6e12bb278d2445eef7a96a", + "signature": "aaj7K0xoWA5QsebnWtOQZnZw47Vh5GGXJlhazaCpkl1gnyaQ/EybuQg2m+v19KqoM+MtqFrbkh13fQVFvKNZ7ynPVz+EDQ6nTvblkKt01IFo9jzO4Gfvnxycq7prMl6kf8abg2Tf3Krhnrkm1Pcv+qYVkte1fgbF7umrD2sfSXbs7KxjKJV2Zeh2NJfKLrmshLU0YVjjEHmjwpasVzX6hDcQ7xavWxGIOf6uvZKgbENH2x8AiOlhbumkl1NLhD/PmWIOndE8buD8Erhy5cNlbO0JCxserF/ly6+NqwhneRXRWfMCHD/acdtrkK6GYWr+JfTmSdkmqwqGLRbOPDKeSneLrbc+RXse30XzlyaD/hzM2X3mvtaPtlfuaj5lnKg0AsYaxNL5QZ68lQDDA6mW/rJnMjl2nz7K/gKdmTlgd2sAjjfGQILaH+TiYKhTGv4gjurdfmrhUxY89w873XbLUZYsb1D/aoINXczpp8gmu7cEFw8BVgEjkNbvoME98OEGxllH8X1vtpB5/7jqVvhKtiHUJ8Y/TgfvMWwtD+KEVV6C2cH3+RsJpDIIDlux8/GNXHzmUUqdRS832nL4jgbSbePXmvJI4yGHZbbCZ1UM6TePtsDGvpAb83I1Rj+KjsBkPZppgwVdMY5jDXkFyDso/MF5QAZHzD5zTySZtu8fzIs=", + "variants": [] + }, + { + "component": "client", + "platform": "macos", + "file": "nxwitness-client_update-5.0.0.35270-macos.zip", + "size": 160431888, + "md5": "a0a7ec56f0efdec004491379354130b6", + "signature": "brtTWDXlH2OLY5D51vus8XEvyWuJqaL+rw4CVdiD18ms3w/OXTO+QHtHmRwbiNFm9zLzgiEM1snhQkIyLFJEC24XnYjwt2BxbWtdK1BOUzwJdNiglIkHtQQpYeVM5NK1teu78AMtKnuF5cKJNZ9J4fYuQqPhOYqLnXNaa9Sy2gq/B/P9kp21r58ov4hAYxSjrOTALIoyKLGQlUE452NXj1ng4t4ueujKrwT19gwq17hx4G5N5DEwrMmrbh2VLANl7I0FyyjwWo6I65Cn+AzAV/wXadoflguCEUMeS12mDzmZrd31l7yVd4SSvgbfdNNOaPWW5RgmjsJ9B+e5NQqB6JH99T8Y0CCvF1x+K9mtz4xtyQ7xBvZZudZSVQBACvMfzKgd/YgLPdEao72MwOpE2KZj5Rke9l+hDImcDPD/TEold26QMZHO3qgspKwmgTLVUrijSThuCCFvc57o1N8w8rruPC0pFyqJbexOTrnrGBMcEFFQ9XaIA5uc3/BN99TwOt2GddPpK5aXOxdTymMjB3kCxrw8sVO9zKk4P7sFYTIT1STuB9/2mCKy/s3DdmPENbiuzypE8LXmChIaecJK4R+XJuwx2fPgICg6Yp6M9QRiqXeFf/XevSXugZnbcyW0lpWwbzBVXPP/BftNp8+BTojsQb1TppZxxByb6k7j9vE=", + "variants": [] + }, + { + "component": "client", + "platform": "macos_arm64", + "file": "nxwitness-client_update-5.0.0.35270-macos_arm64.zip", + "size": 146991991, + "md5": "0f6741c39c00d5c7af8d9b13a25f8fcf", + "signature": "J66YBALqFUTKGYcHMe3FoDzvgcePvO1+uWY2963e1HMJMgSZ8gow9uqN2aV1wxN+Cja6vF0RHJ6JPmyIT2nH/TgGbDo4wG5N3xN2Dpt6CdVY9cF0ClMlnZYbZ8+o/NtR9bX78cEt0EK4BEHfKP1djYnjaBjBlH2s1ihciCwhxNt1vst9MpPiZK2k2+SFp2W1DnnvKXuwntECOaPfNBgBpzdap3EmZnRCQ8oOTGbgbz8pfn9YM4Rg7ZNVf8XWzSldMpuSHmS17dmnNobGF87T7WDV5VgShHrMmMoRsx7yvSS3zN1OD7fL0oEIREuBa191QXvc22T1icN6AXr03MESyy0HD8N2L6SVpoStq+dfRtRSO4NJX+26bcsxoMI4Ddhl/XAz88tGfW2o1vQiS3vx6UaXt/Uaw8OZoYsJREqtYsHSf2rUSMhbR76eD3P3apSp01rAzCcI4ZCwuJAJDlzWNr1oRNGzOSQFGhaRebqJNZINN5xswSbbPxyMSOD7x0MkFHP4Du77I6MvxROwzqCv+jB18En/KblazOGOhS4WP8NAbtV5s21nxerU5CbuMQl8CuuMi8DgwmuveYOsN0XXdk4F/B5SArJszEewqXj1BVfKYmTgJlbSJoF5XS8Hq77S+YispsHR/IaBHxe5M14X4pYK4yCmJA/IN6lBm18QOKk=", + "variants": [] + }, + { + "component": "client", + "platform": "linux_arm64", + "file": "nxwitness-client_update-5.0.0.35270-linux_arm64.zip", + "size": 276050812, + "md5": "83bac919b14dbf30d471b0746f666e30", + "signature": "HXGNqsprAgu+nzs2EJ/NB6ymjsTlphXXyw/ZWnEXQgghtoRqwkPuK2aOfV++gfN+SyzDFfDsC78ELfVD8ExoPlS7FHRpMCBuN3cKcVopFVRoaVuzNwObzsrkxE4A36ZwDqIE9c+lfDNsI8VMuBsEDm3kVscP4du2aqtGeFnc277JMKJIHzPfdWNO2Z5dptGiou/+86EGJAIUrQwon4vWkrZxSC4dg0nOXtQ9rhHhLQg1ArJ4+n9IyNdSbyzSBfGBX6HB1n7GNCWv5JgHRzXJyiS6CGr2HyE6SL8x1FmMV1s2UqNzjX5Co4sp61RYREatVtKvBIDwEIb+G1IsWKriaX7tW/DCKE9QeA4TtzMDPKmhg8LvxBiNmTsY7ZlUDwbZ2nD9lo/0DJmvMEUXxAMAYXSQPX74qu24YSMQWOgBODeypNsqQrRLlpCTnC4t1VS+cYdD8dlwm6Q5485TraJ3lZzTlISUTjuI1tLiJGZmnQklFTcW+hOHjoyo/AdKgG/MmK34jEtKoRbY0d6jRQiAryEHa70M7dYu038Ulz7Hls/nsDAo4RdCAx85LJZeunH4hifnqt/Z5dcFBuP0FNuKLghy+8cBG585HqhKj0qs7PSNWUa1azEPLJq1AoaaOaWTHBlJm81HocY42XHYx13rLfNK46eqIqhAvcsreClg18U=", + "variants": [ + { + "name": "ubuntu", + "minimumVersion": "16.04" + } + ] + }, + { + "component": "server", + "platform": "nx1", + "file": "", + "size": 0, + "md5": "", + "variants": [ + { + "name": "", + "minimumVersion": "3000" + } + ] + } + ] } \ No newline at end of file diff --git a/Samples/Releases/DWSpectrum.1.json b/Samples/Releases/DWSpectrum.1.json index 6996433d..406024d1 100644 --- a/Samples/Releases/DWSpectrum.1.json +++ b/Samples/Releases/DWSpectrum.1.json @@ -1,16 +1,16 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/digitalwatchdog", - "http://beta.networkoptix.com/beta-builds/digitalwatchdog" - ], - "releases": [ - { - "product": "vms", - "version": "5.0.0.35271", - "protocol_version": 5002, - "publication_type": "release", - "release_date": "1660167578448", - "release_delivery_days": null - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/digitalwatchdog", + "http://beta.networkoptix.com/beta-builds/digitalwatchdog" + ], + "releases": [ + { + "product": "vms", + "version": "5.0.0.35271", + "protocol_version": 5002, + "publication_type": "release", + "release_date": "1660167578448", + "release_delivery_days": null + } + ] } \ No newline at end of file diff --git a/Samples/Releases/DWSpectrum.2.json b/Samples/Releases/DWSpectrum.2.json index 5d44fec6..640f8380 100644 --- a/Samples/Releases/DWSpectrum.2.json +++ b/Samples/Releases/DWSpectrum.2.json @@ -1,24 +1,24 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/digitalwatchdog", - "http://beta.networkoptix.com/beta-builds/digitalwatchdog" - ], - "releases": [ - { - "product": "vms", - "version": "5.0.0.35138", - "protocol_version": 5002, - "publication_type": "release", - "release_date": null, - "release_delivery_days": null - }, - { - "product": "vms", - "version": "4.2.0.32842", - "protocol_version": 4201, - "publication_type": "release", - "release_date": "1627938214069", - "release_delivery_days": 30 - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/digitalwatchdog", + "http://beta.networkoptix.com/beta-builds/digitalwatchdog" + ], + "releases": [ + { + "product": "vms", + "version": "5.0.0.35138", + "protocol_version": 5002, + "publication_type": "release", + "release_date": null, + "release_delivery_days": null + }, + { + "product": "vms", + "version": "4.2.0.32842", + "protocol_version": 4201, + "publication_type": "release", + "release_date": "1627938214069", + "release_delivery_days": 30 + } + ] } \ No newline at end of file diff --git a/Samples/Releases/DWSpectrum.3.json b/Samples/Releases/DWSpectrum.3.json index a934068f..248640c9 100644 --- a/Samples/Releases/DWSpectrum.3.json +++ b/Samples/Releases/DWSpectrum.3.json @@ -1,16 +1,16 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/digitalwatchdog", - "http://beta.networkoptix.com/beta-builds/digitalwatchdog" - ], - "releases": [ - { - "product": "vms", - "version": "5.0.0.35271", - "protocol_version": 5002, - "publication_type": "release", - "release_date": "1662033178345", - "release_delivery_days": null - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/digitalwatchdog", + "http://beta.networkoptix.com/beta-builds/digitalwatchdog" + ], + "releases": [ + { + "product": "vms", + "version": "5.0.0.35271", + "protocol_version": 5002, + "publication_type": "release", + "release_date": "1662033178345", + "release_delivery_days": null + } + ] } \ No newline at end of file diff --git a/Samples/Releases/DWSpectrum.4.json b/Samples/Releases/DWSpectrum.4.json index 331dab41..5aff8080 100644 --- a/Samples/Releases/DWSpectrum.4.json +++ b/Samples/Releases/DWSpectrum.4.json @@ -1,24 +1,24 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/digitalwatchdog", - "https://beta.networkoptix.com/beta-builds/digitalwatchdog" - ], - "releases": [ - { - "product": "vms", - "version": "5.0.0.35138", - "protocol_version": 5002, - "publication_type": "release", - "release_date": null, - "release_delivery_days": null - }, - { - "product": "vms", - "version": "4.2.0.32842", - "protocol_version": 4201, - "publication_type": "release", - "release_date": "1627938214069", - "release_delivery_days": 30 - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/digitalwatchdog", + "https://beta.networkoptix.com/beta-builds/digitalwatchdog" + ], + "releases": [ + { + "product": "vms", + "version": "5.0.0.35138", + "protocol_version": 5002, + "publication_type": "release", + "release_date": null, + "release_delivery_days": null + }, + { + "product": "vms", + "version": "4.2.0.32842", + "protocol_version": 4201, + "publication_type": "release", + "release_date": "1627938214069", + "release_delivery_days": 30 + } + ] } \ No newline at end of file diff --git a/Samples/Releases/DWSpectrum.5.json b/Samples/Releases/DWSpectrum.5.json index 406afbbb..2c6e4d95 100644 --- a/Samples/Releases/DWSpectrum.5.json +++ b/Samples/Releases/DWSpectrum.5.json @@ -1,32 +1,32 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/digitalwatchdog", - "https://beta.networkoptix.com/beta-builds/digitalwatchdog" - ], - "releases": [ - { - "product": "vms", - "version": "5.1.0.36751", - "protocol_version": 5107, - "publication_type": "rc", - "release_date": null, - "release_delivery_days": null - }, - { - "product": "vms", - "version": "5.0.0.36634", - "protocol_version": 5005, - "publication_type": "release", - "release_date": null, - "release_delivery_days": null - }, - { - "product": "vms", - "version": "4.2.0.32842", - "protocol_version": 4201, - "publication_type": "release", - "release_date": "1627938214069", - "release_delivery_days": 30 - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/digitalwatchdog", + "https://beta.networkoptix.com/beta-builds/digitalwatchdog" + ], + "releases": [ + { + "product": "vms", + "version": "5.1.0.36751", + "protocol_version": 5107, + "publication_type": "rc", + "release_date": null, + "release_delivery_days": null + }, + { + "product": "vms", + "version": "5.0.0.36634", + "protocol_version": 5005, + "publication_type": "release", + "release_date": null, + "release_delivery_days": null + }, + { + "product": "vms", + "version": "4.2.0.32842", + "protocol_version": 4201, + "publication_type": "release", + "release_date": "1627938214069", + "release_delivery_days": 30 + } + ] } \ No newline at end of file diff --git a/Samples/Releases/DWSpectrum.json b/Samples/Releases/DWSpectrum.json index ce20dba8..e30c89e7 100644 --- a/Samples/Releases/DWSpectrum.json +++ b/Samples/Releases/DWSpectrum.json @@ -1,24 +1,24 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/digitalwatchdog", - "http://beta.networkoptix.com/beta-builds/digitalwatchdog" - ], - "releases": [ - { - "product": "vms", - "version": "5.0.0.35271", - "protocol_version": 5002, - "publication_type": "release", - "release_date": null, - "release_delivery_days": null - }, - { - "product": "vms", - "version": "4.2.0.32842", - "protocol_version": 4201, - "publication_type": "release", - "release_date": "1627938214069", - "release_delivery_days": 30 - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/digitalwatchdog", + "http://beta.networkoptix.com/beta-builds/digitalwatchdog" + ], + "releases": [ + { + "product": "vms", + "version": "5.0.0.35271", + "protocol_version": 5002, + "publication_type": "release", + "release_date": null, + "release_delivery_days": null + }, + { + "product": "vms", + "version": "4.2.0.32842", + "protocol_version": 4201, + "publication_type": "release", + "release_date": "1627938214069", + "release_delivery_days": 30 + } + ] } \ No newline at end of file diff --git a/Samples/Releases/NxMeta.json b/Samples/Releases/NxMeta.json index 7cf308d1..cc2402f0 100644 --- a/Samples/Releases/NxMeta.json +++ b/Samples/Releases/NxMeta.json @@ -1,24 +1,24 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/metavms", - "http://beta.networkoptix.com/beta-builds/metavms" - ], - "releases": [ - { - "product": "vms", - "version": "5.1.0.35151 R1", - "protocol_version": 5105, - "publication_type": "beta", - "release_date": null, - "release_delivery_days": null - }, - { - "product": "vms", - "version": "5.0.0.35269 R11", - "protocol_version": 5002, - "publication_type": "release", - "release_date": "1659433159847", - "release_delivery_days": null - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/metavms", + "http://beta.networkoptix.com/beta-builds/metavms" + ], + "releases": [ + { + "product": "vms", + "version": "5.1.0.35151 R1", + "protocol_version": 5105, + "publication_type": "beta", + "release_date": null, + "release_delivery_days": null + }, + { + "product": "vms", + "version": "5.0.0.35269 R11", + "protocol_version": 5002, + "publication_type": "release", + "release_date": "1659433159847", + "release_delivery_days": null + } + ] } \ No newline at end of file diff --git a/Samples/Releases/NxWitness.2.json b/Samples/Releases/NxWitness.2.json index 17184de5..90e204db 100644 --- a/Samples/Releases/NxWitness.2.json +++ b/Samples/Releases/NxWitness.2.json @@ -1,24 +1,24 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/default", - "https://beta.networkoptix.com/beta-builds/default" - ], - "releases": [ - { - "product": "vms", - "version": "5.1.0.36751", - "protocol_version": 5107, - "publication_type": "rc", - "release_date": null, - "release_delivery_days": null - }, - { - "product": "vms", - "version": "5.0.0.36634", - "protocol_version": 5005, - "publication_type": "release", - "release_date": "1682031478805", - "release_delivery_days": 7 - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/default", + "https://beta.networkoptix.com/beta-builds/default" + ], + "releases": [ + { + "product": "vms", + "version": "5.1.0.36751", + "protocol_version": 5107, + "publication_type": "rc", + "release_date": null, + "release_delivery_days": null + }, + { + "product": "vms", + "version": "5.0.0.36634", + "protocol_version": 5005, + "publication_type": "release", + "release_date": "1682031478805", + "release_delivery_days": 7 + } + ] } \ No newline at end of file diff --git a/Samples/Releases/NxWitness.json b/Samples/Releases/NxWitness.json index 08b2e2bb..8163ccec 100644 --- a/Samples/Releases/NxWitness.json +++ b/Samples/Releases/NxWitness.json @@ -1,24 +1,24 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/default", - "http://beta.networkoptix.com/beta-builds/default" - ], - "releases": [ - { - "product": "vms", - "version": "5.0.0.35270", - "protocol_version": 5002, - "publication_type": "release", - "release_date": null, - "release_delivery_days": null - }, - { - "product": "vms", - "version": "4.2.0.32840", - "protocol_version": 4201, - "publication_type": "release", - "release_date": "1620848664362", - "release_delivery_days": 30 - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/default", + "http://beta.networkoptix.com/beta-builds/default" + ], + "releases": [ + { + "product": "vms", + "version": "5.0.0.35270", + "protocol_version": 5002, + "publication_type": "release", + "release_date": null, + "release_delivery_days": null + }, + { + "product": "vms", + "version": "4.2.0.32840", + "protocol_version": 4201, + "publication_type": "release", + "release_date": "1620848664362", + "release_delivery_days": 30 + } + ] } \ No newline at end of file diff --git a/Samples/Releases/Nxwitness.1.json b/Samples/Releases/Nxwitness.1.json index 6621703c..528aee62 100644 --- a/Samples/Releases/Nxwitness.1.json +++ b/Samples/Releases/Nxwitness.1.json @@ -1,16 +1,16 @@ -{ - "packages_urls": [ - "https://updates.networkoptix.com/default", - "http://beta.networkoptix.com/beta-builds/default" - ], - "releases": [ - { - "product": "vms", - "version": "5.0.0.35270", - "protocol_version": 5002, - "publication_type": "release", - "release_date": "1659371734147", - "release_delivery_days": null - } - ] +{ + "packages_urls": [ + "https://updates.networkoptix.com/default", + "http://beta.networkoptix.com/beta-builds/default" + ], + "releases": [ + { + "product": "vms", + "version": "5.0.0.35270", + "protocol_version": 5002, + "publication_type": "release", + "release_date": "1659371734147", + "release_delivery_days": null + } + ] } \ No newline at end of file diff --git a/Unraid/DWSpectrumLSIO.xml b/Unraid/DWSpectrumLSIO.xml index 3bee4c47..f3749bdd 100644 --- a/Unraid/DWSpectrumLSIO.xml +++ b/Unraid/DWSpectrumLSIO.xml @@ -1,85 +1,85 @@ - - - True - DWSpectrumLSIO - ptr727/dwspectrum-lsio - https://hub.docker.com/r/ptr727/dwspectrum-lsio/ - https://github.com/ptr727/NxWitness - MIT - https://github.com/ptr727/NxWitness - DW Spectrum IPVMS Docker based on LinuxServer - DW Spectrum IPVMS Docker based on LinuxServer - HomeAutomation: Security: Status:Beta - http://[IP]:[PORT:7001] - https://github.com/ptr727/NxWitness/Unraid - https://github.com/ptr727/NxWitness/raw/main/Unraid/DWLogo.png - - - /mnt/user/appdata/dwspectrum-lsio - /config - rw - - - /mnt/user - /media - rw - - - - - 99 - PUID - - - - 100 - PGID - - - - - 99 - 100 - /mnt/user/appdata/dwspectrum-lsio - /mnt/user - + + + True + DWSpectrumLSIO + ptr727/dwspectrum-lsio + https://hub.docker.com/r/ptr727/dwspectrum-lsio/ + https://github.com/ptr727/NxWitness + MIT + https://github.com/ptr727/NxWitness + DW Spectrum IPVMS Docker based on LinuxServer + DW Spectrum IPVMS Docker based on LinuxServer + HomeAutomation: Security: Status:Beta + http://[IP]:[PORT:7001] + https://github.com/ptr727/NxWitness/Unraid + https://github.com/ptr727/NxWitness/raw/main/Unraid/DWLogo.png + + + /mnt/user/appdata/dwspectrum-lsio + /config + rw + + + /mnt/user + /media + rw + + + + + 99 + PUID + + + + 100 + PGID + + + + + 99 + 100 + /mnt/user/appdata/dwspectrum-lsio + /mnt/user + diff --git a/Unraid/NxMetaLSIO.xml b/Unraid/NxMetaLSIO.xml index 1edb7899..457bee4c 100644 --- a/Unraid/NxMetaLSIO.xml +++ b/Unraid/NxMetaLSIO.xml @@ -1,85 +1,85 @@ - - - True - NxMetaLSIO - ptr727/nxmeta-lsio - https://hub.docker.com/r/ptr727/nxmeta-lsio/ - https://github.com/ptr727/NxWitness - MIT - https://github.com/ptr727/NxWitness - NxMeta VMS Docker based on LinuxServer - NxMeta VMS Docker based on LinuxServer - HomeAutomation: Security: Status:Beta - http://[IP]:[PORT:7001] - https://github.com/ptr727/NxWitness/Unraid - https://github.com/ptr727/NxWitness/raw/main/Unraid/NxLogo.png - - - /mnt/user/appdata/nxmeta-lsio - /config - rw - - - /mnt/user - /media - rw - - - - - 99 - PUID - - - - 100 - PGID - - - - - 99 - 100 - /mnt/user/appdata/nxmeta-lsio - /mnt/user - + + + True + NxMetaLSIO + ptr727/nxmeta-lsio + https://hub.docker.com/r/ptr727/nxmeta-lsio/ + https://github.com/ptr727/NxWitness + MIT + https://github.com/ptr727/NxWitness + NxMeta VMS Docker based on LinuxServer + NxMeta VMS Docker based on LinuxServer + HomeAutomation: Security: Status:Beta + http://[IP]:[PORT:7001] + https://github.com/ptr727/NxWitness/Unraid + https://github.com/ptr727/NxWitness/raw/main/Unraid/NxLogo.png + + + /mnt/user/appdata/nxmeta-lsio + /config + rw + + + /mnt/user + /media + rw + + + + + 99 + PUID + + + + 100 + PGID + + + + + 99 + 100 + /mnt/user/appdata/nxmeta-lsio + /mnt/user + diff --git a/Unraid/NxWitnessLSIO.xml b/Unraid/NxWitnessLSIO.xml index da50c289..0afe0fe7 100644 --- a/Unraid/NxWitnessLSIO.xml +++ b/Unraid/NxWitnessLSIO.xml @@ -1,85 +1,85 @@ - - - True - NxWitnessLSIO - ptr727/nxwitness-lsio - https://hub.docker.com/r/ptr727/nxwitness-lsio/ - https://github.com/ptr727/NxWitness - MIT - https://github.com/ptr727/NxWitness - NxWitness VMS Docker based on LinuxServer - NxWitness VMS Docker based on LinuxServer - HomeAutomation: Security: Status:Beta - http://[IP]:[PORT:7001] - https://github.com/ptr727/NxWitness/Unraid - https://github.com/ptr727/NxWitness/raw/main/Unraid/NxLogo.png - - - /mnt/user/appdata/nxwitness-lsio - /config - rw - - - /mnt/user - /media - rw - - - - - 99 - PUID - - - - 100 - PGID - - - - - 99 - 100 - /mnt/user/appdata/nxwitness-lsio - /mnt/user - + + + True + NxWitnessLSIO + ptr727/nxwitness-lsio + https://hub.docker.com/r/ptr727/nxwitness-lsio/ + https://github.com/ptr727/NxWitness + MIT + https://github.com/ptr727/NxWitness + NxWitness VMS Docker based on LinuxServer + NxWitness VMS Docker based on LinuxServer + HomeAutomation: Security: Status:Beta + http://[IP]:[PORT:7001] + https://github.com/ptr727/NxWitness/Unraid + https://github.com/ptr727/NxWitness/raw/main/Unraid/NxLogo.png + + + /mnt/user/appdata/nxwitness-lsio + /config + rw + + + /mnt/user + /media + rw + + + + + 99 + PUID + + + + 100 + PGID + + + + + 99 + 100 + /mnt/user/appdata/nxwitness-lsio + /mnt/user + diff --git a/WORKFLOW.md b/WORKFLOW.md index 26bd88a9..5aa1a356 100644 --- a/WORKFLOW.md +++ b/WORKFLOW.md @@ -1,638 +1,638 @@ -# WORKFLOW.md - -The single guide for this repo's CI/CD **workflows** (GitHub Actions): **code style**, **architecture**, a -**behavioral contract** (expected inputs and outputs), and a **test methodology**. Source code style lives -in [`CODESTYLE.md`](./CODESTYLE.md). This file covers everything under -[`.github/workflows/`](./.github/workflows/). - -It **describes required outcomes, not a required implementation.** A workflow is correct when it satisfies -the contract (section 4), whatever shape its YAML takes. Section 2 keeps workflows legible. Section 3 is -the model. Section 4 is what they must *do*. Sections 5 and 6 are how to verify it and the configuration it -assumes. Each guarantee names the **failure it prevents**, so the reason survives a reimplementation. - -## 0. The model at a glance - -NxWitness ships **Docker images only**: a multi-product, multi-base set of multi-arch images on Docker Hub -(five Nx VMS products x {plain, LSIO} = ten product images, plus two shared base images `nx-base` / -`nx-base-lsio`, across 12 Docker Hub repositories). There is **no NuGet publish**: the .NET project -(`CreateMatrix`) is a build-time **matrix generator** only, not a shipped package. Three things do the work: - -- **CI** runs on **push to every branch**: it validates (unit tests + lint) and smoke-builds a representative - image subset, publishing nothing. A pull request merges only when its required check is green. -- **The publisher** is a **triggered-Docker** workflow, one run = one branch. It runs on a **weekly schedule** - (rebuilds `main` only - full product matrix, shared base refresh for CVEs, versioned release), on a - **path-scoped push to `main`** when codegen commits a new `Make/Matrix.json` pin (publishes the new product - versions at once), and on **manual dispatch** (publishes the branch it is started from: `main` -> - `latest`/`stable`, `develop` -> `:develop`). It never runs on an ordinary merge. -- **Codegen** runs daily, dual-targeting `main` AND `develop`: it regenerates `Make/Version.json` + - `Make/Matrix.json` from upstream Nx product versions and opens a PR against each base, which the merge-bot - auto-merges. A merge to `main` whose matrix changed triggers the publisher's path-scoped push. - -There is no publish-on-merge for code and no two-branch matrix in the publisher - building only the trigger -branch keeps `github.ref` aligned with the branch being versioned. Dependabot pull requests merge themselves -once their checks pass. - -### Glossary - -- **Entry workflow** - has `push` / `schedule` / `workflow_dispatch` triggers. The orchestrator that an event - or a person starts. -- **Reusable workflow (task)** - a `workflow_call` workflow invoked through a `uses:` reference, never - triggered directly. File ends in `-task.yml`. -- **Product image** - one shipped image built from a `Make/Matrix.json` row's Dockerfile (e.g. `NxMeta`, - `NxMeta-LSIO`), pushed to its own Docker Hub repo (`docker.io/ptr727/`). -- **Shared base image** - `nx-base` / `nx-base-lsio`, built once and reused as the `FROM` for the product - images. Its tag (`:ubuntu-noble`) is **branch-agnostic** (one shared tag, not per-branch). -- **Matrix / version inputs** - `Make/Matrix.json` (the per-product image build rows: name, Dockerfile tags, - build args, branch) and `Make/Version.json` (the upstream Nx version pins), both regenerated by codegen. -- **Smoke build** - a CI build that compiles a representative image subset (NxMeta + NxMeta-LSIO, amd64) to - prove the Dockerfiles still build, publishing and pushing nothing. Driven by a `smoke: true` input. -- **Transfer artifact** - a workflow artifact handing data between jobs of one run. The durable copy lives on - the GitHub release / Docker Hub. -- **Threaded version** - the single NBGV `SemVer2` (plus `GitCommitId`) computed once in `get-version-task` - and passed down as `semver2` / `ref` inputs to every consumer, never recomputed in a build task. -- **GitHub App token** - a short-lived installation token from `actions/create-github-app-token`, minted from - the App credentials (`CODEGEN_APP_CLIENT_ID` / `CODEGEN_APP_PRIVATE_KEY`). The merge-bot and the codegen - PR-opener use it, not `GITHUB_TOKEN`: a `GITHUB_TOKEN` push does not trigger downstream workflows, and that - token is read-only on Dependabot pull requests. - -## 1. Purpose and how to use this document - -- **Contract, not implementation.** Conform to the *outcomes* in section 4 and the *architecture* in section - 3. Job names and file layout may vary; the input/output behavior may not. -- **"Operational" - the one definition.** The repo is **operational** when every applicable section-4 - guarantee holds, every applicable section-5B scenario's observed output equals its expected output - (corroborated by a 5C live probe where a live signal exists), and the section-6 configuration is in place. - Anything else is **not operational**. -- **Defect vs N/A.** An item is **N/A** only when this repo has no such concern (e.g. a fork-PR scenario, - since a fork cannot push here, or a NuGet-publish guarantee, since this repo publishes none). A construct - required by an applicable guarantee but absent is a **defect**. -- **Default branch is `main`.** Guarantees say "default branch" portably. This repo writes the literal `main` - in the publish guards and the release-version backstop, and the anchored `^refs/heads/main$` in - `version.json`'s `publicReleaseRefSpec`. - -## 2. Workflow style conventions - -Legibility rules. Necessary but not sufficient: a perfectly styled workflow can still violate section 4. - -- **Action pinning.** Pin every action to a commit SHA with a trailing `# vX.Y.Z` comment. Use `# vX` only - when the upstream floating major tag has no specific patch SHA. **Sole exception: `dotnet/nbgv@master`** is - consumed via the floating `@master` ref, never SHA-pinned - its tag stream lags `master` substantially, so - Dependabot tag-tracking would only propose downgrades to stale tags. The rationale lives in an inline - comment in [`get-version-task.yml`](./.github/workflows/get-version-task.yml); leave that comment intact. A - tool an action *installs* (not a `uses:` ref) is left unpinned to track latest. -- **Filename.** Reusable workflows end in `-task.yml`; entry workflows end in what they do - (`-pull-request.yml`, `-release.yml`). A `-task.yml` is `uses:`-d, never triggered directly. -- **Workflow `name:`.** Reusable names end in **"task"**, entry names in **"action"**. -- **Job and step `name:`.** Every job `name:` ends in **"job"**, every step `name:` in **"step"**, the - aggregator included (`Check pull request workflow status job`). A job name also bound as a ruleset - required-check `context:` is codified in [`repo-config/`](./repo-config/) and changed only **in lockstep** - with the live ruleset. -- **Concurrency.** Every entry workflow declares a `concurrency` group. CI uses - `group: '${{ github.workflow }}-${{ github.ref }}'`, `cancel-in-progress: true`. The publisher overrides - it: a ref-independent group with `cancel-in-progress: false`, so two publishes never overlap (a schedule, a - dispatch, and a `Make/Matrix.json` pin push, or back-to-back dispatches against the shared Docker tags) and - none is cancelled mid-release. Codegen keys on the workflow only (it writes fixed `codegen-*` branches). -- **Shells.** Every multi-line bash `run:` starts with `set -euo pipefail`. -- **Conditionals.** Multi-line `if:` uses the folded scalar `if: >-`. -- **Boolean inputs.** A boolean used by both `workflow_call` and `workflow_dispatch` is declared in both - trigger blocks and compared against `true` and `'true'`. -- **Reusable-workflow permissions.** Job-level `permissions:` are validated before `if:`, so even a skipped - job needs valid permissions. Grant least privilege; a callee's extra scope is granted by the caller. -- **Allowlist `success` and `skipped` explicitly** across an optional dependency: use - `(needs.X.result == 'success' || needs.X.result == 'skipped')`, not `!= 'failure'`. -- **Line endings.** Workflow YAML follows [`.editorconfig`](./.editorconfig) (CRLF). Preserve on every edit. - -## 3. Architecture - -### Three workflows: CI on push, publishing on schedule/pin-push/dispatch, codegen daily - -CI ([`test-pull-request.yml`](./.github/workflows/test-pull-request.yml)) and the publisher -([`publish-release.yml`](./.github/workflows/publish-release.yml)) are separate workflows with separate -concurrency, so they never race. CI re-tests every pushed tree and never publishes; the publisher releases on -its own triggers and never runs on an ordinary merge. Codegen -([`run-periodic-codegen-pull-request.yml`](./.github/workflows/run-periodic-codegen-pull-request.yml) -> -[`run-codegen-pull-request-task.yml`](./.github/workflows/run-codegen-pull-request-task.yml)) keeps the -version/matrix data current. *Prevents a merge from silently cutting a release, and a CI run from racing a -publish on the same ref.* - -### The publisher builds one branch: the trigger ref - -A publish builds exactly **one** branch - the run's trigger ref. The **schedule** and the **pin push** both -run on `main`; a **dispatch** runs on the branch it is started from (`main` or `develop`). The jobs pass -`github.ref_name` as both `ref` and `branch`, so the branch built, versioned, and tagged is always the run's -own ref. *No matrix and no cross-branch ref mixing - `github.ref` is the branch being published.* The jobs -are guarded to the long-lived branches (`main` / `develop`); a stray dispatch from a feature branch is a -no-op. To refresh `:develop`, dispatch the workflow from `develop`. - -Because the run's ref **is** the built branch, GitHub resolves the local `uses: ./...` reusable workflows -from that same branch's commit - so a `develop` dispatch runs develop's own task definitions, and the -schedule runs main's. - -### The publisher's pin-push trigger - -The publisher's `push` trigger is branch-filtered to `main` and path-filtered to `Make/Matrix.json`. So an -ordinary code merge never publishes; only a change to the codegen matrix pin (committed to `main` by the -auto-merged `codegen-main` PR) does, shipping the new upstream Nx product versions immediately. develop's -daily codegen pin update is sync-only - the push trigger is main-only - and never publishes; `:develop` is -refreshed by manual dispatch. The weekly schedule still runs with no matrix change, to refresh the shared -base image for CVEs. - -### The multi-image build layer - -The publisher decomposes into a single `get-version` -> `build-base` -> `build-docker` -> -`github-release` -> `docker-readme` -> `cleanup-artifacts` chain (a multi-product Docker repo, not the -template's single-target branch matrix). [`build-base-images-task.yml`](./.github/workflows/build-base-images-task.yml) -builds the two shared bases; [`build-docker-task.yml`](./.github/workflows/build-docker-task.yml) builds the -product matrix from `Make/Matrix.json` (`max-parallel: 4`). The shared base is built **once** (on the `main` -run) and reused: a `develop` dispatch sets `build_base: false` and pulls main's published base, so it never -overwrites the branch-agnostic `nx-base` tag. The product build reads both branches' registry buildcaches -(`buildcache-main`, `buildcache-develop`) and writes only its own branch's cache, only when pushing. - -### Versioning: compute once, thread everywhere - -NBGV runs once (in [`get-version-task.yml`](./.github/workflows/get-version-task.yml)), classifying from -`github.ref`, and its outputs (`SemVer2`, `GitCommitId`) thread to every consumer via `outputs:` / `needs:` / -`semver2` inputs. `build-docker-task` accepts the threaded `semver2` as the image `LABEL_VERSION` and never -re-runs NBGV - one classification feeds every product leg, so no second NBGV run can reclassify or collide a -tag. A build job may check out a specific commit to compile it (main pins to `GitCommitId`) but consumes the -threaded version. `main` (the public ref, `publicReleaseRefSpec = ^refs/heads/main$`) builds a clean -`X.Y.`; every other branch a prerelease `X.Y.-g`. *Keeps each image's embedded version -and the release tag in agreement.* - -NBGV classifies `publicReleaseRefSpec` from the `GITHUB_REF` environment variable. Because the publisher -builds the **trigger ref** (one branch per run), `GITHUB_REF` already equals the branch being versioned - a -schedule, a pin push, or a `main` dispatch classifies as public; a `develop` dispatch as prerelease - so no -`GITHUB_REF` override is needed (`IGNORE_GITHUB_REF` is only for matrix publishers that build a non-trigger -branch). The main-version backstop (D2.2) catches any misclassification. - -NBGV's version is the GitHub release tag on `main` and the `LABEL_VERSION` build arg baked into the images. -The Docker image **tags** themselves carry the Nx product version from `Make/Matrix.json`, distinct from the -NBGV build version. - -### Validate at entry - -A run that carries a cross-input invariant (`main` must not carry a prerelease suffix) asserts it once with -`::error::` before the release is published. The `github-release` job `needs:` the version job and runs the -backstop step first. - -### Fast CI feedback, head-resolved - -CI runs on push to every branch, so GitHub head-resolves the reusable `./...` workflows from the pushed head: -a pull request that edits a reusable task tests its own copy. CI validates (the reusable `validate-task`: -Husky lint + `dotnet test`) on every push, and smoke-builds a representative image subset only when image -files changed (an inline `git diff` change-gate, no `dorny/paths-filter`), uploading and pushing nothing. One -aggregator job, the ruleset-bound required check, gates the merge. A branch-deletion push (all-zeros -`github.sha`) is skipped by a `!github.event.deleted` guard on every job, so a deletion never runs a failing -build. - -### The Docker-only release - -The `github-release` job tags the built commit and creates the GitHub release (auto source zip + README + -LICENSE; `target_commitish` pinned to `GitCommitId`; skip-existing guard; main-only). This repo ships **no** -`release-asset-*` binaries or packages - the published artifacts are the Docker Hub images - so there is no -release-asset download step and `fail_on_unmatched_files` is omitted. The GitHub release exists only as the -version anchor / tag. The Docker Hub repository overview (the repo `README.md`) is pushed to every product + -base repo on a `main` Docker publish (the `docker-readme` jobs), since Docker Hub does not read the GitHub -README; the repo list is derived inline from `Make/Matrix.json`. - -### Resource lifecycle - -Workflow artifacts are an intra-run handoff; the durable copy lives on the GitHub release / Docker Hub. The -publisher and CI both run a terminal `cleanup-artifacts` job that deletes the run's transfer artifacts so -they do not accumulate against the small account-wide storage quota; it is `continue-on-error` so housekeeping -never reds the run. - -### Self-sufficiency: automatic updates - -Every Dependabot pull request, any ecosystem and any tier, auto-merges once the required checks pass, except -a **semver-major NuGet** bump, which waits for human review. Codegen opens a `codegen-main` -> `main` and a -`codegen-develop` -> `develop` PR daily; the merge-bot auto-merges each independently (`--delete-branch`). -A merged dependency bump does not itself publish; a merged matrix pin on `main` does (the pin-push trigger). -A person steps in only for a breaking change (a red check) or to dispatch a release. - -### Flow diagrams - -Four diagrams trace the architecture above: the pull-request gate, the self-publisher, the bot -automation, and the trigger chain that turns a daily codegen run into a published release. They depict -the same outcomes that the section 4 contract specifies, drawn from the workflow YAML; if a diagram and -a guarantee disagree, one of them is a defect. Triggers are blue, gates yellow, durable/published -outputs green, and stop/skip outcomes red. - -**Pull request (CI) - `test-pull-request.yml`.** Every push head-resolves the reusable tasks, runs the -validate gate, smoke-builds a representative image subset only when image files changed, and a single -aggregator produces the ruleset-bound required check (D1, D6). - -```mermaid -flowchart TD - T(["push: every branch
(or workflow_dispatch)"]):::trig - T --> D{"github.event.deleted?"}:::gate - D -- "yes: branch deletion" --> X(["all jobs + aggregator skip
no failed run, no pending check"]):::stop - D -- "no" --> CH["changes job
inline git diff change-gate
image? base?"] - D -- "no" --> V["validate job
(validate-task.yml)"] - subgraph VT ["validate-task.yml"] - VU["Husky lint (CSharpier,
dotnet format style)
+ dotnet test"] - end - V --> VT - CH --> SG{"image files changed?
(Docker/**, Make/Matrix.json, Make/Version.json)"}:::gate - SG -- "no" --> SS(["smoke-build skipped
(aggregator allows skip)"]):::stop - SG -- "yes" --> S["smoke-build job
build-docker-task.yml
smoke: true, push: false
NxMeta + NxMeta-LSIO, amd64"] - CH --> A - VT --> A - S --> A - SS --> A - A{"Check pull request workflow status job
changes AND validate AND smoke-build
succeeded or skipped?"}:::gate - A -- "yes" --> G(["required check passes
merge unblocked"]):::pub - A -- "no" --> R(["required check fails
merge blocked"]):::stop - classDef trig fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - classDef gate fill:#fef9c3,stroke:#ca8a04,color:#713f12 - classDef pub fill:#dcfce7,stroke:#16a34a,color:#14532d - classDef stop fill:#fee2e2,stroke:#dc2626,color:#7f1d1d -``` - -**Publish - `publish-release.yml`.** A weekly schedule (main), a `Make/Matrix.json` pin push (main), or a -dispatch versions once with NBGV, builds the shared base (main only), validates, builds the 12-image -product matrix with the threaded SemVer2, then cuts the main-only GitHub release and refreshes the Docker -Hub overviews (D0, D2, D3, D4). - -```mermaid -flowchart TD - P1(["schedule: weekly Mon 02:00 UTC
(main only)"]):::trig --> GV - P2(["push: main
paths = Make/Matrix.json (codegen pin)"]):::trig --> GV - P3(["workflow_dispatch
(main or develop)"]):::trig --> GV - GV{"get-version job
ref_name in (main, develop)?"}:::gate - GV -- "feature branch" --> GVS(["all jobs skip
no publish"]):::stop - GV -- "yes" --> GVR["get-version job
(get-version-task.yml)
NBGV @master, runs once
SemVer2 + GitCommitId"] - GVR --> BB{"ref_name == main?"}:::gate - BB -- "develop dispatch" --> BBS(["build-base skipped
reuse main's nx-base"]):::stop - BB -- "main" --> BBJ["build-base job
(build-base-images-task.yml)
nx-base + nx-base-lsio
amd64 + arm64, branch ref (github.ref_name)"] - GVR --> VAL["validate job
(validate-task.yml)
main: pinned to GitCommitId"] - VAL --> BD - BBJ --> BD - BBS --> BD - BD["build-docker job
(build-docker-task.yml, build_base: false)
12-image matrix from Make/Matrix.json
amd64 + arm64, max-parallel 4
LABEL_VERSION = threaded SemVer2"] - BD --> DH[("Docker Hub
10 product repos (branch tags)
+ 2 shared base repos")]:::pub - BD --> RG{"ref_name == main?"}:::gate - RG -- "develop" --> RGS(["no GitHub release
(:develop images only)"]):::stop - RG -- "main" --> VPR{"github-release job
SemVer2 has no prerelease '-'?
(strip +buildmetadata)"}:::gate - VPR -- "prerelease suffix" --> VPRX(["fail ::error::
refuse to publish"]):::stop - VPR -- "clean" --> EX{"tag exists AND not dispatch?"}:::gate - EX -- "yes" --> EXS(["skip release create
(no-op republish)"]):::stop - EX -- "no" --> REL[("GitHub release
tag = SemVer2 at GitCommitId
prerelease: false, source zip + README + LICENSE")]:::pub - BD --> DRR["docker-readme-repos job
derive repo list from Matrix.json"] - DRR --> DRM["docker-readme job (matrix)
push README to each Docker Hub repo"] - DRM --> DRO[("Docker Hub overviews
10 product + 2 base repos")]:::pub - classDef trig fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - classDef gate fill:#fef9c3,stroke:#ca8a04,color:#713f12 - classDef pub fill:#dcfce7,stroke:#16a34a,color:#14532d - classDef stop fill:#fee2e2,stroke:#dc2626,color:#7f1d1d -``` - -**Automation - codegen + Dependabot + merge-bot.** Daily codegen and Dependabot open in-repo bot PRs; the -merge-bot enables auto-merge (or disables it on a maintainer push); the required check gates each merge -(D8). - -```mermaid -flowchart TD - SCH(["schedule daily 04:00 UTC
(or workflow_dispatch)"]):::trig --> CG - subgraph CGT ["run-codegen-pull-request-task.yml (matrix: main, develop)"] - CG["codegen job per branch
regenerate Version.json + Matrix.json
(deterministic, forward-only guard)"] --> CGC{"data changed?"}:::gate - CGC -- "no" --> CGN(["no PR"]):::stop - CGC -- "yes" --> CPR["open codegen-<branch> PR
(App token)"] - end - DEP(["Dependabot opens PR
any ecosystem/tier"]):::trig --> MB - CPR --> MB - subgraph MBT ["merge-bot-pull-request.yml (pull_request_target)"] - MB{"event / author"}:::gate - MB -- "opened/reopened
bot author" --> EN["enable auto-merge --delete-branch
squash develop / merge main"] - MB -- "synchronize by maintainer" --> DIS["disable auto-merge"] - end - EN --> SM{"semver-major NuGet?"}:::gate - SM -- "yes" --> HUM(["wait for human review"]):::stop - SM -- "no" --> CK{"required check passes?"}:::gate - CK -- "yes" --> MRG(["PR merges (App token)"]):::pub - CK -- "no" --> BLK(["merge blocked
maintainer notified"]):::stop - MRG -. "codegen-main Matrix.json change" .-> PUBR(["publisher pin-push auto-publishes main"]):::pub - classDef trig fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - classDef gate fill:#fef9c3,stroke:#ca8a04,color:#713f12 - classDef pub fill:#dcfce7,stroke:#16a34a,color:#14532d - classDef stop fill:#fee2e2,stroke:#dc2626,color:#7f1d1d -``` - -**Trigger chain - daily codegen to published release.** The recurring path that ships a new upstream Nx -product version with no human in the loop: only a `codegen-main` matrix change reaches the publisher's -pin push; the develop pin update is sync-only, and `:develop` / the weekly base refresh come from the -schedule and dispatch (D4.1, D8.3). - -```mermaid -flowchart TD - PER(["schedule daily 04:00 UTC
run-periodic-codegen-pull-request.yml
(or workflow_dispatch)"]):::trig --> CGM - CGM["codegen matrix: main + develop
regenerate Version.json + Matrix.json"] --> DCH{"Matrix.json changed?"}:::gate - DCH -- "no" --> NOPR(["no PR, nothing ships"]):::stop - DCH -- "yes: main" --> PRM["codegen-main -> main PR
(merge-bot auto-merges)"] - DCH -- "yes: develop" --> PRD["codegen-develop -> develop PR
(merge-bot auto-merges)"] - PRD --> SYNC(["develop Matrix.json updated
sync-only, push trigger is main-only
:develop refreshed by dispatch"]):::stop - PRM --> PUSH(["push to main
paths = Make/Matrix.json"]):::trig - PUSH --> PUB["publish-release.yml
pin-push: build base + 12-image matrix"] - PUB --> SINK[("Docker Hub product + base images
+ main GitHub release")]:::pub - SCHED(["weekly schedule (main)
base refresh for CVEs"]):::trig --> PUB - DISP(["workflow_dispatch (main or develop)
force publish that branch"]):::trig --> PUB - classDef trig fill:#dbeafe,stroke:#2563eb,color:#1e3a8a - classDef gate fill:#fef9c3,stroke:#ca8a04,color:#713f12 - classDef pub fill:#dcfce7,stroke:#16a34a,color:#14532d - classDef stop fill:#fee2e2,stroke:#dc2626,color:#7f1d1d -``` - -## 4. Behavioral contract - expected outcomes - -Each is a **MUST**, stated as input -> output plus the failure it prevents. - -### D0 - Architecture - -- **D0.1 CI is one run, one branch.** Input: any push. Output: `test-pull-request` builds/validates exactly - `github.ref_name` and publishes nothing. *Prevents cross-branch ref mixing in CI.* -- **D0.2 The publisher builds one branch: the trigger ref.** Output: the publisher passes `github.ref_name` - as `ref` and `branch`, so it checks out, versions, and tags exactly the run's own branch (the schedule/pin - push's `main`, or a dispatch's branch). No branch matrix; the jobs are guarded to `main`/`develop`. - *Prevents cross-branch ref mixing - `github.ref` is the branch being published.* -- **D0.3 One version, threaded.** Output: NBGV runs once (`get-version-task`); every consumer reads it via - `needs:` outputs / the `semver2` input; no consumer recomputes it. *Allowed:* checking out a specific - commit to compile it, and recording the built commit as the release `target_commitish`. *Prevents an - image's embedded version diverging from its tag, and a second NBGV run colliding image tags.* - -### D1 - CI fast feedback - -- **D1.1 Every push validates; image changes smoke-build.** Output: on any push the `validate` job (the - reusable `validate-task`) runs with no paths filter; `smoke-build` (NxMeta + NxMeta-LSIO, amd64, no push) - runs when the inline change-gate detects an image-file change (`Docker/**`, `Make/Matrix.json`, - `Make/Version.json`). *Prevents a reusable-workflow or Dockerfile break shipping untested.* -- **D1.2 Unit tests always run.** Output: `validate-task` runs `dotnet test` (the codegen tool + its tests). -- **D1.3 Lint enforces the editor checks in CI.** Output: `validate-task` runs Husky (`dotnet husky run`: - CSharpier + `dotnet format style --verify-no-changes`) - the same checks the editor and the pre-commit - hook run. Workflow YAML is lint-checked by `actionlint` (run from the editor / locally). -- **D1.4 Smoke never publishes and never uploads a release asset.** Output: a smoke build compiles the image - subset but makes no GitHub release and no Docker push (`push: false`, `smoke: true`). The Docker login runs - on every build including smoke (for higher pull/cache rate limits against the registry buildcache), so a - Dependabot-triggered push-CI smoke build needs the Docker Hub credentials in both secret stores; fork PRs do - not run this push-CI. -- **D1.5 One required aggregator gates merge.** Output: a single aggregator job must **succeed** (success or, - for the conditionally-skipped `smoke-build`, skipped; never failure/cancelled), `needs:` `changes`, - `validate`, and `smoke-build`, and treats a `changes` failure as blocking (so an image-changing PR cannot - merge on a `changes` failure mis-read as skip). Its name is ruleset-bound (D6.2) and must not be renamed. - -### D2 - Validation at entry - -- **D2.1 Validate the cross-input invariant before publishing.** Output: the `github-release` job asserts each - cross-input invariant with `::error::` (the main-version backstop, D2.2) before the release is created; - downstream steps `needs:` the version job. The publish run also re-runs `validate-task` (the `validate` job - gates `build-docker`), so a publish can never ship a tree that would fail the same lint + `dotnet test` gate - CI enforces on push - on the trigger branch CI already validated, re-checked at publish time. -- **D2.2 Main matches version classification.** Input: a real publish run for `main`. Output: the release - fails loudly if `main` carries a prerelease suffix. It strips `+buildmetadata` before testing for the - prerelease `-`. *Prevents a develop build published as the stable `latest`.* - -### D3 - Versioning and classification - -- **D3.1 NBGV runs once, threaded.** Output: NBGV runs once, classifying from the checked-out branch; no - consumer re-invokes it. The run builds the trigger ref, so `GITHUB_REF` already matches the branch being - versioned and NBGV classifies it correctly (no `IGNORE_GITHUB_REF`). The threaded `semver2` is the image - `LABEL_VERSION`. -- **D3.2 `main` = stable, others = prerelease.** Output: `main` -> `X.Y.Z`, any other branch -> - `X.Y.Z-g`. The release-version backstop names `main`; `publicReleaseRefSpec` is `^refs/heads/main$`. -- **D3.3 Version floor + git height.** Output: `version.json` sets the major.minor floor, NBGV appends the git - height as the patch, never bumped on a cadence. *(Who raises the floor and when is a human-process rule in - `AGENTS.md`.)* - -### D4 - Release / publish - -- **D4.1 Publish only on schedule, the matrix-pin push, or dispatch - never on an ordinary merge.** Output: - `publish-release` triggers are `schedule` (weekly), `workflow_dispatch`, and a `push` **branch-filtered to - `main` and path-filtered to `Make/Matrix.json`**. There is no other `push` trigger and no `PUBLISH_ON_MERGE` - variable. The jobs are guarded to `github.ref_name` in (`main`, `develop`), so a stray dispatch from a - feature branch is a no-op. *Prevents per-merge release churn while still shipping a new product pin at once.* -- **D4.2 A publish builds the one trigger branch in full.** Output: the run builds the shared base (main run - only) + the full product matrix and creates the GitHub release for `github.ref_name` - the schedule/pin push - rebuilds `main` (stable / `latest`); a dispatch publishes its own branch (`main` stable / `latest`, - `develop` prerelease / `:develop`, no GitHub release). *Prevents a half-published image set and cross-branch - ref mixing.* -- **D4.3 Tag the built commit.** Output: the release `target_commitish` is the run's `GitCommitId` (the tip of - `main`), and main's images are built from that pinned commit, never the moving ref. *Prevents the tag / - embedded version landing on the wrong commit.* -- **D4.4 Release contents and gate.** Output: a release is a tag on the built commit plus the auto source zip, - README, and LICENSE - no binary release assets (`fail_on_unmatched_files` omitted; the images are the - artifact). The release is `main`-only (`github.ref_name == 'main'`); `prerelease: false`. *Prevents - publishing a develop build as a stable GitHub release.* -- **D4.5 No-op republish.** Input: a weekly re-run whose version is unchanged. Output: the release-create step - is skipped when the tag already exists (refreshed only on `workflow_dispatch`), while the Docker push still - runs - re-pushing the same tags refreshes the shared base image. *Prevents duplicate releases while still - refreshing the images.* -- **D4.6 Publish is built from the tree CI validated.** Output: the run's ref **is** the published branch, so - the reusable-task definitions and the built tree resolve from that branch - the same tree CI validated on - push (the required check gates every merge to it) with the identical `validate-task` definition - and the publish run re-runs that `validate-task` (the - `validate` job gates `build-docker`). The main-version backstop (D2.2) is the additional in-publisher gate. -- **D4.7 Docker publishing authenticates with Docker Hub credentials.** Output: the base and product builds - log in via `docker/login-action` with `DOCKER_HUB_USERNAME` + `DOCKER_HUB_ACCESS_TOKEN` and push with - `docker/build-push-action`; the Docker Hub overview is pushed with the same token. There is **no NuGet/OIDC - publishing** in this repo. *Prevents a missing-credential publish failure.* -- **D4.8 Branch-scoped Docker buildcache.** Output: the base and product builds read both branches' registry - caches (`buildcache-main`, `buildcache-develop`) and write only their own branch's cache, only when pushing, - so a `main` and a `develop` publish never overwrite each other's cache. *Prevents one branch's publish - destroying the other's cache hit-rate.* -- **D4.9 Multi-arch, multi-product, shared-base fan-out.** Output: the publish builds every product image from - `Make/Matrix.json` for `linux/amd64` + `linux/arm64`, on the shared base built once and reused; each product - image pushes to its own Docker Hub repo. *Prevents a per-arch or per-product gap, and a redundant per-branch - base rebuild overwriting the shared tag.* - -### D5 - Resource cleanup - -- **D5.1 Terminal cleanup, best-effort.** The publisher and CI each run a terminal `cleanup-artifacts` job - (`always()`, `continue-on-error`) that deletes the run's transfer artifacts, independent of the required - aggregator so housekeeping never gates the merge. -- **D5.2 Never red the run on cleanup.** Cleanup failures warn, never fail. - -### D6 - Self-testing workflows - -- **D6.1 A change is testable on its own branch.** Output: a workflow or build change is exercised by CI on - the branch that introduces it, no dependency on reaching `main` first. -- **D6.2 Head-resolution, single producer, fork exception.** Output: CI runs on `push` to every branch so - reusable `./...` logic resolves from the head, and the aggregator's ruleset-bound `context:` is produced by - that push run as the sole producer of that name. Dependabot and codegen PRs are in-repo branches, validated - the same way. A fork cannot push, so it has no run and is validated by maintainer action - the one - exception. *Prevents a dual-producer context race and a false self-test claim for forks.* - -### D7 - Concurrency, permissions, safety - -- **D7.1 The publisher does not cancel mid-flight.** Output: ref-independent group, `cancel-in-progress: - false`. CI uses the `...-${{ github.ref }}` group with `cancel-in-progress: true`; the merge-bot keys on PR - number (D8.1); codegen keys on the workflow. -- **D7.2 Skipped jobs still need valid permissions.** Output: every reusable job runs under valid least-privilege - `permissions:`; a callee's extra scope is granted by the caller. -- **D7.3 Boolean inputs both forms.** Declared in both trigger blocks, compared against `true` and `'true'`. -- **D7.4 Branch-deletion guard.** Output: every CI job carries `if: !github.event.deleted` and the aggregator - `always() && !github.event.deleted`, so a branch-deletion push (all-zeros `github.sha`) runs no failing - build and does not fail the required check. - -### D8 - Bots and automation - -- **D8.1 Merge-bot.** Output: runs on `pull_request_target`, holds the App token, merges the PR by URL without - checking out its code. Enables auto-merge on `opened`/`reopened` with `--delete-branch`; squash on - `develop`, merge-commit on `main` by the PR's base ref; disables auto-merge when a maintainer pushes to a - bot branch (no `--delete-branch` on the disable path). Concurrency keyed on PR number. -- **D8.2 Dependabot auto-merges on green, semver-major NuGet excepted.** Output: every Dependabot PR - auto-merges once the required checks pass, except a semver-major NuGet bump (human review). A failing check - blocks the merge. A merged dependency bump does **not** itself publish (it ships in the next scheduled run - or the next matrix-pin push). -- **D8.3 Codegen dual-targets `main` AND `develop`.** Output: the daily codegen matrix opens a - `codegen-main` -> `main` and a `codegen-develop` -> `develop` PR (strict head/base pairing in the merge-bot), - regenerating `Make/Version.json` + `Make/Matrix.json` **only** (not the Dockerfiles - those are a separate - human-driven `Make/Create.sh` path). The merge-bot auto-merges each independently. develop's matrix update - is sync-only; a merged `codegen-main` matrix change fires the publisher's pin push. *Prevents non-linear - rebase/merge-block conflicts (the rejected single-target scheme) and keeps both branches current with - upstream Nx versions.* -- **D8.4 Forward-only version guard.** Output: the codegen tool's forward-only guard (`ReleaseVersionForward`, - covered by `CreateMatrixTests`) prevents a regenerated matrix from regressing a product version. *Prevents a - generic-tag downgrade slipping in via codegen.* - -### D9 - Style, static, and dropped workflows (see section 2) - -- **D9.1** Every action SHA-pinned with a version comment (sole exception: `dotnet/nbgv@master`, whose tag - stream lags master so Dependabot tag-tracking would only propose downgrades to stale tags - a deliberate - documented float, rationale inline in `get-version-task.yml`); an installed-tool version is left unpinned to - track latest. -- **D9.2** File/workflow/job/step names follow the suffix rules; a ruleset-bound `context:` name moves only in - lockstep with `repo-config/`. -- **D9.3** Bash `run:` blocks start `set -euo pipefail`; multi-line `if:` uses `>-`. -- **D9.4** Line endings follow `.editorconfig`. -- **D9.5 No decorative / dropped workflows.** No date-badge (`build-datebadge-*`), no standalone docker-readme - task (folded into the publisher), no `PUBLISH_ON_MERGE` variable, no `dorny/paths-filter` (replaced by the - inline change-gate). Their presence is a defect to remove. -- **D9.6** Style is enforced in CI by `validate-task` (D1.3), from the same config files the editor and Husky - hook use. - -### D10 - Repository configuration - -- **D10.1 Required configuration is present.** Output: the secrets, branch rulesets, and repository settings - section 6 lists are all in place, codified in [`repo-config/`](./repo-config/) and audited by its - `configure.sh check` (the 5D audit). The detail is in section 6. - -## 5. Test methodology - -### 5A. Static audit (no execution) - -Read the workflow files plus `version.json` and `Make/Matrix.json` and assert the fact behind each applicable -guarantee with a `file:line` citation: - -- **D0:** CI has no branch matrix; the publisher passes `github.ref_name` as `ref`/`branch` and is guarded to - `main`/`develop`; NBGV invoked once in `get-version-task`, `build-docker-task` has no nested `get-version` - and consumes the `semver2` input; the run builds the trigger ref so `GITHUB_REF` matches the versioned - branch. -- **D1:** CI runs on `push` with no paths filter on `validate`; the inline `changes` gate sets `image`/`base`; - `smoke-build` is `smoke: true`, `push: false`, amd64 NxMeta subset; the aggregator `needs:` `changes` + - `validate` + `smoke-build`, blocks on non-success, treats `changes` failure as blocking. -- **D2:** the main release backstop checks the prerelease `-`, strips `+buildmetadata`; `validate-task` is the - shared gate. -- **D3:** `main` appears in the backstop and the release `prerelease: false`/guard; `publicReleaseRefSpec` is - `^refs/heads/main$`; `semver2` threads to `LABEL_VERSION`. -- **D4:** `publish-release` triggers are `schedule` + `workflow_dispatch` + `push` (branches `[main]`, paths - `[Make/Matrix.json]`) only (no other push, no `PUBLISH_ON_MERGE`); jobs guarded to `github.ref_name` in - (`main`, `develop`); `target_commitish` = `GitCommitId`; main pins `ref` to `GitCommitId`, develop reuses - the base (`build_base: false`); the build logs in with `DOCKER_HUB_*`; buildcache branch-scoped and - write-gated on push; release-create gated `exists == false || workflow_dispatch`; release + docker-readme - gated to `main`; the product matrix builds amd64+arm64 from `Make/Matrix.json`. -- **D5:** the publisher and CI each have a terminal `cleanup-artifacts` (`always()`, `continue-on-error`), - independent of the aggregator. -- **D6:** CI is `push` on every branch; the aggregator context has exactly one producer; no `pull_request` - trigger; every CI job has the `!github.event.deleted` guard. -- **D7:** the publisher group is ref-independent with `cancel-in-progress: false`; the merge-bot keys on PR - number; codegen keys on the workflow; CI uses the standard group + deletion guard; reusable jobs declare - permissions. -- **D8/D9:** the merge-bot runs on `pull_request_target` with the App token, keyed on PR number, both merge - jobs use `--delete-branch`; Dependabot auto-merge excepts semver-major NuGet only; codegen dual-targets - main+develop and regenerates only `Version.json`/`Matrix.json`; no date-badge, standalone docker-readme - task, `PUBLISH_ON_MERGE`, or `dorny/paths-filter`; actions SHA-pinned except `nbgv@master`; - names/shells/conditionals per section 2. - -### 5B. End-to-end trace scenarios (deterministic from the YAML) - -| # | Input | Expected output | Exercises | -| --- | --- | --- | --- | -| S1 | push touching `Docker/**` | `validate` + `smoke-build` (NxMeta amd64) run, **no push, no release**; aggregator success; no dangling artifacts | D0.1, D1 | -| S2 | push changing only docs | `validate` runs; the `changes` gate sets `image=false`; `smoke-build` skipped; aggregator success (skip allowed) | D1, D1.5 | -| S3 | push changing only `.github/workflows/**` | `validate` runs head-resolved; `smoke-build` skipped (no image files); aggregator success | D1.1, D6.1 | -| S4 | weekly `schedule` | builds + publishes `main` only: shared base refresh + full product matrix (amd64+arm64) + stable release + `latest`; `target_commitish` = main's SHA; develop untouched; no dangling artifacts | D4.1, D4.2, D4.9 | -| S5 | push to `main` changing `Make/Matrix.json` (codegen pin) | publishes `main` with the new product versions immediately | D4.1, D8.3 | -| S6 | `workflow_dispatch` from `develop` | builds + publishes `develop`: `:develop` images, prerelease classification, `build_base: false` (reuses main's base), **no GitHub release** | D4.1, D4.2, D3.2 | -| S7 | `workflow_dispatch` re-run on `main`, no new commits | release-create refreshed on dispatch (skipped on schedule if the tag exists); Docker re-pushed (base refresh); no duplicate release | D4.5 | -| S8 | `workflow_dispatch` from a feature branch | the `github.ref_name in (main, develop)` guard skips every job -> no publish | D4.1 | -| S9 | merged dependency bump (any) | not a matrix-pin change; merges don't publish -> **no release**; ships in the next scheduled run | D4.1, D8.2 | -| S10 | merged develop codegen PR (`Matrix.json` change on develop) | sync-only; the pin push is main-only -> **no publish** | D8.3 | -| S11 | PR with a CSharpier / format / unit-test failure | `validate` fails -> aggregator blocks the merge | D1.2, D1.3, D1.5 | -| S12 | `version.json` floor bump merged | merges don't publish -> no immediate release; the new floor ships in the next publish | D3.3, D4.1 | -| S13 | Dependabot semver-major NuGet bump | gated on human review -> does not auto-merge; other majors auto-merge on green | D8.2 | -| S14 | branch-deletion push | every CI job + the aggregator skip (`!github.event.deleted`) -> no failing required check | D7.4 | -| S15 | `develop` -> `main` promotion (merge commit) | the merge itself does not publish; if it changed `Matrix.json` the pin push publishes main, else the next schedule does | D4.1, D8.1 | - -### 5C. Live probe (where warranted, never publishing) - -- Open a trivial doc-only PR and confirm S2 (`validate` runs, `smoke-build` skipped, aggregator green, 0 - artifacts left); open an image-touching PR and confirm S1 (smoke-build runs amd64, nothing pushed). -- After a `main` publish (schedule, pin push, or dispatch) confirm a stable release (`isPrerelease == false`) - with multi-arch product images (`docker buildx imagetools inspect` shows amd64 + arm64) and the shared base - tags intact; after a `develop` dispatch confirm `:develop` images, prerelease classification, and that the - shared `nx-base:ubuntu-noble` tag was not overwritten. A re-run adds no duplicate release. Absent publish - rights, record indeterminate and rely on 5A/5B. -- Trigger codegen via dispatch: confirm both `codegen-main` -> `main` and `codegen-develop` -> `develop` PRs - open and the merge-bot auto-merges with branch deletion. - -### 5D. Configuration audit - -Run [`repo-config/configure.sh check`](./repo-config/). It confirms the listed secrets exist (in both -stores), the `main`/`develop` rulesets enforce the required merge method + status check + signed commits + -strict-off, and the repository settings are in place, exiting non-zero on drift. Secret *values* cannot be -read back, so it asserts the names exist (failing if it cannot query them). The GitHub App installation is a -best-effort check (a precise check needs app-level auth, so it notes rather than fails). The Docker Hub -token's validity and push scope across the 12 repositories are a manual checklist item. - -### Assessment - -Operational when every applicable 5A item passes, every applicable 5B scenario matches (corroborated by 5C -where a live signal exists), and 5D configuration is in place. Procedure: **Audit** (5A + 5D) -> **Trace** -(5B) -> **Probe** (5C, without publishing) -> **Verdict** with the failing guarantee(s) and the triggering -input for each. - -## 6. Repository configuration - -The workflows depend on configuration outside the YAML. A misconfiguration surfaces only as a failed run, so -the configuration is part of "operational" (D10; audit 5D). - -**Secrets.** - -- `DOCKER_HUB_USERNAME` / `DOCKER_HUB_ACCESS_TOKEN` - Docker Hub credentials the base and product builds log - in with to push the images and the repository overviews. Required in **both** the Actions and Dependabot - secret stores: a Dependabot-triggered push runs CI whose Docker smoke build logs in too (when an image file - changed), and that run gets the Dependabot store. The access token needs push scope on every product + - base repo under `docker.io/ptr727/` (derived from `Make/Matrix.json` - see `repo-config/README.md`). There - is no NuGet/OIDC publishing. -- `CODEGEN_APP_CLIENT_ID` / `CODEGEN_APP_PRIVATE_KEY` - the GitHub App credentials the merge-bot and the - codegen PR-opener mint the App token from. Required in **both** the Actions and Dependabot secret stores (a - Dependabot-triggered run gets the Dependabot store). The App must be installed on the repo with - `contents: write` and `pull_requests: write`. -- The built-in `GITHUB_TOKEN` needs no setup. **No `PUBLISH_ON_MERGE` variable is used.** - -**Branch rulesets.** - -- `main` - merge-commit merges only; requires the aggregator status check (`Check pull request workflow status - job`); requires signed commits; "require branches up to date before merging" is **off** (a forward-only - `develop` makes every post-release `main` tip unreachable from `develop`, so the strict check would fail - every release). -- `develop` - squash merges only (keeps history linear); requires the same status check; requires signed - commits; "up to date" is **off** (so same-batch bot PRs auto-merge in parallel). -- The required check's `context:` matches the aggregator job name verbatim (D6.2, D9.2). - -**Repository settings.** Auto-merge enabled; squash and merge-commit both allowed (each ruleset narrows its -branch to one); rebase off; auto-delete-on-merge **off** (so `main`/`develop` survive a promotion; the -merge-bot deletes bot branches explicitly with `--delete-branch`). Dependabot version **and** security updates -enabled. The GitHub App installed with the scopes above. - -**Validation.** This configuration is codified in [`repo-config/`](./repo-config/) and applied/audited by -`repo-config/configure.sh`; `check` is the 5D audit. Secret values cannot be read back, so the audit asserts -the names exist (failing if they cannot be queried); the App installation is a best-effort check. +# WORKFLOW.md + +The single guide for this repo's CI/CD **workflows** (GitHub Actions): **code style**, **architecture**, a +**behavioral contract** (expected inputs and outputs), and a **test methodology**. Source code style lives +in [`CODESTYLE.md`](./CODESTYLE.md). This file covers everything under +[`.github/workflows/`](./.github/workflows/). + +It **describes required outcomes, not a required implementation.** A workflow is correct when it satisfies +the contract (section 4), whatever shape its YAML takes. Section 2 keeps workflows legible. Section 3 is +the model. Section 4 is what they must *do*. Sections 5 and 6 are how to verify it and the configuration it +assumes. Each guarantee names the **failure it prevents**, so the reason survives a reimplementation. + +## 0. The model at a glance + +NxWitness ships **Docker images only**: a multi-product, multi-base set of multi-arch images on Docker Hub +(five Nx VMS products x {plain, LSIO} = ten product images, plus two shared base images `nx-base` / +`nx-base-lsio`, across 12 Docker Hub repositories). There is **no NuGet publish**: the .NET project +(`CreateMatrix`) is a build-time **matrix generator** only, not a shipped package. Three things do the work: + +- **CI** runs on **push to every branch**: it validates (unit tests + lint) and smoke-builds a representative + image subset, publishing nothing. A pull request merges only when its required check is green. +- **The publisher** is a **triggered-Docker** workflow, one run = one branch. It runs on a **weekly schedule** + (rebuilds `main` only - full product matrix, shared base refresh for CVEs, versioned release), on a + **path-scoped push to `main`** when codegen commits a new `Make/Matrix.json` pin (publishes the new product + versions at once), and on **manual dispatch** (publishes the branch it is started from: `main` -> + `latest`/`stable`, `develop` -> `:develop`). It never runs on an ordinary merge. +- **Codegen** runs daily, dual-targeting `main` AND `develop`: it regenerates `Make/Version.json` + + `Make/Matrix.json` from upstream Nx product versions and opens a PR against each base, which the merge-bot + auto-merges. A merge to `main` whose matrix changed triggers the publisher's path-scoped push. + +There is no publish-on-merge for code and no two-branch matrix in the publisher - building only the trigger +branch keeps `github.ref` aligned with the branch being versioned. Dependabot pull requests merge themselves +once their checks pass. + +### Glossary + +- **Entry workflow** - has `push` / `schedule` / `workflow_dispatch` triggers. The orchestrator that an event + or a person starts. +- **Reusable workflow (task)** - a `workflow_call` workflow invoked through a `uses:` reference, never + triggered directly. File ends in `-task.yml`. +- **Product image** - one shipped image built from a `Make/Matrix.json` row's Dockerfile (e.g. `NxMeta`, + `NxMeta-LSIO`), pushed to its own Docker Hub repo (`docker.io/ptr727/`). +- **Shared base image** - `nx-base` / `nx-base-lsio`, built once and reused as the `FROM` for the product + images. Its tag (`:ubuntu-noble`) is **branch-agnostic** (one shared tag, not per-branch). +- **Matrix / version inputs** - `Make/Matrix.json` (the per-product image build rows: name, Dockerfile tags, + build args, branch) and `Make/Version.json` (the upstream Nx version pins), both regenerated by codegen. +- **Smoke build** - a CI build that compiles a representative image subset (NxMeta + NxMeta-LSIO, amd64) to + prove the Dockerfiles still build, publishing and pushing nothing. Driven by a `smoke: true` input. +- **Transfer artifact** - a workflow artifact handing data between jobs of one run. The durable copy lives on + the GitHub release / Docker Hub. +- **Threaded version** - the single NBGV `SemVer2` (plus `GitCommitId`) computed once in `get-version-task` + and passed down as `semver2` / `ref` inputs to every consumer, never recomputed in a build task. +- **GitHub App token** - a short-lived installation token from `actions/create-github-app-token`, minted from + the App credentials (`CODEGEN_APP_CLIENT_ID` / `CODEGEN_APP_PRIVATE_KEY`). The merge-bot and the codegen + PR-opener use it, not `GITHUB_TOKEN`: a `GITHUB_TOKEN` push does not trigger downstream workflows, and that + token is read-only on Dependabot pull requests. + +## 1. Purpose and how to use this document + +- **Contract, not implementation.** Conform to the *outcomes* in section 4 and the *architecture* in section + 3. Job names and file layout may vary; the input/output behavior may not. +- **"Operational" - the one definition.** The repo is **operational** when every applicable section-4 + guarantee holds, every applicable section-5B scenario's observed output equals its expected output + (corroborated by a 5C live probe where a live signal exists), and the section-6 configuration is in place. + Anything else is **not operational**. +- **Defect vs N/A.** An item is **N/A** only when this repo has no such concern (e.g. a fork-PR scenario, + since a fork cannot push here, or a NuGet-publish guarantee, since this repo publishes none). A construct + required by an applicable guarantee but absent is a **defect**. +- **Default branch is `main`.** Guarantees say "default branch" portably. This repo writes the literal `main` + in the publish guards and the release-version backstop, and the anchored `^refs/heads/main$` in + `version.json`'s `publicReleaseRefSpec`. + +## 2. Workflow style conventions + +Legibility rules. Necessary but not sufficient: a perfectly styled workflow can still violate section 4. + +- **Action pinning.** Pin every action to a commit SHA with a trailing `# vX.Y.Z` comment. Use `# vX` only + when the upstream floating major tag has no specific patch SHA. **Sole exception: `dotnet/nbgv@master`** is + consumed via the floating `@master` ref, never SHA-pinned - its tag stream lags `master` substantially, so + Dependabot tag-tracking would only propose downgrades to stale tags. The rationale lives in an inline + comment in [`get-version-task.yml`](./.github/workflows/get-version-task.yml); leave that comment intact. A + tool an action *installs* (not a `uses:` ref) is left unpinned to track latest. +- **Filename.** Reusable workflows end in `-task.yml`; entry workflows end in what they do + (`-pull-request.yml`, `-release.yml`). A `-task.yml` is `uses:`-d, never triggered directly. +- **Workflow `name:`.** Reusable names end in **"task"**, entry names in **"action"**. +- **Job and step `name:`.** Every job `name:` ends in **"job"**, every step `name:` in **"step"**, the + aggregator included (`Check pull request workflow status job`). A job name also bound as a ruleset + required-check `context:` is codified in [`repo-config/`](./repo-config/) and changed only **in lockstep** + with the live ruleset. +- **Concurrency.** Every entry workflow declares a `concurrency` group. CI uses + `group: '${{ github.workflow }}-${{ github.ref }}'`, `cancel-in-progress: true`. The publisher overrides + it: a ref-independent group with `cancel-in-progress: false`, so two publishes never overlap (a schedule, a + dispatch, and a `Make/Matrix.json` pin push, or back-to-back dispatches against the shared Docker tags) and + none is cancelled mid-release. Codegen keys on the workflow only (it writes fixed `codegen-*` branches). +- **Shells.** Every multi-line bash `run:` starts with `set -euo pipefail`. +- **Conditionals.** Multi-line `if:` uses the folded scalar `if: >-`. +- **Boolean inputs.** A boolean used by both `workflow_call` and `workflow_dispatch` is declared in both + trigger blocks and compared against `true` and `'true'`. +- **Reusable-workflow permissions.** Job-level `permissions:` are validated before `if:`, so even a skipped + job needs valid permissions. Grant least privilege; a callee's extra scope is granted by the caller. +- **Allowlist `success` and `skipped` explicitly** across an optional dependency: use + `(needs.X.result == 'success' || needs.X.result == 'skipped')`, not `!= 'failure'`. +- **Line endings.** Workflow YAML is LF, per [`.editorconfig`](./.editorconfig)'s `[*]` default (Actions and Dependabot rewrite it that way). Preserve endings on every edit. + +## 3. Architecture + +### Three workflows: CI on push, publishing on schedule/pin-push/dispatch, codegen daily + +CI ([`test-pull-request.yml`](./.github/workflows/test-pull-request.yml)) and the publisher +([`publish-release.yml`](./.github/workflows/publish-release.yml)) are separate workflows with separate +concurrency, so they never race. CI re-tests every pushed tree and never publishes; the publisher releases on +its own triggers and never runs on an ordinary merge. Codegen +([`run-periodic-codegen-pull-request.yml`](./.github/workflows/run-periodic-codegen-pull-request.yml) -> +[`run-codegen-pull-request-task.yml`](./.github/workflows/run-codegen-pull-request-task.yml)) keeps the +version/matrix data current. *Prevents a merge from silently cutting a release, and a CI run from racing a +publish on the same ref.* + +### The publisher builds one branch: the trigger ref + +A publish builds exactly **one** branch - the run's trigger ref. The **schedule** and the **pin push** both +run on `main`; a **dispatch** runs on the branch it is started from (`main` or `develop`). The jobs pass +`github.ref_name` as both `ref` and `branch`, so the branch built, versioned, and tagged is always the run's +own ref. *No matrix and no cross-branch ref mixing - `github.ref` is the branch being published.* The jobs +are guarded to the long-lived branches (`main` / `develop`); a stray dispatch from a feature branch is a +no-op. To refresh `:develop`, dispatch the workflow from `develop`. + +Because the run's ref **is** the built branch, GitHub resolves the local `uses: ./...` reusable workflows +from that same branch's commit - so a `develop` dispatch runs develop's own task definitions, and the +schedule runs main's. + +### The publisher's pin-push trigger + +The publisher's `push` trigger is branch-filtered to `main` and path-filtered to `Make/Matrix.json`. So an +ordinary code merge never publishes; only a change to the codegen matrix pin (committed to `main` by the +auto-merged `codegen-main` PR) does, shipping the new upstream Nx product versions immediately. develop's +daily codegen pin update is sync-only - the push trigger is main-only - and never publishes; `:develop` is +refreshed by manual dispatch. The weekly schedule still runs with no matrix change, to refresh the shared +base image for CVEs. + +### The multi-image build layer + +The publisher decomposes into a single `get-version` -> `build-base` -> `build-docker` -> +`github-release` -> `docker-readme` -> `cleanup-artifacts` chain (a multi-product Docker repo, not the +template's single-target branch matrix). [`build-base-images-task.yml`](./.github/workflows/build-base-images-task.yml) +builds the two shared bases; [`build-docker-task.yml`](./.github/workflows/build-docker-task.yml) builds the +product matrix from `Make/Matrix.json` (`max-parallel: 4`). The shared base is built **once** (on the `main` +run) and reused: a `develop` dispatch sets `build_base: false` and pulls main's published base, so it never +overwrites the branch-agnostic `nx-base` tag. The product build reads both branches' registry buildcaches +(`buildcache-main`, `buildcache-develop`) and writes only its own branch's cache, only when pushing. + +### Versioning: compute once, thread everywhere + +NBGV runs once (in [`get-version-task.yml`](./.github/workflows/get-version-task.yml)), classifying from +`github.ref`, and its outputs (`SemVer2`, `GitCommitId`) thread to every consumer via `outputs:` / `needs:` / +`semver2` inputs. `build-docker-task` accepts the threaded `semver2` as the image `LABEL_VERSION` and never +re-runs NBGV - one classification feeds every product leg, so no second NBGV run can reclassify or collide a +tag. A build job may check out a specific commit to compile it (main pins to `GitCommitId`) but consumes the +threaded version. `main` (the public ref, `publicReleaseRefSpec = ^refs/heads/main$`) builds a clean +`X.Y.`; every other branch a prerelease `X.Y.-g`. *Keeps each image's embedded version +and the release tag in agreement.* + +NBGV classifies `publicReleaseRefSpec` from the `GITHUB_REF` environment variable. Because the publisher +builds the **trigger ref** (one branch per run), `GITHUB_REF` already equals the branch being versioned - a +schedule, a pin push, or a `main` dispatch classifies as public; a `develop` dispatch as prerelease - so no +`GITHUB_REF` override is needed (`IGNORE_GITHUB_REF` is only for matrix publishers that build a non-trigger +branch). The main-version backstop (D2.2) catches any misclassification. + +NBGV's version is the GitHub release tag on `main` and the `LABEL_VERSION` build arg baked into the images. +The Docker image **tags** themselves carry the Nx product version from `Make/Matrix.json`, distinct from the +NBGV build version. + +### Validate at entry + +A run that carries a cross-input invariant (`main` must not carry a prerelease suffix) asserts it once with +`::error::` before the release is published. The `github-release` job `needs:` the version job and runs the +backstop step first. + +### Fast CI feedback, head-resolved + +CI runs on push to every branch, so GitHub head-resolves the reusable `./...` workflows from the pushed head: +a pull request that edits a reusable task tests its own copy. CI validates (the reusable `validate-task`: +Husky lint + `dotnet test`) on every push, and smoke-builds a representative image subset only when image +files changed (an inline `git diff` change-gate, no `dorny/paths-filter`), uploading and pushing nothing. One +aggregator job, the ruleset-bound required check, gates the merge. A branch-deletion push (all-zeros +`github.sha`) is skipped by a `!github.event.deleted` guard on every job, so a deletion never runs a failing +build. + +### The Docker-only release + +The `github-release` job tags the built commit and creates the GitHub release (auto source zip + README + +LICENSE; `target_commitish` pinned to `GitCommitId`; skip-existing guard; main-only). This repo ships **no** +`release-asset-*` binaries or packages - the published artifacts are the Docker Hub images - so there is no +release-asset download step and `fail_on_unmatched_files` is omitted. The GitHub release exists only as the +version anchor / tag. The Docker Hub repository overview (the repo `README.md`) is pushed to every product + +base repo on a `main` Docker publish (the `docker-readme` jobs), since Docker Hub does not read the GitHub +README; the repo list is derived inline from `Make/Matrix.json`. + +### Resource lifecycle + +Workflow artifacts are an intra-run handoff; the durable copy lives on the GitHub release / Docker Hub. The +publisher and CI both run a terminal `cleanup-artifacts` job that deletes the run's transfer artifacts so +they do not accumulate against the small account-wide storage quota; it is `continue-on-error` so housekeeping +never reds the run. + +### Self-sufficiency: automatic updates + +Every Dependabot pull request, any ecosystem and any tier, auto-merges once the required checks pass, except +a **semver-major NuGet** bump, which waits for human review. Codegen opens a `codegen-main` -> `main` and a +`codegen-develop` -> `develop` PR daily; the merge-bot auto-merges each independently (`--delete-branch`). +A merged dependency bump does not itself publish; a merged matrix pin on `main` does (the pin-push trigger). +A person steps in only for a breaking change (a red check) or to dispatch a release. + +### Flow diagrams + +Four diagrams trace the architecture above: the pull-request gate, the self-publisher, the bot +automation, and the trigger chain that turns a daily codegen run into a published release. They depict +the same outcomes that the section 4 contract specifies, drawn from the workflow YAML; if a diagram and +a guarantee disagree, one of them is a defect. Triggers are blue, gates yellow, durable/published +outputs green, and stop/skip outcomes red. + +**Pull request (CI) - `test-pull-request.yml`.** Every push head-resolves the reusable tasks, runs the +validate gate, smoke-builds a representative image subset only when image files changed, and a single +aggregator produces the ruleset-bound required check (D1, D6). + +```mermaid +flowchart TD + T(["push: every branch
(or workflow_dispatch)"]):::trig + T --> D{"github.event.deleted?"}:::gate + D -- "yes: branch deletion" --> X(["all jobs + aggregator skip
no failed run, no pending check"]):::stop + D -- "no" --> CH["changes job
inline git diff change-gate
image? base?"] + D -- "no" --> V["validate job
(validate-task.yml)"] + subgraph VT ["validate-task.yml"] + VU["Husky lint (CSharpier,
dotnet format style)
+ dotnet test"] + end + V --> VT + CH --> SG{"image files changed?
(Docker/**, Make/Matrix.json, Make/Version.json)"}:::gate + SG -- "no" --> SS(["smoke-build skipped
(aggregator allows skip)"]):::stop + SG -- "yes" --> S["smoke-build job
build-docker-task.yml
smoke: true, push: false
NxMeta + NxMeta-LSIO, amd64"] + CH --> A + VT --> A + S --> A + SS --> A + A{"Check pull request workflow status job
changes AND validate AND smoke-build
succeeded or skipped?"}:::gate + A -- "yes" --> G(["required check passes
merge unblocked"]):::pub + A -- "no" --> R(["required check fails
merge blocked"]):::stop + classDef trig fill:#dbeafe,stroke:#2563eb,color:#1e3a8a + classDef gate fill:#fef9c3,stroke:#ca8a04,color:#713f12 + classDef pub fill:#dcfce7,stroke:#16a34a,color:#14532d + classDef stop fill:#fee2e2,stroke:#dc2626,color:#7f1d1d +``` + +**Publish - `publish-release.yml`.** A weekly schedule (main), a `Make/Matrix.json` pin push (main), or a +dispatch versions once with NBGV, builds the shared base (main only), validates, builds the 12-image +product matrix with the threaded SemVer2, then cuts the main-only GitHub release and refreshes the Docker +Hub overviews (D0, D2, D3, D4). + +```mermaid +flowchart TD + P1(["schedule: weekly Mon 02:00 UTC
(main only)"]):::trig --> GV + P2(["push: main
paths = Make/Matrix.json (codegen pin)"]):::trig --> GV + P3(["workflow_dispatch
(main or develop)"]):::trig --> GV + GV{"get-version job
ref_name in (main, develop)?"}:::gate + GV -- "feature branch" --> GVS(["all jobs skip
no publish"]):::stop + GV -- "yes" --> GVR["get-version job
(get-version-task.yml)
NBGV @master, runs once
SemVer2 + GitCommitId"] + GVR --> BB{"ref_name == main?"}:::gate + BB -- "develop dispatch" --> BBS(["build-base skipped
reuse main's nx-base"]):::stop + BB -- "main" --> BBJ["build-base job
(build-base-images-task.yml)
nx-base + nx-base-lsio
amd64 + arm64, branch ref (github.ref_name)"] + GVR --> VAL["validate job
(validate-task.yml)
main: pinned to GitCommitId"] + VAL --> BD + BBJ --> BD + BBS --> BD + BD["build-docker job
(build-docker-task.yml, build_base: false)
12-image matrix from Make/Matrix.json
amd64 + arm64, max-parallel 4
LABEL_VERSION = threaded SemVer2"] + BD --> DH[("Docker Hub
10 product repos (branch tags)
+ 2 shared base repos")]:::pub + BD --> RG{"ref_name == main?"}:::gate + RG -- "develop" --> RGS(["no GitHub release
(:develop images only)"]):::stop + RG -- "main" --> VPR{"github-release job
SemVer2 has no prerelease '-'?
(strip +buildmetadata)"}:::gate + VPR -- "prerelease suffix" --> VPRX(["fail ::error::
refuse to publish"]):::stop + VPR -- "clean" --> EX{"tag exists AND not dispatch?"}:::gate + EX -- "yes" --> EXS(["skip release create
(no-op republish)"]):::stop + EX -- "no" --> REL[("GitHub release
tag = SemVer2 at GitCommitId
prerelease: false, source zip + README + LICENSE")]:::pub + BD --> DRR["docker-readme-repos job
derive repo list from Matrix.json"] + DRR --> DRM["docker-readme job (matrix)
push README to each Docker Hub repo"] + DRM --> DRO[("Docker Hub overviews
10 product + 2 base repos")]:::pub + classDef trig fill:#dbeafe,stroke:#2563eb,color:#1e3a8a + classDef gate fill:#fef9c3,stroke:#ca8a04,color:#713f12 + classDef pub fill:#dcfce7,stroke:#16a34a,color:#14532d + classDef stop fill:#fee2e2,stroke:#dc2626,color:#7f1d1d +``` + +**Automation - codegen + Dependabot + merge-bot.** Daily codegen and Dependabot open in-repo bot PRs; the +merge-bot enables auto-merge (or disables it on a maintainer push); the required check gates each merge +(D8). + +```mermaid +flowchart TD + SCH(["schedule daily 04:00 UTC
(or workflow_dispatch)"]):::trig --> CG + subgraph CGT ["run-codegen-pull-request-task.yml (matrix: main, develop)"] + CG["codegen job per branch
regenerate Version.json + Matrix.json
(deterministic, forward-only guard)"] --> CGC{"data changed?"}:::gate + CGC -- "no" --> CGN(["no PR"]):::stop + CGC -- "yes" --> CPR["open codegen-<branch> PR
(App token)"] + end + DEP(["Dependabot opens PR
any ecosystem/tier"]):::trig --> MB + CPR --> MB + subgraph MBT ["merge-bot-pull-request.yml (pull_request_target)"] + MB{"event / author"}:::gate + MB -- "opened/reopened
bot author" --> EN["enable auto-merge --delete-branch
squash develop / merge main"] + MB -- "synchronize by maintainer" --> DIS["disable auto-merge"] + end + EN --> SM{"semver-major NuGet?"}:::gate + SM -- "yes" --> HUM(["wait for human review"]):::stop + SM -- "no" --> CK{"required check passes?"}:::gate + CK -- "yes" --> MRG(["PR merges (App token)"]):::pub + CK -- "no" --> BLK(["merge blocked
maintainer notified"]):::stop + MRG -. "codegen-main Matrix.json change" .-> PUBR(["publisher pin-push auto-publishes main"]):::pub + classDef trig fill:#dbeafe,stroke:#2563eb,color:#1e3a8a + classDef gate fill:#fef9c3,stroke:#ca8a04,color:#713f12 + classDef pub fill:#dcfce7,stroke:#16a34a,color:#14532d + classDef stop fill:#fee2e2,stroke:#dc2626,color:#7f1d1d +``` + +**Trigger chain - daily codegen to published release.** The recurring path that ships a new upstream Nx +product version with no human in the loop: only a `codegen-main` matrix change reaches the publisher's +pin push; the develop pin update is sync-only, and `:develop` / the weekly base refresh come from the +schedule and dispatch (D4.1, D8.3). + +```mermaid +flowchart TD + PER(["schedule daily 04:00 UTC
run-periodic-codegen-pull-request.yml
(or workflow_dispatch)"]):::trig --> CGM + CGM["codegen matrix: main + develop
regenerate Version.json + Matrix.json"] --> DCH{"Matrix.json changed?"}:::gate + DCH -- "no" --> NOPR(["no PR, nothing ships"]):::stop + DCH -- "yes: main" --> PRM["codegen-main -> main PR
(merge-bot auto-merges)"] + DCH -- "yes: develop" --> PRD["codegen-develop -> develop PR
(merge-bot auto-merges)"] + PRD --> SYNC(["develop Matrix.json updated
sync-only, push trigger is main-only
:develop refreshed by dispatch"]):::stop + PRM --> PUSH(["push to main
paths = Make/Matrix.json"]):::trig + PUSH --> PUB["publish-release.yml
pin-push: build base + 12-image matrix"] + PUB --> SINK[("Docker Hub product + base images
+ main GitHub release")]:::pub + SCHED(["weekly schedule (main)
base refresh for CVEs"]):::trig --> PUB + DISP(["workflow_dispatch (main or develop)
force publish that branch"]):::trig --> PUB + classDef trig fill:#dbeafe,stroke:#2563eb,color:#1e3a8a + classDef gate fill:#fef9c3,stroke:#ca8a04,color:#713f12 + classDef pub fill:#dcfce7,stroke:#16a34a,color:#14532d + classDef stop fill:#fee2e2,stroke:#dc2626,color:#7f1d1d +``` + +## 4. Behavioral contract - expected outcomes + +Each is a **MUST**, stated as input -> output plus the failure it prevents. + +### D0 - Architecture + +- **D0.1 CI is one run, one branch.** Input: any push. Output: `test-pull-request` builds/validates exactly + `github.ref_name` and publishes nothing. *Prevents cross-branch ref mixing in CI.* +- **D0.2 The publisher builds one branch: the trigger ref.** Output: the publisher passes `github.ref_name` + as `ref` and `branch`, so it checks out, versions, and tags exactly the run's own branch (the schedule/pin + push's `main`, or a dispatch's branch). No branch matrix; the jobs are guarded to `main`/`develop`. + *Prevents cross-branch ref mixing - `github.ref` is the branch being published.* +- **D0.3 One version, threaded.** Output: NBGV runs once (`get-version-task`); every consumer reads it via + `needs:` outputs / the `semver2` input; no consumer recomputes it. *Allowed:* checking out a specific + commit to compile it, and recording the built commit as the release `target_commitish`. *Prevents an + image's embedded version diverging from its tag, and a second NBGV run colliding image tags.* + +### D1 - CI fast feedback + +- **D1.1 Every push validates; image changes smoke-build.** Output: on any push the `validate` job (the + reusable `validate-task`) runs with no paths filter; `smoke-build` (NxMeta + NxMeta-LSIO, amd64, no push) + runs when the inline change-gate detects an image-file change (`Docker/**`, `Make/Matrix.json`, + `Make/Version.json`). *Prevents a reusable-workflow or Dockerfile break shipping untested.* +- **D1.2 Unit tests always run.** Output: `validate-task` runs `dotnet test` (the codegen tool + its tests). +- **D1.3 Lint enforces the editor checks in CI.** Output: `validate-task` runs Husky (`dotnet husky run`: + CSharpier + `dotnet format style --verify-no-changes`) - the same checks the editor and the pre-commit + hook run. Workflow YAML is lint-checked by `actionlint` (run from the editor / locally). +- **D1.4 Smoke never publishes and never uploads a release asset.** Output: a smoke build compiles the image + subset but makes no GitHub release and no Docker push (`push: false`, `smoke: true`). The Docker login runs + on every build including smoke (for higher pull/cache rate limits against the registry buildcache), so a + Dependabot-triggered push-CI smoke build needs the Docker Hub credentials in both secret stores; fork PRs do + not run this push-CI. +- **D1.5 One required aggregator gates merge.** Output: a single aggregator job must **succeed** (success or, + for the conditionally-skipped `smoke-build`, skipped; never failure/cancelled), `needs:` `changes`, + `validate`, and `smoke-build`, and treats a `changes` failure as blocking (so an image-changing PR cannot + merge on a `changes` failure mis-read as skip). Its name is ruleset-bound (D6.2) and must not be renamed. + +### D2 - Validation at entry + +- **D2.1 Validate the cross-input invariant before publishing.** Output: the `github-release` job asserts each + cross-input invariant with `::error::` (the main-version backstop, D2.2) before the release is created; + downstream steps `needs:` the version job. The publish run also re-runs `validate-task` (the `validate` job + gates `build-docker`), so a publish can never ship a tree that would fail the same lint + `dotnet test` gate + CI enforces on push - on the trigger branch CI already validated, re-checked at publish time. +- **D2.2 Main matches version classification.** Input: a real publish run for `main`. Output: the release + fails loudly if `main` carries a prerelease suffix. It strips `+buildmetadata` before testing for the + prerelease `-`. *Prevents a develop build published as the stable `latest`.* + +### D3 - Versioning and classification + +- **D3.1 NBGV runs once, threaded.** Output: NBGV runs once, classifying from the checked-out branch; no + consumer re-invokes it. The run builds the trigger ref, so `GITHUB_REF` already matches the branch being + versioned and NBGV classifies it correctly (no `IGNORE_GITHUB_REF`). The threaded `semver2` is the image + `LABEL_VERSION`. +- **D3.2 `main` = stable, others = prerelease.** Output: `main` -> `X.Y.Z`, any other branch -> + `X.Y.Z-g`. The release-version backstop names `main`; `publicReleaseRefSpec` is `^refs/heads/main$`. +- **D3.3 Version floor + git height.** Output: `version.json` sets the major.minor floor, NBGV appends the git + height as the patch, never bumped on a cadence. *(Who raises the floor and when is a human-process rule in + `AGENTS.md`.)* + +### D4 - Release / publish + +- **D4.1 Publish only on schedule, the matrix-pin push, or dispatch - never on an ordinary merge.** Output: + `publish-release` triggers are `schedule` (weekly), `workflow_dispatch`, and a `push` **branch-filtered to + `main` and path-filtered to `Make/Matrix.json`**. There is no other `push` trigger and no `PUBLISH_ON_MERGE` + variable. The jobs are guarded to `github.ref_name` in (`main`, `develop`), so a stray dispatch from a + feature branch is a no-op. *Prevents per-merge release churn while still shipping a new product pin at once.* +- **D4.2 A publish builds the one trigger branch in full.** Output: the run builds the shared base (main run + only) + the full product matrix and creates the GitHub release for `github.ref_name` - the schedule/pin push + rebuilds `main` (stable / `latest`); a dispatch publishes its own branch (`main` stable / `latest`, + `develop` prerelease / `:develop`, no GitHub release). *Prevents a half-published image set and cross-branch + ref mixing.* +- **D4.3 Tag the built commit.** Output: the release `target_commitish` is the run's `GitCommitId` (the tip of + `main`), and main's images are built from that pinned commit, never the moving ref. *Prevents the tag / + embedded version landing on the wrong commit.* +- **D4.4 Release contents and gate.** Output: a release is a tag on the built commit plus the auto source zip, + README, and LICENSE - no binary release assets (`fail_on_unmatched_files` omitted; the images are the + artifact). The release is `main`-only (`github.ref_name == 'main'`); `prerelease: false`. *Prevents + publishing a develop build as a stable GitHub release.* +- **D4.5 No-op republish.** Input: a weekly re-run whose version is unchanged. Output: the release-create step + is skipped when the tag already exists (refreshed only on `workflow_dispatch`), while the Docker push still + runs - re-pushing the same tags refreshes the shared base image. *Prevents duplicate releases while still + refreshing the images.* +- **D4.6 Publish is built from the tree CI validated.** Output: the run's ref **is** the published branch, so + the reusable-task definitions and the built tree resolve from that branch - the same tree CI validated on + push (the required check gates every merge to it) with the identical `validate-task` definition - and the publish run re-runs that `validate-task` (the + `validate` job gates `build-docker`). The main-version backstop (D2.2) is the additional in-publisher gate. +- **D4.7 Docker publishing authenticates with Docker Hub credentials.** Output: the base and product builds + log in via `docker/login-action` with `DOCKER_HUB_USERNAME` + `DOCKER_HUB_ACCESS_TOKEN` and push with + `docker/build-push-action`; the Docker Hub overview is pushed with the same token. There is **no NuGet/OIDC + publishing** in this repo. *Prevents a missing-credential publish failure.* +- **D4.8 Branch-scoped Docker buildcache.** Output: the base and product builds read both branches' registry + caches (`buildcache-main`, `buildcache-develop`) and write only their own branch's cache, only when pushing, + so a `main` and a `develop` publish never overwrite each other's cache. *Prevents one branch's publish + destroying the other's cache hit-rate.* +- **D4.9 Multi-arch, multi-product, shared-base fan-out.** Output: the publish builds every product image from + `Make/Matrix.json` for `linux/amd64` + `linux/arm64`, on the shared base built once and reused; each product + image pushes to its own Docker Hub repo. *Prevents a per-arch or per-product gap, and a redundant per-branch + base rebuild overwriting the shared tag.* + +### D5 - Resource cleanup + +- **D5.1 Terminal cleanup, best-effort.** The publisher and CI each run a terminal `cleanup-artifacts` job + (`always()`, `continue-on-error`) that deletes the run's transfer artifacts, independent of the required + aggregator so housekeeping never gates the merge. +- **D5.2 Never red the run on cleanup.** Cleanup failures warn, never fail. + +### D6 - Self-testing workflows + +- **D6.1 A change is testable on its own branch.** Output: a workflow or build change is exercised by CI on + the branch that introduces it, no dependency on reaching `main` first. +- **D6.2 Head-resolution, single producer, fork exception.** Output: CI runs on `push` to every branch so + reusable `./...` logic resolves from the head, and the aggregator's ruleset-bound `context:` is produced by + that push run as the sole producer of that name. Dependabot and codegen PRs are in-repo branches, validated + the same way. A fork cannot push, so it has no run and is validated by maintainer action - the one + exception. *Prevents a dual-producer context race and a false self-test claim for forks.* + +### D7 - Concurrency, permissions, safety + +- **D7.1 The publisher does not cancel mid-flight.** Output: ref-independent group, `cancel-in-progress: + false`. CI uses the `...-${{ github.ref }}` group with `cancel-in-progress: true`; the merge-bot keys on PR + number (D8.1); codegen keys on the workflow. +- **D7.2 Skipped jobs still need valid permissions.** Output: every reusable job runs under valid least-privilege + `permissions:`; a callee's extra scope is granted by the caller. +- **D7.3 Boolean inputs both forms.** Declared in both trigger blocks, compared against `true` and `'true'`. +- **D7.4 Branch-deletion guard.** Output: every CI job carries `if: !github.event.deleted` and the aggregator + `always() && !github.event.deleted`, so a branch-deletion push (all-zeros `github.sha`) runs no failing + build and does not fail the required check. + +### D8 - Bots and automation + +- **D8.1 Merge-bot.** Output: runs on `pull_request_target`, holds the App token, merges the PR by URL without + checking out its code. Enables auto-merge on `opened`/`reopened` with `--delete-branch`; squash on + `develop`, merge-commit on `main` by the PR's base ref; disables auto-merge when a maintainer pushes to a + bot branch (no `--delete-branch` on the disable path). Concurrency keyed on PR number. +- **D8.2 Dependabot auto-merges on green, semver-major NuGet excepted.** Output: every Dependabot PR + auto-merges once the required checks pass, except a semver-major NuGet bump (human review). A failing check + blocks the merge. A merged dependency bump does **not** itself publish (it ships in the next scheduled run + or the next matrix-pin push). +- **D8.3 Codegen dual-targets `main` AND `develop`.** Output: the daily codegen matrix opens a + `codegen-main` -> `main` and a `codegen-develop` -> `develop` PR (strict head/base pairing in the merge-bot), + regenerating `Make/Version.json` + `Make/Matrix.json` **only** (not the Dockerfiles - those are a separate + human-driven `Make/Create.sh` path). The merge-bot auto-merges each independently. develop's matrix update + is sync-only; a merged `codegen-main` matrix change fires the publisher's pin push. *Prevents non-linear + rebase/merge-block conflicts (the rejected single-target scheme) and keeps both branches current with + upstream Nx versions.* +- **D8.4 Forward-only version guard.** Output: the codegen tool's forward-only guard (`ReleaseVersionForward`, + covered by `CreateMatrixTests`) prevents a regenerated matrix from regressing a product version. *Prevents a + generic-tag downgrade slipping in via codegen.* + +### D9 - Style, static, and dropped workflows (see section 2) + +- **D9.1** Every action SHA-pinned with a version comment (sole exception: `dotnet/nbgv@master`, whose tag + stream lags master so Dependabot tag-tracking would only propose downgrades to stale tags - a deliberate + documented float, rationale inline in `get-version-task.yml`); an installed-tool version is left unpinned to + track latest. +- **D9.2** File/workflow/job/step names follow the suffix rules; a ruleset-bound `context:` name moves only in + lockstep with `repo-config/`. +- **D9.3** Bash `run:` blocks start `set -euo pipefail`; multi-line `if:` uses `>-`. +- **D9.4** Line endings follow `.editorconfig`. +- **D9.5 No decorative / dropped workflows.** No date-badge (`build-datebadge-*`), no standalone docker-readme + task (folded into the publisher), no `PUBLISH_ON_MERGE` variable, no `dorny/paths-filter` (replaced by the + inline change-gate). Their presence is a defect to remove. +- **D9.6** Style is enforced in CI by `validate-task` (D1.3), from the same config files the editor and Husky + hook use. + +### D10 - Repository configuration + +- **D10.1 Required configuration is present.** Output: the secrets, branch rulesets, and repository settings + section 6 lists are all in place, codified in [`repo-config/`](./repo-config/) and audited by its + `configure.sh check` (the 5D audit). The detail is in section 6. + +## 5. Test methodology + +### 5A. Static audit (no execution) + +Read the workflow files plus `version.json` and `Make/Matrix.json` and assert the fact behind each applicable +guarantee with a `file:line` citation: + +- **D0:** CI has no branch matrix; the publisher passes `github.ref_name` as `ref`/`branch` and is guarded to + `main`/`develop`; NBGV invoked once in `get-version-task`, `build-docker-task` has no nested `get-version` + and consumes the `semver2` input; the run builds the trigger ref so `GITHUB_REF` matches the versioned + branch. +- **D1:** CI runs on `push` with no paths filter on `validate`; the inline `changes` gate sets `image`/`base`; + `smoke-build` is `smoke: true`, `push: false`, amd64 NxMeta subset; the aggregator `needs:` `changes` + + `validate` + `smoke-build`, blocks on non-success, treats `changes` failure as blocking. +- **D2:** the main release backstop checks the prerelease `-`, strips `+buildmetadata`; `validate-task` is the + shared gate. +- **D3:** `main` appears in the backstop and the release `prerelease: false`/guard; `publicReleaseRefSpec` is + `^refs/heads/main$`; `semver2` threads to `LABEL_VERSION`. +- **D4:** `publish-release` triggers are `schedule` + `workflow_dispatch` + `push` (branches `[main]`, paths + `[Make/Matrix.json]`) only (no other push, no `PUBLISH_ON_MERGE`); jobs guarded to `github.ref_name` in + (`main`, `develop`); `target_commitish` = `GitCommitId`; main pins `ref` to `GitCommitId`, develop reuses + the base (`build_base: false`); the build logs in with `DOCKER_HUB_*`; buildcache branch-scoped and + write-gated on push; release-create gated `exists == false || workflow_dispatch`; release + docker-readme + gated to `main`; the product matrix builds amd64+arm64 from `Make/Matrix.json`. +- **D5:** the publisher and CI each have a terminal `cleanup-artifacts` (`always()`, `continue-on-error`), + independent of the aggregator. +- **D6:** CI is `push` on every branch; the aggregator context has exactly one producer; no `pull_request` + trigger; every CI job has the `!github.event.deleted` guard. +- **D7:** the publisher group is ref-independent with `cancel-in-progress: false`; the merge-bot keys on PR + number; codegen keys on the workflow; CI uses the standard group + deletion guard; reusable jobs declare + permissions. +- **D8/D9:** the merge-bot runs on `pull_request_target` with the App token, keyed on PR number, both merge + jobs use `--delete-branch`; Dependabot auto-merge excepts semver-major NuGet only; codegen dual-targets + main+develop and regenerates only `Version.json`/`Matrix.json`; no date-badge, standalone docker-readme + task, `PUBLISH_ON_MERGE`, or `dorny/paths-filter`; actions SHA-pinned except `nbgv@master`; + names/shells/conditionals per section 2. + +### 5B. End-to-end trace scenarios (deterministic from the YAML) + +| # | Input | Expected output | Exercises | +| --- | --- | --- | --- | +| S1 | push touching `Docker/**` | `validate` + `smoke-build` (NxMeta amd64) run, **no push, no release**; aggregator success; no dangling artifacts | D0.1, D1 | +| S2 | push changing only docs | `validate` runs; the `changes` gate sets `image=false`; `smoke-build` skipped; aggregator success (skip allowed) | D1, D1.5 | +| S3 | push changing only `.github/workflows/**` | `validate` runs head-resolved; `smoke-build` skipped (no image files); aggregator success | D1.1, D6.1 | +| S4 | weekly `schedule` | builds + publishes `main` only: shared base refresh + full product matrix (amd64+arm64) + stable release + `latest`; `target_commitish` = main's SHA; develop untouched; no dangling artifacts | D4.1, D4.2, D4.9 | +| S5 | push to `main` changing `Make/Matrix.json` (codegen pin) | publishes `main` with the new product versions immediately | D4.1, D8.3 | +| S6 | `workflow_dispatch` from `develop` | builds + publishes `develop`: `:develop` images, prerelease classification, `build_base: false` (reuses main's base), **no GitHub release** | D4.1, D4.2, D3.2 | +| S7 | `workflow_dispatch` re-run on `main`, no new commits | release-create refreshed on dispatch (skipped on schedule if the tag exists); Docker re-pushed (base refresh); no duplicate release | D4.5 | +| S8 | `workflow_dispatch` from a feature branch | the `github.ref_name in (main, develop)` guard skips every job -> no publish | D4.1 | +| S9 | merged dependency bump (any) | not a matrix-pin change; merges don't publish -> **no release**; ships in the next scheduled run | D4.1, D8.2 | +| S10 | merged develop codegen PR (`Matrix.json` change on develop) | sync-only; the pin push is main-only -> **no publish** | D8.3 | +| S11 | PR with a CSharpier / format / unit-test failure | `validate` fails -> aggregator blocks the merge | D1.2, D1.3, D1.5 | +| S12 | `version.json` floor bump merged | merges don't publish -> no immediate release; the new floor ships in the next publish | D3.3, D4.1 | +| S13 | Dependabot semver-major NuGet bump | gated on human review -> does not auto-merge; other majors auto-merge on green | D8.2 | +| S14 | branch-deletion push | every CI job + the aggregator skip (`!github.event.deleted`) -> no failing required check | D7.4 | +| S15 | `develop` -> `main` promotion (merge commit) | the merge itself does not publish; if it changed `Matrix.json` the pin push publishes main, else the next schedule does | D4.1, D8.1 | + +### 5C. Live probe (where warranted, never publishing) + +- Open a trivial doc-only PR and confirm S2 (`validate` runs, `smoke-build` skipped, aggregator green, 0 + artifacts left); open an image-touching PR and confirm S1 (smoke-build runs amd64, nothing pushed). +- After a `main` publish (schedule, pin push, or dispatch) confirm a stable release (`isPrerelease == false`) + with multi-arch product images (`docker buildx imagetools inspect` shows amd64 + arm64) and the shared base + tags intact; after a `develop` dispatch confirm `:develop` images, prerelease classification, and that the + shared `nx-base:ubuntu-noble` tag was not overwritten. A re-run adds no duplicate release. Absent publish + rights, record indeterminate and rely on 5A/5B. +- Trigger codegen via dispatch: confirm both `codegen-main` -> `main` and `codegen-develop` -> `develop` PRs + open and the merge-bot auto-merges with branch deletion. + +### 5D. Configuration audit + +Run [`repo-config/configure.sh check`](./repo-config/). It confirms the listed secrets exist (in both +stores), the `main`/`develop` rulesets enforce the required merge method + status check + signed commits + +strict-off, and the repository settings are in place, exiting non-zero on drift. Secret *values* cannot be +read back, so it asserts the names exist (failing if it cannot query them). The GitHub App installation is a +best-effort check (a precise check needs app-level auth, so it notes rather than fails). The Docker Hub +token's validity and push scope across the 12 repositories are a manual checklist item. + +### Assessment + +Operational when every applicable 5A item passes, every applicable 5B scenario matches (corroborated by 5C +where a live signal exists), and 5D configuration is in place. Procedure: **Audit** (5A + 5D) -> **Trace** +(5B) -> **Probe** (5C, without publishing) -> **Verdict** with the failing guarantee(s) and the triggering +input for each. + +## 6. Repository configuration + +The workflows depend on configuration outside the YAML. A misconfiguration surfaces only as a failed run, so +the configuration is part of "operational" (D10; audit 5D). + +**Secrets.** + +- `DOCKER_HUB_USERNAME` / `DOCKER_HUB_ACCESS_TOKEN` - Docker Hub credentials the base and product builds log + in with to push the images and the repository overviews. Required in **both** the Actions and Dependabot + secret stores: a Dependabot-triggered push runs CI whose Docker smoke build logs in too (when an image file + changed), and that run gets the Dependabot store. The access token needs push scope on every product + + base repo under `docker.io/ptr727/` (derived from `Make/Matrix.json` - see `repo-config/README.md`). There + is no NuGet/OIDC publishing. +- `CODEGEN_APP_CLIENT_ID` / `CODEGEN_APP_PRIVATE_KEY` - the GitHub App credentials the merge-bot and the + codegen PR-opener mint the App token from. Required in **both** the Actions and Dependabot secret stores (a + Dependabot-triggered run gets the Dependabot store). The App must be installed on the repo with + `contents: write` and `pull_requests: write`. +- The built-in `GITHUB_TOKEN` needs no setup. **No `PUBLISH_ON_MERGE` variable is used.** + +**Branch rulesets.** + +- `main` - merge-commit merges only; requires the aggregator status check (`Check pull request workflow status + job`); requires signed commits; "require branches up to date before merging" is **off** (a forward-only + `develop` makes every post-release `main` tip unreachable from `develop`, so the strict check would fail + every release). +- `develop` - squash merges only (keeps history linear); requires the same status check; requires signed + commits; "up to date" is **off** (so same-batch bot PRs auto-merge in parallel). +- The required check's `context:` matches the aggregator job name verbatim (D6.2, D9.2). + +**Repository settings.** Auto-merge enabled; squash and merge-commit both allowed (each ruleset narrows its +branch to one); rebase off; auto-delete-on-merge **off** (so `main`/`develop` survive a promotion; the +merge-bot deletes bot branches explicitly with `--delete-branch`). Dependabot version **and** security updates +enabled. The GitHub App installed with the scopes above. + +**Validation.** This configuration is codified in [`repo-config/`](./repo-config/) and applied/audited by +`repo-config/configure.sh`; `check` is the 5D audit. Secret values cannot be read back, so the audit asserts +the names exist (failing if they cannot be queried); the App installation is a best-effort check. diff --git a/codecov.yml b/codecov.yml index 896c3789..7200a8c9 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,16 +1,16 @@ -# Codecov configuration. Coverage is reported and trended, never gated. -# - informational: true makes project and patch statuses advisory (always pass), so a coverage delta never blocks a PR. -# - Distinct from the upload step's fail_ci_if_error: false, which only guards upload errors. -# - A repo may override this to enforce a coverage threshold. -coverage: - status: - project: - default: - informational: true - patch: - default: - informational: true - -# Exclude code intentionally not unit-tested from the coverage denominator. Add the repo's own paths. -# ignore: -# - "Sandbox/**" +# Codecov configuration. Coverage is reported and trended, never gated. +# - informational: true makes project and patch statuses advisory (always pass), so a coverage delta never blocks a PR. +# - Distinct from the upload step's fail_ci_if_error: false, which only guards upload errors. +# - A repo may override this to enforce a coverage threshold. +coverage: + status: + project: + default: + informational: true + patch: + default: + informational: true + +# Exclude code intentionally not unit-tested from the coverage denominator. Add the repo's own paths. +# ignore: +# - "Sandbox/**" diff --git a/cspell.json b/cspell.json index e2c505b9..aab8007f 100644 --- a/cspell.json +++ b/cspell.json @@ -1,178 +1,178 @@ -{ - "version": "0.2", - "language": "en-US", - "ignorePaths": [ - ".git/**", - "reports/*/**", - "**/*.min.*" - ], - "ignoreRegExpList": [ - "/```mermaid[\\s\\S]*?```/g" - ], - "words": [ - "accessibilities", - "Allman", - "apikey", - "arahata", - "astral", - "autoremove", - "btrfs", - "buildcache", - "buildcaches", - "buildmetadata", - "buildtransitive", - "Buildx", - "charliermarsh", - "chowned", - "chowns", - "codegen", - "commitish", - "conftest", - "contentfiles", - "cref", - "crefs", - "csdevkit", - "datebadge", - "davidanson", - "debuglevel", - "devcontainer", - "diffable", - "digitalwatchdog", - "distros", - "dockerbuild", - "dockerdirectory", - "Dockerfiles", - "dockerhub", - "Docstrings", - "dogfoods", - "dorny", - "dotnettools", - "downstreams", - "dryrun", - "dwspectrum", - "Emby", - "envsubst", - "extensionless", - "fanaticpythoner", - "finalizers", - "Genericize", - "gpgsign", - "gruntfuggly", - "HACS", - "Hanwha", - "hatchling", - "hddpool", - "heredocs", - "homeassistant", - "idempotent", - "idempotently", - "imagetools", - "IPVMS", - "isort", - "Jellyfin", - "Keychain", - "kicad", - "kinnairdclan", - "lastbuild", - "libsecret", - "LINQ", - "logfile", - "lsio", - "lsiobase", - "macvlan", - "makedirectory", - "matrixpath", - "mediaserver", - "metavms", - "mktemp", - "Moby", - "mypy", - "nameof", - "nbgv", - "nektos", - "Nerdbank", - "networkoptix", - "Newtonsoft", - "noatime", - "nodev", - "noninteractive", - "nonroot", - "noqa", - "nosuid", - "nuget", - "nugetlibrary", - "nvrpool", - "nxgo", - "nxmeta", - "nxvms", - "nxwitness", - "onCreateCommand", - "Optix", - "othercommand", - "paramref", - "PGID", - "Pieter", - "pipefail", - "posixacl", - "postCreateCommand", - "prerelease", - "ProjectTemplate", - "PUID", - "purpleair", - "pydantic", - "pypa", - "pypi", - "pypilibrary", - "pyproject", - "pyright", - "pyrightconfig", - "pytest", - "quoteoftheday", - "reimplementation", - "relatime", - "Repot", - "resharper", - "rhysd", - "Rubba", - "ruff", - "sdist", - "seccomp", - "semver", - "Serilog", - "settingsfile", - "shellcheck", - "shfs", - "signingkey", - "slnx", - "snupkg", - "softprops", - "somecommand", - "ssdpool", - "sshconfig", - "subsetting", - "subvol", - "subvolid", - "timonwong", - "tmpfs", - "Triaging", - "trunc", - "tzdata", - "unbuilt", - "Unraid", - "unvalidated", - "updateversion", - "USERPROFILE", - "venv", - "versionlabel", - "versionpath", - "Viljoen", - "whitelabel", - "winget", - "Wisenet", - "wisenetwave", - "xattr", - "Xsession", - "xunit", - "yzhang" - ] -} +{ + "version": "0.2", + "language": "en-US", + "ignorePaths": [ + ".git/**", + "reports/*/**", + "**/*.min.*" + ], + "ignoreRegExpList": [ + "/```mermaid[\\s\\S]*?```/g" + ], + "words": [ + "accessibilities", + "Allman", + "apikey", + "arahata", + "astral", + "autoremove", + "btrfs", + "buildcache", + "buildcaches", + "buildmetadata", + "buildtransitive", + "Buildx", + "charliermarsh", + "chowned", + "chowns", + "codegen", + "commitish", + "conftest", + "contentfiles", + "cref", + "crefs", + "csdevkit", + "datebadge", + "davidanson", + "debuglevel", + "devcontainer", + "diffable", + "digitalwatchdog", + "distros", + "dockerbuild", + "dockerdirectory", + "Dockerfiles", + "dockerhub", + "Docstrings", + "dogfoods", + "dorny", + "dotnettools", + "downstreams", + "dryrun", + "dwspectrum", + "Emby", + "envsubst", + "extensionless", + "fanaticpythoner", + "finalizers", + "Genericize", + "gpgsign", + "gruntfuggly", + "HACS", + "Hanwha", + "hatchling", + "hddpool", + "heredocs", + "homeassistant", + "idempotent", + "idempotently", + "imagetools", + "IPVMS", + "isort", + "Jellyfin", + "Keychain", + "kicad", + "kinnairdclan", + "lastbuild", + "libsecret", + "LINQ", + "logfile", + "lsio", + "lsiobase", + "macvlan", + "makedirectory", + "matrixpath", + "mediaserver", + "metavms", + "mktemp", + "Moby", + "mypy", + "nameof", + "nbgv", + "nektos", + "Nerdbank", + "networkoptix", + "Newtonsoft", + "noatime", + "nodev", + "noninteractive", + "nonroot", + "noqa", + "nosuid", + "nuget", + "nugetlibrary", + "nvrpool", + "nxgo", + "nxmeta", + "nxvms", + "nxwitness", + "onCreateCommand", + "Optix", + "othercommand", + "paramref", + "PGID", + "Pieter", + "pipefail", + "posixacl", + "postCreateCommand", + "prerelease", + "ProjectTemplate", + "PUID", + "purpleair", + "pydantic", + "pypa", + "pypi", + "pypilibrary", + "pyproject", + "pyright", + "pyrightconfig", + "pytest", + "quoteoftheday", + "reimplementation", + "relatime", + "Repot", + "resharper", + "rhysd", + "Rubba", + "ruff", + "sdist", + "seccomp", + "semver", + "Serilog", + "settingsfile", + "shellcheck", + "shfs", + "signingkey", + "slnx", + "snupkg", + "softprops", + "somecommand", + "ssdpool", + "sshconfig", + "subsetting", + "subvol", + "subvolid", + "timonwong", + "tmpfs", + "Triaging", + "trunc", + "tzdata", + "unbuilt", + "Unraid", + "unvalidated", + "updateversion", + "USERPROFILE", + "venv", + "versionlabel", + "versionpath", + "Viljoen", + "whitelabel", + "winget", + "Wisenet", + "wisenetwave", + "xattr", + "Xsession", + "xunit", + "yzhang" + ] +} diff --git a/repo-config/README.md b/repo-config/README.md index 5cd59cc6..324d9b46 100644 --- a/repo-config/README.md +++ b/repo-config/README.md @@ -1,75 +1,75 @@ -# repo-config - -Repository configuration as code - the parts of "operational" that live in GitHub settings rather than -in workflow YAML: branch rulesets, repository settings, and the secrets the workflows read. This is the -concrete form of [`WORKFLOW.md`](../WORKFLOW.md) section 6 and guarantee **D10**, and the implementation -of its **5D configuration audit**. - -This directory is intentionally **not** under `.github/` - that path is GitHub's own (workflows, issue -templates); repository administration config-as-code is the maintainer's, so it lives here. - -## Files - -- [`configure.sh`](./configure.sh) - idempotent `gh api` script with two modes: - - `./repo-config/configure.sh check` - validate only, no writes; exits non-zero on drift (the 5D - audit). Read-only, but it reads the rulesets and secrets endpoints, so it still needs a `gh` token - with admin on the repo. - - `./repo-config/configure.sh apply` - create-or-update the rulesets and settings to match this - directory (needs admin; writes). -- [`ruleset-develop.json`](./ruleset-develop.json) - the `develop` branch ruleset (squash-only, linear - history, signed commits, the required status check, strict-status **off**). -- [`ruleset-main.json`](./ruleset-main.json) - the `main` branch ruleset (merge-commit-only, signed - commits, the same required check, strict **off**; no linear-history rule). -- [`settings.json`](./settings.json) - repository settings (auto-merge on; squash **and** merge-commit - allowed; rebase off; auto-delete-on-merge **off**). The repo-wide auto-delete **setting** is off so a - `develop -> main` promotion does not delete `develop` (GitHub's auto-delete would remove the merged head - branch). Per-merge deletion is explicit instead: the merge-bot deletes a merged bot branch with - `gh pr merge --delete-branch`, and a feature branch is deleted the same way (or via the merge UI's delete - button) - so `main`/`develop` survive while bot/feature branches are still cleaned up. - -## What it does not store - -Secret **values** are never readable through the API, so the script only asserts the required secret -**names** exist (`DOCKER_HUB_USERNAME` / `DOCKER_HUB_ACCESS_TOKEN` for the images, and the App credentials -`CODEGEN_APP_CLIENT_ID` / `CODEGEN_APP_PRIVATE_KEY` for the merge-bot and the codegen pull-request -automation), and *notes* (best-effort) whether a GitHub App is installed - a precise check needs app-level -auth, so the App-installation check does not fail the audit. The Docker Hub and App credentials must be set -in **both** the Actions and Dependabot secret stores, since a Dependabot-triggered run gets the Dependabot -store. Set the values in the repository (or organization) secret store directly. There is no NuGet -publishing here (the .NET project is a build-time matrix generator only); the GitHub release uses the -built-in `GITHUB_TOKEN`. The Docker Hub access token's validity and push scope are verified by hand, not by -this script. - -The Docker Hub repositories this token must push to are derived from -[`Make/Matrix.json`](../Make/Matrix.json): one `docker.io/ptr727/` per product image plus the shared -bases `nx-base` and `nx-base-lsio`. As of writing that is `dwspectrum`, `dwspectrum-lsio`, `nxgo`, -`nxgo-lsio`, `nxmeta`, `nxmeta-lsio`, `nxwitness`, `nxwitness-lsio`, `wisenetwave`, `wisenetwave-lsio`, -`nx-base`, and `nx-base-lsio` (12 repositories). - -## Applying, and the required-check rename lockstep - -The live ruleset's required status check is matched by **name** to the aggregator job in -[`test-pull-request.yml`](../.github/workflows/test-pull-request.yml) (`Check pull request workflow -status job`). GitHub binds the check by that exact string, so the ruleset JSON here, the live ruleset, and -the aggregator job name must move **in lockstep** ([`WORKFLOW.md`](../WORKFLOW.md) D6.2). If they drift, a -pull request runs CI but its required check never resolves and the PR cannot merge. - -So whenever the ruleset JSON or that job name changes, run `apply` against the live repo in the same -change that ships the workflow edit, then `check`: - -```sh -REPO=ptr727/NxWitness ./repo-config/configure.sh apply # sync live rulesets + settings + security -REPO=ptr727/NxWitness ./repo-config/configure.sh check # confirm no drift -``` - -First-time adoption is the same step: the live ruleset predates the renamed aggregator, so the first -`apply` is what lets a pull request against the new workflows go green. Both modes need a `gh` login -with admin on the repo (the rulesets and secrets endpoints require it). `apply` writes, `check` only -reads. - -## Why both a script and JSON - -The JSON files are the unambiguous source of truth for the configuration; the script applies and audits -them idempotently. An agent can also derive the same checks on the fly from `WORKFLOW.md` section 6, but -the committed script and JSON codify the exact intended state so the configuration is reproducible and -diffable rather than tribal knowledge. +# repo-config + +Repository configuration as code - the parts of "operational" that live in GitHub settings rather than +in workflow YAML: branch rulesets, repository settings, and the secrets the workflows read. This is the +concrete form of [`WORKFLOW.md`](../WORKFLOW.md) section 6 and guarantee **D10**, and the implementation +of its **5D configuration audit**. + +This directory is intentionally **not** under `.github/` - that path is GitHub's own (workflows, issue +templates); repository administration config-as-code is the maintainer's, so it lives here. + +## Files + +- [`configure.sh`](./configure.sh) - idempotent `gh api` script with two modes: + - `./repo-config/configure.sh check` - validate only, no writes; exits non-zero on drift (the 5D + audit). Read-only, but it reads the rulesets and secrets endpoints, so it still needs a `gh` token + with admin on the repo. + - `./repo-config/configure.sh apply` - create-or-update the rulesets and settings to match this + directory (needs admin; writes). +- [`ruleset-develop.json`](./ruleset-develop.json) - the `develop` branch ruleset (squash-only, linear + history, signed commits, the required status check, strict-status **off**). +- [`ruleset-main.json`](./ruleset-main.json) - the `main` branch ruleset (merge-commit-only, signed + commits, the same required check, strict **off**; no linear-history rule). +- [`settings.json`](./settings.json) - repository settings (auto-merge on; squash **and** merge-commit + allowed; rebase off; auto-delete-on-merge **off**). The repo-wide auto-delete **setting** is off so a + `develop -> main` promotion does not delete `develop` (GitHub's auto-delete would remove the merged head + branch). Per-merge deletion is explicit instead: the merge-bot deletes a merged bot branch with + `gh pr merge --delete-branch`, and a feature branch is deleted the same way (or via the merge UI's delete + button) - so `main`/`develop` survive while bot/feature branches are still cleaned up. + +## What it does not store + +Secret **values** are never readable through the API, so the script only asserts the required secret +**names** exist (`DOCKER_HUB_USERNAME` / `DOCKER_HUB_ACCESS_TOKEN` for the images, and the App credentials +`CODEGEN_APP_CLIENT_ID` / `CODEGEN_APP_PRIVATE_KEY` for the merge-bot and the codegen pull-request +automation), and *notes* (best-effort) whether a GitHub App is installed - a precise check needs app-level +auth, so the App-installation check does not fail the audit. The Docker Hub and App credentials must be set +in **both** the Actions and Dependabot secret stores, since a Dependabot-triggered run gets the Dependabot +store. Set the values in the repository (or organization) secret store directly. There is no NuGet +publishing here (the .NET project is a build-time matrix generator only); the GitHub release uses the +built-in `GITHUB_TOKEN`. The Docker Hub access token's validity and push scope are verified by hand, not by +this script. + +The Docker Hub repositories this token must push to are derived from +[`Make/Matrix.json`](../Make/Matrix.json): one `docker.io/ptr727/` per product image plus the shared +bases `nx-base` and `nx-base-lsio`. As of writing that is `dwspectrum`, `dwspectrum-lsio`, `nxgo`, +`nxgo-lsio`, `nxmeta`, `nxmeta-lsio`, `nxwitness`, `nxwitness-lsio`, `wisenetwave`, `wisenetwave-lsio`, +`nx-base`, and `nx-base-lsio` (12 repositories). + +## Applying, and the required-check rename lockstep + +The live ruleset's required status check is matched by **name** to the aggregator job in +[`test-pull-request.yml`](../.github/workflows/test-pull-request.yml) (`Check pull request workflow +status job`). GitHub binds the check by that exact string, so the ruleset JSON here, the live ruleset, and +the aggregator job name must move **in lockstep** ([`WORKFLOW.md`](../WORKFLOW.md) D6.2). If they drift, a +pull request runs CI but its required check never resolves and the PR cannot merge. + +So whenever the ruleset JSON or that job name changes, run `apply` against the live repo in the same +change that ships the workflow edit, then `check`: + +```sh +REPO=ptr727/NxWitness ./repo-config/configure.sh apply # sync live rulesets + settings + security +REPO=ptr727/NxWitness ./repo-config/configure.sh check # confirm no drift +``` + +First-time adoption is the same step: the live ruleset predates the renamed aggregator, so the first +`apply` is what lets a pull request against the new workflows go green. Both modes need a `gh` login +with admin on the repo (the rulesets and secrets endpoints require it). `apply` writes, `check` only +reads. + +## Why both a script and JSON + +The JSON files are the unambiguous source of truth for the configuration; the script applies and audits +them idempotently. An agent can also derive the same checks on the fly from `WORKFLOW.md` section 6, but +the committed script and JSON codify the exact intended state so the configuration is reproducible and +diffable rather than tribal knowledge. diff --git a/repo-config/ruleset-develop.json b/repo-config/ruleset-develop.json index daf7dd46..ed938f87 100644 --- a/repo-config/ruleset-develop.json +++ b/repo-config/ruleset-develop.json @@ -1,45 +1,45 @@ -{ - "name": "develop", - "target": "branch", - "enforcement": "active", - "conditions": { - "ref_name": { - "include": ["refs/heads/develop"], - "exclude": [] - } - }, - "rules": [ - { "type": "deletion" }, - { "type": "non_fast_forward" }, - { "type": "required_linear_history" }, - { "type": "required_signatures" }, - { - "type": "pull_request", - "parameters": { - "allowed_merge_methods": ["squash"], - "dismiss_stale_reviews_on_push": true, - "require_code_owner_review": false, - "require_last_push_approval": false, - "required_approving_review_count": 0, - "required_review_thread_resolution": true - } - }, - { - "type": "required_status_checks", - "parameters": { - "do_not_enforce_on_create": false, - "strict_required_status_checks_policy": false, - "required_status_checks": [ - { "context": "Check pull request workflow status job", "integration_id": 15368 } - ] - } - }, - { - "type": "copilot_code_review", - "parameters": { - "review_draft_pull_requests": true, - "review_on_push": true - } - } - ] -} +{ + "name": "develop", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": ["refs/heads/develop"], + "exclude": [] + } + }, + "rules": [ + { "type": "deletion" }, + { "type": "non_fast_forward" }, + { "type": "required_linear_history" }, + { "type": "required_signatures" }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["squash"], + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_approving_review_count": 0, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "do_not_enforce_on_create": false, + "strict_required_status_checks_policy": false, + "required_status_checks": [ + { "context": "Check pull request workflow status job", "integration_id": 15368 } + ] + } + }, + { + "type": "copilot_code_review", + "parameters": { + "review_draft_pull_requests": true, + "review_on_push": true + } + } + ] +} diff --git a/repo-config/ruleset-main.json b/repo-config/ruleset-main.json index 0864a0e1..3bda11e7 100644 --- a/repo-config/ruleset-main.json +++ b/repo-config/ruleset-main.json @@ -1,44 +1,44 @@ -{ - "name": "main", - "target": "branch", - "enforcement": "active", - "conditions": { - "ref_name": { - "include": ["refs/heads/main"], - "exclude": [] - } - }, - "rules": [ - { "type": "deletion" }, - { "type": "non_fast_forward" }, - { "type": "required_signatures" }, - { - "type": "pull_request", - "parameters": { - "allowed_merge_methods": ["merge"], - "dismiss_stale_reviews_on_push": true, - "require_code_owner_review": false, - "require_last_push_approval": false, - "required_approving_review_count": 0, - "required_review_thread_resolution": true - } - }, - { - "type": "required_status_checks", - "parameters": { - "do_not_enforce_on_create": false, - "strict_required_status_checks_policy": false, - "required_status_checks": [ - { "context": "Check pull request workflow status job", "integration_id": 15368 } - ] - } - }, - { - "type": "copilot_code_review", - "parameters": { - "review_draft_pull_requests": true, - "review_on_push": true - } - } - ] -} +{ + "name": "main", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": ["refs/heads/main"], + "exclude": [] + } + }, + "rules": [ + { "type": "deletion" }, + { "type": "non_fast_forward" }, + { "type": "required_signatures" }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["merge"], + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_approving_review_count": 0, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "do_not_enforce_on_create": false, + "strict_required_status_checks_policy": false, + "required_status_checks": [ + { "context": "Check pull request workflow status job", "integration_id": 15368 } + ] + } + }, + { + "type": "copilot_code_review", + "parameters": { + "review_draft_pull_requests": true, + "review_on_push": true + } + } + ] +} diff --git a/repo-config/settings.json b/repo-config/settings.json index fc373efd..0dc0a589 100644 --- a/repo-config/settings.json +++ b/repo-config/settings.json @@ -1,7 +1,7 @@ -{ - "allow_squash_merge": true, - "allow_merge_commit": true, - "allow_rebase_merge": false, - "allow_auto_merge": true, - "delete_branch_on_merge": false -} +{ + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": false, + "allow_auto_merge": true, + "delete_branch_on_merge": false +} diff --git a/version.json b/version.json index 8b44c81e..4dba1c3b 100644 --- a/version.json +++ b/version.json @@ -1,10 +1,10 @@ -{ - "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "2.15", - "publicReleaseRefSpec": [ - "^refs/heads/main$" - ], - "nugetPackageVersion": { - "semVer": 2 - } -} +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "2.15", + "publicReleaseRefSpec": [ + "^refs/heads/main$" + ], + "nugetPackageVersion": { + "semVer": 2 + } +}