Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/core/github.ts
3557 views
1
/*
2
* github.ts
3
*
4
* Copyright (C) 2021-2023 Posit Software, PBC
5
*/
6
7
import { which } from "./path.ts";
8
import { execProcess } from "./process.ts";
9
10
import { join } from "../deno_ral/path.ts";
11
import { existsSync } from "../deno_ral/fs.ts";
12
import { isHttpUrl } from "./url.ts";
13
import { GitHubContext } from "./github-types.ts";
14
import { gitBranchExists } from "./git.ts";
15
16
export async function gitHubContext(dir: string) {
17
// establish dir
18
const context: GitHubContext = {
19
git: false,
20
repo: false,
21
};
22
23
// check for git
24
context.git = !!(await which("git"));
25
26
// check for a repo in this directory
27
if (context.git) {
28
context.repo = (await execProcess({
29
cmd: "git",
30
args: ["rev-parse"],
31
cwd: dir,
32
stdout: "piped",
33
stderr: "piped",
34
})).success;
35
36
// check for an origin remote
37
if (context.repo) {
38
const result = await execProcess({
39
cmd: "git",
40
args: ["config", "--get", "remote.origin.url"],
41
cwd: dir,
42
stdout: "piped",
43
stderr: "piped",
44
});
45
if (result.success) {
46
context.originUrl = result.stdout?.trim();
47
48
// check for a gh-pages branch
49
const ghPagesRemote = await execProcess({
50
cmd: "git",
51
args: [
52
"ls-remote",
53
"--quiet",
54
"--exit-code",
55
"origin",
56
"gh-pages",
57
],
58
stdout: "piped",
59
stderr: "piped",
60
});
61
62
context.ghPagesRemote = ghPagesRemote.success;
63
if (!ghPagesRemote.success) {
64
// when no gh-pages branch on remote, check local to avoid creation error
65
// as if local branch exists, we don't want to create a new one
66
// https://git-scm.com/docs/git-ls-remote#Documentation/git-ls-remote.txt---exit-code
67
if (ghPagesRemote.code === 2) {
68
context.ghPagesLocal = await gitBranchExists("gh-pages");
69
} else {
70
// if we go there, this means something is not right with the remote origin
71
throw new Error(
72
`There is an error while retrieving information from remote 'origin'.\n Git error: ${ghPagesRemote.stderr}. \n Git status code: ${ghPagesRemote.code}.`,
73
);
74
}
75
}
76
77
// determine siteUrl
78
context.siteUrl = siteUrl(
79
dir,
80
context.originUrl!,
81
);
82
83
const repo = repoInfo(context.originUrl!);
84
if (repo) {
85
context.repoUrl = repo.repoUrl;
86
context.organization = repo.organization;
87
context.repository = repo.repository;
88
}
89
}
90
}
91
}
92
93
return context;
94
}
95
96
const kGithubCom = "github.com";
97
const kGithubIo = "github.io";
98
99
const kGithubGitPattern = /^git@([^:]+):([^\/]+)\/(.+?)(?:\.git)?$/;
100
const kGithubHttpsPattern = /^https:\/\/([^\/]+)\/([^\/]+)\/(.+?)(?:\.git)?$/;
101
102
function repoInfo(originUrl: string) {
103
// pick apart origin url for github.com
104
const match = originUrl?.match(kGithubGitPattern) ||
105
originUrl?.match(kGithubHttpsPattern);
106
if (match && match.includes(kGithubCom)) {
107
return {
108
repoUrl: `https://${match[1]}/${match[2]}/${match[3]}/`,
109
organization: match[2],
110
repository: match[3],
111
};
112
}
113
}
114
115
function siteUrl(
116
dir: string,
117
originUrl: string,
118
) {
119
// check for CNAME file
120
const cname = join(dir, "CNAME");
121
if (existsSync(cname)) {
122
const url = Deno.readTextFileSync(cname).trim();
123
if (isHttpUrl(url)) {
124
return url;
125
} else {
126
return `https://${url}`;
127
}
128
} else {
129
// pick apart origin url for github.com
130
const match = originUrl?.match(kGithubGitPattern) ||
131
originUrl?.match(kGithubHttpsPattern);
132
133
if (match && match.includes(kGithubCom)) {
134
const server = match[1].replace(kGithubCom, kGithubIo);
135
const domain = `${match[2]}.${server}`;
136
// user's root site uses just the domain
137
if (domain === match[3]) {
138
return `https://${domain}/`;
139
} else {
140
return `https://${domain}/${match[3]}/`;
141
}
142
}
143
}
144
}
145
146