diff --git a/AutomatedTesting/project.json b/AutomatedTesting/project.json index 5ee4ee68f8..9222777ef3 100644 --- a/AutomatedTesting/project.json +++ b/AutomatedTesting/project.json @@ -10,5 +10,7 @@ "version_name": "1.0.0.0", "orientation": "landscape" }, - "engine": "o3de" -} \ No newline at end of file + "engine": "o3de", + "display_name": "AutomatedTesting", + "icon_path": "preview.png" +} diff --git a/Code/Tools/ProjectManager/CMakeLists.txt b/Code/Tools/ProjectManager/CMakeLists.txt index 9974125ffb..b0e7ee9697 100644 --- a/Code/Tools/ProjectManager/CMakeLists.txt +++ b/Code/Tools/ProjectManager/CMakeLists.txt @@ -89,6 +89,9 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) 3rdParty::Qt::Widgets 3rdParty::Python 3rdParty::pybind11 + AZ::AzCore + AZ::AzCoreTestCommon + AZ::AzTestShared AZ::AzTest AZ::AzFramework AZ::AzFrameworkTestShared diff --git a/Code/Tools/ProjectManager/Source/ExternalLinkDialog.cpp b/Code/Tools/ProjectManager/Source/ExternalLinkDialog.cpp index 122a5cbf5a..2f3d615863 100644 --- a/Code/Tools/ProjectManager/Source/ExternalLinkDialog.cpp +++ b/Code/Tools/ProjectManager/Source/ExternalLinkDialog.cpp @@ -10,8 +10,6 @@ #include #include -#include - #include #include #include @@ -87,12 +85,6 @@ namespace O3DE::ProjectManager void ExternalLinkDialog::SetSkipDialogSetting(bool state) { - auto settingsRegistry = AZ::SettingsRegistry::Get(); - if (settingsRegistry) - { - QString settingsKey = GetExternalLinkWarningKey(); - settingsRegistry->Set(settingsKey.toStdString().c_str(), state); - SaveProjectManagerSettings(); - } + PMSettings::SetProjectManagerKey(PMSettings::GetExternalLinkWarningKey(), state); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h index 23e95cf487..264971428a 100644 --- a/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h +++ b/Code/Tools/ProjectManager/Source/GemCatalog/GemInfo.h @@ -9,7 +9,6 @@ #pragma once #if !defined(Q_MOC_RUN) -#include #include #include #include diff --git a/Code/Tools/ProjectManager/Source/LinkWidget.cpp b/Code/Tools/ProjectManager/Source/LinkWidget.cpp index f25e38db67..87ccdb9aed 100644 --- a/Code/Tools/ProjectManager/Source/LinkWidget.cpp +++ b/Code/Tools/ProjectManager/Source/LinkWidget.cpp @@ -33,13 +33,7 @@ namespace O3DE::ProjectManager { // Check if user request not to be shown external link warning dialog bool skipDialog = false; - auto settingsRegistry = AZ::SettingsRegistry::Get(); - - if (settingsRegistry) - { - QString settingsKey = GetExternalLinkWarningKey(); - settingsRegistry->Get(skipDialog, settingsKey.toStdString().c_str()); - } + PMSettings::GetProjectManagerKey(skipDialog, PMSettings::GetExternalLinkWarningKey()); if (!skipDialog) { diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp index 4febb65782..4c306fc715 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp @@ -16,6 +16,8 @@ #include #include #include + +#include #include #include @@ -41,9 +43,11 @@ namespace O3DE::ProjectManager { const QString defaultName = GetDefaultProjectName(); const QString defaultPath = QDir::toNativeSeparators(GetDefaultProjectPath() + "/" + defaultName); + const QString randomUuid = GenerateNewProjectId(); m_projectName->lineEdit()->setText(defaultName); m_projectPath->lineEdit()->setText(defaultPath); + m_projectId->lineEdit()->setText(randomUuid); // if we don't use a QFrame we cannot "contain" the widgets inside and move them around // as a group @@ -173,6 +177,14 @@ namespace O3DE::ProjectManager return QDir::toNativeSeparators(GetDefaultProjectPath() + "/" + projectName); } + QString NewProjectSettingsScreen::GenerateNewProjectId() + { + AZStd::string uuid; + AZ::Uuid::CreateRandom().ToString(uuid); + + return QString(uuid.c_str()); + } + ProjectManagerScreen NewProjectSettingsScreen::GetScreenEnum() { return ProjectManagerScreen::NewProjectSettings; @@ -225,7 +237,7 @@ namespace O3DE::ProjectManager moreGemsLabel->setObjectName("moreGems"); templateDetailsLayout->addWidget(moreGemsLabel); - QLabel* browseCatalogLabel = new QLabel(tr("Browse the Gems Catalog to further customize your project."), this); + QLabel* browseCatalogLabel = new QLabel(tr("Browse the Gems Catalog to further customize your project."), this); browseCatalogLabel->setObjectName("browseCatalog"); browseCatalogLabel->setWordWrap(true); templateDetailsLayout->addWidget(browseCatalogLabel); diff --git a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h index 42c47cb1ff..cfb215ba0c 100644 --- a/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h @@ -47,6 +47,7 @@ namespace O3DE::ProjectManager private: QString GetDefaultProjectName(); QString GetDefaultProjectPath(); + QString GenerateNewProjectId(); QString GetProjectAutoPath(); QFrame* CreateTemplateDetails(int margin); void UpdateTemplateDetails(const ProjectTemplateInfo& templateInfo); diff --git a/Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp b/Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp index ecb125ae4e..4fc188b289 100644 --- a/Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectBuilderController.cpp @@ -29,14 +29,8 @@ namespace O3DE::ProjectManager m_worker = new ProjectBuilderWorker(m_projectInfo); m_worker->moveToThread(&m_workerThread); - auto settingsRegistry = AZ::SettingsRegistry::Get(); - if (settingsRegistry) - { - // Remove key here in case Project Manager crashing while building that causes HandleResults to not be called - QString settingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName); - settingsRegistry->Remove(settingsKey.toStdString().c_str()); - SaveProjectManagerSettings(); - } + // Remove key here in case Project Manager crashing while building that causes HandleResults to not be called + PMSettings::SetProjectBuiltSuccessfully(m_projectInfo, false); connect(&m_workerThread, &QThread::finished, m_worker, &ProjectBuilderWorker::deleteLater); connect(&m_workerThread, &QThread::started, m_worker, &ProjectBuilderWorker::BuildProject); @@ -91,8 +85,6 @@ namespace O3DE::ProjectManager void ProjectBuilderController::HandleResults(const QString& result) { - QString settingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName); - if (!result.isEmpty()) { if (result.contains(tr("log"))) @@ -122,12 +114,7 @@ namespace O3DE::ProjectManager emit NotifyBuildProject(m_projectInfo); } - auto settingsRegistry = AZ::SettingsRegistry::Get(); - if (settingsRegistry) - { - settingsRegistry->Remove(settingsKey.toStdString().c_str()); - SaveProjectManagerSettings(); - } + PMSettings::SetProjectBuiltSuccessfully(m_projectInfo, false); emit Done(false); return; @@ -136,12 +123,7 @@ namespace O3DE::ProjectManager { m_projectInfo.m_buildFailed = false; - auto settingsRegistry = AZ::SettingsRegistry::Get(); - if (settingsRegistry) - { - settingsRegistry->Set(settingsKey.toStdString().c_str(), true); - SaveProjectManagerSettings(); - } + PMSettings::SetProjectBuiltSuccessfully(m_projectInfo, true); } emit Done(true); diff --git a/Code/Tools/ProjectManager/Source/ProjectInfo.cpp b/Code/Tools/ProjectManager/Source/ProjectInfo.cpp index b4a1e84831..0d19fff74a 100644 --- a/Code/Tools/ProjectManager/Source/ProjectInfo.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectInfo.cpp @@ -9,14 +9,13 @@ #include #include -#include - namespace O3DE::ProjectManager { ProjectInfo::ProjectInfo( const QString& path, const QString& projectName, const QString& displayName, + const QString& id, const QString& origin, const QString& summary, const QString& iconPath, @@ -26,6 +25,7 @@ namespace O3DE::ProjectManager : m_path(path) , m_projectName(projectName) , m_displayName(displayName) + , m_id(id) , m_origin(origin) , m_summary(summary) , m_iconPath(iconPath) @@ -49,6 +49,10 @@ namespace O3DE::ProjectManager { return false; } + if (m_id != rhs.m_id) + { + return false; + } if (m_origin != rhs.m_origin) { return false; @@ -80,7 +84,7 @@ namespace O3DE::ProjectManager bool ProjectInfo::IsValid() const { - return !m_path.isEmpty() && !m_projectName.isEmpty(); + return !m_path.isEmpty() && !m_projectName.isEmpty() && !m_id.isEmpty(); } const QString& ProjectInfo::GetProjectDisplayName() const diff --git a/Code/Tools/ProjectManager/Source/ProjectInfo.h b/Code/Tools/ProjectManager/Source/ProjectInfo.h index 2ce1e8c491..4dcb21aa24 100644 --- a/Code/Tools/ProjectManager/Source/ProjectInfo.h +++ b/Code/Tools/ProjectManager/Source/ProjectInfo.h @@ -9,7 +9,6 @@ #pragma once #if !defined(Q_MOC_RUN) -#include #include #include #include @@ -26,6 +25,7 @@ namespace O3DE::ProjectManager const QString& path, const QString& projectName, const QString& displayName, + const QString& id, const QString& origin, const QString& summary, const QString& iconPath, @@ -45,6 +45,7 @@ namespace O3DE::ProjectManager // From project.json QString m_projectName; QString m_displayName; + QString m_id; QString m_origin; QString m_summary; QString m_iconPath; diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerSettings.cpp b/Code/Tools/ProjectManager/Source/ProjectManagerSettings.cpp index 66480aab3e..12dd569ad5 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerSettings.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectManagerSettings.cpp @@ -14,46 +14,235 @@ namespace O3DE::ProjectManager { - void SaveProjectManagerSettings() + namespace PMSettings { - auto settingsRegistry = AZ::SettingsRegistry::Get(); - AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings; - dumperSettings.m_prettifyOutput = true; - dumperSettings.m_jsonPointerPrefix = ProjectManagerKeyPrefix; - - AZStd::string stringBuffer; - AZ::IO::ByteContainerStream stringStream(&stringBuffer); - if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream( - *settingsRegistry, ProjectManagerKeyPrefix, stringStream, dumperSettings)) + bool SaveProjectManagerSettings() { - AZ_Warning("ProjectManager", false, "Could not save Project Manager settings to stream"); - return; + auto settingsRegistry = AZ::SettingsRegistry::Get(); + AZ::SettingsRegistryMergeUtils::DumperSettings dumperSettings; + dumperSettings.m_prettifyOutput = true; + dumperSettings.m_jsonPointerPrefix = ProjectManagerKeyPrefix; + + AZStd::string stringBuffer; + AZ::IO::ByteContainerStream stringStream(&stringBuffer); + if (!AZ::SettingsRegistryMergeUtils::DumpSettingsRegistryToStream( + *settingsRegistry, ProjectManagerKeyPrefix, stringStream, dumperSettings)) + { + AZ_Warning("ProjectManager", false, "Could not save Project Manager settings to stream"); + return false; + } + + AZ::IO::FixedMaxPath o3deUserPath = AZ::Utils::GetO3deManifestDirectory(); + o3deUserPath /= AZ::SettingsRegistryInterface::RegistryFolder; + o3deUserPath /= "ProjectManager.setreg"; + + bool saved = false; + constexpr auto configurationMode = + AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; + + AZ::IO::SystemFile outputFile; + if (outputFile.Open(o3deUserPath.c_str(), configurationMode)) + { + saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size(); + } + + AZ_Warning("ProjectManager", saved, "Unable to save Project Manager registry file to path: %s", o3deUserPath.c_str()); + return saved; } - AZ::IO::FixedMaxPath o3deUserPath = AZ::Utils::GetO3deManifestDirectory(); - o3deUserPath /= AZ::SettingsRegistryInterface::RegistryFolder; - o3deUserPath /= "ProjectManager.setreg"; - - bool saved = false; - constexpr auto configurationMode = - AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY; - - AZ::IO::SystemFile outputFile; - if (outputFile.Open(o3deUserPath.c_str(), configurationMode)) + bool GetProjectManagerKey(QString& result, const QString& settingsKey) { - saved = outputFile.Write(stringBuffer.data(), stringBuffer.size()) == stringBuffer.size(); + bool success = false; + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + AZStd::string settingsValue; + success = settingsRegistry->Get(settingsValue, settingsKey.toStdString().c_str()); + + result = settingsValue.c_str(); + } + + return success; } - AZ_Warning("ProjectManager", saved, "Unable to save Project Manager registry file to path: %s", o3deUserPath.c_str()); - } + bool GetProjectManagerKey(bool& result, const QString& settingsKey) + { + bool success = false; + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + success = settingsRegistry->Get(result, settingsKey.toStdString().c_str()); + } - QString GetProjectBuiltSuccessfullyKey(const QString& projectName) - { - return QString("%1/Projects/%2/BuiltSuccessfully").arg(ProjectManagerKeyPrefix).arg(projectName); - } + return success; + } - QString GetExternalLinkWarningKey() - { - return QString("%1/SkipExternalLinkWarning").arg(ProjectManagerKeyPrefix); - } -} + bool SetProjectManagerKey(const QString& settingsKey, const QString& settingsValue, bool saveToDisk) + { + bool success = false; + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + success = settingsRegistry->Set(settingsKey.toStdString().c_str(), settingsValue.toStdString().c_str()); + + if (saveToDisk) + { + SaveProjectManagerSettings(); + } + } + + return success; + } + + bool SetProjectManagerKey(const QString& settingsKey, bool settingsValue, bool saveToDisk) + { + bool success = false; + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + success = settingsRegistry->Set(settingsKey.toStdString().c_str(), settingsValue); + + if (saveToDisk) + { + SaveProjectManagerSettings(); + } + } + + return success; + } + + bool RemoveProjectManagerKey(const QString& settingsKey, bool saveToDisk) + { + bool success = false; + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + success = settingsRegistry->Remove(settingsKey.toStdString().c_str()); + + if (saveToDisk) + { + SaveProjectManagerSettings(); + } + } + + return success; + } + + bool CopyProjectManagerKeyString(const QString& settingsKeyOrig, const QString& settingsKeyDest, bool removeOrig, bool saveToDisk) + { + bool success = false; + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + AZStd::string settingsValue; + success = settingsRegistry->Get(settingsValue, settingsKeyOrig.toStdString().c_str()); + + if (success) + { + success = settingsRegistry->Set(settingsKeyDest.toStdString().c_str(), settingsValue); + if (success) + { + if (removeOrig) + { + success = settingsRegistry->Remove(settingsKeyOrig.toStdString().c_str()); + } + + if (saveToDisk) + { + SaveProjectManagerSettings(); + } + } + } + } + + return success; + } + + QString GetProjectKey(const ProjectInfo& projectInfo) + { + return QString("%1/Projects/%2/%3").arg(ProjectManagerKeyPrefix, projectInfo.m_id, projectInfo.m_projectName); + } + + QString GetExternalLinkWarningKey() + { + return QString("%1/SkipExternalLinkWarning").arg(ProjectManagerKeyPrefix); + } + + QString GetProjectsBuiltSuccessfullyKey() + { + return QString("%1/SuccessfulBuildPaths").arg(ProjectManagerKeyPrefix); + } + + bool GetBuiltSuccessfullyPaths(AZStd::set& result) + { + bool success = false; + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + QString builtKey = GetProjectsBuiltSuccessfullyKey(); + success = settingsRegistry->GetObject>(result, builtKey.toStdString().c_str()); + } + + return success; + } + + bool GetProjectBuiltSuccessfully(bool& result, const ProjectInfo& projectInfo) + { + AZStd::set builtPathsResult; + bool success = GetBuiltSuccessfullyPaths(builtPathsResult); + if (success) + { + // Check if buildPath is listed as successfully built + AZStd::string projectPath = projectInfo.m_path.toStdString().c_str(); + if (builtPathsResult.contains(projectPath)) + { + result = true; + } + } + // No project built statuses known + else + { + result = false; + } + + return success; + } + + bool SetProjectBuiltSuccessfully(const ProjectInfo& projectInfo, bool successfullyBuilt, bool saveToDisk) + { + AZStd::set builtPathsResult; + bool success = GetBuiltSuccessfullyPaths(builtPathsResult); + + AZStd::string projectPath = projectInfo.m_path.toStdString().c_str(); + if (successfullyBuilt) + { + //Add successfully built path to set + builtPathsResult.insert(projectPath); + } + else + { + // Remove unsuccessfully built path from set + builtPathsResult.erase(projectPath); + } + + auto settingsRegistry = AZ::SettingsRegistry::Get(); + if (settingsRegistry) + { + QString builtKey = GetProjectsBuiltSuccessfullyKey(); + success = settingsRegistry->SetObject>(builtKey.toStdString().c_str(), builtPathsResult); + + if (saveToDisk) + { + SaveProjectManagerSettings(); + } + } + else + { + success = false; + } + + return success; + } + + } // namespace PMSettings +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectManagerSettings.h b/Code/Tools/ProjectManager/Source/ProjectManagerSettings.h index 93cfbc5ceb..2a92fcab0f 100644 --- a/Code/Tools/ProjectManager/Source/ProjectManagerSettings.h +++ b/Code/Tools/ProjectManager/Source/ProjectManagerSettings.h @@ -9,14 +9,29 @@ #pragma once #if !defined(Q_MOC_RUN) -#include +#include +#include #endif namespace O3DE::ProjectManager { - static constexpr char ProjectManagerKeyPrefix[] = "/O3DE/ProjectManager"; + namespace PMSettings + { + static constexpr char ProjectManagerKeyPrefix[] = "/O3DE/ProjectManager"; - void SaveProjectManagerSettings(); - QString GetProjectBuiltSuccessfullyKey(const QString& projectName); - QString GetExternalLinkWarningKey(); -} + bool SaveProjectManagerSettings(); + bool GetProjectManagerKey(QString& result, const QString& settingsKey); + bool GetProjectManagerKey(bool& result, const QString& settingsKey); + bool SetProjectManagerKey(const QString& settingsKey, const QString& settingsValue, bool saveToDisk = true); + bool SetProjectManagerKey(const QString& settingsKey, bool settingsValue, bool saveToDisk = true); + bool RemoveProjectManagerKey(const QString& settingsKey, bool saveToDisk = true); + bool CopyProjectManagerKeyString( + const QString& settingsKeyOrig, const QString& settingsKeyDest, bool removeOrig = false, bool saveToDisk = true); + + QString GetProjectKey(const ProjectInfo& projectInfo); + QString GetExternalLinkWarningKey(); + + bool GetProjectBuiltSuccessfully(bool& result, const ProjectInfo& projectInfo); + bool SetProjectBuiltSuccessfully(const ProjectInfo& projectInfo, bool successfullyBuilt, bool saveToDisk = true); + } // namespace PMSettings +} // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp index d4ac43d655..cf15cb5bbc 100644 --- a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp @@ -60,6 +60,10 @@ namespace O3DE::ProjectManager connect(m_projectPath->lineEdit(), &QLineEdit::textChanged, this, &ProjectSettingsScreen::OnProjectPathUpdated); m_verticalLayout->addWidget(m_projectPath); + m_projectId = new FormLineEditWidget(tr("Project ID"), "", this); + connect(m_projectId->lineEdit(), &QLineEdit::textChanged, this, &ProjectSettingsScreen::OnProjectIdUpdated); + m_verticalLayout->addWidget(m_projectId); + projectSettingsFrame->setLayout(m_verticalLayout); m_horizontalLayout->addWidget(projectSettingsFrame); @@ -94,6 +98,7 @@ namespace O3DE::ProjectManager // currently we don't have separate fields for changing the project name and display name projectInfo.m_displayName = projectInfo.m_projectName; projectInfo.m_path = m_projectPath->lineEdit()->text(); + projectInfo.m_id = m_projectId->lineEdit()->text(); return projectInfo; } @@ -142,6 +147,19 @@ namespace O3DE::ProjectManager return projectPathIsValid; } + bool ProjectSettingsScreen::ValidateProjectId() + { + bool projectIdIsValid = true; + if (m_projectId->lineEdit()->text().isEmpty()) + { + projectIdIsValid = false; + m_projectId->setErrorLabelText(tr("Project ID cannot be empty.")); + } + + m_projectId->setErrorLabelVisible(!projectIdIsValid); + return projectIdIsValid; + } + void ProjectSettingsScreen::OnProjectNameUpdated() { ValidateProjectName(); @@ -149,11 +167,16 @@ namespace O3DE::ProjectManager void ProjectSettingsScreen::OnProjectPathUpdated() { - Validate(); + ValidateProjectName() && ValidateProjectPath(); + } + + void ProjectSettingsScreen::OnProjectIdUpdated() + { + ValidateProjectId(); } bool ProjectSettingsScreen::Validate() { - return ValidateProjectName() && ValidateProjectPath(); + return ValidateProjectName() && ValidateProjectPath() && ValidateProjectId(); } } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.h b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.h index 752e286ce1..928cde833e 100644 --- a/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.h +++ b/Code/Tools/ProjectManager/Source/ProjectSettingsScreen.h @@ -35,10 +35,12 @@ namespace O3DE::ProjectManager protected slots: virtual void OnProjectNameUpdated(); virtual void OnProjectPathUpdated(); + virtual void OnProjectIdUpdated(); protected: bool ValidateProjectName(); virtual bool ValidateProjectPath(); + bool ValidateProjectId(); QString GetDefaultProjectPath(); @@ -46,6 +48,7 @@ namespace O3DE::ProjectManager QVBoxLayout* m_verticalLayout; FormLineEditWidget* m_projectName; FormBrowseEditWidget* m_projectPath; + FormLineEditWidget* m_projectId; }; } // namespace O3DE::ProjectManager diff --git a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp index acd753a1cb..9816595548 100644 --- a/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/ProjectsScreen.cpp @@ -291,13 +291,9 @@ namespace O3DE::ProjectManager // Check whether project manager has successfully built the project if (currentButton) { - auto settingsRegistry = AZ::SettingsRegistry::Get(); bool projectBuiltSuccessfully = false; - if (settingsRegistry) - { - QString settingsKey = GetProjectBuiltSuccessfullyKey(project.m_projectName); - settingsRegistry->Get(projectBuiltSuccessfully, settingsKey.toStdString().c_str()); - } + PMSettings::GetProjectBuiltSuccessfully(projectBuiltSuccessfully, project); + if (!projectBuiltSuccessfully) { currentButton->ShowBuildRequired(); diff --git a/Code/Tools/ProjectManager/Source/PythonBindings.cpp b/Code/Tools/ProjectManager/Source/PythonBindings.cpp index 12f97e6d2e..8d11028af6 100644 --- a/Code/Tools/ProjectManager/Source/PythonBindings.cpp +++ b/Code/Tools/ProjectManager/Source/PythonBindings.cpp @@ -687,8 +687,24 @@ namespace O3DE::ProjectManager auto createProjectResult = m_engineTemplate.attr("create_project")( projectPath, - QString_To_Py_String(projectInfo.m_projectName), - QString_To_Py_Path(projectTemplatePath) + QString_To_Py_String(projectInfo.m_projectName), // project_path + QString_To_Py_Path(projectTemplatePath), // template_path + pybind11::none(), // template_name + pybind11::none(), // project_restricted_path + pybind11::none(), // project_restricted_name + pybind11::none(), // template_restricted_path + pybind11::none(), // template_restricted_name + pybind11::none(), // project_restricted_platform_relative_path + pybind11::none(), // template_restricted_platform_relative_path + pybind11::none(), // keep_restricted_in_project + pybind11::none(), // keep_license_text + pybind11::none(), // replace + pybind11::none(), // force + pybind11::none(), // no_register + pybind11::none(), // system_component_class_id + pybind11::none(), // editor_system_component_class_id + pybind11::none(), // module_id + QString_To_Py_String(projectInfo.m_id) // project_id ); if (createProjectResult.cast() == 0) { @@ -822,6 +838,7 @@ namespace O3DE::ProjectManager { projectInfo.m_projectName = Py_To_String(projectData["project_name"]); projectInfo.m_displayName = Py_To_String_Optional(projectData, "display_name", projectInfo.m_projectName); + projectInfo.m_id = Py_To_String_Optional(projectData, "project_id", projectInfo.m_id); projectInfo.m_origin = Py_To_String_Optional(projectData, "origin", projectInfo.m_origin); projectInfo.m_summary = Py_To_String_Optional(projectData, "summary", projectInfo.m_summary); projectInfo.m_iconPath = Py_To_String_Optional(projectData, "icon", ProjectPreviewImagePath); @@ -926,6 +943,7 @@ namespace O3DE::ProjectManager QString_To_Py_Path(projectInfo.m_path), pybind11::none(), // proj_name not used QString_To_Py_String(projectInfo.m_projectName), + QString_To_Py_String(projectInfo.m_id), QString_To_Py_String(projectInfo.m_origin), QString_To_Py_String(projectInfo.m_displayName), QString_To_Py_String(projectInfo.m_summary), diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp index 36d78ed2ac..71dae91a81 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectCtrl.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -306,17 +305,10 @@ namespace O3DE::ProjectManager if (newProjectSettings.m_projectName != m_projectInfo.m_projectName) { - // update reg key - QString oldSettingsKey = GetProjectBuiltSuccessfullyKey(m_projectInfo.m_projectName); - QString newSettingsKey = GetProjectBuiltSuccessfullyKey(newProjectSettings.m_projectName); - - auto settingsRegistry = AZ::SettingsRegistry::Get(); - bool projectBuiltSuccessfully = false; - if (settingsRegistry && settingsRegistry->Get(projectBuiltSuccessfully, oldSettingsKey.toStdString().c_str())) - { - settingsRegistry->Set(newSettingsKey.toStdString().c_str(), projectBuiltSuccessfully); - SaveProjectManagerSettings(); - } + // Remove project build successfully paths for both old and new project names + // because a full rebuild is required when moving projects + PMSettings::SetProjectBuiltSuccessfully(m_projectInfo, false); + PMSettings::SetProjectBuiltSuccessfully(newProjectSettings, false); } if (!newProjectSettings.m_newPreviewImagePath.isEmpty()) diff --git a/Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.cpp b/Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.cpp index a430102c27..7c8f981f59 100644 --- a/Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.cpp +++ b/Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.cpp @@ -56,6 +56,7 @@ namespace O3DE::ProjectManager { m_projectInfo.m_displayName = m_projectName->lineEdit()->text(); m_projectInfo.m_path = m_projectPath->lineEdit()->text(); + m_projectInfo.m_id = m_projectId->lineEdit()->text(); if (m_userChangedPreview) { @@ -70,8 +71,9 @@ namespace O3DE::ProjectManager m_projectInfo = projectInfo; m_projectName->lineEdit()->setText(projectInfo.GetProjectDisplayName()); - m_projectPath->lineEdit()->setText(projectInfo.m_path); + m_projectId->lineEdit()->setText(projectInfo.m_id); + UpdateProjectPreviewPath(); } diff --git a/Code/Tools/ProjectManager/project_manager_tests_files.cmake b/Code/Tools/ProjectManager/project_manager_tests_files.cmake index 2bfe343038..df9a0070a1 100644 --- a/Code/Tools/ProjectManager/project_manager_tests_files.cmake +++ b/Code/Tools/ProjectManager/project_manager_tests_files.cmake @@ -10,8 +10,9 @@ set(FILES Resources/ProjectManager.qrc Resources/ProjectManager.qss tests/ApplicationTests.cpp - tests/PythonBindingsTests.cpp tests/GemCatalogTests.cpp + tests/ProjectManagerSettingsTests.cpp + tests/PythonBindingsTests.cpp tests/main.cpp tests/UtilsTests.cpp ) diff --git a/Code/Tools/ProjectManager/tests/ProjectManagerSettingsTests.cpp b/Code/Tools/ProjectManager/tests/ProjectManagerSettingsTests.cpp new file mode 100644 index 0000000000..7ab93f798c --- /dev/null +++ b/Code/Tools/ProjectManager/tests/ProjectManagerSettingsTests.cpp @@ -0,0 +1,176 @@ +/* + * 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 + * + */ + +#include +#include +#include +#include +#include + +#include + +namespace O3DE::ProjectManager +{ + class ProjectManagerSettingsTests + : public ::UnitTest::ScopedAllocatorSetupFixture + { + public: + ~ProjectManagerSettingsTests() override = default; + void SetUp() override + { + UnitTest::ScopedAllocatorSetupFixture::SetUp(); + + m_registry = AZStd::make_unique(); + // Store off the old global settings registry to restore after each test + m_oldSettingsRegistry = AZ::SettingsRegistry::Get(); + if (m_oldSettingsRegistry != nullptr) + { + AZ::SettingsRegistry::Unregister(m_oldSettingsRegistry); + } + AZ::SettingsRegistry::Register(m_registry.get()); + + m_serializeContext = AZStd::make_unique(); + m_registrationContext = AZStd::make_unique(); + + m_registry->SetContext(m_serializeContext.get()); + m_registry->SetContext(m_registrationContext.get()); + + m_projectInfo.m_path = "Z:/ProjectTestPath"; + } + + void TearDown() override + { + m_registrationContext.reset(); + m_serializeContext.reset(); + + // Restore the old global settings registry + AZ::SettingsRegistry::Unregister(m_registry.get()); + if (m_oldSettingsRegistry != nullptr) + { + AZ::SettingsRegistry::Register(m_oldSettingsRegistry); + m_oldSettingsRegistry = nullptr; + } + m_registry.reset(); + + UnitTest::ScopedAllocatorSetupFixture::TearDown(); + } + + protected: + const QString m_settingsPath = "/Testing/TestKey"; + const QString m_newSettingsPath = "/Testing/NewTestKey"; + ProjectInfo m_projectInfo; + + private: + AZ::SettingsRegistryInterface* m_oldSettingsRegistry = nullptr; + AZStd::unique_ptr m_registry; + AZStd::unique_ptr m_serializeContext; + AZStd::unique_ptr m_registrationContext; + }; + + TEST_F(ProjectManagerSettingsTests, PMSettings_GetUnsetPathBool_ReturnsFalse) + { + bool settingsResult = false; + EXPECT_FALSE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + EXPECT_FALSE(settingsResult); + } + + TEST_F(ProjectManagerSettingsTests, PMSettings_SetAndGetValueBool_Success) + { + bool settingsResult = false; + EXPECT_FALSE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + + // Don't save to disk in test + EXPECT_TRUE(PMSettings::SetProjectManagerKey(m_settingsPath, true, /*saveToDisk*/ false)); + + settingsResult = false; + EXPECT_TRUE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + EXPECT_TRUE(settingsResult); + } + + TEST_F(ProjectManagerSettingsTests, PMSettings_GetUnsetPathString_ReturnsFalse) + { + QString settingsResult; + EXPECT_FALSE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + EXPECT_TRUE(settingsResult.isEmpty()); + } + + TEST_F(ProjectManagerSettingsTests, PMSettings_SetAndGetValueString_Success) + { + QString settingsResult; + EXPECT_FALSE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + + QString settingsValue = "TestValue"; + + // Don't save to disk in test + EXPECT_TRUE(PMSettings::SetProjectManagerKey(m_settingsPath, settingsValue, /*saveToDisk*/ false)); + + EXPECT_TRUE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + EXPECT_TRUE(settingsResult == settingsValue); + } + + TEST_F(ProjectManagerSettingsTests, PMSettings_CopyStringRemoveOriginal_SuccessAndRemovesOriginal) + { + QString settingsResult; + EXPECT_FALSE(PMSettings::GetProjectManagerKey(settingsResult, m_newSettingsPath)); + + QString settingsValue = "TestValue"; + + // Don't save to disk in test + EXPECT_TRUE(PMSettings::SetProjectManagerKey(m_settingsPath, settingsValue, /*saveToDisk*/ false)); + + EXPECT_TRUE(PMSettings::CopyProjectManagerKeyString(m_settingsPath, m_newSettingsPath, /*removeOrig*/ true, /*saveToDisk*/ false)); + + // Check that old path value is removed + EXPECT_FALSE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + + EXPECT_TRUE(PMSettings::GetProjectManagerKey(settingsResult, m_newSettingsPath)); + EXPECT_TRUE(settingsResult == settingsValue); + } + + TEST_F(ProjectManagerSettingsTests, PMSettings_RemoveProjectManagerKey_RemovesKey) + { + QString settingsResult; + EXPECT_FALSE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + + QString settingsValue = "TestValue"; + + // Don't save to disk in test + EXPECT_TRUE(PMSettings::SetProjectManagerKey(m_settingsPath, settingsValue, /*saveToDisk*/ false)); + EXPECT_TRUE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + + EXPECT_TRUE(PMSettings::RemoveProjectManagerKey(m_settingsPath, /*saveToDisk*/ false)); + EXPECT_FALSE(PMSettings::GetProjectManagerKey(settingsResult, m_settingsPath)); + } + + TEST_F(ProjectManagerSettingsTests, PMSettings_GetUnsetBuildPath_ReturnsFalse) + { + bool buildResult = true; + EXPECT_FALSE(PMSettings::GetProjectBuiltSuccessfully(buildResult, m_projectInfo)); + EXPECT_FALSE(buildResult); + } + + TEST_F(ProjectManagerSettingsTests, PMSettings_SetProjectBuiltSuccessfully_ReturnsTrue) + { + // Don't save to disk in test + EXPECT_TRUE(PMSettings::SetProjectBuiltSuccessfully(m_projectInfo, true, /*saveToDisk*/ false)); + + bool buildResult = false; + EXPECT_TRUE(PMSettings::GetProjectBuiltSuccessfully(buildResult, m_projectInfo)); + EXPECT_TRUE(buildResult); + } + + TEST_F(ProjectManagerSettingsTests, PMSettings_SetProjectBuiltUnsuccessfully_ReturnsFalse) + { + // Don't save to disk in test + EXPECT_TRUE(PMSettings::SetProjectBuiltSuccessfully(m_projectInfo, true, /*saveToDisk*/ false)); + + bool buildResult = true; + EXPECT_TRUE(PMSettings::GetProjectBuiltSuccessfully(buildResult, m_projectInfo)); + EXPECT_FALSE(buildResult); + } +} diff --git a/Templates/DefaultProject/Template/project.json b/Templates/DefaultProject/Template/project.json index b52b0baf27..f8d4643ce9 100644 --- a/Templates/DefaultProject/Template/project.json +++ b/Templates/DefaultProject/Template/project.json @@ -1,5 +1,6 @@ { "project_name": "${Name}", + "project_id": "${ProjectId}", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", "display_name": "${Name}", diff --git a/Templates/MinimalProject/Template/project.json b/Templates/MinimalProject/Template/project.json index b52b0baf27..f8d4643ce9 100644 --- a/Templates/MinimalProject/Template/project.json +++ b/Templates/MinimalProject/Template/project.json @@ -1,5 +1,6 @@ { "project_name": "${Name}", + "project_id": "${ProjectId}", "origin": "The primary repo for ${Name} goes here: i.e. http://www.mydomain.com", "license": "What license ${Name} uses goes here: i.e. https://opensource.org/licenses/MIT", "display_name": "${Name}", diff --git a/q b/q new file mode 100644 index 0000000000..fc1c268aef --- /dev/null +++ b/q @@ -0,0 +1,48 @@ + Prism/AddEditGemsButton + Prism/AddLicenseToGemCatalog + Prism/AddRemoveRepoWorkingUI + Prism/AddRepoDialog +* Prism/CmakeVariablesPerProject + Prism/CreateRepositoryScreen + Prism/CustomBuildParameters + Prism/CustomTabWidgetForEngine + Prism/DeleteUpdateGemsUI + Prism/DisplayAddGemInfo + Prism/EnterInFileDialog + Prism/ExternalLinkWarning + Prism/FasterBuildTimes + Prism/FixGemCart + Prism/FixGemCatalogHyperlinks + Prism/FixGemSelectedFilter + Prism/FixRemovedProjectStillShown + Prism/FixTabOverlayOnStartup + Prism/GemCatalogMenu + Prism/GemCatalogTests + Prism/GemCatalogWithoutProject + Prism/GemRepoInspector + Prism/RefreshAfterDownload + Prism/RefreshGemRepos + Prism/RefreshOnDownload + Prism/RefreshRepoButtonDisablesRepo + Prism/RemoteGemWarning + Prism/RemoveTabFocus + Prism/ResetBuildAndCacheAfterProjectMove + Prism/SearchForGemsFromTags + Prism/ShowGemDownloadStatus + Prism/ShowGemPreview + Prism/ShowGemRepoGems + Prism/ShowGemsInRepo + Prism/ShowRepoGems + Prism/ShowRepoList + Prism/UpdateTemplatePics + Prism/UseGemDisplayNames + Prism/VS2019InstallInstructions + Prism/WarnExternalLink + Prism/WarnVSNotInstalled + Prism/cherry-pick-gem-catalog-fix + Prism/gitRepos + development + new-feature + racoonteurs + stabilization/2110 + stabilization/2111RTE diff --git a/scripts/o3de/o3de/engine_template.py b/scripts/o3de/o3de/engine_template.py index e93f972301..963ee3eb84 100755 --- a/scripts/o3de/o3de/engine_template.py +++ b/scripts/o3de/o3de/engine_template.py @@ -1113,7 +1113,7 @@ def create_from_template(destination_path: pathlib.Path, try: template_json_data = json.load(s) except KeyError as e: - logger.error(f'Could read template json {template_json}: {str(e)}.') + logger.error(f'Could not read template json {template_json}: {str(e)}.') return 1 # read template name from the json @@ -1338,7 +1338,8 @@ def create_project(project_path: pathlib.Path, no_register: bool = False, system_component_class_id: str = None, editor_system_component_class_id: str = None, - module_id: str = None) -> int: + module_id: str = None, + project_id: str = None) -> int: """ Template instantiation specialization that makes all default assumptions for a Project template instantiation, reducing the effort needed in instancing a project @@ -1366,6 +1367,7 @@ def create_project(project_path: pathlib.Path, :param editor_system_component_class_id: optionally specify a uuid for the editor system component class, default is random uuid :param module_id: optionally specify a uuid for the module class, default is random uuid + :param project_id: optionally specify a str for the project id, default is random uuid :return: 0 for success or non 0 failure code """ if template_name and template_path: @@ -1405,7 +1407,7 @@ def create_project(project_path: pathlib.Path, try: template_json_data = json.load(s) except json.JSONDecodeError as e: - logger.error(f'Could read template json {template_json}: {str(e)}.') + logger.error(f'Could not read template json {template_json}: {str(e)}.') return 1 # read template name from the json @@ -1577,6 +1579,12 @@ def create_project(project_path: pathlib.Path, replacements.append(("${NameLower}", project_name.lower())) replacements.append(("${SanitizedCppName}", sanitized_cpp_name)) + # was a project id specified + if project_id: + replacements.append(("${ProjectId}", project_id)) + else: + replacements.append(("${ProjectId}", '{' + str(uuid.uuid4()) + '}')) + # module id is a uuid with { and - if module_id: replacements.append(("${ModuleClassId}", module_id)) @@ -1784,7 +1792,7 @@ def create_gem(gem_path: pathlib.Path, try: template_json_data = json.load(s) except json.JSONDecodeError as e: - logger.error(f'Could read template json {template_json}: {str(e)}.') + logger.error(f'Could not read template json {template_json}: {str(e)}.') return 1 # read template name from the json @@ -2123,7 +2131,8 @@ def _run_create_project(args: argparse) -> int: args.no_register, args.system_component_class_id, args.editor_system_component_class_id, - args.module_id) + args.module_id, + args.project_id) def _run_create_gem(args: argparse) -> int: @@ -2404,6 +2413,9 @@ def add_args(subparsers) -> None: create_project_subparser.add_argument('--module-id', type=uuid.UUID, required=False, help='The uuid you want to associate with the module, default is a random' ' uuid Ex. {b60c92eb-3139-454b-a917-a9d3c5819594}') + create_project_subparser.add_argument('--project-id', type=str, required=False, + help='The str id you want to associate with the project, default is a random uuid' + ' Ex. {b60c92eb-3139-454b-a917-a9d3c5819594}') create_project_subparser.add_argument('-f', '--force', action='store_true', default=False, help='Copies over instantiated template directory even if it exist.') create_project_subparser.add_argument('--no-register', action='store_true', default=False, diff --git a/scripts/o3de/o3de/project_properties.py b/scripts/o3de/o3de/project_properties.py index 04731ad3de..88c05537e9 100644 --- a/scripts/o3de/o3de/project_properties.py +++ b/scripts/o3de/o3de/project_properties.py @@ -29,6 +29,7 @@ def get_project_props(name: str = None, path: pathlib.Path = None) -> dict: def edit_project_props(proj_path: pathlib.Path = None, proj_name: str = None, new_name: str = None, + new_id: str = None, new_origin: str = None, new_display: str = None, new_summary: str = None, @@ -40,14 +41,15 @@ def edit_project_props(proj_path: pathlib.Path = None, if not proj_json: return 1 - - if new_origin: - proj_json['origin'] = new_origin if new_name: if not utils.validate_identifier(new_name): logger.error(f'Project name must be fewer than 64 characters, contain only alphanumeric, "_" or "-" characters, and start with a letter. {new_name}') return 1 - proj_json['project_name'] = new_name + proj_json['project_name'] = new_name + if new_id: + proj_json['project_id'] = new_id + if new_origin: + proj_json['origin'] = new_origin if new_display: proj_json['display_name'] = new_display if new_summary: @@ -78,6 +80,7 @@ def _edit_project_props(args: argparse) -> int: return edit_project_props(args.project_path, args.project_name, args.project_new_name, + args.project_id, args.project_origin, args.project_display, args.project_summary, @@ -95,6 +98,8 @@ def add_parser_args(parser): group = parser.add_argument_group('properties', 'arguments for modifying individual project properties.') group.add_argument('-pnn', '--project-new-name', type=str, required=False, help='Sets the name for the project.') + group.add_argument('-pid', '--project-id', type=str, required=False, + help='Sets the ID for the project.') group.add_argument('-po', '--project-origin', type=str, required=False, help='Sets description or url for project origin (such as project host, repository, owner...etc).') group.add_argument('-pd', '--project-display', type=str, required=False, diff --git a/scripts/o3de/o3de/register.py b/scripts/o3de/o3de/register.py index 2500df1568..aaadfb5709 100644 --- a/scripts/o3de/o3de/register.py +++ b/scripts/o3de/o3de/register.py @@ -570,7 +570,7 @@ def remove_invalid_o3de_projects(manifest_path: pathlib.Path = None) -> int: result = 0 for project in json_data.get('projects', []): - if not validation.valid_o3de_project_json(pathlib.Path(project).resolve() / 'project.json'): + if not validation.valid_o3de_project_json(pathlib.Path(project).resolve() / 'project.json', generate_uuid=True): logger.warning(f"Project path {project} is invalid.") # Attempt to unregister all invalid projects even if previous projects failed to unregister # but combine the result codes of each command. diff --git a/scripts/o3de/o3de/validation.py b/scripts/o3de/o3de/validation.py index 5c683f0667..a671e88edf 100644 --- a/scripts/o3de/o3de/validation.py +++ b/scripts/o3de/o3de/validation.py @@ -10,6 +10,7 @@ This file validating o3de object json files """ import json import pathlib +import uuid def valid_o3de_json_dict(json_data: dict, key: str) -> bool: return key in json_data @@ -44,7 +45,7 @@ def valid_o3de_engine_json(file_name: str or pathlib.Path) -> bool: return True -def valid_o3de_project_json(file_name: str or pathlib.Path) -> bool: +def valid_o3de_project_json(file_name: str or pathlib.Path, generate_uuid: bool = True) -> bool: file_name = pathlib.Path(file_name).resolve() if not file_name.is_file(): return False @@ -53,8 +54,22 @@ def valid_o3de_project_json(file_name: str or pathlib.Path) -> bool: try: json_data = json.load(f) test = json_data['project_name'] + + if not generate_uuid: + test = json_data['project_id'] + else: + test = json_data.get('project_id', 'No ID') + generate_new_id = test == 'No ID' + except (json.JSONDecodeError, KeyError) as e: return False + + # Generate a random uuid for the project json if it is missing instead of failing if generate_uuid is true + if generate_uuid and generate_new_id: + with file_name.open('w') as f: + new_uuid = '{' + str(uuid.uuid4()) + '}' + json_data.update({'project_id': new_uuid}) + f.write(json.dumps(json_data, indent=4) + '\n') return True diff --git a/scripts/o3de/tests/unit_test_project_properties.py b/scripts/o3de/tests/unit_test_project_properties.py index 0236e6cf90..1bcc6dcef3 100644 --- a/scripts/o3de/tests/unit_test_project_properties.py +++ b/scripts/o3de/tests/unit_test_project_properties.py @@ -16,6 +16,7 @@ from o3de import project_properties TEST_PROJECT_JSON_PAYLOAD = ''' { "project_name": "TestProject", + "project_id": "{24114e69-306d-4de6-b3b4-4cb1a3eca58e}" "origin": "The primary repo for TestProject goes here: i.e. http://www.mydomain.com", "license": "What license TestProject uses goes here: i.e. https://opensource.org/licenses/MIT", "display_name": "TestProject", @@ -45,20 +46,20 @@ def init_project_json_data(request): @pytest.mark.usefixtures('init_project_json_data') class TestEditProjectProperties: - @pytest.mark.parametrize("project_path, project_name, project_new_name, project_origin, project_display,\ - project_summary, project_icon, add_tags, delete_tags,\ - replace_tags, expected_result", [ + @pytest.mark.parametrize("project_path, project_name, project_new_name, project_id, project_origin,\ + project_display, project_summary, project_icon,\ + add_tags, delete_tags, replace_tags, expected_result", [ pytest.param(pathlib.PurePath('E:/TestProject'), - 'test', 'test', 'editing by pytest', 'Unit Test', 'pyTest project', 'pytest.bmp', 'A B C', + 'test', 'test', 'editing by pytest', 'ID', 'Unit Test', 'pyTest project', 'pytest.bmp', 'A B C', 'B', 'D E F', 0), pytest.param('', - 'test', 'test', 'editing by pytest', 'Unit Test', 'pyTest project', 'pytest.bmp', 'A B C', + 'test', 'test', 'editing by pytest', 'ID', 'Unit Test', 'pyTest project', 'pytest.bmp', 'A B C', 'B', 'D E F', 1) ] ) - def test_edit_project_properties(self, project_path, project_name, project_new_name, project_origin, project_display, - project_summary, project_icon, add_tags, delete_tags, - replace_tags, expected_result): + def test_edit_project_properties(self, project_path, project_name, project_new_name, project_origin, project_id, + project_display,project_summary, project_icon, add_tags, + delete_tags, replace_tags, expected_result): def get_project_json_data(project_name: str, project_path) -> dict: if not project_path: @@ -72,12 +73,14 @@ class TestEditProjectProperties: with patch('o3de.manifest.get_project_json_data', side_effect=get_project_json_data) as get_project_json_data_patch, \ patch('o3de.manifest.save_o3de_manifest', side_effect=save_o3de_manifest) as save_o3de_manifest_patch: - result = project_properties.edit_project_props(project_path, project_name, project_new_name, project_origin, - project_display, project_summary, project_icon, - add_tags, delete_tags, replace_tags) + result = project_properties.edit_project_props(project_path, project_name, project_new_name, project_id, + project_origin, project_display, project_summary, project_icon, + add_tags, delete_tags, replace_tags) assert result == expected_result if project_path: assert self.project_json.data + assert self.project_json.data.get('project_name', '') == project_new_name + assert self.project_json.data.get('project_id', '') == project_id assert self.project_json.data.get('origin', '') == project_origin assert self.project_json.data.get('display_name', '') == project_display assert self.project_json.data.get('summary', '') == project_summary