diff --git a/.github/badges/tests.svg b/.github/badges/tests.svg
index 828f2d7..8306689 100644
--- a/.github/badges/tests.svg
+++ b/.github/badges/tests.svg
@@ -1 +1 @@
-
+
diff --git a/code/+matbox/+setup/+internal/+github/parseRepositoryURL.m b/code/+matbox/+setup/+internal/+github/parseRepositoryURL.m
index 5cff13c..6e48bff 100644
--- a/code/+matbox/+setup/+internal/+github/parseRepositoryURL.m
+++ b/code/+matbox/+setup/+internal/+github/parseRepositoryURL.m
@@ -4,7 +4,10 @@
% [owner, repositoryName, gitRef] = parseRepositoryURL(repoUrl) splits a
% GitHub repository URL of the form https://github.com//[@ref]
% into its parts. gitRef is the branch name, tag name, or commit SHA
-% following "@", or a missing string when the URL has no "@" suffix.
+% following the first "@", including any "/" in it (as in
+% "release/1.0"), or a missing string when the URL has no "@" suffix.
+% A URL with path segments after the repository name and no "@", such
+% as https://github.com///tree/, is an error.
arguments
repoUrl (1,1) matlab.net.URI
@@ -21,11 +24,17 @@
owner = pathNames(1);
repositoryName = pathNames(2);
+ % A git reference may contain "/" and "@", so the reference is
+ % everything after the first "@", up to the end of the path.
gitRef = string(missing);
- if contains(repositoryName, '@')
- splitName = split(repositoryName, '@');
- repositoryName = splitName(1);
- gitRef = splitName(2);
+ if contains(repositoryName, "@")
+ gitRef = strjoin([extractAfter(repositoryName, "@"), pathNames(3:end)], "/");
+ repositoryName = extractBefore(repositoryName, "@");
+ elseif numel(pathNames) > 2
+ error("MATBOX:GitHub:InvalidRepositoryURL", ...
+ "The repository URL ""%s"" has path segments after the repository name. " + ...
+ "Give a branch, tag or commit as https://github.com//@.", ...
+ string(repoUrl))
end
if nargout < 3
diff --git a/code/+matbox/+setup/installFromSourceUri.m b/code/+matbox/+setup/installFromSourceUri.m
index 807cdb5..6eb2bc6 100644
--- a/code/+matbox/+setup/installFromSourceUri.m
+++ b/code/+matbox/+setup/installFromSourceUri.m
@@ -50,7 +50,9 @@
"ToolboxIdentifier", packageUuid);
elseif startsWith(sourceUri, "https://github.com/")
- [repoUrl, gitRef] = parseGitHubSourceUri(sourceUri);
+ [ownerName, repositoryName, gitRef] = ...
+ matbox.setup.internal.github.parseRepositoryURL(sourceUri);
+ repoUrl = "https://github.com/" + ownerName + "/" + repositoryName;
repoTargetFolder = matbox.setup.internal.installGithubRepository( ...
repoUrl, ...
gitRef, ...
@@ -109,13 +111,3 @@
version, splitUri{2}, strjoin(" - " + string(packageInfo.versions), newline));
end
end
-
-function [repoUrl, gitRef] = parseGitHubSourceUri(repoUrl)
-% parseGitHubSourceUri - Extract the git reference (branch, tag or commit) if present
- gitRef = string(missing);
- if contains(repoUrl, '@')
- splitUrl = strsplit(repoUrl, '@');
- repoUrl = splitUrl{1};
- gitRef = splitUrl{2};
- end
-end
diff --git a/tools/tests/+matboxtools/+unittest/GithubReferenceTest.m b/tools/tests/+matboxtools/+unittest/GithubReferenceTest.m
index 7601e42..69a1a5c 100644
--- a/tools/tests/+matboxtools/+unittest/GithubReferenceTest.m
+++ b/tools/tests/+matboxtools/+unittest/GithubReferenceTest.m
@@ -15,6 +15,9 @@
% exercise the rename to "-" done by installGithubRepository.
TagName = "v0.1.0"
CommitSha = "4e8eaa6"
+ % A branch name in the hierarchical form, used only by parsing tests,
+ % so it need not exist in the test repository.
+ HierarchicalBranchName = "release/1.0"
end
methods (TestMethodSetup)
@@ -49,6 +52,41 @@ function testParseUrlWithCommitReference(testCase)
testCase.verifyEqual(gitRef, testCase.CommitSha)
end
+ function testParseUrlWithSlashInReference(testCase)
+ [~, repositoryName, gitRef] = matbox.setup.internal.github.parseRepositoryURL( ...
+ testCase.RepositoryUrl + "@" + testCase.HierarchicalBranchName);
+ testCase.verifyEqual(repositoryName, testCase.RepositoryName)
+ testCase.verifyEqual(gitRef, testCase.HierarchicalBranchName)
+ end
+
+ function testParseUrlWithTrailingSlashAfterReference(testCase)
+ [~, ~, gitRef] = matbox.setup.internal.github.parseRepositoryURL( ...
+ testCase.RepositoryUrl + "@" + testCase.HierarchicalBranchName + "/");
+ testCase.verifyEqual(gitRef, testCase.HierarchicalBranchName)
+ end
+
+ function testParseUrlWithAtSignInReference(testCase)
+ % Only the first "@" separates the repository name from the
+ % reference; git allows "@" inside a reference name.
+ [~, repositoryName, gitRef] = matbox.setup.internal.github.parseRepositoryURL( ...
+ testCase.RepositoryUrl + "@fix@2");
+ testCase.verifyEqual(repositoryName, testCase.RepositoryName)
+ testCase.verifyEqual(gitRef, "fix@2")
+ end
+
+ function testParseUrlWithPathAfterRepositoryNameErrors(testCase)
+ testCase.verifyError(@() matbox.setup.internal.github.parseRepositoryURL( ...
+ testCase.RepositoryUrl + "/tree/main"), "MATBOX:GitHub:InvalidRepositoryURL")
+ end
+
+ function testInstallWithPathAfterRepositoryNameErrors(testCase)
+ % The URI is parsed before any request to GitHub, so this test
+ % needs no network access.
+ testCase.verifyError(@() matbox.setup.installFromSourceUri( ...
+ testCase.RepositoryUrl + "/tree/main", "InstallationLocation", pwd), ...
+ "MATBOX:GitHub:InvalidRepositoryURL")
+ end
+
function testLookForRepositoryFindsTagFolder(testCase)
folderPath = testCase.createRepositoryFolder( ...
testCase.RepositoryName + "-" + testCase.TagName);