@hanseltime/template-repo-sync 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/lib/cjs/checkout-drivers/git-checkout.d.ts +8 -0
- package/lib/cjs/checkout-drivers/git-checkout.js +29 -0
- package/lib/cjs/checkout-drivers/index.d.ts +2 -0
- package/lib/cjs/checkout-drivers/index.js +18 -0
- package/lib/cjs/checkout-drivers/types.d.ts +14 -0
- package/lib/cjs/checkout-drivers/types.js +2 -0
- package/lib/cjs/clone-drivers/git-clone.d.ts +2 -1
- package/lib/cjs/clone-drivers/git-clone.js +4 -1
- package/lib/cjs/clone-drivers/types.d.ts +12 -1
- package/lib/cjs/template-sync.d.ts +13 -0
- package/lib/cjs/template-sync.js +16 -1
- package/lib/esm/checkout-drivers/git-checkout.js +29 -0
- package/lib/esm/checkout-drivers/index.js +18 -0
- package/lib/esm/checkout-drivers/types.js +2 -0
- package/lib/esm/clone-drivers/git-clone.js +4 -1
- package/lib/esm/template-sync.js +16 -1
- package/package.json +1 -1
- package/src/checkout-drivers/git-checkout.spec.ts +69 -0
- package/src/checkout-drivers/git-checkout.ts +38 -0
- package/src/checkout-drivers/index.ts +2 -0
- package/src/checkout-drivers/types.ts +14 -0
- package/src/clone-drivers/git-clone.ts +6 -2
- package/src/clone-drivers/types.ts +13 -1
- package/src/template-sync.spec.ts +53 -2
- package/src/template-sync.ts +33 -1
- package/test-fixtures/testGitRepo/README.md +17 -0
- package/test-fixtures/testGitRepo/gitDir/COMMIT_EDITMSG +14 -0
- package/test-fixtures/testGitRepo/gitDir/HEAD +1 -0
- package/test-fixtures/testGitRepo/gitDir/config +7 -0
- package/test-fixtures/testGitRepo/gitDir/description +1 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/applypatch-msg.sample +15 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/commit-msg.sample +24 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/fsmonitor-watchman.sample +174 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/post-update.sample +8 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/pre-applypatch.sample +14 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/pre-commit.sample +49 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/pre-merge-commit.sample +13 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/pre-push.sample +53 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/pre-rebase.sample +169 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/pre-receive.sample +24 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/prepare-commit-msg.sample +42 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/push-to-checkout.sample +78 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/sendemail-validate.sample +77 -0
- package/test-fixtures/testGitRepo/gitDir/hooks/update.sample +128 -0
- package/test-fixtures/testGitRepo/gitDir/index +0 -0
- package/test-fixtures/testGitRepo/gitDir/info/exclude +6 -0
- package/test-fixtures/testGitRepo/gitDir/logs/HEAD +5 -0
- package/test-fixtures/testGitRepo/gitDir/logs/refs/heads/master +2 -0
- package/test-fixtures/testGitRepo/gitDir/logs/refs/heads/test-branch +2 -0
- package/test-fixtures/testGitRepo/gitDir/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 +0 -0
- package/test-fixtures/testGitRepo/gitDir/objects/6e/187bee8d02f39d0a1be8331dd8fe6a00c9b613 +0 -0
- package/test-fixtures/testGitRepo/gitDir/objects/73/185f1f3f5a6345e087d9f46dc4af77cc59449f +2 -0
- package/test-fixtures/testGitRepo/gitDir/objects/90/e7ea1089f939840e9649fd617584c1ad117159 +3 -0
- package/test-fixtures/testGitRepo/gitDir/objects/ba/99a452b9097047e9bfa8d5a08b3e452fcb364a +0 -0
- package/test-fixtures/testGitRepo/gitDir/objects/c5/8d400177cd5180b8566f82a127fafc5bf394b7 +0 -0
- package/test-fixtures/testGitRepo/gitDir/objects/e2/e668265db019249a7e8296d85f79000e3d71cf +0 -0
- package/test-fixtures/testGitRepo/gitDir/objects/e4/243e430c1ab69f3e344249f5b1859e90abc883 +1 -0
- package/test-fixtures/testGitRepo/gitDir/objects/ec/6c1cb72312605282ac61858cf1eaf1ea9f1d02 +0 -0
- package/test-fixtures/testGitRepo/gitDir/objects/fc/89cecc4ac0b5b075bd7d0ce9e09b2f50598b82 +0 -0
- package/test-fixtures/testGitRepo/gitDir/refs/heads/master +1 -0
- package/test-fixtures/testGitRepo/gitDir/refs/heads/test-branch +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [1.3.1](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v1.3.0...v1.3.1) (2024-06-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* skipping checkout default is specified ([ad7ba7d](https://github.com/HanseltimeIndustries/template-repo-sync/commit/ad7ba7dc795883f813f70ed2e9140ac6a9030845))
|
|
7
|
+
|
|
8
|
+
# [1.3.0](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v1.2.0...v1.3.0) (2024-06-09)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* support specifying the template branch ([54bad24](https://github.com/HanseltimeIndustries/template-repo-sync/commit/54bad24935bb8609617b8d30409aa8a47fc64139))
|
|
14
|
+
|
|
1
15
|
# [1.2.0](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v1.1.0...v1.2.0) (2024-03-11)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function gitCheckout(options: {
|
|
2
|
+
/** The directory where we cloned to */
|
|
3
|
+
tmpDir: string;
|
|
4
|
+
/** The name of the remote that git checks out against */
|
|
5
|
+
remoteName: string;
|
|
6
|
+
/** The branch to checkout against */
|
|
7
|
+
branch: string;
|
|
8
|
+
}): Promise<boolean>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.gitCheckout = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
async function gitCheckout(options) {
|
|
6
|
+
const { branch, remoteName, tmpDir } = options;
|
|
7
|
+
const remoteInfo = (0, child_process_1.execSync)(`git remote show ${remoteName}`, {
|
|
8
|
+
cwd: tmpDir,
|
|
9
|
+
env: process.env,
|
|
10
|
+
}).toString();
|
|
11
|
+
const defaultMatch = /HEAD branch:\s*([a-zA-Z0-9_-]+)/.exec(remoteInfo);
|
|
12
|
+
if (!defaultMatch || !defaultMatch[1]) {
|
|
13
|
+
throw new Error(`Could not determine default branch of cloned repo.\nAttempted to find in remote info:\n${remoteInfo} `);
|
|
14
|
+
}
|
|
15
|
+
const defaultBranch = defaultMatch[1];
|
|
16
|
+
// Skip this if the default branch is already pulled
|
|
17
|
+
if (defaultBranch !== branch) {
|
|
18
|
+
(0, child_process_1.execSync)(`git fetch ${remoteName} ${branch}`, {
|
|
19
|
+
cwd: tmpDir,
|
|
20
|
+
env: process.env,
|
|
21
|
+
});
|
|
22
|
+
(0, child_process_1.execSync)(`git checkout -b ${branch} --track ${remoteName}/${branch}`, {
|
|
23
|
+
cwd: tmpDir,
|
|
24
|
+
env: process.env,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
exports.gitCheckout = gitCheckout;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./git-checkout"), exports);
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A function that will checkout a given "branch" after the repo has been
|
|
3
|
+
* "cloned" by a clone drvier.
|
|
4
|
+
*
|
|
5
|
+
* @returns true if the checkout succeeded
|
|
6
|
+
*/
|
|
7
|
+
export type TemplateCheckoutDriverFn = (options: {
|
|
8
|
+
/** The directory where we cloned to */
|
|
9
|
+
tmpDir: string;
|
|
10
|
+
/** The name of the remote that git checks out against */
|
|
11
|
+
remoteName: string;
|
|
12
|
+
/** The branch to checkout against */
|
|
13
|
+
branch: string;
|
|
14
|
+
}) => Promise<boolean>;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import { CloneReturn } from "./types";
|
|
2
|
+
export declare function gitClone(tmpDir: string, repoUrl: string): Promise<CloneReturn>;
|
|
@@ -9,6 +9,9 @@ async function gitClone(tmpDir, repoUrl) {
|
|
|
9
9
|
cwd: tmpDir,
|
|
10
10
|
env: process.env,
|
|
11
11
|
});
|
|
12
|
-
return
|
|
12
|
+
return {
|
|
13
|
+
dir: (0, path_1.resolve)(tmpDir, CLONE_DIR),
|
|
14
|
+
remoteName: "origin",
|
|
15
|
+
};
|
|
13
16
|
}
|
|
14
17
|
exports.gitClone = gitClone;
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated - return the remote name in the CloneReturn
|
|
3
|
+
*/
|
|
4
|
+
type CloneDir = string;
|
|
5
|
+
export interface CloneReturn {
|
|
6
|
+
/** The directory where the clone occurred - absolute path to avoid working dir issues */
|
|
7
|
+
dir: string;
|
|
8
|
+
/** The name of the remote for the particular technology that you used - passed to checkout drivers */
|
|
9
|
+
remoteName: string;
|
|
10
|
+
}
|
|
1
11
|
/**
|
|
2
12
|
* A function that clones the template repo into the provided tmpDir
|
|
3
13
|
* and then returns the relative path within that directory to the template root
|
|
4
14
|
*/
|
|
5
|
-
export type TemplateCloneDriverFn = (tmpDir: string, repoUrl: string) => Promise<
|
|
15
|
+
export type TemplateCloneDriverFn = (tmpDir: string, repoUrl: string) => Promise<CloneReturn | CloneDir>;
|
|
16
|
+
export {};
|
|
@@ -2,8 +2,17 @@ import { Change } from "diff";
|
|
|
2
2
|
import { TemplateCloneDriverFn } from "./clone-drivers";
|
|
3
3
|
import { TemplateDiffDriverFn } from "./diff-drivers";
|
|
4
4
|
import { TemplateRefDriverFn } from "./ref-drivers/types";
|
|
5
|
+
import { TemplateCheckoutDriverFn } from "./checkout-drivers";
|
|
5
6
|
export interface TemplateSyncOptions {
|
|
7
|
+
/**
|
|
8
|
+
* This is the url of the template repo
|
|
9
|
+
*/
|
|
6
10
|
repoUrl: string;
|
|
11
|
+
/**
|
|
12
|
+
* Optional Branch to check out - if not specified, this checks out the
|
|
13
|
+
* default branch of the template repo
|
|
14
|
+
*/
|
|
15
|
+
branch?: string;
|
|
7
16
|
/**
|
|
8
17
|
* The directory for cloning our template repo into via the cloneDriver
|
|
9
18
|
*/
|
|
@@ -29,6 +38,10 @@ export interface TemplateSyncOptions {
|
|
|
29
38
|
* Defaults to using git current ref
|
|
30
39
|
*/
|
|
31
40
|
currentRefDriver?: TemplateRefDriverFn;
|
|
41
|
+
/**
|
|
42
|
+
* Defaults to using git checkout driver
|
|
43
|
+
*/
|
|
44
|
+
checkoutDriver?: TemplateCheckoutDriverFn;
|
|
32
45
|
}
|
|
33
46
|
export interface TemplateSyncReturn {
|
|
34
47
|
/**
|
package/lib/cjs/template-sync.js
CHANGED
|
@@ -33,13 +33,28 @@ const diff_drivers_1 = require("./diff-drivers");
|
|
|
33
33
|
const ref_drivers_1 = require("./ref-drivers");
|
|
34
34
|
const formatting_1 = require("./formatting");
|
|
35
35
|
const commentJSON = __importStar(require("comment-json"));
|
|
36
|
+
const checkout_drivers_1 = require("./checkout-drivers");
|
|
36
37
|
exports.TEMPLATE_SYNC_CONFIG = "templatesync";
|
|
37
38
|
exports.TEMPLATE_SYNC_LOCAL_CONFIG = "templatesync.local";
|
|
38
39
|
async function templateSync(options) {
|
|
39
40
|
const cloneDriver = options.cloneDriver ?? git_clone_1.gitClone;
|
|
40
41
|
const diffDriver = options.diffDriver ?? diff_drivers_1.gitDiff;
|
|
41
42
|
const currentRefDriver = options.currentRefDriver ?? ref_drivers_1.gitCurrentRef;
|
|
42
|
-
const
|
|
43
|
+
const checkoutDriver = options.checkoutDriver ?? checkout_drivers_1.gitCheckout;
|
|
44
|
+
const cloneReturn = await cloneDriver(options.tmpCloneDir, options.repoUrl);
|
|
45
|
+
const { dir: tempCloneDir, remoteName } = typeof cloneReturn === "string"
|
|
46
|
+
? {
|
|
47
|
+
dir: cloneReturn,
|
|
48
|
+
remoteName: "origin", // Default to this
|
|
49
|
+
}
|
|
50
|
+
: cloneReturn;
|
|
51
|
+
if (options.branch) {
|
|
52
|
+
await checkoutDriver({
|
|
53
|
+
tmpDir: tempCloneDir,
|
|
54
|
+
remoteName,
|
|
55
|
+
branch: options.branch,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
43
58
|
// Get the clone Config
|
|
44
59
|
const cloneConfigPath = (0, path_1.join)(tempCloneDir, `${exports.TEMPLATE_SYNC_CONFIG}.json`);
|
|
45
60
|
const templateSyncConfig = (0, fs_1.existsSync)(cloneConfigPath)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.gitCheckout = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
async function gitCheckout(options) {
|
|
6
|
+
const { branch, remoteName, tmpDir } = options;
|
|
7
|
+
const remoteInfo = (0, child_process_1.execSync)(`git remote show ${remoteName}`, {
|
|
8
|
+
cwd: tmpDir,
|
|
9
|
+
env: process.env,
|
|
10
|
+
}).toString();
|
|
11
|
+
const defaultMatch = /HEAD branch:\s*([a-zA-Z0-9_-]+)/.exec(remoteInfo);
|
|
12
|
+
if (!defaultMatch || !defaultMatch[1]) {
|
|
13
|
+
throw new Error(`Could not determine default branch of cloned repo.\nAttempted to find in remote info:\n${remoteInfo} `);
|
|
14
|
+
}
|
|
15
|
+
const defaultBranch = defaultMatch[1];
|
|
16
|
+
// Skip this if the default branch is already pulled
|
|
17
|
+
if (defaultBranch !== branch) {
|
|
18
|
+
(0, child_process_1.execSync)(`git fetch ${remoteName} ${branch}`, {
|
|
19
|
+
cwd: tmpDir,
|
|
20
|
+
env: process.env,
|
|
21
|
+
});
|
|
22
|
+
(0, child_process_1.execSync)(`git checkout -b ${branch} --track ${remoteName}/${branch}`, {
|
|
23
|
+
cwd: tmpDir,
|
|
24
|
+
env: process.env,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
exports.gitCheckout = gitCheckout;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./git-checkout"), exports);
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
@@ -9,6 +9,9 @@ async function gitClone(tmpDir, repoUrl) {
|
|
|
9
9
|
cwd: tmpDir,
|
|
10
10
|
env: process.env,
|
|
11
11
|
});
|
|
12
|
-
return
|
|
12
|
+
return {
|
|
13
|
+
dir: (0, path_1.resolve)(tmpDir, CLONE_DIR),
|
|
14
|
+
remoteName: "origin",
|
|
15
|
+
};
|
|
13
16
|
}
|
|
14
17
|
exports.gitClone = gitClone;
|
package/lib/esm/template-sync.js
CHANGED
|
@@ -33,13 +33,28 @@ const diff_drivers_1 = require("./diff-drivers");
|
|
|
33
33
|
const ref_drivers_1 = require("./ref-drivers");
|
|
34
34
|
const formatting_1 = require("./formatting");
|
|
35
35
|
const commentJSON = __importStar(require("comment-json"));
|
|
36
|
+
const checkout_drivers_1 = require("./checkout-drivers");
|
|
36
37
|
exports.TEMPLATE_SYNC_CONFIG = "templatesync";
|
|
37
38
|
exports.TEMPLATE_SYNC_LOCAL_CONFIG = "templatesync.local";
|
|
38
39
|
async function templateSync(options) {
|
|
39
40
|
const cloneDriver = options.cloneDriver ?? git_clone_1.gitClone;
|
|
40
41
|
const diffDriver = options.diffDriver ?? diff_drivers_1.gitDiff;
|
|
41
42
|
const currentRefDriver = options.currentRefDriver ?? ref_drivers_1.gitCurrentRef;
|
|
42
|
-
const
|
|
43
|
+
const checkoutDriver = options.checkoutDriver ?? checkout_drivers_1.gitCheckout;
|
|
44
|
+
const cloneReturn = await cloneDriver(options.tmpCloneDir, options.repoUrl);
|
|
45
|
+
const { dir: tempCloneDir, remoteName } = typeof cloneReturn === "string"
|
|
46
|
+
? {
|
|
47
|
+
dir: cloneReturn,
|
|
48
|
+
remoteName: "origin", // Default to this
|
|
49
|
+
}
|
|
50
|
+
: cloneReturn;
|
|
51
|
+
if (options.branch) {
|
|
52
|
+
await checkoutDriver({
|
|
53
|
+
tmpDir: tempCloneDir,
|
|
54
|
+
remoteName,
|
|
55
|
+
branch: options.branch,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
43
58
|
// Get the clone Config
|
|
44
59
|
const cloneConfigPath = (0, path_1.join)(tempCloneDir, `${exports.TEMPLATE_SYNC_CONFIG}.json`);
|
|
45
60
|
const templateSyncConfig = (0, fs_1.existsSync)(cloneConfigPath)
|
package/package.json
CHANGED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { join, resolve } from "path";
|
|
2
|
+
import { TEST_FIXTURES_DIR, tempDir } from "../test-utils";
|
|
3
|
+
import { copy, mkdtemp, moveSync } from "fs-extra";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { gitCheckout } from "./git-checkout";
|
|
6
|
+
import { readFileSync } from "fs";
|
|
7
|
+
|
|
8
|
+
const gitRemoteDir = resolve(TEST_FIXTURES_DIR, "testGitRepo");
|
|
9
|
+
|
|
10
|
+
describe("gitCheckout", () => {
|
|
11
|
+
let tmpRemoteDir: string;
|
|
12
|
+
let tmpRepoDir: string;
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
const baseTmpDir = await mkdtemp(tempDir());
|
|
15
|
+
// Make the remote dir
|
|
16
|
+
tmpRemoteDir = join(baseTmpDir, "remote");
|
|
17
|
+
await copy(gitRemoteDir, tmpRemoteDir);
|
|
18
|
+
// rehydrate the git repo
|
|
19
|
+
moveSync(join(tmpRemoteDir, "gitDir"), join(tmpRemoteDir, ".git"));
|
|
20
|
+
|
|
21
|
+
// Perform the clone we expect
|
|
22
|
+
execSync(`git clone ${tmpRemoteDir} testGitRepo`, {
|
|
23
|
+
cwd: baseTmpDir,
|
|
24
|
+
});
|
|
25
|
+
tmpRepoDir = join(baseTmpDir, "testGitRepo");
|
|
26
|
+
});
|
|
27
|
+
it("checks out the appropriate branch", async () => {
|
|
28
|
+
await gitCheckout({
|
|
29
|
+
tmpDir: tmpRepoDir,
|
|
30
|
+
remoteName: "origin",
|
|
31
|
+
branch: "test-branch", // We set this in the gitDir
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(readFileSync(join(tmpRepoDir, "README.md")).toString()).toContain(
|
|
35
|
+
"# This is the test-branch",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
it("throws an error if the branch is not found", async () => {
|
|
39
|
+
await expect(
|
|
40
|
+
async () =>
|
|
41
|
+
await gitCheckout({
|
|
42
|
+
tmpDir: tmpRepoDir,
|
|
43
|
+
remoteName: "origin",
|
|
44
|
+
branch: "test-branch-not-there", // This is not set in gitDir
|
|
45
|
+
}),
|
|
46
|
+
).rejects.toThrow();
|
|
47
|
+
});
|
|
48
|
+
it("throws an error if the origin is not found", async () => {
|
|
49
|
+
await expect(
|
|
50
|
+
async () =>
|
|
51
|
+
await gitCheckout({
|
|
52
|
+
tmpDir: tmpRepoDir,
|
|
53
|
+
remoteName: "originNA",
|
|
54
|
+
branch: "test-branch", // This is not set in gitDir
|
|
55
|
+
}),
|
|
56
|
+
).rejects.toThrow();
|
|
57
|
+
});
|
|
58
|
+
it("does not throw if the branch was the default", async () => {
|
|
59
|
+
await gitCheckout({
|
|
60
|
+
tmpDir: tmpRepoDir,
|
|
61
|
+
remoteName: "origin",
|
|
62
|
+
branch: "master", // We set this in the gitDir
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(readFileSync(join(tmpRepoDir, "README.md")).toString()).toContain(
|
|
66
|
+
"# This is the master branch",
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
|
|
3
|
+
export async function gitCheckout(options: {
|
|
4
|
+
/** The directory where we cloned to */
|
|
5
|
+
tmpDir: string;
|
|
6
|
+
/** The name of the remote that git checks out against */
|
|
7
|
+
remoteName: string;
|
|
8
|
+
/** The branch to checkout against */
|
|
9
|
+
branch: string;
|
|
10
|
+
}): Promise<boolean> {
|
|
11
|
+
const { branch, remoteName, tmpDir } = options;
|
|
12
|
+
|
|
13
|
+
const remoteInfo = execSync(`git remote show ${remoteName}`, {
|
|
14
|
+
cwd: tmpDir,
|
|
15
|
+
env: process.env,
|
|
16
|
+
}).toString();
|
|
17
|
+
const defaultMatch = /HEAD branch:\s*([a-zA-Z0-9_-]+)/.exec(remoteInfo);
|
|
18
|
+
if (!defaultMatch || !defaultMatch[1]) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Could not determine default branch of cloned repo.\nAttempted to find in remote info:\n${remoteInfo} `,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const defaultBranch = defaultMatch[1];
|
|
25
|
+
// Skip this if the default branch is already pulled
|
|
26
|
+
if (defaultBranch !== branch) {
|
|
27
|
+
execSync(`git fetch ${remoteName} ${branch}`, {
|
|
28
|
+
cwd: tmpDir,
|
|
29
|
+
env: process.env,
|
|
30
|
+
});
|
|
31
|
+
execSync(`git checkout -b ${branch} --track ${remoteName}/${branch}`, {
|
|
32
|
+
cwd: tmpDir,
|
|
33
|
+
env: process.env,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A function that will checkout a given "branch" after the repo has been
|
|
3
|
+
* "cloned" by a clone drvier.
|
|
4
|
+
*
|
|
5
|
+
* @returns true if the checkout succeeded
|
|
6
|
+
*/
|
|
7
|
+
export type TemplateCheckoutDriverFn = (options: {
|
|
8
|
+
/** The directory where we cloned to */
|
|
9
|
+
tmpDir: string;
|
|
10
|
+
/** The name of the remote that git checks out against */
|
|
11
|
+
remoteName: string;
|
|
12
|
+
/** The branch to checkout against */
|
|
13
|
+
branch: string;
|
|
14
|
+
}) => Promise<boolean>;
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { execSync } from "child_process";
|
|
2
2
|
import { resolve } from "path";
|
|
3
|
+
import { CloneReturn } from "./types";
|
|
3
4
|
|
|
4
5
|
const CLONE_DIR = "cloned_repo";
|
|
5
6
|
|
|
6
7
|
export async function gitClone(
|
|
7
8
|
tmpDir: string,
|
|
8
9
|
repoUrl: string,
|
|
9
|
-
): Promise<
|
|
10
|
+
): Promise<CloneReturn> {
|
|
10
11
|
execSync(`git clone ${repoUrl} ${CLONE_DIR}`, {
|
|
11
12
|
cwd: tmpDir,
|
|
12
13
|
env: process.env,
|
|
13
14
|
});
|
|
14
15
|
|
|
15
|
-
return
|
|
16
|
+
return {
|
|
17
|
+
dir: resolve(tmpDir, CLONE_DIR),
|
|
18
|
+
remoteName: "origin",
|
|
19
|
+
};
|
|
16
20
|
}
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated - return the remote name in the CloneReturn
|
|
3
|
+
*/
|
|
4
|
+
type CloneDir = string;
|
|
5
|
+
|
|
6
|
+
export interface CloneReturn {
|
|
7
|
+
/** The directory where the clone occurred - absolute path to avoid working dir issues */
|
|
8
|
+
dir: string;
|
|
9
|
+
/** The name of the remote for the particular technology that you used - passed to checkout drivers */
|
|
10
|
+
remoteName: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
/**
|
|
2
14
|
* A function that clones the template repo into the provided tmpDir
|
|
3
15
|
* and then returns the relative path within that directory to the template root
|
|
@@ -5,4 +17,4 @@
|
|
|
5
17
|
export type TemplateCloneDriverFn = (
|
|
6
18
|
tmpDir: string,
|
|
7
19
|
repoUrl: string,
|
|
8
|
-
) => Promise<
|
|
20
|
+
) => Promise<CloneReturn | CloneDir>;
|
|
@@ -7,14 +7,20 @@ import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
|
7
7
|
|
|
8
8
|
// Just return the test-fixture directory
|
|
9
9
|
const dummyCloneDriver = async () => {
|
|
10
|
-
return
|
|
10
|
+
return {
|
|
11
|
+
dir: resolve(TEST_FIXTURES_DIR, "template"),
|
|
12
|
+
remoteName: "ourRemote",
|
|
13
|
+
};
|
|
11
14
|
};
|
|
12
15
|
|
|
16
|
+
const dummyCheckoutDriver = jest.fn();
|
|
17
|
+
|
|
13
18
|
const downstreamDir = resolve(TEST_FIXTURES_DIR, "downstream");
|
|
14
19
|
|
|
15
20
|
describe("templateSync", () => {
|
|
16
21
|
let tmpDir: string;
|
|
17
22
|
beforeEach(async () => {
|
|
23
|
+
jest.resetAllMocks();
|
|
18
24
|
tmpDir = await mkdtemp(tempDir());
|
|
19
25
|
await copy(downstreamDir, tmpDir);
|
|
20
26
|
});
|
|
@@ -32,6 +38,35 @@ describe("templateSync", () => {
|
|
|
32
38
|
cloneDriver: dummyCloneDriver,
|
|
33
39
|
repoUrl: "not-important",
|
|
34
40
|
repoDir: emptyTmpDir,
|
|
41
|
+
checkoutDriver: dummyCheckoutDriver,
|
|
42
|
+
}),
|
|
43
|
+
).toEqual({
|
|
44
|
+
// Expect no changes since there was no local sync file
|
|
45
|
+
localSkipFiles: [],
|
|
46
|
+
localFileChanges: {},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Verify the files
|
|
50
|
+
await fileMatchTemplate(emptyTmpDir, "templatesync.json");
|
|
51
|
+
await fileMatchTemplate(emptyTmpDir, "package.json");
|
|
52
|
+
await fileMatchTemplate(emptyTmpDir, "src/templated.ts");
|
|
53
|
+
|
|
54
|
+
// Expect the ignores to not be a problem
|
|
55
|
+
expect(existsSync(resolve(emptyTmpDir, "src/index.ts"))).toBeFalsy();
|
|
56
|
+
expect(existsSync(resolve(emptyTmpDir, "src/custom-bin"))).toBeFalsy();
|
|
57
|
+
|
|
58
|
+
expect(dummyCheckoutDriver).not.toHaveBeenCalled();
|
|
59
|
+
});
|
|
60
|
+
it("Checks out the branch and then appropriately merges", async () => {
|
|
61
|
+
const emptyTmpDir = await mkdtemp(tempDir());
|
|
62
|
+
expect(
|
|
63
|
+
await templateSync({
|
|
64
|
+
tmpCloneDir: "stubbed-by-driver",
|
|
65
|
+
cloneDriver: dummyCloneDriver,
|
|
66
|
+
repoUrl: "not-important",
|
|
67
|
+
repoDir: emptyTmpDir,
|
|
68
|
+
branch: "new-template-test",
|
|
69
|
+
checkoutDriver: dummyCheckoutDriver,
|
|
35
70
|
}),
|
|
36
71
|
).toEqual({
|
|
37
72
|
// Expect no changes since there was no local sync file
|
|
@@ -47,6 +82,12 @@ describe("templateSync", () => {
|
|
|
47
82
|
// Expect the ignores to not be a problem
|
|
48
83
|
expect(existsSync(resolve(emptyTmpDir, "src/index.ts"))).toBeFalsy();
|
|
49
84
|
expect(existsSync(resolve(emptyTmpDir, "src/custom-bin"))).toBeFalsy();
|
|
85
|
+
const cloneInfo = await dummyCloneDriver();
|
|
86
|
+
expect(dummyCheckoutDriver).toHaveBeenCalledWith({
|
|
87
|
+
tmpDir: cloneInfo.dir,
|
|
88
|
+
remoteName: cloneInfo.remoteName,
|
|
89
|
+
branch: "new-template-test",
|
|
90
|
+
});
|
|
50
91
|
});
|
|
51
92
|
it("appropriately merges according to just the templatesync config file in an existing repo", async () => {
|
|
52
93
|
// Remove the local sync overrides
|
|
@@ -57,6 +98,7 @@ describe("templateSync", () => {
|
|
|
57
98
|
cloneDriver: dummyCloneDriver,
|
|
58
99
|
repoUrl: "not-important",
|
|
59
100
|
repoDir: tmpDir,
|
|
101
|
+
checkoutDriver: dummyCheckoutDriver,
|
|
60
102
|
});
|
|
61
103
|
|
|
62
104
|
expect(result.localSkipFiles).toEqual([]);
|
|
@@ -93,6 +135,7 @@ describe("templateSync", () => {
|
|
|
93
135
|
// Expect the ignores to not be a problem
|
|
94
136
|
await fileMatchDownstream(tmpDir, "src/index.ts");
|
|
95
137
|
await fileMatchDownstream(tmpDir, "plugins/custom-plugin.js");
|
|
138
|
+
expect(dummyCheckoutDriver).not.toHaveBeenCalled();
|
|
96
139
|
});
|
|
97
140
|
it("appropriately merges according to the templatesync config file and the local config in an existing repo", async () => {
|
|
98
141
|
// Remove the local sync overrides
|
|
@@ -127,6 +170,7 @@ describe("templateSync", () => {
|
|
|
127
170
|
cloneDriver: dummyCloneDriver,
|
|
128
171
|
repoUrl: "not-important",
|
|
129
172
|
repoDir: tmpDir,
|
|
173
|
+
checkoutDriver: dummyCheckoutDriver,
|
|
130
174
|
});
|
|
131
175
|
|
|
132
176
|
expect(result.localSkipFiles).toEqual(["src/templated.ts"]);
|
|
@@ -152,6 +196,7 @@ describe("templateSync", () => {
|
|
|
152
196
|
// Expect the ignores to not be a problem
|
|
153
197
|
await fileMatchDownstream(tmpDir, "src/index.ts");
|
|
154
198
|
await fileMatchDownstream(tmpDir, "plugins/custom-plugin.js");
|
|
199
|
+
expect(dummyCheckoutDriver).not.toHaveBeenCalled();
|
|
155
200
|
});
|
|
156
201
|
it("appropriately merges according to the templatesync config file and the local config in an existing repo with afterRef", async () => {
|
|
157
202
|
// Remove the local sync overrides
|
|
@@ -178,6 +223,7 @@ describe("templateSync", () => {
|
|
|
178
223
|
repoUrl: "not-important",
|
|
179
224
|
repoDir: tmpDir,
|
|
180
225
|
diffDriver: mockDiffDriver,
|
|
226
|
+
checkoutDriver: dummyCheckoutDriver,
|
|
181
227
|
});
|
|
182
228
|
|
|
183
229
|
// since there was no override for this file, not changes from the local file
|
|
@@ -191,6 +237,7 @@ describe("templateSync", () => {
|
|
|
191
237
|
await fileMatchDownstream(tmpDir, "src/index.ts");
|
|
192
238
|
await fileMatchDownstream(tmpDir, "plugins/custom-plugin.js");
|
|
193
239
|
await fileMatchDownstream(tmpDir, "package.json");
|
|
240
|
+
expect(dummyCheckoutDriver).not.toHaveBeenCalled();
|
|
194
241
|
});
|
|
195
242
|
it("updates the local templatesync with the current ref if updateAfterRef is true", async () => {
|
|
196
243
|
// Remove the local sync overrides
|
|
@@ -224,6 +271,7 @@ describe("templateSync", () => {
|
|
|
224
271
|
updateAfterRef: true,
|
|
225
272
|
diffDriver: mockDiffDriver,
|
|
226
273
|
currentRefDriver: mockCurrentRefDriver,
|
|
274
|
+
checkoutDriver: dummyCheckoutDriver,
|
|
227
275
|
});
|
|
228
276
|
|
|
229
277
|
// since there was no override for this file, not changes from the local file
|
|
@@ -247,6 +295,7 @@ describe("templateSync", () => {
|
|
|
247
295
|
...mockLocalConfig,
|
|
248
296
|
afterRef: "newestSha",
|
|
249
297
|
});
|
|
298
|
+
expect(dummyCheckoutDriver).not.toHaveBeenCalled();
|
|
250
299
|
});
|
|
251
300
|
it("creates the local templatesync with the current ref if updateAfterRef is true and no local template exists", async () => {
|
|
252
301
|
// Remove the local sync overrides
|
|
@@ -267,6 +316,7 @@ describe("templateSync", () => {
|
|
|
267
316
|
updateAfterRef: true,
|
|
268
317
|
diffDriver: mockDiffDriver,
|
|
269
318
|
currentRefDriver: mockCurrentRefDriver,
|
|
319
|
+
checkoutDriver: dummyCheckoutDriver,
|
|
270
320
|
});
|
|
271
321
|
|
|
272
322
|
// since there was no override for this file, not changes from the local file
|
|
@@ -313,6 +363,7 @@ describe("templateSync", () => {
|
|
|
313
363
|
afterRef: "newestSha",
|
|
314
364
|
});
|
|
315
365
|
});
|
|
366
|
+
expect(dummyCheckoutDriver).not.toHaveBeenCalled();
|
|
316
367
|
});
|
|
317
368
|
|
|
318
369
|
// helper
|
|
@@ -330,7 +381,7 @@ async function fileMatch(
|
|
|
330
381
|
source: "downstream" | "template",
|
|
331
382
|
) {
|
|
332
383
|
const dir =
|
|
333
|
-
source === "downstream" ? downstreamDir : await dummyCloneDriver();
|
|
384
|
+
source === "downstream" ? downstreamDir : (await dummyCloneDriver()).dir;
|
|
334
385
|
expect((await readFile(resolve(tmpDir, relPath))).toString()).toEqual(
|
|
335
386
|
(await readFile(resolve(dir, relPath))).toString(),
|
|
336
387
|
);
|