@opencrvs/create-countryconfig 2.0.1-rc.fed4de8 → 2.0.2-rc.0aa832c

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.
Files changed (2) hide show
  1. package/index.js +191 -24
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -17,8 +17,170 @@ const fs = require('fs')
17
17
 
18
18
  const COUNTRYCONFIG_REPO_URL =
19
19
  'https://github.com/opencrvs/opencrvs-countryconfig.git'
20
- const INFRASTRUCTURE_REPO_URL =
21
- 'https://github.com/opencrvs/infrastructure.git'
20
+ const INFRASTRUCTURE_REPO_URL = 'https://github.com/opencrvs/infrastructure.git'
21
+
22
+ const { version } = JSON.parse(
23
+ fs.readFileSync(path.join(__dirname, 'package.json'), 'utf-8')
24
+ )
25
+
26
+ function tagExists(repoUrl, tag) {
27
+ try {
28
+ execSync(
29
+ 'git ls-remote --exit-code --tags ' + repoUrl + ' refs/tags/' + tag,
30
+ {
31
+ stdio: 'pipe'
32
+ }
33
+ )
34
+ return true
35
+ } catch (err) {
36
+ return false
37
+ }
38
+ }
39
+
40
+ function branchExists(repoUrl, branch) {
41
+ try {
42
+ execSync(
43
+ 'git ls-remote --exit-code --heads ' + repoUrl + ' refs/heads/' + branch,
44
+ {
45
+ stdio: 'pipe'
46
+ }
47
+ )
48
+ return true
49
+ } catch (err) {
50
+ return false
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Release tags (e.g. "v2.1.0"), highest first. Delegates the version-aware
56
+ * ordering to git itself rather than hand-parsing semver, then filters down
57
+ * to strict "vX.Y.Z" tags - `--sort=-version:refname` alone still leaves in
58
+ * non-release refs (e.g. "vtesting", "v2.0.0-beta") and peeled annotated-tag
59
+ * lines ("refs/tags/v2.0.0^{}").
60
+ */
61
+ function listReleaseTags(repoUrl) {
62
+ const output = execSync(
63
+ 'git ls-remote --tags --sort=-version:refname ' + repoUrl,
64
+ { encoding: 'utf-8' }
65
+ )
66
+
67
+ return output
68
+ .split('\n')
69
+ .map((line) => line.split('\t')[1])
70
+ .filter(Boolean)
71
+ .map((ref) => ref.replace('refs/tags/', ''))
72
+ .filter((tag) => /^v\d+\.\d+\.\d+$/.test(tag))
73
+ }
74
+
75
+ /**
76
+ * The highest release tag present in *both* repositories - used as the
77
+ * fallback when the version-specific tag can't be found in one or both, so
78
+ * scaffolding still lands on a real, matched release rather than develop.
79
+ */
80
+ function getLatestCommonReleaseTag() {
81
+ const infrastructureTags = new Set(listReleaseTags(INFRASTRUCTURE_REPO_URL))
82
+ return (
83
+ listReleaseTags(COUNTRYCONFIG_REPO_URL).find((tag) =>
84
+ infrastructureTags.has(tag)
85
+ ) || null
86
+ )
87
+ }
88
+
89
+ /**
90
+ * A prerelease-shaped own version (e.g. "2.1.0-rc.f5ea803", what npm resolves
91
+ * `@next` to - a build published from every push to a branch) never has a
92
+ * matching release tag, so it's resolved as a branch instead. Its base
93
+ * version (stripped of the "-rc.<sha>" suffix) tells apart two different
94
+ * situations: an RC for a version already being stabilized on its own
95
+ * "release/X.Y.Z" branch (e.g. "2.0.1-rc.*" while a patch release is in
96
+ * progress) versus an RC for a version that hasn't been branched off yet and
97
+ * only exists on develop (e.g. "2.1.0-rc.*" while that release branch hasn't
98
+ * been cut). Scaffold from the release branch when it exists in both
99
+ * repositories, otherwise fall back to develop.
100
+ *
101
+ * Otherwise, the own "X.Y.Z" version - whether resolved via npm's `latest`
102
+ * dist-tag (bare invocation) or an explicit `@X.Y.Z` pin - scaffolds from the
103
+ * matching "vX.Y.Z" tag when it exists in both repositories. If it doesn't
104
+ * (e.g. `latest` lagging behind the repos, or a pin that predates one repo's
105
+ * tagging), fall back to the highest release tag common to both, rather than
106
+ * a mismatched pairing of one tagged repo at that version and another repo
107
+ * at a different release. Exits with an error if no matching release tag
108
+ * exists in both repositories.
109
+ */
110
+ function resolveRef() {
111
+ if (version.includes('-')) {
112
+ const releaseBranch = 'release/' + version.split('-')[0]
113
+ if (
114
+ branchExists(COUNTRYCONFIG_REPO_URL, releaseBranch) &&
115
+ branchExists(INFRASTRUCTURE_REPO_URL, releaseBranch)
116
+ ) {
117
+ return releaseBranch
118
+ }
119
+ return 'develop'
120
+ }
121
+
122
+ const tag = 'v' + version
123
+ if (
124
+ tagExists(COUNTRYCONFIG_REPO_URL, tag) &&
125
+ tagExists(INFRASTRUCTURE_REPO_URL, tag)
126
+ ) {
127
+ return tag
128
+ }
129
+
130
+ const latestCommonTag = getLatestCommonReleaseTag()
131
+ if (latestCommonTag) {
132
+ console.warn(
133
+ '\nWarning: tag "' +
134
+ tag +
135
+ '" was not found in both repositories; falling back to the latest ' +
136
+ 'available release, ' +
137
+ latestCommonTag +
138
+ '.'
139
+ )
140
+ return latestCommonTag
141
+ }
142
+
143
+ console.error(
144
+ '\nError: no matching release tag was found in both the country config and ' +
145
+ 'infrastructure repositories.'
146
+ )
147
+ process.exit(1)
148
+ }
149
+
150
+ function cloneRepository(
151
+ repoUrl,
152
+ ref,
153
+ targetDir,
154
+ { keepHistory = false } = {}
155
+ ) {
156
+ const depthFlag = keepHistory ? '' : '--depth 1 '
157
+ execSync(
158
+ 'git clone ' +
159
+ depthFlag +
160
+ '--branch ' +
161
+ ref +
162
+ ' ' +
163
+ repoUrl +
164
+ ' ' +
165
+ targetDir,
166
+ { stdio: 'inherit' }
167
+ )
168
+
169
+ if (!keepHistory) {
170
+ try {
171
+ fs.rmSync(path.join(targetDir, '.git'), {
172
+ recursive: true,
173
+ force: true
174
+ })
175
+ } catch (err) {
176
+ console.error(
177
+ 'Failed to remove .git directory from ' + targetDir + ':',
178
+ err.message
179
+ )
180
+ process.exit(1)
181
+ }
182
+ }
183
+ }
22
184
 
23
185
  const projectName = process.argv[2]
24
186
 
@@ -33,31 +195,35 @@ const countryconfigDirName = projectName + '-countryconfig'
33
195
  const infrastructureDirName = projectName + '-infrastructure'
34
196
 
35
197
  const countryconfigTargetDir = path.resolve(process.cwd(), countryconfigDirName)
36
- const infrastructureTargetDir = path.resolve(process.cwd(), infrastructureDirName)
198
+ const infrastructureTargetDir = path.resolve(
199
+ process.cwd(),
200
+ infrastructureDirName
201
+ )
37
202
 
38
203
  if (fs.existsSync(countryconfigTargetDir)) {
39
- console.error('Error: Directory "' + countryconfigDirName + '" already exists.')
204
+ console.error(
205
+ 'Error: Directory "' + countryconfigDirName + '" already exists.'
206
+ )
40
207
  process.exit(1)
41
208
  }
42
209
 
43
210
  if (fs.existsSync(infrastructureTargetDir)) {
44
- console.error('Error: Directory "' + infrastructureDirName + '" already exists.')
211
+ console.error(
212
+ 'Error: Directory "' + infrastructureDirName + '" already exists.'
213
+ )
45
214
  process.exit(1)
46
215
  }
47
216
 
48
- console.log('\nScaffolding OpenCRVS country config in ./' + countryconfigDirName + '...\n')
217
+ const ref = resolveRef()
49
218
 
50
- try {
51
- execSync('git clone --depth 1 ' + COUNTRYCONFIG_REPO_URL + ' ' + countryconfigDirName, { stdio: 'inherit' })
52
- } catch (err) {
53
- console.error('Failed to clone the country config repository:', err.message)
54
- process.exit(1)
55
- }
219
+ console.log(
220
+ '\nScaffolding OpenCRVS country config in ./' + countryconfigDirName + '...\n'
221
+ )
56
222
 
57
223
  try {
58
- fs.rmSync(path.join(countryconfigTargetDir, '.git'), { recursive: true, force: true })
224
+ cloneRepository(COUNTRYCONFIG_REPO_URL, ref, countryconfigDirName)
59
225
  } catch (err) {
60
- console.error('Failed to remove .git directory from country config:', err.message)
226
+ console.error('Failed to clone the country config repository:', err.message)
61
227
  process.exit(1)
62
228
  }
63
229
 
@@ -72,25 +238,26 @@ if (fs.existsSync(pkgPath)) {
72
238
  process.exit(1)
73
239
  }
74
240
  } else {
75
- console.warn('Warning: No package.json found in the cloned country config repository. Project name was not updated.')
241
+ console.warn(
242
+ 'Warning: No package.json found in the cloned country config repository. Project name was not updated.'
243
+ )
76
244
  }
77
245
 
78
- console.log('\nScaffolding OpenCRVS infrastructure in ./' + infrastructureDirName + '...\n')
246
+ console.log(
247
+ '\nScaffolding OpenCRVS infrastructure in ./' +
248
+ infrastructureDirName +
249
+ '...\n'
250
+ )
79
251
 
80
252
  try {
81
- execSync('git clone --depth 1 ' + INFRASTRUCTURE_REPO_URL + ' ' + infrastructureDirName, { stdio: 'inherit' })
253
+ cloneRepository(INFRASTRUCTURE_REPO_URL, ref, infrastructureDirName, {
254
+ keepHistory: true
255
+ })
82
256
  } catch (err) {
83
257
  console.error('Failed to clone the infrastructure repository:', err.message)
84
258
  process.exit(1)
85
259
  }
86
260
 
87
- try {
88
- fs.rmSync(path.join(infrastructureTargetDir, '.git'), { recursive: true, force: true })
89
- } catch (err) {
90
- console.error('Failed to remove .git directory from infrastructure:', err.message)
91
- process.exit(1)
92
- }
93
-
94
261
  console.log('\nDone! Your project has been set up in two directories:\n')
95
262
  console.log(' ./' + countryconfigDirName + ' -- country configuration')
96
263
  console.log(' ./' + infrastructureDirName + ' -- server infrastructure\n')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencrvs/create-countryconfig",
3
- "version": "2.0.1-rc.fed4de8",
3
+ "version": "2.0.2-rc.0aa832c",
4
4
  "description": "Scaffold a new OpenCRVS country configuration",
5
5
  "bin": {
6
6
  "create-countryconfig": "./index.js"