@opencrvs/create-countryconfig 2.1.0-rc.d1b9774 → 2.1.0-rc.d235243
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/index.js +164 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
const path = require('path')
|
|
15
15
|
const fs = require('fs')
|
|
16
|
+
const { execSync } = require('child_process')
|
|
16
17
|
const readline = require('readline/promises')
|
|
17
18
|
const degit = require('degit').default
|
|
18
19
|
|
|
@@ -21,6 +22,14 @@ const CORE_REPOSITORY = 'opencrvs/opencrvs-core'
|
|
|
21
22
|
const COUNTRYCONFIG_TEMPLATE_REPOSITORY_SUBPATH =
|
|
22
23
|
'packages/countryconfig-template'
|
|
23
24
|
|
|
25
|
+
const CORE_REPO_URL = 'https://github.com/' + CORE_REPOSITORY + '.git'
|
|
26
|
+
const INFRASTRUCTURE_REPO_URL =
|
|
27
|
+
'https://github.com/' + INFRASTRUCTURE_REPOSITORY + '.git'
|
|
28
|
+
|
|
29
|
+
const { version } = JSON.parse(
|
|
30
|
+
fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8')
|
|
31
|
+
)
|
|
32
|
+
|
|
24
33
|
function joinValues(values, separator) {
|
|
25
34
|
return values
|
|
26
35
|
.filter((value) => !!value)
|
|
@@ -28,19 +37,162 @@ function joinValues(values, separator) {
|
|
|
28
37
|
.trim()
|
|
29
38
|
}
|
|
30
39
|
|
|
40
|
+
function tagExists(repoUrl, tag) {
|
|
41
|
+
try {
|
|
42
|
+
execSync(
|
|
43
|
+
'git ls-remote --exit-code --tags ' + repoUrl + ' refs/tags/' + tag,
|
|
44
|
+
{ stdio: 'pipe' }
|
|
45
|
+
)
|
|
46
|
+
return true
|
|
47
|
+
} catch (err) {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function branchExists(repoUrl, branch) {
|
|
53
|
+
try {
|
|
54
|
+
execSync(
|
|
55
|
+
'git ls-remote --exit-code --heads ' + repoUrl + ' refs/heads/' + branch,
|
|
56
|
+
{ stdio: 'pipe' }
|
|
57
|
+
)
|
|
58
|
+
return true
|
|
59
|
+
} catch (err) {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Release tags (e.g. "v2.1.0"), highest first. Delegates the version-aware
|
|
66
|
+
* ordering to git itself rather than hand-parsing semver, then filters down
|
|
67
|
+
* to strict "vX.Y.Z" tags - `--sort=-version:refname` alone still leaves in
|
|
68
|
+
* non-release refs (e.g. "vtesting", "v2.0.0-beta") and peeled annotated-tag
|
|
69
|
+
* lines ("refs/tags/v2.0.0^{}").
|
|
70
|
+
*/
|
|
71
|
+
function listReleaseTags(repoUrl) {
|
|
72
|
+
const output = execSync(
|
|
73
|
+
'git ls-remote --tags --sort=-version:refname ' + repoUrl,
|
|
74
|
+
{ encoding: 'utf-8' }
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return output
|
|
78
|
+
.split('\n')
|
|
79
|
+
.map((line) => line.split('\t')[1])
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.map((ref) => ref.replace('refs/tags/', ''))
|
|
82
|
+
.filter((tag) => /^v\d+\.\d+\.\d+$/.test(tag))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The highest release tag present in *both* repositories - used as the
|
|
87
|
+
* fallback when the version-specific tag can't be found in one or both, so
|
|
88
|
+
* scaffolding still lands on a real, matched release rather than develop.
|
|
89
|
+
*/
|
|
90
|
+
function getLatestCommonReleaseTag() {
|
|
91
|
+
const infrastructureTags = new Set(listReleaseTags(INFRASTRUCTURE_REPO_URL))
|
|
92
|
+
return (
|
|
93
|
+
listReleaseTags(CORE_REPO_URL).find((tag) => infrastructureTags.has(tag)) ||
|
|
94
|
+
null
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Resolves the ref that both repositories are cloned from. A single ref
|
|
100
|
+
* clones both, so every candidate must exist in *both* - a tag present in
|
|
101
|
+
* only one of them can't be used.
|
|
102
|
+
*
|
|
103
|
+
* The exact "v<version>" tag wins whenever it exists: it pins a commit, so
|
|
104
|
+
* a release, an explicit `@X.Y.Z` pin, or a blessed prerelease (npm `@beta`,
|
|
105
|
+
* published from that tag) reproduces however late it's scaffolded.
|
|
106
|
+
*
|
|
107
|
+
* Otherwise a prerelease falls back to a branch, since a rolling release
|
|
108
|
+
* candidate (npm `@next`) is never tagged: "release/X.Y.Z" of the base
|
|
109
|
+
* version when that release has been cut, otherwise develop. A release
|
|
110
|
+
* falls back to the highest tag common to both repositories - never a
|
|
111
|
+
* mismatched pairing of the two at different releases - or errors.
|
|
112
|
+
*/
|
|
113
|
+
function resolveRef() {
|
|
114
|
+
const versionTag = 'v' + version
|
|
115
|
+
if (
|
|
116
|
+
tagExists(CORE_REPO_URL, versionTag) &&
|
|
117
|
+
tagExists(INFRASTRUCTURE_REPO_URL, versionTag)
|
|
118
|
+
) {
|
|
119
|
+
return versionTag
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (version.includes('-')) {
|
|
123
|
+
const releaseBranch = 'release/' + version.split('-')[0]
|
|
124
|
+
if (
|
|
125
|
+
branchExists(CORE_REPO_URL, releaseBranch) &&
|
|
126
|
+
branchExists(INFRASTRUCTURE_REPO_URL, releaseBranch)
|
|
127
|
+
) {
|
|
128
|
+
return releaseBranch
|
|
129
|
+
}
|
|
130
|
+
return 'develop'
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const latestCommonTag = getLatestCommonReleaseTag()
|
|
134
|
+
if (latestCommonTag) {
|
|
135
|
+
console.warn(
|
|
136
|
+
'\nWarning: tag "' +
|
|
137
|
+
versionTag +
|
|
138
|
+
'" was not found in both repositories; falling back to the latest ' +
|
|
139
|
+
'available release, ' +
|
|
140
|
+
latestCommonTag +
|
|
141
|
+
'.'
|
|
142
|
+
)
|
|
143
|
+
return latestCommonTag
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.error(
|
|
147
|
+
'\nError: no matching release tag was found in both the core and ' +
|
|
148
|
+
'infrastructure repositories.'
|
|
149
|
+
)
|
|
150
|
+
process.exit(1)
|
|
151
|
+
}
|
|
152
|
+
|
|
31
153
|
/**
|
|
32
154
|
* Clones a repository from GitHub to a target directory.
|
|
33
155
|
*
|
|
34
156
|
* @param {*} param0 repository - The repository to clone (e.g., 'opencrvs/opencrvs-core').
|
|
35
157
|
* @param {*} param0 repositorySubPath - The subpath within the repository to clone (optional). Otherwise the entire repository will be cloned.
|
|
36
158
|
* @param {*} param0 branch - The branch to clone (optional). Defaults to the default branch if not specified.
|
|
159
|
+
* @param {*} param0 keepHistory - Keep the repository's git history instead of degit's usual history-free copy (optional, defaults to false). The cloned "origin" remote is replaced with "upstream", leaving the directory ready for the user to add their own fork/repo as "origin". Not supported together with repositorySubPath, since a plain git clone can't fetch a single subdirectory.
|
|
37
160
|
*
|
|
38
161
|
* @param {*} targetDir - The target directory where the repository will be cloned.
|
|
39
162
|
*/
|
|
40
163
|
async function cloneRepository(
|
|
41
|
-
{ repository, repositorySubPath, branch },
|
|
164
|
+
{ repository, repositorySubPath, branch, keepHistory = false },
|
|
42
165
|
targetDir
|
|
43
166
|
) {
|
|
167
|
+
if (keepHistory && repositorySubPath) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
'cloneRepository: keepHistory is not supported together with repositorySubPath.'
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (keepHistory) {
|
|
174
|
+
const repoUrl = `https://github.com/${repository}.git`
|
|
175
|
+
console.log(
|
|
176
|
+
`Cloning repository from ${repoUrl}#${branch} to ${targetDir}...`
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
execSync('git clone --branch ' + branch + ' ' + repoUrl + ' ' + targetDir, {
|
|
180
|
+
stdio: 'inherit'
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
console.log(
|
|
184
|
+
`Copied files from ${repoUrl}#${branch} to ${targetDir} succesfully.`
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
console.log(`Replacing 'origin' remote with 'upstream' in ${targetDir}...`)
|
|
188
|
+
execSync('git remote remove origin', { cwd: targetDir, stdio: 'inherit' })
|
|
189
|
+
execSync('git remote add upstream ' + repoUrl, {
|
|
190
|
+
cwd: targetDir,
|
|
191
|
+
stdio: 'inherit'
|
|
192
|
+
})
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
44
196
|
const repositoryPath = joinValues([repository, repositorySubPath], '/')
|
|
45
197
|
const fullPath = joinValues([repositoryPath, branch], '#')
|
|
46
198
|
|
|
@@ -271,6 +423,8 @@ async function main() {
|
|
|
271
423
|
ensureTargetDirectoryDoesNotExist(countryconfigDirName)
|
|
272
424
|
ensureTargetDirectoryDoesNotExist(infrastructureDirName)
|
|
273
425
|
|
|
426
|
+
const ref = resolveRef()
|
|
427
|
+
|
|
274
428
|
// Gather all answers up front so the operator isn't interrupted mid-clone.
|
|
275
429
|
const organisation = await promptOrganisation()
|
|
276
430
|
const countryCode = await promptCountryCode()
|
|
@@ -280,7 +434,8 @@ async function main() {
|
|
|
280
434
|
await cloneRepository(
|
|
281
435
|
{
|
|
282
436
|
repository: CORE_REPOSITORY,
|
|
283
|
-
repositorySubPath: COUNTRYCONFIG_TEMPLATE_REPOSITORY_SUBPATH
|
|
437
|
+
repositorySubPath: COUNTRYCONFIG_TEMPLATE_REPOSITORY_SUBPATH,
|
|
438
|
+
branch: ref
|
|
284
439
|
},
|
|
285
440
|
countryconfigTargetPath
|
|
286
441
|
)
|
|
@@ -293,7 +448,7 @@ async function main() {
|
|
|
293
448
|
|
|
294
449
|
try {
|
|
295
450
|
await cloneRepository(
|
|
296
|
-
{ repository: INFRASTRUCTURE_REPOSITORY },
|
|
451
|
+
{ repository: INFRASTRUCTURE_REPOSITORY, branch: ref, keepHistory: true },
|
|
297
452
|
infrastructureTargetPath
|
|
298
453
|
)
|
|
299
454
|
} catch (err) {
|
|
@@ -319,7 +474,12 @@ async function main() {
|
|
|
319
474
|
console.log(' tilt up\n')
|
|
320
475
|
console.log('To get started with the infrastructure:\n')
|
|
321
476
|
console.log(' cd ' + infrastructureDirName)
|
|
322
|
-
console.log(' git
|
|
477
|
+
console.log(' git remote add origin <your-infrastructure-repo-url>')
|
|
478
|
+
console.log(' git push -u origin ' + ref + '\n')
|
|
479
|
+
console.log(
|
|
480
|
+
'The "upstream" remote points at the official infrastructure repository, ' +
|
|
481
|
+
'so you can pull future releases with `git fetch upstream`.\n'
|
|
482
|
+
)
|
|
323
483
|
}
|
|
324
484
|
|
|
325
485
|
main()
|