-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathgit.service.ts
More file actions
123 lines (98 loc) · 4.21 KB
/
Copy pathgit.service.ts
File metadata and controls
123 lines (98 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import simpleGit from "simple-git";
import * as fs from "fs";
import * as path from "path";
import * as os from "node:os";
import { Context } from "../../command/cli-context";
import { v4 as uuid } from "uuid";
import { SimpleGit } from "simple-git/dist/typings/simple-git";
import { GitProfile } from "../git-profile.interface";
import { FileConstants } from "../../utils/file.constants";
export class GitService {
private readonly gitProfile: GitProfile;
constructor(context: Context) {
this.gitProfile = context.gitProfile;
}
public async pullFromBranch(branch: string): Promise<string> {
this.validateGitProfileExistence();
const repoUrl = this.getRepoUrl();
const targetDir = path.join(os.tmpdir(), `content-cli-${uuid()}`);
fs.mkdirSync(targetDir, { recursive: true, mode: FileConstants.DEFAULT_FOLDER_PERMISSIONS });
const git = simpleGit();
try {
await git.clone(repoUrl, targetDir, ["--branch", branch, "--single-branch"]);
} catch (err) {
throw new Error(`Failed to pull from ${branch}: ${err}`);
}
this.cleanupGitDirectory(targetDir);
return targetDir;
}
public async pushToBranch(sourceDir: string, branch: string): Promise<void> {
this.validateGitProfileExistence();
const workingDir = path.join(os.tmpdir(), `content-cli-${uuid()}`);
fs.mkdirSync(workingDir, { recursive: true, mode: FileConstants.DEFAULT_FOLDER_PERMISSIONS });
const repoUrl = this.getRepoUrl();
const git = simpleGit();
try {
await git.clone(repoUrl, workingDir);
const repoGit = simpleGit({ baseDir: workingDir });
await repoGit.addConfig("user.name", this.gitProfile.username ?? "content-cli");
await repoGit.addConfig("user.email", `${this.gitProfile.username ?? "cli"}@users.noreply.github.com`);
await this.cleanupNonGitFiles(workingDir);
await this.checkoutOrCreateLocalBranch(repoGit, branch);
fs.cpSync(sourceDir, workingDir, { recursive: true });
await repoGit.add("./*");
const commitMessage = "Update from content-cli";
await repoGit.commit(commitMessage);
await repoGit.push(["--set-upstream", "origin", branch]);
} catch (err) {
throw new Error(`Failed to push to ${branch}: ${err}`);
} finally {
fs.rmSync(workingDir, { recursive: true, force: true });
}
}
private async cleanupNonGitFiles(workingDir: string): Promise<void> {
for (const entry of fs.readdirSync(workingDir)) {
if (entry !== ".git") {
fs.rmSync(path.join(workingDir, entry), { recursive: true, force: true });
}
}
}
private async checkoutOrCreateLocalBranch(git: SimpleGit, branch: string): Promise<void> {
const branches = await git.branch();
if (branches.current === branch) {
return;
}
if (branches.all.includes(branch)) {
await git.checkout(branch);
return;
}
if (branches.all.includes(`remotes/origin/${branch}`)) {
await git.checkout(["--track", `origin/${branch}`]);
return;
}
await git.checkoutLocalBranch(branch);
}
private getRepoUrl(): string {
const { authenticationType, repository, token, username } = this.gitProfile;
if (authenticationType === "SSH") {
return `git@github.com:${repository}.git`;
}
if (token) {
const safeUsername = encodeURIComponent(username ?? "git");
const safeToken = encodeURIComponent(token);
return `https://${safeUsername}:${safeToken}@github.com/${repository}.git`;
}
return `https://github.com/${repository}.git`;
}
private cleanupGitDirectory(repoDirectory: string): void {
const gitPath = path.join(repoDirectory, ".git");
if (fs.existsSync(gitPath)) {
fs.rmSync(gitPath, { recursive: true, force: true });
}
}
private validateGitProfileExistence(): void {
if (!this.gitProfile) {
throw new Error("No configured Git profile.");
}
}
}