Adding o3de.py subcommands for engine.json and gem.json modification (#2411)
The new commands are edit-engine-properties and edit-gem-properties They can modify specific fields within these manifest files, that should affect build system. More importantly, this commands can be used to modify the "engine_name" field in the engine.json and the "gem_name" field in the gem.json Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>monroegm-disable-blank-issue-2
parent
7b7adb8573
commit
6868a48bb0
@ -0,0 +1,82 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from o3de import manifest, utils
|
||||
|
||||
logger = logging.getLogger()
|
||||
logging.basicConfig()
|
||||
|
||||
def edit_engine_props(engine_path: pathlib.Path = None,
|
||||
engine_name: str = None,
|
||||
new_name: str = None,
|
||||
new_version: str = None) -> int:
|
||||
if not engine_path and not engine_name:
|
||||
logger.error(f'Either a engine path or a engine name must be supplied to lookup engine.json')
|
||||
return 1
|
||||
if not engine_path:
|
||||
engine_path = manifest.get_registered(engine_name=engine_name)
|
||||
|
||||
if not engine_path:
|
||||
logger.error(f'Error unable locate engine path: No engine with name {engine_name} is registered in any manifest')
|
||||
return 1
|
||||
|
||||
engine_json_data = manifest.get_engine_json_data(engine_path=engine_path)
|
||||
if not engine_json_data:
|
||||
return 1
|
||||
|
||||
if new_name:
|
||||
if not utils.validate_identifier(new_name):
|
||||
logger.error(f'Engine name must be fewer than 64 characters, contain only alphanumeric, "_" or "-"'
|
||||
f' characters, and start with a letter. {new_name}')
|
||||
return 1
|
||||
engine_json_data['engine_name'] = new_name
|
||||
if new_version:
|
||||
engine_json_data['O3DEVersion'] = new_version
|
||||
|
||||
return 0 if manifest.save_o3de_manifest(engine_json_data, pathlib.Path(engine_path) / 'engine.json') else 1
|
||||
|
||||
def _edit_engine_props(args: argparse) -> int:
|
||||
return edit_engine_props(args.engine_path,
|
||||
args.engine_name,
|
||||
args.engine_new_name,
|
||||
args.engine_version)
|
||||
|
||||
def add_parser_args(parser):
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument('-ep', '--engine-path', type=pathlib.Path, required=False,
|
||||
help='The path to the engine.')
|
||||
group.add_argument('-en', '--engine-name', type=str, required=False,
|
||||
help='The name of the engine.')
|
||||
group = parser.add_argument_group('properties', 'arguments for modifying individual engine properties.')
|
||||
group.add_argument('-enn', '--engine-new-name', type=str, required=False,
|
||||
help='Sets the name for the engine.')
|
||||
group.add_argument('-ev', '--engine-version', type=str, required=False,
|
||||
help='Sets the version for the engine.')
|
||||
parser.set_defaults(func=_edit_engine_props)
|
||||
|
||||
def add_args(subparsers) -> None:
|
||||
enable_engine_props_subparser = subparsers.add_parser('edit-engine-properties')
|
||||
add_parser_args(enable_engine_props_subparser)
|
||||
|
||||
|
||||
def main():
|
||||
the_parser = argparse.ArgumentParser()
|
||||
add_parser_args(the_parser)
|
||||
the_args = the_parser.parse_args()
|
||||
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
|
||||
sys.exit(ret)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,163 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from o3de import manifest, utils
|
||||
|
||||
logger = logging.getLogger()
|
||||
logging.basicConfig()
|
||||
|
||||
|
||||
def update_values_in_key_list(existing_values: list, new_values: list or str, remove_values: list or str,
|
||||
replace_values: list or str):
|
||||
"""
|
||||
Updates values within a list by first appending values in the new_values list, removing values in the remove_values
|
||||
list and then replacing values in the replace_values list
|
||||
:param existing_values list with existing values to modify
|
||||
:param new_values list with values to add to the existing value list
|
||||
:param remove_values list with values to remove from the existing value list
|
||||
:param replace_values list with values to replace in the existing value list
|
||||
|
||||
returns updated existing value list
|
||||
"""
|
||||
if new_values:
|
||||
new_values = new_values.split() if isinstance(new_values, str) else new_values
|
||||
existing_values.extend(new_values)
|
||||
if remove_values:
|
||||
remove_values = remove_values.split() if isinstance(remove_values, str) else remove_values
|
||||
existing_values = list(filter(lambda value: value not in remove_values, existing_values))
|
||||
if replace_values:
|
||||
replace_values = replace_values.split() if isinstance(replace_values, str) else replace_values
|
||||
existing_values = replace_values
|
||||
|
||||
return existing_values
|
||||
|
||||
|
||||
def edit_gem_props(gem_path: pathlib.Path = None,
|
||||
gem_name: str = None,
|
||||
new_name: str = None,
|
||||
new_display: str = None,
|
||||
new_origin: str = None,
|
||||
new_type: str = None,
|
||||
new_summary: str = None,
|
||||
new_icon: str = None,
|
||||
new_requirements: str = None,
|
||||
new_tags: list or str = None,
|
||||
remove_tags: list or str = None,
|
||||
replace_tags: list or str = None,
|
||||
) -> int:
|
||||
|
||||
if not gem_path and not gem_name:
|
||||
logger.error(f'Either a gem path or a gem name must be supplied to lookup gem.json')
|
||||
return 1
|
||||
if not gem_path:
|
||||
gem_path = manifest.get_registered(gem_name=gem_name)
|
||||
|
||||
if not gem_path:
|
||||
logger.error(f'Error unable locate gem path: No gem with name {gem_name} is registered in any manifest')
|
||||
return 1
|
||||
|
||||
gem_json_data = manifest.get_gem_json_data(gem_path=gem_path)
|
||||
if not gem_json_data:
|
||||
return 1
|
||||
|
||||
update_key_dict = {}
|
||||
if new_name:
|
||||
if not utils.validate_identifier(new_name):
|
||||
logger.error(f'Engine name must be fewer than 64 characters, contain only alphanumeric, "_" or "-"'
|
||||
f' characters, and start with a letter. {new_name}')
|
||||
return 1
|
||||
update_key_dict['gem_name'] = new_name
|
||||
if new_display:
|
||||
update_key_dict['display_name'] = new_display
|
||||
if new_origin:
|
||||
update_key_dict['origin'] = new_origin
|
||||
if new_type:
|
||||
update_key_dict['type'] = new_type
|
||||
if new_summary:
|
||||
update_key_dict['summary'] = new_summary
|
||||
if new_icon:
|
||||
update_key_dict['icon_path'] = new_icon
|
||||
if new_requirements:
|
||||
update_key_dict['icon_requirements'] = new_requirements
|
||||
|
||||
update_key_dict['user_tags'] = update_values_in_key_list(gem_json_data.get('user_tags', []), new_tags,
|
||||
remove_tags, replace_tags)
|
||||
|
||||
gem_json_data.update(update_key_dict)
|
||||
|
||||
return 0 if manifest.save_o3de_manifest(gem_json_data, pathlib.Path(gem_path) / 'gem.json') else 1
|
||||
|
||||
|
||||
def _edit_gem_props(args: argparse) -> int:
|
||||
return edit_gem_props(args.gem_path,
|
||||
args.gem_name,
|
||||
args.gem_new_name,
|
||||
args.gem_display,
|
||||
args.gem_origin,
|
||||
args.gem_type,
|
||||
args.gem_summary,
|
||||
args.gem_icon,
|
||||
args.gem_requirements,
|
||||
args.add_tags,
|
||||
args.remove_tags,
|
||||
args.replace_tags)
|
||||
|
||||
|
||||
def add_parser_args(parser):
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument('-gp', '--gem-path', type=pathlib.Path, required=False,
|
||||
help='The path to the gem.')
|
||||
group.add_argument('-gn', '--gem-name', type=str, required=False,
|
||||
help='The name of the gem.')
|
||||
group = parser.add_argument_group('properties', 'arguments for modifying individual gem properties.')
|
||||
group.add_argument('-gnn', '--gem-new-name', type=str, required=False,
|
||||
help='Sets the name for the gem.')
|
||||
group.add_argument('-gd', '--gem-display', type=str, required=False,
|
||||
help='Sets the gem display name.')
|
||||
group.add_argument('-go', '--gem-origin', type=str, required=False,
|
||||
help='Sets description for gem origin.')
|
||||
group.add_argument('-gt', '--gem-type', type=str, required=False, choices=['Code', 'Tool', 'Asset'],
|
||||
help='Sets the gem type. Can only be one of the selected choices')
|
||||
group.add_argument('-gs', '--gem-summary', type=str, required=False,
|
||||
help='Sets the summary description of the gem.')
|
||||
group.add_argument('-gi', '--gem-icon', type=str, required=False,
|
||||
help='Sets the path to the projects icon resource.')
|
||||
group.add_argument('-gr', '--gem-requirements', type=str, required=False,
|
||||
help='Sets the description of the requirements needed to use the gem')
|
||||
group = parser.add_mutually_exclusive_group(required=False)
|
||||
group.add_argument('-at', '--add-tags', type=str, nargs='*', required=False,
|
||||
help='Adds tag(s) to user_tags property. Can be specified multiple times')
|
||||
group.add_argument('-dt', '--remove-tags', type=str, nargs='*', required=False,
|
||||
help='Removes tag(s) from the user_tags property. Can be specified multiple times')
|
||||
group.add_argument('-rt', '--replace-tags', type=str, nargs='*', required=False,
|
||||
help='Replace tag(s) in user_tags property. Can be specified multiple times')
|
||||
parser.set_defaults(func=_edit_gem_props)
|
||||
|
||||
|
||||
def add_args(subparsers) -> None:
|
||||
enable_gem_props_subparser = subparsers.add_parser('edit-gem-properties')
|
||||
add_parser_args(enable_gem_props_subparser)
|
||||
|
||||
|
||||
def main():
|
||||
the_parser = argparse.ArgumentParser()
|
||||
add_parser_args(the_parser)
|
||||
the_args = the_parser.parse_args()
|
||||
ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1
|
||||
sys.exit(ret)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -0,0 +1,72 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
import json
|
||||
import pytest
|
||||
import pathlib
|
||||
from unittest.mock import patch
|
||||
|
||||
from o3de import engine_properties
|
||||
|
||||
|
||||
TEST_ENGINE_JSON_PAYLOAD = '''
|
||||
{
|
||||
"engine_name": "o3de",
|
||||
"restricted_name": "o3de",
|
||||
"FileVersion": 1,
|
||||
"O3DEVersion": "0.0.0.0",
|
||||
"O3DECopyrightYear": 2021,
|
||||
"O3DEBuildNumber": 0,
|
||||
"external_subdirectories": [
|
||||
"Gems/TestGem2"
|
||||
],
|
||||
"projects": [
|
||||
],
|
||||
"templates": [
|
||||
"Templates/MinimalProject"
|
||||
]
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def init_engine_json_data(request):
|
||||
class EngineJsonData:
|
||||
def __init__(self):
|
||||
self.data = json.loads(TEST_ENGINE_JSON_PAYLOAD)
|
||||
request.cls.engine_json = EngineJsonData()
|
||||
|
||||
@pytest.mark.usefixtures('init_engine_json_data')
|
||||
class TestEditEngineProperties:
|
||||
@pytest.mark.parametrize("engine_path, engine_name, engine_new_name, engine_version, expected_result", [
|
||||
pytest.param(pathlib.PurePath('D:/o3de'), None, 'o3de-install', '1.0.0.0', 0),
|
||||
pytest.param(None, 'o3de-install', 'o3de-sdk', '1.0.0.1', 0),
|
||||
pytest.param(None, 'o3de-sdk', None, '2.0.0.0', 0),
|
||||
]
|
||||
)
|
||||
def test_edit_engine_properties(self, engine_path, engine_name, engine_new_name, engine_version, expected_result):
|
||||
|
||||
def get_engine_json_data(engine_path: pathlib.Path) -> dict:
|
||||
return self.engine_json.data
|
||||
|
||||
def get_engine_path(engine_name: str) -> pathlib.Path:
|
||||
return pathlib.Path('D:/o3de')
|
||||
|
||||
def save_o3de_manifest(new_engine_data: dict, engine_path: pathlib.Path) -> bool:
|
||||
self.engine_json.data = new_engine_data
|
||||
return True
|
||||
|
||||
with patch('o3de.manifest.get_engine_json_data', side_effect=get_engine_json_data) as get_engine_json_data_patch, \
|
||||
patch('o3de.manifest.save_o3de_manifest', side_effect=save_o3de_manifest) as save_o3de_manifest_patch, \
|
||||
patch('o3de.manifest.get_registered', side_effect=get_engine_path) as get_registered_patch:
|
||||
result = engine_properties.edit_engine_props(engine_path, engine_name, engine_new_name, engine_version)
|
||||
assert result == expected_result
|
||||
if engine_new_name:
|
||||
assert self.engine_json.data.get('engine_name', '') == engine_new_name
|
||||
if engine_version:
|
||||
assert self.engine_json.data.get('O3DEVersion', '') == engine_version
|
||||
@ -0,0 +1,99 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
import json
|
||||
import pytest
|
||||
import pathlib
|
||||
from unittest.mock import patch
|
||||
|
||||
from o3de import gem_properties
|
||||
|
||||
|
||||
TEST_GEM_JSON_PAYLOAD = '''
|
||||
{
|
||||
"gem_name": "TestGem",
|
||||
"display_name": "TestGem",
|
||||
"license": "What license TestGem uses goes here: i.e. https://opensource.org/licenses/MIT",
|
||||
"origin": "The primary repo for TestGem goes here: i.e. http://www.mydomain.com",
|
||||
"type": "Code",
|
||||
"summary": "A short description of TestGem.",
|
||||
"canonical_tags": [
|
||||
"Gem"
|
||||
],
|
||||
"user_tags": [
|
||||
"TestGem"
|
||||
],
|
||||
"icon_path": "preview.png",
|
||||
"requirements": ""
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
@pytest.fixture(scope='class')
|
||||
def init_gem_json_data(request):
|
||||
class GemJsonData:
|
||||
def __init__(self):
|
||||
self.data = json.loads(TEST_GEM_JSON_PAYLOAD)
|
||||
request.cls.gem_json = GemJsonData()
|
||||
|
||||
@pytest.mark.usefixtures('init_gem_json_data')
|
||||
class TestEditGemProperties:
|
||||
@pytest.mark.parametrize("gem_path, gem_name, gem_new_name, gem_display, gem_origin,\
|
||||
gem_type, gem_summary, gem_icon, gem_requirements,\
|
||||
add_tags, remove_tags, replace_tags, expected_tags, expected_result", [
|
||||
pytest.param(pathlib.PurePath('D:/TestProject'),
|
||||
None, 'TestGem2', 'New Gem Name', 'O3DE', 'Code', 'Gem that exercises Default Gem Template',
|
||||
'preview.png', '',
|
||||
['Physics', 'Rendering', 'Scripting'], None, None, ['TestGem', 'Physics', 'Rendering', 'Scripting'],
|
||||
0),
|
||||
pytest.param(None,
|
||||
'TestGem2', None, 'New Gem Name', 'O3DE', 'Asset', 'Gem that exercises Default Gem Template',
|
||||
'preview.png', '', None, ['Physics'], None, ['TestGem', 'Rendering', 'Scripting'], 0),
|
||||
pytest.param(None,
|
||||
'TestGem2', None, 'New Gem Name', 'O3DE', 'Tool', 'Gem that exercises Default Gem Template',
|
||||
'preview.png', '', None, None, ['Animation', 'TestGem'], ['Animation', 'TestGem'], 0)
|
||||
]
|
||||
)
|
||||
def test_edit_gem_properties(self, gem_path, gem_name, gem_new_name, gem_display, gem_origin,
|
||||
gem_type, gem_summary, gem_icon, gem_requirements,
|
||||
add_tags, remove_tags, replace_tags,
|
||||
expected_tags, expected_result):
|
||||
|
||||
def get_gem_json_data(gem_path: pathlib.Path) -> dict:
|
||||
return self.gem_json.data
|
||||
|
||||
def get_gem_path(gem_name: str) -> pathlib.Path:
|
||||
return pathlib.Path('D:/TestProject')
|
||||
|
||||
def save_o3de_manifest(new_gem_data: dict, gem_path: pathlib.Path) -> bool:
|
||||
self.gem_json.data = new_gem_data
|
||||
return True
|
||||
|
||||
with patch('o3de.manifest.get_gem_json_data', side_effect=get_gem_json_data) as get_gem_json_data_patch, \
|
||||
patch('o3de.manifest.save_o3de_manifest', side_effect=save_o3de_manifest) as save_o3de_manifest_patch, \
|
||||
patch('o3de.manifest.get_registered', side_effect=get_gem_path) as get_registered_patch:
|
||||
result = gem_properties.edit_gem_props(gem_path, gem_name, gem_new_name, gem_display, gem_origin,
|
||||
gem_type, gem_summary, gem_icon, gem_requirements,
|
||||
add_tags, remove_tags, replace_tags)
|
||||
assert result == expected_result
|
||||
if gem_new_name:
|
||||
assert self.gem_json.data.get('gem_name', '') == gem_new_name
|
||||
if gem_display:
|
||||
assert self.gem_json.data.get('display_name', '') == gem_display
|
||||
if gem_origin:
|
||||
assert self.gem_json.data.get('origin', '') == gem_origin
|
||||
if gem_type:
|
||||
assert self.gem_json.data.get('type', '') == gem_type
|
||||
if gem_summary:
|
||||
assert self.gem_json.data.get('summary', '') == gem_summary
|
||||
if gem_icon:
|
||||
assert self.gem_json.data.get('icon_path', '') == gem_icon
|
||||
if gem_requirements:
|
||||
assert self.gem_json.data.get('requirments', '') == gem_requirements
|
||||
|
||||
assert set(self.gem_json.data.get('user_tags', [])) == set(expected_tags)
|
||||
Loading…
Reference in New Issue