@hanseltime/template-repo-sync 1.0.1 → 1.1.0

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 CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.1.0](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v1.0.1...v1.1.0) (2024-03-04)
2
+
3
+
4
+ ### Features
5
+
6
+ * adding updateRef Option ([b553e65](https://github.com/HanseltimeIndustries/template-repo-sync/commit/b553e65ebce59777ccb80728d540c5734d41cab7))
7
+
1
8
  ## [1.0.1](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v1.0.0...v1.0.1) (2024-03-03)
2
9
 
3
10
 
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Template Sync Action
1
+ # Template Sync
2
2
 
3
3
  This npm package seeks to provide further granularity for people hoping to maintain a base template repo in github that
4
4
  is either imported or used as a literal template repo.
@@ -61,7 +61,30 @@ export interface Config {
61
61
  }
62
62
  ```
63
63
 
64
- ### Example 1
64
+ ### Example 1 - Using a custom plugin
65
+
66
+ In this scenario, you have installed a package that exposes the correct plugin interface for handling .ini file contents in
67
+ your implementing repository and set up this templatesync.local config file. Because of this, we can be assured that .ini files
68
+ in the local repo will be merged using the plugin we specified.
69
+
70
+ ```typescript
71
+ {
72
+
73
+ merge: {
74
+ ".ini": {
75
+ // If you are running under pacakge manager like yarn or npm,
76
+ // you can provide a valid pacakge or .js fil from your package to run
77
+ plugin: 'my-installed-npm-package',
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### Example 2 - Using a custom plugin for some paths
84
+
85
+ Just like in example 1, we have installed a plugin that exposes the correct plugin interface. Now though,
86
+ instead of applying that plugin to all '.ini' files, we are saying that, for this particular set of .ini
87
+ files, only the ones in custom-configs/ will use this merge operator.
65
88
 
66
89
  ```typescript
67
90
  {
@@ -70,7 +93,15 @@ export interface Config {
70
93
  ".ini": {
71
94
  // If you are running under pacakge manager like yarn or npm,
72
95
  // you can provide a valid pacakge or .js fil from your package to run
73
- driver: 'my-installed-npm-package',
96
+ plugin: 'my-installed-npm-package',
97
+ rule: [
98
+ {
99
+ glob: 'custom-configs/**',
100
+ options: {
101
+ myPluginParam: 'some parameter',
102
+ }
103
+ }
104
+ ]
74
105
  }
75
106
  }
76
107
  }
@@ -91,3 +122,35 @@ but it does mean that you will only see the changes to files that are newer than
91
122
 
92
123
  As always, you can remove the SHA/Tag from your local config and this will trigger a full sync in the event that you made the wrong
93
124
  assumption about merging templates correctly.
125
+
126
+ ```typescript
127
+ {
128
+ afterRef: 'git sha',
129
+ merge: {
130
+ ".ini": {
131
+ // If you are running under pacakge manager like yarn or npm,
132
+ // you can provide a valid pacakge or .js fil from your package to run
133
+ plugin: 'my-installed-npm-package',
134
+ rule: [
135
+ {
136
+ glob: 'custom-configs/**',
137
+ options: {
138
+ myPluginParam: 'some parameter',
139
+ }
140
+ }
141
+ ]
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ ## Programmatic API
148
+
149
+ The programmatic api for this package is centered around `templateSync`. The way the function is written, we try to
150
+ allow escape hatches for other styles of comparison in the form of "TemplateDriver" functions. As part of the current
151
+ implementation, all of these `drivers` represent git actions, but for the sake of expandability, may be set up to evaluate
152
+ things like helm chart renderings (at least that is the hope). If you write a driver, please consider contributing it back.
153
+
154
+ Please see [template-sync](./src/template-sync.ts) for the most up to date options.
155
+
156
+ TODO: we should update use case examples
@@ -82,11 +82,11 @@ async function merge(current, fromTemplateRepo, context) {
82
82
  const fromTemplateJson = JSON.parse(fromTemplateRepo);
83
83
  if (context.mergeArguments === "merge-current") {
84
84
  // Performs Lodash Merge with current as the override
85
- return JSON.stringify((0, lodash_merge_1.default)(fromTemplateJson, currentJson), null, 4);
85
+ return JSON.stringify((0, lodash_merge_1.default)(fromTemplateJson, currentJson), null, (0, infer_json_indent_1.inferJSONIndent)(current));
86
86
  }
87
87
  if (context.mergeArguments === "merge-template") {
88
88
  // Performs Lodash Merge with current as the override
89
- return JSON.stringify((0, lodash_merge_1.default)(currentJson, fromTemplateJson), null, 4);
89
+ return JSON.stringify((0, lodash_merge_1.default)(currentJson, fromTemplateJson), null, (0, infer_json_indent_1.inferJSONIndent)(current));
90
90
  }
91
91
  const { missingIsDelete, ignoreNewProperties, paths } = context.mergeArguments;
92
92
  const returnJson = (0, lodash_clonedeep_1.default)(currentJson);
@@ -0,0 +1,3 @@
1
+ export declare function gitCurrentRef(options: {
2
+ rootDir: string;
3
+ }): Promise<string>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gitCurrentRef = void 0;
4
+ const child_process_1 = require("child_process");
5
+ async function gitCurrentRef(options) {
6
+ return (0, child_process_1.execSync)(`git rev-parse HEAD`, {
7
+ cwd: options.rootDir,
8
+ })
9
+ .toString()
10
+ .trim();
11
+ }
12
+ exports.gitCurrentRef = gitCurrentRef;
@@ -0,0 +1 @@
1
+ export * from "./git-current-ref";
@@ -0,0 +1,17 @@
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-current-ref"), exports);
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A function that operates within the root dir and returns the
3
+ * current ref for its state. This abstracts the idea of getting
4
+ * the current commit sha, so that things like other custom templating
5
+ * frameworks can provide their own ref
6
+ */
7
+ export type TemplateRefDriverFn = (options: {
8
+ /** The root dir where we want to get the "current" ref */
9
+ rootDir: string;
10
+ }) => Promise<string>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,7 @@
1
1
  import { Change } from "diff";
2
2
  import { TemplateCloneDriverFn } from "./clone-drivers";
3
3
  import { TemplateDiffDriverFn } from "./diff-drivers";
4
+ import { TemplateRefDriverFn } from "./ref-drivers/types";
4
5
  export interface TemplateSyncOptions {
5
6
  repoUrl: string;
6
7
  /**
@@ -11,6 +12,11 @@ export interface TemplateSyncOptions {
11
12
  * The repo directory path that we are going to merge toward
12
13
  */
13
14
  repoDir: string;
15
+ /**
16
+ * If set to true, template sync will apply the current ref
17
+ * of the template repo to afterRef
18
+ */
19
+ updateAfterRef?: boolean;
14
20
  /**
15
21
  * Defaults to using git clone
16
22
  */
@@ -19,6 +25,10 @@ export interface TemplateSyncOptions {
19
25
  * Defaults to using git diff
20
26
  */
21
27
  diffDriver?: TemplateDiffDriverFn;
28
+ /**
29
+ * Defaults to using git current ref
30
+ */
31
+ currentRefDriver?: TemplateRefDriverFn;
22
32
  }
23
33
  export interface TemplateSyncReturn {
24
34
  /**
@@ -7,11 +7,14 @@ const match_1 = require("./match");
7
7
  const merge_file_1 = require("./merge-file");
8
8
  const git_clone_1 = require("./clone-drivers/git-clone");
9
9
  const diff_drivers_1 = require("./diff-drivers");
10
+ const ref_drivers_1 = require("./ref-drivers");
11
+ const formatting_1 = require("./formatting");
10
12
  exports.TEMPLATE_SYNC_CONFIG = "templatesync";
11
13
  exports.TEMPLATE_SYNC_LOCAL_CONFIG = "templatesync.local";
12
14
  async function templateSync(options) {
13
15
  const cloneDriver = options.cloneDriver ?? git_clone_1.gitClone;
14
16
  const diffDriver = options.diffDriver ?? diff_drivers_1.gitDiff;
17
+ const currentRefDriver = options.currentRefDriver ?? ref_drivers_1.gitCurrentRef;
15
18
  const tempCloneDir = await cloneDriver(options.tmpCloneDir, options.repoUrl);
16
19
  // Get the clone Config
17
20
  const cloneConfigPath = (0, path_1.join)(tempCloneDir, `${exports.TEMPLATE_SYNC_CONFIG}.json`);
@@ -48,6 +51,21 @@ async function templateSync(options) {
48
51
  localFileChanges[f] = result.localChanges;
49
52
  }
50
53
  }));
54
+ // apply after ref
55
+ if (options.updateAfterRef) {
56
+ const ref = await currentRefDriver({
57
+ rootDir: tempCloneDir,
58
+ });
59
+ if ((0, fs_1.existsSync)(localConfigPath)) {
60
+ const configStr = (0, fs_1.readFileSync)(localConfigPath).toString();
61
+ const config = JSON.parse(configStr);
62
+ config.afterRef = ref;
63
+ (0, fs_1.writeFileSync)(localConfigPath, JSON.stringify(config, null, (0, formatting_1.inferJSONIndent)(configStr)));
64
+ }
65
+ else {
66
+ (0, fs_1.writeFileSync)(localConfigPath, JSON.stringify({ afterRef: ref }, null, 4));
67
+ }
68
+ }
51
69
  return {
52
70
  localSkipFiles,
53
71
  localFileChanges,
@@ -82,11 +82,11 @@ async function merge(current, fromTemplateRepo, context) {
82
82
  const fromTemplateJson = JSON.parse(fromTemplateRepo);
83
83
  if (context.mergeArguments === "merge-current") {
84
84
  // Performs Lodash Merge with current as the override
85
- return JSON.stringify((0, lodash_merge_1.default)(fromTemplateJson, currentJson), null, 4);
85
+ return JSON.stringify((0, lodash_merge_1.default)(fromTemplateJson, currentJson), null, (0, infer_json_indent_1.inferJSONIndent)(current));
86
86
  }
87
87
  if (context.mergeArguments === "merge-template") {
88
88
  // Performs Lodash Merge with current as the override
89
- return JSON.stringify((0, lodash_merge_1.default)(currentJson, fromTemplateJson), null, 4);
89
+ return JSON.stringify((0, lodash_merge_1.default)(currentJson, fromTemplateJson), null, (0, infer_json_indent_1.inferJSONIndent)(current));
90
90
  }
91
91
  const { missingIsDelete, ignoreNewProperties, paths } = context.mergeArguments;
92
92
  const returnJson = (0, lodash_clonedeep_1.default)(currentJson);
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gitCurrentRef = void 0;
4
+ const child_process_1 = require("child_process");
5
+ async function gitCurrentRef(options) {
6
+ return (0, child_process_1.execSync)(`git rev-parse HEAD`, {
7
+ cwd: options.rootDir,
8
+ })
9
+ .toString()
10
+ .trim();
11
+ }
12
+ exports.gitCurrentRef = gitCurrentRef;
@@ -0,0 +1,17 @@
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-current-ref"), exports);
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -7,11 +7,14 @@ const match_1 = require("./match");
7
7
  const merge_file_1 = require("./merge-file");
8
8
  const git_clone_1 = require("./clone-drivers/git-clone");
9
9
  const diff_drivers_1 = require("./diff-drivers");
10
+ const ref_drivers_1 = require("./ref-drivers");
11
+ const formatting_1 = require("./formatting");
10
12
  exports.TEMPLATE_SYNC_CONFIG = "templatesync";
11
13
  exports.TEMPLATE_SYNC_LOCAL_CONFIG = "templatesync.local";
12
14
  async function templateSync(options) {
13
15
  const cloneDriver = options.cloneDriver ?? git_clone_1.gitClone;
14
16
  const diffDriver = options.diffDriver ?? diff_drivers_1.gitDiff;
17
+ const currentRefDriver = options.currentRefDriver ?? ref_drivers_1.gitCurrentRef;
15
18
  const tempCloneDir = await cloneDriver(options.tmpCloneDir, options.repoUrl);
16
19
  // Get the clone Config
17
20
  const cloneConfigPath = (0, path_1.join)(tempCloneDir, `${exports.TEMPLATE_SYNC_CONFIG}.json`);
@@ -48,6 +51,21 @@ async function templateSync(options) {
48
51
  localFileChanges[f] = result.localChanges;
49
52
  }
50
53
  }));
54
+ // apply after ref
55
+ if (options.updateAfterRef) {
56
+ const ref = await currentRefDriver({
57
+ rootDir: tempCloneDir,
58
+ });
59
+ if ((0, fs_1.existsSync)(localConfigPath)) {
60
+ const configStr = (0, fs_1.readFileSync)(localConfigPath).toString();
61
+ const config = JSON.parse(configStr);
62
+ config.afterRef = ref;
63
+ (0, fs_1.writeFileSync)(localConfigPath, JSON.stringify(config, null, (0, formatting_1.inferJSONIndent)(configStr)));
64
+ }
65
+ else {
66
+ (0, fs_1.writeFileSync)(localConfigPath, JSON.stringify({ afterRef: ref }, null, 4));
67
+ }
68
+ }
51
69
  return {
52
70
  localSkipFiles,
53
71
  localFileChanges,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hanseltime/template-repo-sync",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "An npm library that enables pluggable, customizable synchronization between template repos",
5
5
  "main": "lib/cjs/index.js",
6
6
  "types": "lib/cjs/index.d.ts",
@@ -93,12 +93,20 @@ export async function merge(
93
93
 
94
94
  if (context.mergeArguments === "merge-current") {
95
95
  // Performs Lodash Merge with current as the override
96
- return JSON.stringify(lodashMerge(fromTemplateJson, currentJson), null, 4);
96
+ return JSON.stringify(
97
+ lodashMerge(fromTemplateJson, currentJson),
98
+ null,
99
+ inferJSONIndent(current),
100
+ );
97
101
  }
98
102
 
99
103
  if (context.mergeArguments === "merge-template") {
100
104
  // Performs Lodash Merge with current as the override
101
- return JSON.stringify(lodashMerge(currentJson, fromTemplateJson), null, 4);
105
+ return JSON.stringify(
106
+ lodashMerge(currentJson, fromTemplateJson),
107
+ null,
108
+ inferJSONIndent(current),
109
+ );
102
110
  }
103
111
 
104
112
  const { missingIsDelete, ignoreNewProperties, paths } =
@@ -0,0 +1,12 @@
1
+ import { execSync } from "child_process";
2
+ import { gitCurrentRef } from "./git-current-ref";
3
+
4
+ describe("getCurrentRef", () => {
5
+ it("gets the current sha", async () => {
6
+ expect(
7
+ await gitCurrentRef({
8
+ rootDir: process.cwd(),
9
+ }),
10
+ ).toEqual(execSync("git rev-parse HEAD").toString().trim());
11
+ });
12
+ });
@@ -0,0 +1,9 @@
1
+ import { execSync } from "child_process";
2
+
3
+ export async function gitCurrentRef(options: { rootDir: string }) {
4
+ return execSync(`git rev-parse HEAD`, {
5
+ cwd: options.rootDir,
6
+ })
7
+ .toString()
8
+ .trim();
9
+ }
@@ -0,0 +1 @@
1
+ export * from "./git-current-ref";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A function that operates within the root dir and returns the
3
+ * current ref for its state. This abstracts the idea of getting
4
+ * the current commit sha, so that things like other custom templating
5
+ * frameworks can provide their own ref
6
+ */
7
+ export type TemplateRefDriverFn = (options: {
8
+ /** The root dir where we want to get the "current" ref */
9
+ rootDir: string;
10
+ }) => Promise<string>;
@@ -192,6 +192,127 @@ describe("templateSync", () => {
192
192
  await fileMatchDownstream(tmpDir, "plugins/custom-plugin.js");
193
193
  await fileMatchDownstream(tmpDir, "package.json");
194
194
  });
195
+ it("updates the local templatesync with the current ref if updateAfterRef is true", async () => {
196
+ // Remove the local sync overrides
197
+ await rm(join(tmpDir, "templatesync.local.json"));
198
+
199
+ const mockLocalConfig = {
200
+ afterRef: "dummySha",
201
+ ignore: [
202
+ // We don't have a need for this in here, but it's an example of keeping things cleaner for our custom plugins
203
+ "plugins/**",
204
+ ],
205
+ };
206
+
207
+ writeFileSync(
208
+ join(tmpDir, "templatesync.local.json"),
209
+ JSON.stringify(mockLocalConfig),
210
+ );
211
+
212
+ // We will only update the templated.ts
213
+ const mockDiffDriver = jest
214
+ .fn()
215
+ .mockImplementation(async () => ["src/templated.ts"]);
216
+ const mockCurrentRefDriver = jest
217
+ .fn()
218
+ .mockImplementation(async () => "newestSha");
219
+ const result = await templateSync({
220
+ tmpCloneDir: "stubbed-by-driver",
221
+ cloneDriver: dummyCloneDriver,
222
+ repoUrl: "not-important",
223
+ repoDir: tmpDir,
224
+ updateAfterRef: true,
225
+ diffDriver: mockDiffDriver,
226
+ currentRefDriver: mockCurrentRefDriver,
227
+ });
228
+
229
+ // since there was no override for this file, not changes from the local file
230
+ expect(result.localFileChanges).toEqual(expect.objectContaining({}));
231
+
232
+ // Verify the files
233
+ await fileMatchTemplate(tmpDir, "templatesync.json");
234
+ await fileMatchTemplate(tmpDir, "src/templated.ts");
235
+
236
+ // Expect the none of the diff files to work
237
+ await fileMatchDownstream(tmpDir, "src/index.ts");
238
+ await fileMatchDownstream(tmpDir, "plugins/custom-plugin.js");
239
+ await fileMatchDownstream(tmpDir, "package.json");
240
+
241
+ // Ensure we have updated the local template field
242
+ expect(
243
+ JSON.parse(
244
+ (await readFile(join(tmpDir, "templatesync.local.json"))).toString(),
245
+ ),
246
+ ).toEqual({
247
+ ...mockLocalConfig,
248
+ afterRef: "newestSha",
249
+ });
250
+ });
251
+ it("creates the local templatesync with the current ref if updateAfterRef is true and no local template exists", async () => {
252
+ // Remove the local sync overrides
253
+ await rm(join(tmpDir, "templatesync.local.json"));
254
+
255
+ // We will only update the templated.ts
256
+ const mockDiffDriver = jest
257
+ .fn()
258
+ .mockImplementation(async () => ["src/templated.ts"]);
259
+ const mockCurrentRefDriver = jest
260
+ .fn()
261
+ .mockImplementation(async () => "newestSha");
262
+ const result = await templateSync({
263
+ tmpCloneDir: "stubbed-by-driver",
264
+ cloneDriver: dummyCloneDriver,
265
+ repoUrl: "not-important",
266
+ repoDir: tmpDir,
267
+ updateAfterRef: true,
268
+ diffDriver: mockDiffDriver,
269
+ currentRefDriver: mockCurrentRefDriver,
270
+ });
271
+
272
+ // since there was no override for this file, not changes from the local file
273
+ expect(result.localFileChanges).toEqual(expect.objectContaining({}));
274
+
275
+ // Verify the files
276
+ await fileMatchTemplate(tmpDir, "templatesync.json");
277
+ await fileMatchTemplate(tmpDir, "src/templated.ts");
278
+ const packageJson = JSON.parse(
279
+ readFileSync(resolve(tmpDir, "package.json")).toString(),
280
+ );
281
+
282
+ expect(packageJson).toEqual({
283
+ name: "mypkg",
284
+ description: "my description",
285
+ dependencies: {
286
+ mypackage: "^1.2.0",
287
+ newpacakge: "^22.2.2",
288
+ package2: "3.22.1",
289
+ huh: "~1.0.0",
290
+ },
291
+ engines: {
292
+ node: ">=15",
293
+ },
294
+ scripts: {
295
+ build: "build",
296
+ test: "jest",
297
+ myscript: "somescript",
298
+ },
299
+ // By default we add new top-level fields
300
+ version: "new-version",
301
+ });
302
+
303
+ // Expect the none of the diff files to work
304
+ await fileMatchDownstream(tmpDir, "src/index.ts");
305
+ await fileMatchDownstream(tmpDir, "plugins/custom-plugin.js");
306
+
307
+ // Ensure we have updated the local template field
308
+ expect(
309
+ JSON.parse(
310
+ (await readFile(join(tmpDir, "templatesync.local.json"))).toString(),
311
+ ),
312
+ ).toEqual({
313
+ afterRef: "newestSha",
314
+ });
315
+ });
195
316
  });
196
317
 
197
318
  // helper
@@ -1,5 +1,5 @@
1
1
  import { join } from "path";
2
- import { existsSync, readFileSync } from "fs";
2
+ import { existsSync, readFileSync, writeFileSync } from "fs";
3
3
  import { getAllFilesInDir } from "./match";
4
4
  import { Config, LocalConfig } from "./types";
5
5
  import { mergeFile } from "./merge-file";
@@ -7,6 +7,9 @@ import { gitClone } from "./clone-drivers/git-clone";
7
7
  import { Change } from "diff";
8
8
  import { TemplateCloneDriverFn } from "./clone-drivers";
9
9
  import { TemplateDiffDriverFn, gitDiff } from "./diff-drivers";
10
+ import { gitCurrentRef } from "./ref-drivers";
11
+ import { TemplateRefDriverFn } from "./ref-drivers/types";
12
+ import { inferJSONIndent } from "./formatting";
10
13
 
11
14
  export interface TemplateSyncOptions {
12
15
  repoUrl: string;
@@ -21,6 +24,12 @@ export interface TemplateSyncOptions {
21
24
  */
22
25
  repoDir: string;
23
26
 
27
+ /**
28
+ * If set to true, template sync will apply the current ref
29
+ * of the template repo to afterRef
30
+ */
31
+ updateAfterRef?: boolean;
32
+
24
33
  /**
25
34
  * Defaults to using git clone
26
35
  */
@@ -30,6 +39,11 @@ export interface TemplateSyncOptions {
30
39
  * Defaults to using git diff
31
40
  */
32
41
  diffDriver?: TemplateDiffDriverFn;
42
+
43
+ /**
44
+ * Defaults to using git current ref
45
+ */
46
+ currentRefDriver?: TemplateRefDriverFn;
33
47
  }
34
48
 
35
49
  export interface TemplateSyncReturn {
@@ -56,6 +70,7 @@ export async function templateSync(
56
70
  ): Promise<TemplateSyncReturn> {
57
71
  const cloneDriver = options.cloneDriver ?? gitClone;
58
72
  const diffDriver = options.diffDriver ?? gitDiff;
73
+ const currentRefDriver = options.currentRefDriver ?? gitCurrentRef;
59
74
  const tempCloneDir = await cloneDriver(options.tmpCloneDir, options.repoUrl);
60
75
 
61
76
  // Get the clone Config
@@ -106,6 +121,28 @@ export async function templateSync(
106
121
  }),
107
122
  );
108
123
 
124
+ // apply after ref
125
+ if (options.updateAfterRef) {
126
+ const ref = await currentRefDriver({
127
+ rootDir: tempCloneDir,
128
+ });
129
+
130
+ if (existsSync(localConfigPath)) {
131
+ const configStr = readFileSync(localConfigPath).toString();
132
+ const config = JSON.parse(configStr) as LocalConfig;
133
+ config.afterRef = ref;
134
+ writeFileSync(
135
+ localConfigPath,
136
+ JSON.stringify(config, null, inferJSONIndent(configStr)),
137
+ );
138
+ } else {
139
+ writeFileSync(
140
+ localConfigPath,
141
+ JSON.stringify({ afterRef: ref }, null, 4),
142
+ );
143
+ }
144
+ }
145
+
109
146
  return {
110
147
  localSkipFiles,
111
148
  localFileChanges,