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 @@ -tests: 83 passedtests83 passed +tests: 88 passedtests88 passed 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);