@hanseltime/template-repo-sync 1.1.0 → 1.3.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 +14 -0
- package/README.md +14 -0
- package/docs/merge-plugins/CURRENT_PLUGINS.md +168 -0
- package/docs/merge-plugins/DEVELOPMENT.md +129 -0
- package/docs/merge-plugins/README.md +85 -0
- package/lib/cjs/checkout-drivers/git-checkout.d.ts +8 -0
- package/lib/cjs/checkout-drivers/git-checkout.js +17 -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/plugins/json-merge.js +30 -7
- package/lib/cjs/template-sync.d.ts +13 -0
- package/lib/cjs/template-sync.js +47 -8
- package/lib/cjs/types.d.ts +1 -1
- package/lib/esm/checkout-drivers/git-checkout.js +17 -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/plugins/json-merge.js +30 -7
- package/lib/esm/template-sync.js +47 -8
- package/package.json +5 -8
- package/src/checkout-drivers/git-checkout.spec.ts +58 -0
- package/src/checkout-drivers/git-checkout.ts +22 -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/plugins/json-merge.spec.ts +86 -0
- package/src/plugins/json-merge.ts +9 -7
- package/src/template-sync.spec.ts +53 -2
- package/src/template-sync.ts +46 -9
- package/src/types.ts +1 -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.0](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v1.2.0...v1.3.0) (2024-06-09)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* support specifying the template branch ([54bad24](https://github.com/HanseltimeIndustries/template-repo-sync/commit/54bad24935bb8609617b8d30409aa8a47fc64139))
|
|
7
|
+
|
|
8
|
+
# [1.2.0](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v1.1.0...v1.2.0) (2024-03-11)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* adding comment-json support for merges ([c4393aa](https://github.com/HanseltimeIndustries/template-repo-sync/commit/c4393aaa6bf3bb985670f4377f2a370338450618))
|
|
14
|
+
|
|
1
15
|
# [1.1.0](https://github.com/HanseltimeIndustries/template-repo-sync/compare/v1.0.1...v1.1.0) (2024-03-04)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -13,6 +13,16 @@ best practice development patterns, then we naturally want to have a way to allo
|
|
|
13
13
|
changes, while also having control over things that may be specifically changed due to their need to support something beyond the
|
|
14
14
|
orgnaization standard.
|
|
15
15
|
|
|
16
|
+
- [Template Sync](#template-sync)
|
|
17
|
+
- [How to use this](#how-to-use-this)
|
|
18
|
+
- [Config file](#config-file)
|
|
19
|
+
- [File format](#file-format)
|
|
20
|
+
- [Example 1 - Using a custom plugin](#example-1---using-a-custom-plugin)
|
|
21
|
+
- [Example 2 - Using a custom plugin for some paths](#example-2---using-a-custom-plugin-for-some-paths)
|
|
22
|
+
- [From SHA/Tag directive](#from-shatag-directive)
|
|
23
|
+
- [Programmatic API](#programmatic-api)
|
|
24
|
+
<!-- Created with Markdown All In One VsCode Entension, rerun to update -->
|
|
25
|
+
|
|
16
26
|
# How to use this
|
|
17
27
|
|
|
18
28
|
This repository publishes a github action that can be used for ease of use in github. It also provides itself as an npm package
|
|
@@ -32,6 +42,10 @@ This library will always respect the overrides of the local template sync file i
|
|
|
32
42
|
templates and their repos, will also provide a list of all files whose template sync behavior was either ignored or overridden by the local
|
|
33
43
|
file. In this way, teams should be able to track (with a little extra CI/CD wiring) or at the very least, explicitly acknowledge a deviation.
|
|
34
44
|
|
|
45
|
+
All config files have the ability to write custom merge plugins either in repo or published as packages for larger use.
|
|
46
|
+
|
|
47
|
+
Please see the [plugins](./docs/merge-plugins/) documentation for more information beyond the simple examples in this readme.
|
|
48
|
+
|
|
35
49
|
### File format
|
|
36
50
|
|
|
37
51
|
```typescript
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Current Merge Plugins
|
|
2
|
+
|
|
3
|
+
This document lists all merge plugins that are provided provided as defaults for certain file extensions.
|
|
4
|
+
These should be found in the [plugins folder](src/plugins)
|
|
5
|
+
|
|
6
|
+
- [Current Merge Plugins](#current-merge-plugins)
|
|
7
|
+
- [Json Merge Plugin](#json-merge-plugin)
|
|
8
|
+
- [Configuration Options:](#configuration-options)
|
|
9
|
+
- [Simple merge spec](#simple-merge-spec)
|
|
10
|
+
- [JsonPath config](#jsonpath-config)
|
|
11
|
+
- [Example](#example)
|
|
12
|
+
- [About Comments](#about-comments)
|
|
13
|
+
|
|
14
|
+
<!-- Created with Markdown All In One VsCode Extension -->
|
|
15
|
+
|
|
16
|
+
## Json Merge Plugin
|
|
17
|
+
|
|
18
|
+
The json merge plugin allows you to configure jsonpath based merges on any .json file.
|
|
19
|
+
|
|
20
|
+
## Configuration Options:
|
|
21
|
+
|
|
22
|
+
### Simple merge spec
|
|
23
|
+
|
|
24
|
+
At it's simplest, you can take advantage of lodash merge behavior by just specifying one of:
|
|
25
|
+
|
|
26
|
+
- overwrite - the template completely overwrites the file
|
|
27
|
+
- merge-template - keys are merged together with the template overwriting any matching keys on local file
|
|
28
|
+
- merge-current - keys are merged together with the local file keeping any keys that match in the template
|
|
29
|
+
|
|
30
|
+
Example config:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"merge": {
|
|
35
|
+
".json": {
|
|
36
|
+
"rules": [
|
|
37
|
+
{
|
|
38
|
+
"glob": "metadata.json",
|
|
39
|
+
"options": "merge-template"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"glob": "template-lock.json",
|
|
43
|
+
"options": "overwrite"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"glob": "package.json",
|
|
47
|
+
"options": "merge-current"
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### JsonPath config
|
|
56
|
+
|
|
57
|
+
If you would like further control over what merges within a .json file, you can actually specify, via way of jsonpath operators,
|
|
58
|
+
the level of merge per field.
|
|
59
|
+
|
|
60
|
+
A few rules:
|
|
61
|
+
|
|
62
|
+
- Once you have provided jsonpath options, only the json path options (or new fields if the option is enabled) will be merged
|
|
63
|
+
- jsonpaths are run from first to last. This means you can layer merges.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
interface Options {
|
|
67
|
+
/**
|
|
68
|
+
* If set to true, this means we won't add new properties from the template
|
|
69
|
+
*/
|
|
70
|
+
ignoreNewProperties?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* If set to true, overwrite will apply undefined values as deleted for the jsonpaths
|
|
73
|
+
* or for values that are supposed to be merged on top of other values
|
|
74
|
+
*/
|
|
75
|
+
missingIsDelete?: boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Note, if multiple json paths match a rule, we pick the first one in the list that matches
|
|
78
|
+
*/
|
|
79
|
+
paths: /**
|
|
80
|
+
* We only override jsonpaths. Anything not specified is kept the same.
|
|
81
|
+
*/
|
|
82
|
+
[jsonPath: `$.${string}`, options: BaseJsonMergeOptions][];
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Example
|
|
87
|
+
|
|
88
|
+
This
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"merge": {
|
|
93
|
+
".json": {
|
|
94
|
+
"rules": [
|
|
95
|
+
{
|
|
96
|
+
"glob": "metadata.json",
|
|
97
|
+
"options": {
|
|
98
|
+
"ignoreNewProperties": false,
|
|
99
|
+
"missingIsDelete:": true,
|
|
100
|
+
"paths": [
|
|
101
|
+
["$.path", "template-merge"] // if we delete path in the template, it will delete the path
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"glob": "template-lock.json",
|
|
107
|
+
"options": "overwrite"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"glob": "package.json",
|
|
111
|
+
"options": {
|
|
112
|
+
"ignoreNewProperties": false,
|
|
113
|
+
"paths": [
|
|
114
|
+
["$.scripts.*", "template-merge"],
|
|
115
|
+
["$.scripts.specific-script", "template-currrent"] // We end up keeping the current template
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### About Comments
|
|
126
|
+
|
|
127
|
+
There are numerous json files that support comments now; tsconfig.json is a prime example of this. In order to support this,
|
|
128
|
+
this library makes use of comment-json for parsing and stringifying. This means that, minimally, you will not run into errors
|
|
129
|
+
when merging commented json files (and that you can comment on your templatesync config files).
|
|
130
|
+
|
|
131
|
+
One thing to note however, is that only include comments from the template if they are inside of an object that is being merged.
|
|
132
|
+
This is because this plugin has not yet defined a good configuration for merging comments. If you run into a pertinent need for this,
|
|
133
|
+
please feel free to open an issue and potentially contribute a fix in a PR.
|
|
134
|
+
|
|
135
|
+
Example of comment merging:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
// In template
|
|
139
|
+
|
|
140
|
+
{
|
|
141
|
+
// I have a comment here
|
|
142
|
+
"newField": 44,
|
|
143
|
+
"overridingField": {
|
|
144
|
+
// Comment in here
|
|
145
|
+
"value": "v",
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// In the extending repo
|
|
150
|
+
{
|
|
151
|
+
// My custom comment
|
|
152
|
+
"newField": 88,
|
|
153
|
+
"overridingField": 66
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// After merging with template-merge
|
|
157
|
+
{
|
|
158
|
+
// My custom comment
|
|
159
|
+
"newField": 44,
|
|
160
|
+
"overridingField": {
|
|
161
|
+
// Comment in here
|
|
162
|
+
"value": "v",
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
From the above example, you can see that we specifically keep as many of the comments as possible from the extending repo
|
|
168
|
+
and only the comment that was fully nested inside new value that we were adding was kept.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Plugin Development
|
|
2
|
+
|
|
3
|
+
- [Plugin Development](#plugin-development)
|
|
4
|
+
- [Example: hello \<world\>](#example-hello-world) - [Example one: local file](#example-one-local-file) - [Example two: npm package](#example-two-npm-package)
|
|
5
|
+
<!-- Created with Markdown All In One VsCode Extension -->
|
|
6
|
+
|
|
7
|
+
The templatesync.json and templatesync.local.config files make use of a `"merge"` property where you can
|
|
8
|
+
customize the baseline behavior of ignore or overwrite from template.
|
|
9
|
+
|
|
10
|
+
This library provides a set of typescript types for you to create additional plugins.
|
|
11
|
+
|
|
12
|
+
At the core of it, you need to have a file or npm package that exposes the interface:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
export interface MergePlugin<PluginOptions> {
|
|
16
|
+
/**
|
|
17
|
+
* This method will be called when a file from the template and it's analog in the downstream repo
|
|
18
|
+
* have some differences. The plugin must perform the merge and return the appropriate file contents
|
|
19
|
+
* as a string
|
|
20
|
+
*
|
|
21
|
+
* TODO: we may create a V2 plugin that could deal with large files and not pass around strings in memory,
|
|
22
|
+
* but for now, this is the current implementation
|
|
23
|
+
*
|
|
24
|
+
* @param current - The downstream repo's current file contents
|
|
25
|
+
* @param fromTemplateRepo - the current
|
|
26
|
+
* @param context - an object defining the context around the file and the specific options
|
|
27
|
+
*/
|
|
28
|
+
merge(
|
|
29
|
+
current: string,
|
|
30
|
+
fromTemplateRepo: string,
|
|
31
|
+
context: MergeContext<PluginOptions>,
|
|
32
|
+
): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Given an options object for the merge, this validates the options object and returns error messages if there is anything wrong.
|
|
35
|
+
* @param options any json value that the user provided - must be validated against the expected options
|
|
36
|
+
*/
|
|
37
|
+
validate(options: unknown): string[] | undefined;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Example: hello \<world>
|
|
42
|
+
|
|
43
|
+
So let's say that we want to have a plugin that will take every file assigned to it, and just write hello {world} instead.
|
|
44
|
+
(Not much of a merge, but so it goes)
|
|
45
|
+
|
|
46
|
+
We will define and export an options object, validate function, and merge function
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// src/hello-plugin.ts
|
|
50
|
+
import { MergePlugin, MergeContext } from '@hanseltime/template-repo-sync'
|
|
51
|
+
|
|
52
|
+
export interface HelloOptions: {
|
|
53
|
+
/** the name of the world we're greeting */
|
|
54
|
+
world: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const validate: MergePlugin<HelloOptions>['validate'] = (options: unknown) => {
|
|
58
|
+
const errors: string[] = [];
|
|
59
|
+
// In our case, we have decided you HAVE to use an object
|
|
60
|
+
if (typeof options !== 'object') {
|
|
61
|
+
errors.push('must provide an object');
|
|
62
|
+
return errors;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// make sure there aren't extra keys
|
|
66
|
+
const { world, ...rest } = options;
|
|
67
|
+
const unknownKeys = Object.keys(rest);
|
|
68
|
+
if (unknownKeys.length > 0 ) {
|
|
69
|
+
errors.push(`Unexpected options keys: ${unknownKeys.join(' ')}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!world) {
|
|
73
|
+
errors.push(`Must provide a valid world value`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return errors
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const merge: MergePlugin<HelloOptions>['merge'] = async (current, fromTemplateRepo, options: HelloOptions) => {
|
|
80
|
+
// Note, we don't use the current with this simple plugin, but we would use the first 2 args normally
|
|
81
|
+
return `Hello ${options.world}`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
With all of that set up, as long as we have the package available to the pacakge manager running our script, we can use it:
|
|
87
|
+
|
|
88
|
+
### Example one: local file
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"merge": {
|
|
93
|
+
".txt": {
|
|
94
|
+
"plugin": "dist/hello-plugin.js", // Note, we make it point to the compiled .js so you will need to build and commit the file
|
|
95
|
+
"rules": [
|
|
96
|
+
{
|
|
97
|
+
"glob": "**/*",
|
|
98
|
+
"options": {
|
|
99
|
+
"world": "chad"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Example two: npm package
|
|
109
|
+
|
|
110
|
+
Let's assume that you published this as an npm package to @myscope/hello-merge. Once you have install the pacakge to the project, you can
|
|
111
|
+
simply reference the package (assuming that it exposes the required functions as it's index file).
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"merge": {
|
|
116
|
+
".txt": {
|
|
117
|
+
"plugin": "@myscope/hello-merge",
|
|
118
|
+
"rules": [
|
|
119
|
+
{
|
|
120
|
+
"glob": "**/*",
|
|
121
|
+
"options": {
|
|
122
|
+
"world": "chad"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Merge Plugins
|
|
2
|
+
|
|
3
|
+
The templatesync.json and templatesync.local.config files make use of a `"merge"` property where you can
|
|
4
|
+
customize the baseline behavior of just ignoring or overwriting from the template.
|
|
5
|
+
|
|
6
|
+
# Example Use Case
|
|
7
|
+
|
|
8
|
+
One example of this behavior is around an npm package.json. If you were making a template for a particular
|
|
9
|
+
set of boilerplate for an npm package, you would probably provide an example package.json like:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"name": "<fill in your package name",
|
|
14
|
+
"description": "<fill in your package description",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"publish": "our-artifact-script"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "^5.0.0",
|
|
22
|
+
"our-artifact-package": "^1.0.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
In this scenario, we expect the template user to declare their own `name` and `description`, and add their own
|
|
28
|
+
`scripts` and `devDependencies`. However, we are hoping to make sure that the publish method and and its
|
|
29
|
+
our-artifact-package are kept up-to-date on syncs. Because of this, we would make use of the default json merge
|
|
30
|
+
plugin that is built-in with this library:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"merge": {
|
|
35
|
+
".json": {
|
|
36
|
+
// This uses the default plugin for json merges
|
|
37
|
+
"rules": [
|
|
38
|
+
{
|
|
39
|
+
"glob": "package.json",
|
|
40
|
+
"options": {
|
|
41
|
+
"paths": [
|
|
42
|
+
["$.scripts.publish", "merge-template"], // Any changes to publish are so critical that we want them to sync
|
|
43
|
+
["$.devDependencies", "merge-template"] // Always ensure dev dependency versions for our scripts are updated
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
As a repo maintainer, we can be sure that when people sync from our repo in its current state, the publish script
|
|
54
|
+
and devDependency should be synced from package.json, without anything else!
|
|
55
|
+
|
|
56
|
+
## What if the repo extender is annoyed?
|
|
57
|
+
|
|
58
|
+
If the repo extender already made the decision to update the publish method for a good reason, they may find it
|
|
59
|
+
tedious to constantly get their publish script overwritten and then have to undo it. Due to the local
|
|
60
|
+
config file, they can specify their own merge configuration for the package.json.
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"merge": {
|
|
65
|
+
".json": {
|
|
66
|
+
// This uses the default plugin for json merges
|
|
67
|
+
"rules": [
|
|
68
|
+
{
|
|
69
|
+
"glob": "package.json",
|
|
70
|
+
"options": {
|
|
71
|
+
"paths": [
|
|
72
|
+
["$.devDependencies", "merge-template"] // Always ensure dev dependency versions for our scripts are updated
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This configuration will now override the template repo's merge and only allow devDependencies to be updated. We report
|
|
83
|
+
this override as part of the output of the sync call, and for things like our
|
|
84
|
+
[github action](https://github.com/HanseltimeIndustries/template-repo-sync-action), we format that output into the PR
|
|
85
|
+
that is opened up when performing a sync.
|
|
@@ -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,17 @@
|
|
|
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
|
+
(0, child_process_1.execSync)(`git fetch ${remoteName} ${branch}`, {
|
|
8
|
+
cwd: tmpDir,
|
|
9
|
+
env: process.env,
|
|
10
|
+
});
|
|
11
|
+
(0, child_process_1.execSync)(`git checkout -b ${branch} --track ${remoteName}/${branch}`, {
|
|
12
|
+
cwd: tmpDir,
|
|
13
|
+
env: process.env,
|
|
14
|
+
});
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
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 {};
|
|
@@ -1,13 +1,36 @@
|
|
|
1
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
27
|
};
|
|
5
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
29
|
exports.merge = exports.validate = void 0;
|
|
7
30
|
const lodash_merge_1 = __importDefault(require("lodash.merge"));
|
|
8
|
-
const lodash_clonedeep_1 = __importDefault(require("lodash.clonedeep"));
|
|
9
31
|
const jsonpath_1 = __importDefault(require("jsonpath"));
|
|
10
32
|
const infer_json_indent_1 = require("../formatting/infer-json-indent");
|
|
33
|
+
const commentJSON = __importStar(require("comment-json"));
|
|
11
34
|
function stringOptionError(value) {
|
|
12
35
|
if (value === "overwrite" ||
|
|
13
36
|
value === "merge-template" ||
|
|
@@ -78,18 +101,18 @@ async function merge(current, fromTemplateRepo, context) {
|
|
|
78
101
|
if (context.mergeArguments === "overwrite") {
|
|
79
102
|
return fromTemplateRepo;
|
|
80
103
|
}
|
|
81
|
-
const currentJson =
|
|
82
|
-
const fromTemplateJson =
|
|
104
|
+
const currentJson = commentJSON.parse(current);
|
|
105
|
+
const fromTemplateJson = commentJSON.parse(fromTemplateRepo);
|
|
83
106
|
if (context.mergeArguments === "merge-current") {
|
|
84
107
|
// Performs Lodash Merge with current as the override
|
|
85
|
-
return
|
|
108
|
+
return commentJSON.stringify((0, lodash_merge_1.default)(fromTemplateJson, currentJson), null, (0, infer_json_indent_1.inferJSONIndent)(current));
|
|
86
109
|
}
|
|
87
110
|
if (context.mergeArguments === "merge-template") {
|
|
88
111
|
// Performs Lodash Merge with current as the override
|
|
89
|
-
return
|
|
112
|
+
return commentJSON.stringify((0, lodash_merge_1.default)(currentJson, fromTemplateJson), null, (0, infer_json_indent_1.inferJSONIndent)(current));
|
|
90
113
|
}
|
|
91
114
|
const { missingIsDelete, ignoreNewProperties, paths } = context.mergeArguments;
|
|
92
|
-
const returnJson =
|
|
115
|
+
const returnJson = commentJSON.parse(current);
|
|
93
116
|
paths.forEach((p) => {
|
|
94
117
|
const [jPath, overrideType] = p;
|
|
95
118
|
const fromTemplatePaths = new Map();
|
|
@@ -143,7 +166,7 @@ async function merge(current, fromTemplateRepo, context) {
|
|
|
143
166
|
}
|
|
144
167
|
});
|
|
145
168
|
}
|
|
146
|
-
return
|
|
169
|
+
return commentJSON.stringify(returnJson, null, (0, infer_json_indent_1.inferJSONIndent)(current));
|
|
147
170
|
}
|
|
148
171
|
exports.merge = merge;
|
|
149
172
|
/**
|
|
@@ -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
|
/**
|