Bläddra i källkod

Merge pull request #12085 from aws-lumberyard-dev/Prism/RemoteTemplateAndRepoFixes

Remote templates and repo script fixes
AMZN-Phil 3 år sedan
förälder
incheckning
6b36a75805

+ 8 - 0
Code/Tools/ProjectManager/Resources/ProjectManager.qss

@@ -235,6 +235,14 @@ QTabBar::tab:focus {
     margin-left: 50px;
 }
 
+#addRemoteProjectDialog #formErrorLabel {
+    margin-left: 0px;
+}
+
+#addRemoteTemplateDialog #formErrorLabel {
+    margin-left: 0px;
+}
+
 #formTitleLabel {
     font-size:21px;
     color:#ffffff;

+ 1 - 1
Code/Tools/ProjectManager/Source/AddRemoteProjectDialog.cpp

@@ -49,7 +49,7 @@ namespace O3DE::ProjectManager
         m_repoPath = new FormLineEditWidget(tr("Remote URL"), "", this);
         m_repoPath->setMinimumSize(QSize(600, 0));
         m_repoPath->setErrorLabelText(tr("Not a valid remote source."));
-        m_repoPath->lineEdit()->setPlaceholderText("http://github.com/o3de/example.git");
+        m_repoPath->lineEdit()->setPlaceholderText("https://github.com/o3de/example.git");
         vLayout->addWidget(m_repoPath);
 
         vLayout->addSpacing(10);

+ 9 - 1
Code/Tools/ProjectManager/Source/AddRemoteTemplateDialog.cpp

@@ -48,6 +48,8 @@ namespace O3DE::ProjectManager
 
         m_repoPath = new FormLineEditWidget(tr("Remote URL"), "", this);
         m_repoPath->setMinimumSize(QSize(600, 0));
+        m_repoPath->setErrorLabelText(tr("Not a valid remote template source."));
+        m_repoPath->lineEdit()->setPlaceholderText("https://github.com/o3de/example.git");
         vLayout->addWidget(m_repoPath);
 
         vLayout->addSpacing(10);
@@ -84,6 +86,7 @@ namespace O3DE::ProjectManager
             {
                 // wait for a second before attempting to validate so we're less likely to do it per keypress
                 m_inputTimer->start(1000);
+                m_repoPath->SetValidationState(FormLineEditWidget::ValidationState::Validating);
                 
             });
 
@@ -93,7 +96,12 @@ namespace O3DE::ProjectManager
     void AddRemoteTemplateDialog::ValidateURI()
     {
         // validate URI, if it's a valid repository set the add button as active
-        SetDialogReady(PythonBindingsInterface::Get()->ValidateRepository(m_repoPath->lineEdit()->text()));
+        bool validRepository = PythonBindingsInterface::Get()->ValidateRepository(m_repoPath->lineEdit()->text());
+        SetDialogReady(validRepository);
+        m_repoPath->SetValidationState(
+            validRepository ? FormLineEditWidget::ValidationState::ValidationSuccess
+                            : FormLineEditWidget::ValidationState::ValidationFailed);
+        m_repoPath->setErrorLabelVisible(!validRepository);
     }
 
     void AddRemoteTemplateDialog::AddTemplateSource()

+ 13 - 4
Code/Tools/ProjectManager/Source/CreateProjectCtrl.cpp

@@ -233,7 +233,7 @@ namespace O3DE::ProjectManager
     {
         if (m_stack->currentWidget() == m_newProjectSettingsScreen)
         {
-            return m_newProjectSettingsScreen->Validate();
+            return m_newProjectSettingsScreen->Validate().IsSuccess();
         }
 
         return true;
@@ -241,7 +241,8 @@ namespace O3DE::ProjectManager
 
     void CreateProjectCtrl::CreateProject()
     {
-        if (m_newProjectSettingsScreen->Validate())
+        AZ::Outcome<void, QString> settingsValidation = m_newProjectSettingsScreen->Validate();
+        if (settingsValidation.IsSuccess())
         {
             if (!m_projectGemCatalogScreen->GetDownloadController()->IsDownloadQueueEmpty())
             {
@@ -279,8 +280,16 @@ namespace O3DE::ProjectManager
         }
         else
         {
-            QMessageBox::warning(
-                this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
+            const QString& errorMessage = settingsValidation.GetError();
+            if (errorMessage.isEmpty())
+            {
+                QMessageBox::warning(
+                    this, tr("Invalid project settings"), tr("Please correct the indicated project settings and try again."));
+            }
+            else
+            {
+                QMessageBox::warning(this, tr("Invalid project settings"), errorMessage);
+            }
         }
     }
 

+ 54 - 7
Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.cpp

@@ -41,7 +41,6 @@
 namespace O3DE::ProjectManager
 {
     constexpr const char* k_templateIndexProperty = "TemplateIndex";
-    constexpr const char* k_addRemoteTemplateProperty = "AddRemoteTemplate";
     constexpr const char* k_templateNameProperty = "TemplateName";
 
     NewProjectSettingsScreen::NewProjectSettingsScreen(DownloadController* downloadController, QWidget* parent)
@@ -101,10 +100,48 @@ namespace O3DE::ProjectManager
                             emit OnTemplateSelectionChanged(/*oldIndex=*/oldIndex, /*newIndex=*/m_selectedTemplateIndex);
                         }
                     }
-                    else if (button && button->property(k_addRemoteTemplateProperty).isValid())
+                    else if (button == m_remoteTemplateButton)
                     {
                         AddRemoteTemplateDialog* addRemoteTemplateDialog = new AddRemoteTemplateDialog(this);
-                        addRemoteTemplateDialog->exec();
+                        if (addRemoteTemplateDialog->exec() == QDialog::DialogCode::Accepted)
+                        {
+                            auto remoteTemplatesResult =
+                                PythonBindingsInterface::Get()->GetProjectTemplatesForRepo(addRemoteTemplateDialog->GetRepoPath());
+                            if (remoteTemplatesResult.IsSuccess() && !remoteTemplatesResult.GetValue().isEmpty())
+                            {
+                                // remove remote template button from layout so we can insert the new templates before it
+                                m_templateFlowLayout->removeWidget(m_remoteTemplateButton);
+
+                                int currentTemplateIndex = m_templates.size();
+                                const QVector<ProjectTemplateInfo>& remoteTemplates = remoteTemplatesResult.GetValue();
+                                for (const ProjectTemplateInfo& remoteTemplate : remoteTemplates)
+                                {
+                                    m_templates.push_back(remoteTemplate);
+
+                                    // create template button
+                                    QString projectPreviewPath = QDir(remoteTemplate.m_path).filePath(ProjectPreviewImagePath);
+                                    QFileInfo doesPreviewExist(projectPreviewPath);
+                                    if (!doesPreviewExist.exists() || !doesPreviewExist.isFile())
+                                    {
+                                        projectPreviewPath = ":/DefaultTemplate.png";
+                                    }
+                                    TemplateButton* templateButton =
+                                        new TemplateButton(projectPreviewPath, remoteTemplate.m_displayName, this);
+                                    templateButton->SetIsRemote(remoteTemplate.m_isRemote);
+                                    templateButton->setCheckable(true);
+                                    templateButton->setProperty(k_templateIndexProperty, currentTemplateIndex);
+                                    templateButton->setProperty(k_templateNameProperty, remoteTemplate.m_name);
+
+                                    m_projectTemplateButtonGroup->addButton(templateButton);
+                                    m_templateFlowLayout->addWidget(templateButton);
+                                    m_templateButtons.append(templateButton);
+                                    ++currentTemplateIndex;
+                                }
+
+                                // add remote template button back to layout
+                                m_templateFlowLayout->addWidget(m_remoteTemplateButton);
+                            }
+                        }
                     }
                 });
 
@@ -259,10 +296,9 @@ namespace O3DE::ProjectManager
             }
 
             // Insert the add a remote template button
-            TemplateButton* remoteTemplateButton = new TemplateButton(":/DefaultTemplate.png", tr("Add remote Template"), this);
-            remoteTemplateButton->setProperty(k_addRemoteTemplateProperty, true);
-            m_projectTemplateButtonGroup->addButton(remoteTemplateButton);
-            m_templateFlowLayout->addWidget(remoteTemplateButton);
+            m_remoteTemplateButton = new TemplateButton(":/DefaultTemplate.png", tr("Add remote Template"), this);
+            m_projectTemplateButtonGroup->addButton(m_remoteTemplateButton);
+            m_templateFlowLayout->addWidget(m_remoteTemplateButton);
 
             // Select the first project template (default selection).
             SelectProjectTemplate(0, /*blockSignals=*/true);
@@ -397,6 +433,17 @@ namespace O3DE::ProjectManager
             m_projectTemplateButtonGroup->blockSignals(false);
         }
     }
+
+    AZ::Outcome<void, QString> NewProjectSettingsScreen::Validate() const
+    {
+        if (m_selectedTemplateIndex != -1 && m_templates[m_selectedTemplateIndex].m_isRemote)
+        {
+            return AZ::Failure<QString>(tr("You cannot create a new project with a template that has not been downloaded. Please download it before proceeding."));
+        }
+
+        return ProjectSettingsScreen::Validate();
+    }
+
     void NewProjectSettingsScreen::OnProjectNameUpdated()
     {
         if (ValidateProjectName() && !m_userChangedProjectPath)

+ 3 - 0
Code/Tools/ProjectManager/Source/NewProjectSettingsScreen.h

@@ -41,6 +41,8 @@ namespace O3DE::ProjectManager
 
         void SelectProjectTemplate(int index, bool blockSignals = false);
 
+        AZ::Outcome<void, QString> Validate() const override;
+
     signals:
         void OnTemplateSelectionChanged(int oldIndex, int newIndex);
 
@@ -64,6 +66,7 @@ namespace O3DE::ProjectManager
         QLabel* m_templateDisplayName;
         QLabel* m_templateSummary;
         QPushButton* m_downloadTemplateButton;
+        TemplateButton* m_remoteTemplateButton = nullptr;
         TagContainerWidget* m_templateIncludedGems;
         QVector<ProjectTemplateInfo> m_templates;
         QVector<TemplateButton*> m_templateButtons;

+ 10 - 4
Code/Tools/ProjectManager/Source/ProjectSettingsScreen.cpp

@@ -90,7 +90,7 @@ namespace O3DE::ProjectManager
         return projectInfo;
     }
 
-    bool ProjectSettingsScreen::ValidateProjectName()
+    bool ProjectSettingsScreen::ValidateProjectName() const
     {
         bool projectNameIsValid = true;
         if (m_projectName->lineEdit()->text().isEmpty())
@@ -116,7 +116,7 @@ namespace O3DE::ProjectManager
         return projectNameIsValid;
     }
 
-    bool ProjectSettingsScreen::ValidateProjectPath()
+    bool ProjectSettingsScreen::ValidateProjectPath() const
     {
         bool projectPathIsValid = true;
         QDir path(m_projectPath->lineEdit()->text());
@@ -145,8 +145,14 @@ namespace O3DE::ProjectManager
         ValidateProjectName() && ValidateProjectPath();
     }
 
-    bool ProjectSettingsScreen::Validate()
+    AZ::Outcome<void, QString> ProjectSettingsScreen::Validate() const
     {
-        return ValidateProjectName() && ValidateProjectPath();
+        if (ValidateProjectName() && ValidateProjectPath())
+        {
+            return AZ::Success();
+        }
+
+        // Returning empty string to use the default error message
+        return AZ::Failure<QString>("");
     }
 } // namespace O3DE::ProjectManager

+ 5 - 3
Code/Tools/ProjectManager/Source/ProjectSettingsScreen.h

@@ -10,6 +10,8 @@
 #if !defined(Q_MOC_RUN)
 #include <ProjectInfo.h>
 #include <ScreenWidget.h>
+
+#include <AzCore/Outcome/Outcome.h>
 #endif
 
 QT_FORWARD_DECLARE_CLASS(QHBoxLayout)
@@ -30,15 +32,15 @@ namespace O3DE::ProjectManager
 
         virtual ProjectInfo GetProjectInfo();
 
-        virtual bool Validate();
+        virtual AZ::Outcome<void, QString> Validate() const;
 
     protected slots:
         virtual void OnProjectNameUpdated();
         virtual void OnProjectPathUpdated();
 
     protected:
-        bool ValidateProjectName();
-        virtual bool ValidateProjectPath();
+        bool ValidateProjectName() const;
+        virtual bool ValidateProjectPath() const;
 
         QString GetDefaultProjectPath();
 

+ 38 - 5
Code/Tools/ProjectManager/Source/PythonBindings.cpp

@@ -662,7 +662,7 @@ namespace O3DE::ProjectManager
         return AZ::Success(AZStd::move(gems));
     }
 
-    AZ::Outcome<QVector<AZStd::string>, AZStd::string> PythonBindings::GetEnabledGemNames(const QString& projectPath)
+    AZ::Outcome<QVector<AZStd::string>, AZStd::string> PythonBindings::GetEnabledGemNames(const QString& projectPath) const
     {
         // Retrieve the path to the cmake file that lists the enabled gems.
         pybind11::str enabledGemsFilename;
@@ -1180,7 +1180,7 @@ namespace O3DE::ProjectManager
         return AZ::Success();
     }
 
-    ProjectTemplateInfo PythonBindings::ProjectTemplateInfoFromPath(pybind11::handle path)
+    ProjectTemplateInfo PythonBindings::ProjectTemplateInfoFromPath(pybind11::handle path) const
     {
         ProjectTemplateInfo templateInfo(TemplateInfoFromPath(path));
         if (templateInfo.IsValid())
@@ -1204,7 +1204,7 @@ namespace O3DE::ProjectManager
         return templateInfo;
     }
 
-    TemplateInfo PythonBindings::TemplateInfoFromPath(pybind11::handle path)
+    TemplateInfo PythonBindings::TemplateInfoFromPath(pybind11::handle path) const
     {
         TemplateInfo templateInfo;
         templateInfo.m_path = Py_To_String(path);
@@ -1294,7 +1294,41 @@ namespace O3DE::ProjectManager
         }
     }
 
-    AZ::Outcome<QVector<ProjectTemplateInfo>> PythonBindings::GetProjectTemplatesForAllRepos()
+    AZ::Outcome<QVector<ProjectTemplateInfo>> PythonBindings::GetProjectTemplatesForRepo(const QString& repoUri) const
+    {
+        QVector<ProjectTemplateInfo> templates;
+
+        bool result = ExecuteWithLock(
+            [&]
+            {
+                using namespace pybind11::literals;
+
+                auto templatePaths = m_repo.attr("get_template_json_paths_from_cached_repo")(
+                    "repo_uri"_a = QString_To_Py_String(repoUri)
+                    );
+
+                if (pybind11::isinstance<pybind11::set>(templatePaths))
+                {
+                    for (auto path : templatePaths)
+                    {
+                        ProjectTemplateInfo remoteTemplate = ProjectTemplateInfoFromPath(path);
+                        remoteTemplate.m_isRemote = true;
+                        templates.push_back(remoteTemplate);
+                    }
+                }
+            });
+
+        if (!result)
+        {
+            return AZ::Failure();
+        }
+        else
+        {
+            return AZ::Success(AZStd::move(templates));
+        }
+    }
+
+    AZ::Outcome<QVector<ProjectTemplateInfo>> PythonBindings::GetProjectTemplatesForAllRepos() const
     {
         QVector<ProjectTemplateInfo> templates;
 
@@ -1430,7 +1464,6 @@ namespace O3DE::ProjectManager
             try
             {
                 // required
-                gemRepoInfo.m_repoUri = Py_To_String(data["repo_uri"]);
                 gemRepoInfo.m_name = Py_To_String(data["repo_name"]);
                 gemRepoInfo.m_origin = Py_To_String(data["origin"]);
 

+ 5 - 4
Code/Tools/ProjectManager/Source/PythonBindings.h

@@ -48,7 +48,7 @@ namespace O3DE::ProjectManager
         AZ::Outcome<GemInfo> GetGemInfo(const QString& path, const QString& projectPath = {}) override;
         AZ::Outcome<QVector<GemInfo>, AZStd::string> GetEngineGemInfos() override;
         AZ::Outcome<QVector<GemInfo>, AZStd::string> GetAllGemInfos(const QString& projectPath) override;
-        AZ::Outcome<QVector<AZStd::string>, AZStd::string> GetEnabledGemNames(const QString& projectPath) override;
+        AZ::Outcome<QVector<AZStd::string>, AZStd::string> GetEnabledGemNames(const QString& projectPath) const override;
         AZ::Outcome<void, AZStd::string> RegisterGem(const QString& gemPath, const QString& projectPath = {}) override;
         AZ::Outcome<void, AZStd::string> UnregisterGem(const QString& gemPath, const QString& projectPath = {}) override;
 
@@ -84,7 +84,8 @@ namespace O3DE::ProjectManager
 
         // Templates
         AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplates() override;
-        AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplatesForAllRepos() override;
+        AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplatesForRepo(const QString& repoUri) const override;
+        AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplatesForAllRepos() const override;
         AZ::Outcome<QVector<TemplateInfo>> GetGemTemplates() override;
 
         void AddErrorString(AZStd::string errorString) override;
@@ -102,8 +103,8 @@ namespace O3DE::ProjectManager
         GemInfo GemInfoFromPath(pybind11::handle path, pybind11::handle pyProjectPath);
         GemRepoInfo GetGemRepoInfo(pybind11::handle repoUri);
         ProjectInfo ProjectInfoFromPath(pybind11::handle path);
-        ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path);
-        TemplateInfo TemplateInfoFromPath(pybind11::handle path);
+        ProjectTemplateInfo ProjectTemplateInfoFromPath(pybind11::handle path) const;
+        TemplateInfo TemplateInfoFromPath(pybind11::handle path) const;
         AZ::Outcome<void, AZStd::string> GemRegistration(const QString& gemPath, const QString& projectPath, bool remove = false);
         bool StopPython();
         IPythonBindings::ErrorPair GetErrorPair();

+ 8 - 2
Code/Tools/ProjectManager/Source/PythonBindingsInterface.h

@@ -141,7 +141,7 @@ namespace O3DE::ProjectManager
          * @param[in] projectPath Absolute file path to the project.
          * @return A list of gem names of all the enabled gems for a given project or a error message on failure.
          */
-        virtual AZ::Outcome<QVector<AZStd::string>, AZStd::string> GetEnabledGemNames(const QString& projectPath) = 0;
+        virtual AZ::Outcome<QVector<AZStd::string>, AZStd::string> GetEnabledGemNames(const QString& projectPath) const = 0;
 
         /**
          * Registers the gem to the specified project, or to the o3de_manifest.json if no project path is given
@@ -248,11 +248,17 @@ namespace O3DE::ProjectManager
          */
         virtual AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplates() = 0;
 
+        /**
+         * Gathers all project templates for the given repo.
+         * @return An outcome with a list of all ProjectTemplateInfos from the given repo on success
+         */
+        virtual AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplatesForRepo(const QString& repoUri) const = 0;
+
         /**
          * Gathers all project templates for all templates registered from repos.
          * @return An outcome with a list of all ProjectTemplateInfos on success
          */
-        virtual AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplatesForAllRepos() = 0;
+        virtual AZ::Outcome<QVector<ProjectTemplateInfo>> GetProjectTemplatesForAllRepos() const = 0;
 
         // Gem Repos
 

+ 11 - 5
Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.cpp

@@ -176,9 +176,15 @@ namespace O3DE::ProjectManager
         }
     }
 
-    bool UpdateProjectSettingsScreen::Validate()
+    AZ::Outcome<void, QString> UpdateProjectSettingsScreen::Validate() const
     {
-        return ProjectSettingsScreen::Validate() && ValidateProjectPreview() && ValidateProjectId();
+        if (!(ValidateProjectPreview() && ValidateProjectId()))
+        {
+            // Returning empty string to use the default error message
+            return AZ::Failure<QString>("");
+        }
+
+        return ProjectSettingsScreen::Validate();
     }
 
     void UpdateProjectSettingsScreen::ResetProjectPreviewPath()
@@ -212,7 +218,7 @@ namespace O3DE::ProjectManager
     } 
 
 
-    bool UpdateProjectSettingsScreen::ValidateProjectPath()
+    bool UpdateProjectSettingsScreen::ValidateProjectPath() const
     {
         bool projectPathIsValid = true;
         QDir path(m_projectPath->lineEdit()->text());
@@ -226,7 +232,7 @@ namespace O3DE::ProjectManager
         return projectPathIsValid;
     }
 
-    bool UpdateProjectSettingsScreen::ValidateProjectPreview()
+    bool UpdateProjectSettingsScreen::ValidateProjectPreview() const
     {
         bool projectPreviewIsValid = true;
 
@@ -261,7 +267,7 @@ namespace O3DE::ProjectManager
         return projectPreviewIsValid;
     }
 
-    bool UpdateProjectSettingsScreen::ValidateProjectId()
+    bool UpdateProjectSettingsScreen::ValidateProjectId() const
     {
         bool projectIdIsValid = true;
         if (m_projectId->lineEdit()->text().isEmpty())

+ 4 - 4
Code/Tools/ProjectManager/Source/UpdateProjectSettingsScreen.h

@@ -29,7 +29,7 @@ namespace O3DE::ProjectManager
         ProjectInfo GetProjectInfo() override;
         void SetProjectInfo(const ProjectInfo& projectInfo);
 
-        bool Validate() override;
+        AZ::Outcome<void, QString> Validate() const override;
 
         void ResetProjectPreviewPath();
 
@@ -40,9 +40,9 @@ namespace O3DE::ProjectManager
         void OnProjectEngineUpdated(int index);
 
     protected:
-        bool ValidateProjectPath() override;
-        virtual bool ValidateProjectPreview();
-        bool ValidateProjectId();
+        bool ValidateProjectPath() const override;
+        virtual bool ValidateProjectPreview() const;
+        bool ValidateProjectId() const;
 
         inline constexpr static int s_collapseButtonSize = 24;
 

+ 2 - 2
Code/Tools/ProjectManager/tests/MockPythonBindings.h

@@ -29,7 +29,7 @@ namespace O3DE::ProjectManager
         MOCK_METHOD2(GetGemInfo, AZ::Outcome<GemInfo>(const QString&, const QString&));
         MOCK_METHOD0(GetEngineGemInfos, AZ::Outcome<QVector<GemInfo>, AZStd::string>());
         MOCK_METHOD1(GetAllGemInfos, AZ::Outcome<QVector<GemInfo>, AZStd::string>(const QString&));
-        MOCK_METHOD1(GetEnabledGemNames, AZ::Outcome<QVector<AZStd::string>, AZStd::string>(const QString&));
+        MOCK_CONST_METHOD1(GetEnabledGemNames, AZ::Outcome<QVector<AZStd::string>, AZStd::string>(const QString&));
         MOCK_METHOD2(RegisterGem, AZ::Outcome<void, AZStd::string>(const QString&, const QString&));
         MOCK_METHOD2(UnregisterGem, AZ::Outcome<void, AZStd::string>(const QString&, const QString&));
 
@@ -46,7 +46,7 @@ namespace O3DE::ProjectManager
 
         // ProjectTemplate
         MOCK_METHOD0(GetProjectTemplates, AZ::Outcome<QVector<ProjectTemplateInfo>>());
-        MOCK_METHOD0(GetProjectTemplatesForAllRepos, AZ::Outcome<QVector<ProjectTemplateInfo>>());
+        MOCK_CONST_METHOD0(GetProjectTemplatesForAllRepos, AZ::Outcome<QVector<ProjectTemplateInfo>>());
         MOCK_METHOD0(GetGemTemplates, AZ::Outcome<QVector<TemplateInfo>>());
 
         // Gem Repos

+ 8 - 1
scripts/o3de/o3de/github_utils.py

@@ -65,6 +65,13 @@ class GitHubProvider(gitproviderinterface.GitProviderInterface):
 
 def get_github_provider(parsed_uri) -> GitHubProvider or None:
     if 'github.com' in parsed_uri.netloc:
-        return GitHubProvider
+        components = parsed_uri.path.split('/')
+        components = [ele for ele in components if ele.strip()]
+
+        if len(components) < 2:
+            return None
+
+        if components[1].endswith(".git"):
+            return GitHubProvider
 
     return None

+ 6 - 6
scripts/o3de/o3de/repo.py

@@ -296,27 +296,27 @@ def search_repo(manifest_json_data: dict,
                 template_name: str = None,
                 restricted_name: str = None) -> dict or None:
     if isinstance(engine_name, str) or isinstance(engine_name, pathlib.PurePath):
-        o3de_object_uris = manifest_json_data['engines']
+        o3de_object_uris = manifest_json_data.get('engines', [])
         manifest_json = 'engine.json'
         json_key = 'engine_name'
         search_func = lambda manifest_json_data: manifest_json_data if manifest_json_data.get(json_key, '') == engine_name else None
     elif isinstance(project_name, str) or isinstance(project_name, pathlib.PurePath):
-        o3de_object_uris = manifest_json_data['projects']
+        o3de_object_uris = manifest_json_data.get('projects', [])
         manifest_json = 'project.json'
         json_key = 'project_name'
         search_func = lambda manifest_json_data: manifest_json_data if manifest_json_data.get(json_key, '') == project_name else None
     elif isinstance(gem_name, str) or isinstance(gem_name, pathlib.PurePath):
-        o3de_object_uris = manifest_json_data['gems']
+        o3de_object_uris = manifest_json_data.get('gems', [])
         manifest_json = 'gem.json'
         json_key = 'gem_name'
         search_func = lambda manifest_json_data: manifest_json_data if manifest_json_data.get(json_key, '') == gem_name else None
     elif isinstance(template_name, str) or isinstance(template_name, pathlib.PurePath):
-        o3de_object_uris = manifest_json_data['templates']
+        o3de_object_uris = manifest_json_data.get('templates', [])
         manifest_json = 'template.json'
         json_key = 'template_name'
         search_func = lambda manifest_json_data: manifest_json_data if manifest_json_data.get(json_key, '') == template_name else None
     elif isinstance(restricted_name, str) or isinstance(restricted_name, pathlib.PurePath):
-        o3de_object_uris = manifest_json_data['restricted']
+        o3de_object_uris = manifest_json_data.get('restricted', [])
         manifest_json = 'restricted.json'
         json_key = 'restricted_name'
         search_func = lambda manifest_json_data: manifest_json_data if manifest_json_data.get(json_key, '') == restricted_name else None
@@ -345,7 +345,7 @@ def search_o3de_object(manifest_json, o3de_object_uris, search_func):
     for o3de_object_uri in o3de_object_uris:
         manifest_uri = f'{o3de_object_uri}/{manifest_json}'
         cache_file, _ = get_cache_file_uri(manifest_uri)
-        
+
         if cache_file.is_file():
             with cache_file.open('r') as f:
                 try: