diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d2e7850d..8657f906d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features - Added support for GitHub enterprise projects with a `githubprivate.com` domain, #1743. +- Added support for GitLab repositories, #1728. ### Bug Fixes diff --git a/src/lib/converter/plugins/GitHubPlugin.ts b/src/lib/converter/plugins/SourceLinkPlugin.ts similarity index 90% rename from src/lib/converter/plugins/GitHubPlugin.ts rename to src/lib/converter/plugins/SourceLinkPlugin.ts index 3b3f35c71..2c7d074d5 100644 --- a/src/lib/converter/plugins/GitHubPlugin.ts +++ b/src/lib/converter/plugins/SourceLinkPlugin.ts @@ -49,17 +49,17 @@ export class Repository { project?: string; /** - * The hostname for this GitHub or Bitbucket project. + * The hostname for this GitHub/Bitbucket/.etc project. * * Defaults to: `github.com` (for normal, public GitHub instance projects) * - * Or the hostname for an enterprise version of GitHub, e.g. `github.acme.com` + * Can be the hostname for an enterprise version of GitHub, e.g. `github.acme.com` * (if found as a match in the list of git remotes). */ hostname = "github.com"; /** - * Whether this is a GitHub or Bitbucket repository. + * Whether this is a GitHub, Bitbucket, or other type of repository. */ type: RepositoryType = RepositoryType.GitHub; @@ -89,6 +89,10 @@ export class Repository { match = /(bitbucket.org)[:/]([^/]+)\/(.*)/.exec(repoLinks[i]); } + if (!match) { + match = /(gitlab.com)[:/]([^/]+)\/(.*)/.exec(repoLinks[i]); + } + if (match) { this.hostname = match[1]; this.user = match[2]; @@ -103,9 +107,13 @@ export class Repository { } } - if (this.hostname.includes("bitbucket.org")) + if (this.hostname.includes("bitbucket.org")) { this.type = RepositoryType.Bitbucket; - else this.type = RepositoryType.GitHub; + } else if (this.hostname.includes("gitlab.com")) { + this.type = RepositoryType.GitLab; + } else { + this.type = RepositoryType.GitHub; + } let out = git("-C", path, "ls-files"); if (out.status === 0) { @@ -149,10 +157,13 @@ export class Repository { `https://${this.hostname}`, this.user, this.project, - this.type === "github" ? "blob" : "src", + this.type === RepositoryType.GitLab ? "-" : undefined, + this.type === RepositoryType.Bitbucket ? "src" : "blob", this.branch, fileName.substr(this.path.length + 1), - ].join("/"); + ] + .filter((s) => !!s) + .join("/"); } /** @@ -190,6 +201,7 @@ export class Repository { switch (repositoryType) { default: case RepositoryType.GitHub: + case RepositoryType.GitLab: return "L" + lineNumber; case RepositoryType.Bitbucket: return "lines-" + lineNumber; @@ -201,8 +213,8 @@ export class Repository { * A handler that watches for repositories with GitHub origin and links * their source files to the related GitHub pages. */ -@Component({ name: "git-hub" }) -export class GitHubPlugin extends ConverterComponent { +@Component({ name: "source-link" }) +export class SourceLinkPlugin extends ConverterComponent { /** * List of known repositories. */ diff --git a/src/lib/converter/plugins/index.ts b/src/lib/converter/plugins/index.ts index d5f287b87..2bc97b75f 100644 --- a/src/lib/converter/plugins/index.ts +++ b/src/lib/converter/plugins/index.ts @@ -1,7 +1,7 @@ export { CategoryPlugin } from "./CategoryPlugin"; export { CommentPlugin } from "./CommentPlugin"; export { DecoratorPlugin } from "./DecoratorPlugin"; -export { GitHubPlugin } from "./GitHubPlugin"; +export { SourceLinkPlugin } from "./SourceLinkPlugin"; export { GroupPlugin } from "./GroupPlugin"; export { ImplementsPlugin } from "./ImplementsPlugin"; export { PackagePlugin } from "./PackagePlugin"; diff --git a/src/lib/models/sources/repository.ts b/src/lib/models/sources/repository.ts index b6d3837a6..20937846b 100644 --- a/src/lib/models/sources/repository.ts +++ b/src/lib/models/sources/repository.ts @@ -1,4 +1,5 @@ export enum RepositoryType { GitHub = "github", Bitbucket = "bitbucket", + GitLab = "gitlab", } diff --git a/src/test/GitHubPlugin.test.ts b/src/test/SourceLinkPlugin.test.ts similarity index 64% rename from src/test/GitHubPlugin.test.ts rename to src/test/SourceLinkPlugin.test.ts index 6616f1ef5..6834288d1 100644 --- a/src/test/GitHubPlugin.test.ts +++ b/src/test/SourceLinkPlugin.test.ts @@ -1,11 +1,11 @@ -import * as github from "../lib/converter/plugins/GitHubPlugin"; +import { Repository } from "../lib/converter/plugins/SourceLinkPlugin"; import { RepositoryType } from "../lib/models"; import { strictEqual as equal } from "assert"; describe("Repository", function () { describe("constructor", function () { it("defaults to github.com hostname", function () { - const repository = new github.Repository("", "", []); + const repository = new Repository("", "", []); equal(repository.hostname, "github.com"); equal(repository.type, RepositoryType.GitHub); @@ -14,7 +14,7 @@ describe("Repository", function () { it("handles a personal GitHub HTTPS URL", function () { const mockRemotes = ["https://github.com/joebloggs/foobar.git"]; - const repository = new github.Repository("", "", mockRemotes); + const repository = new Repository("", "", mockRemotes); equal(repository.hostname, "github.com"); equal(repository.user, "joebloggs"); @@ -25,7 +25,7 @@ describe("Repository", function () { it("handles an enterprise GitHub URL", function () { const mockRemotes = ["git@github.acme.com:joebloggs/foobar.git"]; - const repository = new github.Repository("", "", mockRemotes); + const repository = new Repository("", "", mockRemotes); equal(repository.hostname, "github.acme.com"); equal(repository.user, "joebloggs"); @@ -38,7 +38,7 @@ describe("Repository", function () { "ssh://org@bigcompany.githubprivate.com/joebloggs/foobar.git", ]; - const repository = new github.Repository("", "", mockRemotes); + const repository = new Repository("", "", mockRemotes); equal(repository.hostname, "bigcompany.githubprivate.com"); equal(repository.user, "joebloggs"); @@ -51,7 +51,7 @@ describe("Repository", function () { "https://joebloggs@bitbucket.org/joebloggs/foobar.git", ]; - const repository = new github.Repository("", "", mockRemotes); + const repository = new Repository("", "", mockRemotes); equal(repository.hostname, "bitbucket.org"); equal(repository.user, "joebloggs"); @@ -62,13 +62,35 @@ describe("Repository", function () { it("handles a Bitbucket SSH URL", function () { const mockRemotes = ["git@bitbucket.org:joebloggs/foobar.git"]; - const repository = new github.Repository("", "", mockRemotes); + const repository = new Repository("", "", mockRemotes); equal(repository.hostname, "bitbucket.org"); equal(repository.user, "joebloggs"); equal(repository.project, "foobar"); equal(repository.type, RepositoryType.Bitbucket); }); + + it("handles a GitLab HTTPS URL", function () { + const mockRemotes = ["https://gitlab.com/joebloggs/foobar.git"]; + + const repository = new Repository("", "", mockRemotes); + + equal(repository.hostname, "gitlab.com"); + equal(repository.user, "joebloggs"); + equal(repository.project, "foobar"); + equal(repository.type, RepositoryType.GitLab); + }); + + it("handles a GitLab SSH URL", function () { + const mockRemotes = ["git@gitlab.com:joebloggs/foobar.git"]; + + const repository = new Repository("", "", mockRemotes); + + equal(repository.hostname, "gitlab.com"); + equal(repository.user, "joebloggs"); + equal(repository.project, "foobar"); + equal(repository.type, RepositoryType.GitLab); + }); }); describe("getURL", () => { @@ -78,7 +100,7 @@ describe("Repository", function () { it("returns a GitHub URL", function () { const mockRemotes = ["https://github.com/joebloggs/foobar.git"]; - const repository = new github.Repository( + const repository = new Repository( repositoryPath, "main", mockRemotes @@ -96,7 +118,7 @@ describe("Repository", function () { "https://joebloggs@bitbucket.org/joebloggs/foobar.git", ]; - const repository = new github.Repository( + const repository = new Repository( repositoryPath, "main", mockRemotes @@ -108,5 +130,21 @@ describe("Repository", function () { "https://bitbucket.org/joebloggs/foobar/src/main/src/index.ts" ); }); + + it("returns a GitLab URL", function () { + const mockRemotes = ["https://gitlab.com/joebloggs/foobar.git"]; + + const repository = new Repository( + repositoryPath, + "main", + mockRemotes + ); + repository.files = [filePath]; + + equal( + repository.getURL(filePath), + "https://gitlab.com/joebloggs/foobar/-/blob/main/src/index.ts" + ); + }); }); });