Skip to content

Commit 93a63dc

Browse files
author
meshkodiak[bot]
authored
Merge pull request #105 from meshcloud/feature/cli-add-integration-with-git-2
Add new git command to Unipipe CLI
2 parents 62a3fea + 054187a commit 93a63dc

File tree

5 files changed

+445
-6
lines changed

5 files changed

+445
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## vNext
44

55
### CLI
6-
- no changes
6+
- New command `unipipe git` runs Git pull/push commands resiliently. It takes care of retrying and rebasing if needed to make sure a push will be successful.
77

88
### OSB
99
- no changes

cli/unipipe/commands/git.test.ts

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
import { assertSpyCall, assertSpyCalls, Stub, stub, returnsNext } from "../dev_deps.ts";
2+
import { withTempDir } from "../test-util.ts";
3+
import { Repository } from "../repository.ts";
4+
import { commandPull, commandPush, GitOpts } from "./git.ts";
5+
6+
Deno.test(
7+
"can pull with fast forward with no local changes",
8+
async () =>
9+
await withTempDir(async (dir) => {
10+
11+
const stub = createMockedGit([
12+
successfulResult(),
13+
successfulResult(),
14+
successfulResult()
15+
]);
16+
17+
await commandPull(new Repository(dir));
18+
19+
assertSpyGit(dir, stub, 0, ["git", "add", "."]);
20+
assertSpyGit(dir, stub, 1, ["git", "diff-index", "--quiet", "HEAD", "--"])
21+
assertSpyGit(dir, stub, 2, ["git", "pull", "--ff-only"])
22+
assertSpyCalls(stub, 3);
23+
24+
stub.restore();
25+
})
26+
);
27+
28+
Deno.test(
29+
"can pull with fast forward with local changes",
30+
async () =>
31+
await withTempDir(async (dir) => {
32+
33+
const stub = createMockedGit([
34+
successfulResult(),
35+
failedResult(),
36+
successfulResult(),
37+
successfulResult(),
38+
successfulResult()
39+
]);
40+
41+
await commandPull(new Repository(dir));
42+
43+
assertSpyGit(dir, stub, 0, [ "git", "add", "." ]);
44+
assertSpyGit(dir, stub, 1, [ "git", "diff-index", "--quiet", "HEAD", "--" ]);
45+
assertSpyGit(dir, stub, 2, [ "git", "stash" ]);
46+
assertSpyGit(dir, stub, 3, [ "git", "pull", "--ff-only" ]);
47+
assertSpyGit(dir, stub, 4, [ "git", "stash", "pop" ]);
48+
assertSpyCalls(stub, 5);
49+
50+
stub.restore();
51+
})
52+
);
53+
54+
Deno.test(
55+
"can pull with rebase with no local changes",
56+
async () =>
57+
await withTempDir(async (dir) => {
58+
59+
const stub = createMockedGit([
60+
successfulResult(),
61+
successfulResult(),
62+
failedResult(),
63+
successfulResult(),
64+
successfulResult(),
65+
]);
66+
67+
await commandPull(new Repository(dir));
68+
69+
assertSpyGit(dir, stub, 0, [ "git", "add", "." ]);
70+
assertSpyGit(dir, stub, 1, [ "git", "diff-index", "--quiet", "HEAD", "--" ]);
71+
assertSpyGit(dir, stub, 2, [ "git", "pull", "--ff-only" ]);
72+
assertSpyGit(dir, stub, 3, [ "git", "pull", "--rebase" ]);
73+
assertSpyCalls(stub, 4);
74+
75+
stub.restore();
76+
})
77+
);
78+
79+
Deno.test(
80+
"can pull with rebase with local changes",
81+
async () =>
82+
await withTempDir(async (dir) => {
83+
84+
const stub = createMockedGit([
85+
successfulResult(),
86+
failedResult(),
87+
successfulResult(),
88+
failedResult(),
89+
successfulResult(),
90+
successfulResult(),
91+
successfulResult()
92+
]);
93+
94+
await commandPull(new Repository(dir));
95+
96+
assertSpyGit(dir, stub, 0, [ "git", "add", "." ]);
97+
assertSpyGit(dir, stub, 1, [ "git", "diff-index", "--quiet", "HEAD", "--" ]);
98+
assertSpyGit(dir, stub, 2, [ "git", "stash" ]);
99+
assertSpyGit(dir, stub, 3, [ "git", "pull", "--ff-only" ]);
100+
assertSpyGit(dir, stub, 4, [ "git", "pull", "--rebase" ]);
101+
assertSpyGit(dir, stub, 5, [ "git", "stash", "pop" ]);
102+
assertSpyCalls(stub, 6);
103+
104+
stub.restore();
105+
})
106+
);
107+
108+
Deno.test(
109+
"do not push if the repository is not initialized",
110+
async () =>
111+
await withTempDir(async (dir) => {
112+
113+
const stub = createMockedGit([
114+
failedResult()
115+
]);
116+
117+
await commandPush(new Repository(dir), {});
118+
119+
assertSpyGit(dir, stub, 0, [ "git", "add", "." ]);
120+
assertSpyCalls(stub, 1);
121+
122+
stub.restore();
123+
})
124+
);
125+
126+
Deno.test(
127+
"do not commit if there are no changes while pushing",
128+
async () =>
129+
await withTempDir(async (dir) => {
130+
131+
const stub = createMockedGit([
132+
successfulResult(),
133+
successfulResult()
134+
]);
135+
136+
await commandPush(new Repository(dir), {});
137+
138+
assertSpyGit(dir, stub, 0, [ "git", "add", "." ]);
139+
assertSpyGit(dir, stub, 1, [ "git", "diff-index", "--quiet", "HEAD", "--" ]);
140+
assertSpyCalls(stub, 2);
141+
142+
stub.restore();
143+
})
144+
);
145+
146+
Deno.test(
147+
"can commit and push changes without upstream conflicts",
148+
async () =>
149+
await withTempDir(async (dir) => {
150+
151+
const stub = createMockedGit([
152+
successfulResult(),
153+
failedResult(),
154+
successfulResult(),
155+
successfulResult()
156+
]);
157+
158+
await commandPush(new Repository(dir), {});
159+
160+
assertSpyGit(dir, stub, 0, [ "git", "add", "." ]);
161+
assertSpyGit(dir, stub, 1, [ "git", "diff-index", "--quiet", "HEAD", "--" ]);
162+
assertSpyGit(dir, stub, 2, [ "git", "commit", "-a", "-m", "Unipipe CLI: Commit changes", "--author", "Unipipe CLI <unipipe-cli@meshcloud.io>"]);
163+
assertSpyGit(dir, stub, 3, [ "git", "push" ]);
164+
assertSpyCalls(stub, 4);
165+
166+
stub.restore();
167+
})
168+
);
169+
170+
Deno.test(
171+
"can commit and push changes with upstream conflicts (fast-forward)",
172+
async () =>
173+
await withTempDir(async (dir) => {
174+
175+
const stub = createMockedGit([
176+
successfulResult(),
177+
failedResult(),
178+
successfulResult(),
179+
failedResult(),
180+
successfulResult(),
181+
successfulResult()
182+
]);
183+
184+
await commandPush(new Repository(dir), {});
185+
186+
assertSpyGit(dir, stub, 0, [ "git", "add", "." ]);
187+
assertSpyGit(dir, stub, 1, [ "git", "diff-index", "--quiet", "HEAD", "--" ]);
188+
assertSpyGit(dir, stub, 2, [ "git", "commit", "-a", "-m", "Unipipe CLI: Commit changes", "--author", "Unipipe CLI <unipipe-cli@meshcloud.io>"]);
189+
assertSpyGit(dir, stub, 3, [ "git", "push" ]);
190+
assertSpyGit(dir, stub, 4, [ "git", "pull", "--ff-only" ]);
191+
assertSpyGit(dir, stub, 5, [ "git", "push" ]);
192+
assertSpyCalls(stub, 6);
193+
194+
stub.restore();
195+
})
196+
);
197+
198+
Deno.test(
199+
"can commit and push changes with upstream conflicts (rebase)",
200+
async () =>
201+
await withTempDir(async (dir) => {
202+
203+
const stub = createMockedGit([
204+
successfulResult(),
205+
failedResult(),
206+
successfulResult(),
207+
failedResult(),
208+
failedResult(),
209+
successfulResult(),
210+
successfulResult()
211+
]);
212+
213+
await commandPush(new Repository(dir), {});
214+
215+
assertSpyGit(dir, stub, 0, [ "git", "add", "." ]);
216+
assertSpyGit(dir, stub, 1, [ "git", "diff-index", "--quiet", "HEAD", "--" ]);
217+
assertSpyGit(dir, stub, 2, [ "git", "commit", "-a", "-m", "Unipipe CLI: Commit changes", "--author", "Unipipe CLI <unipipe-cli@meshcloud.io>", ]);
218+
assertSpyGit(dir, stub, 3, [ "git", "push" ]);
219+
assertSpyGit(dir, stub, 4, [ "git", "pull", "--ff-only" ]);
220+
assertSpyGit(dir, stub, 5, [ "git", "pull", "--rebase" ]);
221+
assertSpyGit(dir, stub, 6, [ "git", "push" ]);
222+
assertSpyCalls(stub, 7);
223+
224+
stub.restore();
225+
})
226+
);
227+
228+
Deno.test(
229+
"can commit with custom author and commit message",
230+
async () =>
231+
await withTempDir(async (dir) => {
232+
233+
const stub = createMockedGit([
234+
successfulResult(),
235+
failedResult(),
236+
successfulResult(),
237+
successfulResult()
238+
]);
239+
240+
const opts: GitOpts = {
241+
name: "John Doe",
242+
email: "john@doe.loc",
243+
message: "Some changes"
244+
}
245+
246+
await commandPush(new Repository(dir), opts);
247+
248+
assertSpyGit(dir, stub, 0, [ "git", "add", "." ]);
249+
assertSpyGit(dir, stub, 1, [ "git", "diff-index", "--quiet", "HEAD", "--" ]);
250+
assertSpyGit(dir, stub, 2, [ "git", "commit", "-a", "-m", "Unipipe CLI: Some changes", "--author", "John Doe <john@doe.loc>"]);
251+
assertSpyGit(dir, stub, 3, [ "git", "push" ]);
252+
assertSpyCalls(stub, 4);
253+
254+
stub.restore();
255+
})
256+
);
257+
258+
function createMockedGit(mockedResults: unknown[]): Stub {
259+
return stub(
260+
Deno,
261+
"run",
262+
returnsNext(mockedResults)
263+
);
264+
}
265+
266+
function successfulResult() {
267+
return createMockedResult(true)
268+
}
269+
270+
function failedResult() {
271+
return createMockedResult(false)
272+
}
273+
274+
function createMockedResult(success: boolean) {
275+
return {
276+
status: () => {
277+
return {
278+
success: success,
279+
code: 0,
280+
signal: undefined
281+
}
282+
}
283+
};
284+
}
285+
286+
function assertSpyGit(dir: string, stub: Stub, callIndex: number, args: string[]) {
287+
assertSpyCall(stub, callIndex, {
288+
args: [{
289+
cmd: args,
290+
cwd: dir
291+
}]
292+
});
293+
}

0 commit comments

Comments
 (0)