diff --git a/code/+matbox/+setup/installFromSourceUri.m b/code/+matbox/+setup/installFromSourceUri.m new file mode 100644 index 0000000..02ce94f --- /dev/null +++ b/code/+matbox/+setup/installFromSourceUri.m @@ -0,0 +1,111 @@ +function installResult = installFromSourceUri(sourceUri, options) +%installFromSourceUri Install dependency from a supported source URI. +% +% installResult = matbox.setup.installFromSourceUri(sourceUri) +% installs from a supported URI and returns a normalized install result. +% +% Input Arguments: +% sourceUri (string) - URI for package installation +% Supported URI schemes: +% - fex://[/version] +% - https://github.com//[@branch] +% +% Output Arguments: +% installResult (struct) - Structure with the following fields: +% - FilePath - Path to location where toolbox is installed +% - InstallationType - Type of installation (folder or mltbx) +% - ToolboxIdentifier - UUID for toolbox (FEX only) + + arguments + sourceUri (1,1) string + options.InstallationLocation (1,1) string = matbox.setup.internal.getDefaultAddonFolder() + options.AddToPath (1,1) logical = true + options.Update (1,1) logical = false + options.Verbose (1,1) logical = true + options.AgreeToLicense (1,1) logical = false + end + + if startsWith(sourceUri, "fex://") + [packageUuid, title, version] = getFEXPackageSpecification(sourceUri); + [packageTargetFolder, installationType] = matbox.setup.internal.installFexPackage( ... + packageUuid, ... + options.InstallationLocation, ... + "Title", title, ... + "Version", version, ... + "AddToPath", options.AddToPath, ... + "Verbose", options.Verbose, ... + "AgreeToLicense", options.AgreeToLicense); + + installResult = struct( ... + "FilePath", packageTargetFolder, ... + "InstallationType", installationType, ... + "ToolboxIdentifier", packageUuid); + + elseif startsWith(sourceUri, "https://github.com/") + [repoUrl, branchName] = parseGitHubSourceUri(sourceUri); + repoTargetFolder = matbox.setup.internal.installGithubRepository( ... + repoUrl, ... + branchName, ... + "InstallationLocation", options.InstallationLocation, ... + "AddToPath", options.AddToPath, ... + "Update", options.Update, ... + "Verbose", options.Verbose); + + installResult = struct( ... + "FilePath", repoTargetFolder, ... + "InstallationType", "folder", ... + "ToolboxIdentifier", ""); + + else + error("MatBox:Setup:UnsupportedSourceUri", ... + 'Unsupported source URI: %s', sourceUri) + end + + if ~nargout + clear installResult + end +end + +function [packageUuid, title, version] = getFEXPackageSpecification(uri) +% getFEXPackageSpecification - Get UUID and version for package +% +% NB: This function relies on an undocumented api, and might break in the +% future. + + version = "latest"; % Initialize default value + + FEX_API_URL = "https://addons.mathworks.com/registry/v1/"; + + splitUri = strsplit(uri, '/'); + + packageNumber = regexp(splitUri{2}, '\d*(?=-)', 'match', 'once'); + title = extractAfter(splitUri{2}, [packageNumber '-']); + try + packageInfo = webread(FEX_API_URL + num2str(packageNumber)); + packageUuid = packageInfo.uuid; + catch ME + switch ME.identifier + case 'MATLAB:webservices:HTTP404StatusCodeError' + error('FEX package with identifier "%s" was not found', splitUri{2}) + otherwise + rethrow(ME) + end + end + + if numel(splitUri) == 3 + version = string( splitUri{3} ); + assert( any(strcmp(packageInfo.versions, version) ), ... + 'Specified version "%s" is not supported for FEX package "%s"', ... + version, splitUri{2}); + end +end + +function [repoUrl, branchName] = parseGitHubSourceUri(repoUrl) +% parseGitHubSourceUri - Extract branchname if present + branchName = string(missing); + if contains(repoUrl, '@') + splitUrl = strsplit(repoUrl, '@'); + repoUrl = splitUrl{1}; + branchName = splitUrl{2}; + end +end diff --git a/code/+matbox/installRequirements.m b/code/+matbox/installRequirements.m index 603b5e7..a225ef9 100644 --- a/code/+matbox/installRequirements.m +++ b/code/+matbox/installRequirements.m @@ -8,7 +8,6 @@ function installRequirements(toolboxFolder, mode, options) end arguments - % options.UseDefaultInstallationLocation (1,1) logical = true % Tentative, not implemented yet! options.UpdateSearchPath (1,1) logical = true options.SaveSearchPath (1,1) logical = true options.InstallationLocation (1,1) string = matbox.setup.internal.getDefaultAddonFolder() @@ -18,7 +17,7 @@ function installRequirements(toolboxFolder, mode, options) % Parse mode/flags mode = string(mode); - doUpdate = any(strcmp(mode, 'update')) || any( strcmp(mode, 'u') ); + doUpdate = any(strcmp(mode, 'update')) || any(strcmp(mode, 'u')); installationLocation = options.InstallationLocation; if ~isfolder(installationLocation); mkdir(installationLocation); end @@ -26,77 +25,20 @@ function installRequirements(toolboxFolder, mode, options) reqs = matbox.setup.internal.getRequirements(toolboxFolder); for i = 1:numel(reqs) switch reqs(i).Type - - case 'GitHub' - [repoUrl, branchName] = parseGitHubUrl(reqs(i).URI); - matbox.setup.internal.installGithubRepository( ... - repoUrl, ... - branchName, ... - "InstallationLocation", options.InstallationLocation, ... + case {'GitHub', 'FileExchange'} + matbox.setup.installFromSourceUri( ... + reqs(i).URI, ... + "InstallationLocation", installationLocation, ... "AddToPath", options.UpdateSearchPath, ... "Update", doUpdate, ... - "Verbose", options.Verbose) - - case 'FileExchange' - [packageUuid, title, version] = getFEXPackageSpecification( reqs(i).URI ); - matbox.setup.internal.installFexPackage(... - packageUuid, ... - installationLocation, ... - "Title", title, ... - "Version", version, ... - "AddToPath", options.UpdateSearchPath, ... "Verbose", options.Verbose, ... "AgreeToLicense", options.AgreeToLicenses); - case 'Unknown' continue end end + if options.UpdateSearchPath && options.SaveSearchPath savepath() end end - -function [packageUuid, title, version] = getFEXPackageSpecification(uri) -% getFEXPackageSpecification - Get UUID and version for package -% -% NB: This function relies on an undocumented api, and might break in the -% future. - - version = "latest"; % Initialize default value - - FEX_API_URL = "https://addons.mathworks.com/registry/v1/"; - - splitUri = strsplit(uri, '/'); - - packageNumber = regexp(splitUri{2}, '\d*(?=-)', 'match', 'once'); - title = extractAfter(splitUri{2}, [packageNumber '-']); - try - packageInfo = webread(FEX_API_URL + num2str(packageNumber)); - packageUuid = packageInfo.uuid; - catch ME - switch ME.identifier - case 'MATLAB:webservices:HTTP404StatusCodeError' - error('FEX package with identifier "%s" was not found', splitUri{2}) - otherwise - rethrow(ME) - end - end - - if numel(splitUri) == 3 - version = string( splitUri{3} ); - assert( any(strcmp(packageInfo.versions, version) ), ... - 'Specified version "%s" is not supported for FEX package "%s"', ... - version, splitUri{2}); - end -end - -function [repoUrl, branchName] = parseGitHubUrl(repoUrl) -% parseGitHubUrl - Extract branchname if present - branchName = string(missing); - if contains(repoUrl, '@') - splitUrl = strsplit(repoUrl, '@'); - repoUrl = splitUrl{1}; - branchName = splitUrl{2}; - end -end