@tangelo/tangelo-configuration-toolkit 1.17.1 → 1.19.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.
@@ -1,4 +1,6 @@
1
1
  const {execSync, spawn} = require('child_process');
2
+ const fs = require('fs-extra');
3
+ const globby = require('globby');
2
4
  const inquirer = require('inquirer');
3
5
  const path = require('path');
4
6
 
@@ -36,172 +38,203 @@ const cmdExec = commands => new Promise(resolve => {
36
38
  });
37
39
 
38
40
 
39
- module.exports = function git (argv) {
40
-
41
- if (argv.init) {
42
- const remoteTdiUrl = `${_paths.gitremote}/${_paths.tdi}.git`;
43
- let branches;
44
-
45
- try { branches = execSync('git ls-remote --heads ' + remoteTdiUrl, {encoding: 'UTF-8'}); }
46
- catch (e) { _error(' '); } // execSync already prints an error to the console
47
-
48
- branches = branches
49
- .match(/refs\/heads\/\S+/g)
50
- .map(s => s.replace('refs/heads/', ''))
51
- .sort((a, b) => {
52
- const aRelease = /^release\//.test(a), bRelease = /^release\//.test(b);
53
- if (aRelease && bRelease) return a > b ? -1 : 1;
54
- if (aRelease) return -1;
55
- if (bRelease) return 1;
56
- return a > b ? 1 : -1;
57
- })
58
- ;
59
- inquirer
60
- .prompt([{
61
- message: 'Choose a branch for TDI submodule: ', name: 'branch', type: 'list',
62
- choices: branches, pageSize: 3, loop: false
63
- }])
64
- .then(a => {
65
- _write();
66
- cmdExec([
67
- ['git init', 'Initialize repo:'],
68
- [`git remote add origin ${_paths.gitremote.replace(/\//, ':')}/${path.basename(process.cwd()).replace(/\W/, '-')}.git`],
69
- [`git submodule add -b ${a.branch} ${remoteTdiUrl}`, 'Add TDI submodule:'],
70
- ['git commit -m "added tdi submodule"', 'Commit:']
71
- ])
72
- .then(() => {
73
- _info(`Next steps:
74
- 1. Go to https://bitbucket.org/repo/create?workspace=tangelosoftware and create a repository named "${path.basename(process.cwd()).replace(/\W/, '-')}", with options for including README/.gitignore disabled.
75
- 2. Open this folder in Sourcetree (ctrl+O), and push master branch to origin.
76
- `.replace(/^\s{8}/gm, ''));
77
- });
78
- });
41
+ function init () {
42
+ const remoteTdiUrl = `${_paths.gitremote}/${_paths.tdi}.git`;
43
+ let branches;
44
+
45
+ try { branches = execSync('git ls-remote --heads ' + remoteTdiUrl, {encoding: 'UTF-8'}); }
46
+ catch (e) { _error(' '); } // execSync already prints an error to the console
47
+
48
+ branches = branches
49
+ .match(/refs\/heads\/\S+/g)
50
+ .map(s => s.replace('refs/heads/', ''))
51
+ .sort((a, b) => {
52
+ const aRelease = /^release\//.test(a), bRelease = /^release\//.test(b);
53
+ if (aRelease && bRelease) return a > b ? -1 : 1;
54
+ if (aRelease) return -1;
55
+ if (bRelease) return 1;
56
+ return a > b ? 1 : -1;
57
+ })
58
+ ;
59
+ inquirer
60
+ .prompt([{
61
+ message: 'Choose a branch for TDI submodule: ', name: 'branch', type: 'list',
62
+ choices: branches, pageSize: 3, loop: false
63
+ }])
64
+ .then(a => {
65
+ _write();
66
+ cmdExec([
67
+ ['git init', 'Initialize repo:'],
68
+ [`git remote add origin ${_paths.gitremote.replace(/\//, ':')}/${path.basename(process.cwd()).replace(/\W/, '-')}.git`],
69
+ [`git submodule add -b ${a.branch} ${remoteTdiUrl}`, 'Add TDI submodule:'],
70
+ ['git commit -m "added tdi submodule"', 'Commit:']
71
+ ])
72
+ .then(() => {
73
+ _info(`Next steps:
74
+ 1. Go to https://bitbucket.org/repo/create?workspace=tangelosoftware and create a repository named "${path.basename(process.cwd()).replace(/\W/, '-')}", with options for including README/.gitignore disabled.
75
+ 2. Open this folder in Sourcetree (ctrl+O), and push master branch to origin.
76
+ `.replace(/^\s{8}/gm, ''));
77
+ });
78
+ });
79
+ }
80
+
81
+
82
+ function reset () {
83
+ _info('Reset and clean repo:');
84
+ cmdExec([
85
+ ['git reset --hard'],
86
+ ['git clean -f -d']
87
+ ]);
88
+ }
89
+
90
+
91
+ function clone ({clone: repoName}) {
92
+
93
+ if (typeof repoName !== 'string') _error('Pass a repository name!');
94
+
95
+ _info('Clone a client repository and do basic project initialization');
96
+ cmdExec([
97
+ // Clone repository
98
+ ['git clone git@bitbucket.org:tangelosoftware/' + repoName + '.git']
99
+ ])
100
+ .then(() => {
101
+ // Change directory for the next commands, it should have the same name as the clone argument from commandline at this point
102
+ process.chdir('./' + repoName);
103
+ cmdExec([
104
+ // Retrieve TDI submodule for this client
105
+ ['git submodule update --init', 'Fetch submodules such as TDI'],
106
+ // Create symlinks for TDI
107
+ ['tct b -s', 'Create TDI symlinks'],
108
+ // Create Oxygen project file and set name equal to repo name
109
+ [`tct b -x ${repoName}`, 'Create Oxygen project file'],
110
+ ]);
111
+ })
112
+ .catch(e => {throw e;})
113
+ ;
114
+ }
115
+
116
+
117
+ /**
118
+ * Updates the repository to the latest commit.
119
+ * Checks the version of the Fonto instances and warns if there are changes.
120
+ */
121
+ function updateRepo () {
122
+ const readFontoVersionFromFile = relPath => fs.readJSONSync(path.join(_paths.repo, 'config/cmscustom', relPath)).sdkVersion;
123
+ const fontoLocations = globby.sync('*/*/fonto/manifest.json', {cwd: path.join(_paths.repo, 'config/cmscustom')});
124
+ const fontoVersions = {};
125
+ fontoLocations.forEach(p => {fontoVersions[p] = readFontoVersionFromFile(p);});
126
+ const tdiLogBeforeUpdate = _git.commitTdi.local();
127
+
128
+ _info(`Fetch all`);
129
+ const cmdFetch = execGitCommand('fetch -pf --all', _paths.repo);
130
+ if (cmdFetch.error) _warn(`Fetch failed\n${cmdFetch.error}`);
131
+
132
+ _info(`Pull repository`);
133
+ const cmdPull = execGitCommand(`pull`, _paths.repo);
134
+ if (cmdPull.error) _warn(`Pull failed\n${cmdPull.error}`);
135
+
136
+ _info(`Checked out at commit:`);
137
+ const repoLog = _git.commitLocal();
138
+ _write([_formatDate(repoLog.date), repoLog.tags, repoLog.hash].filter(v=>v).join(' - '));
139
+
140
+ if (fontoLocations.find(p => fontoVersions[p] !== readFontoVersionFromFile(p))) _warn(`\nChanges in Fonto instance versions have been pulled. Therefore you must execute ${'tct fonto --init'.cyan} before building and deploying Fonto.`);
141
+
142
+ // update submodule recursively (set to version that belongs to repo)
143
+ execGitCommand(`submodule update --recursive`, _paths.repo);
144
+
145
+ // clear memoize cache; which is filled with the old TDI data due to tdiLogBeforeUpdate
146
+ _git.commitTdi.local.clear();
147
+ const tdiLog = _git.commitTdi.local();
148
+
149
+ _info(`\nTDI submodule:`);
150
+ _write(`Submodule in repo-commit: ${[_formatDate(tdiLog.date), tdiLog.tags, tdiLog.hash].filter(v=>v).join(' - ')}`);
151
+
152
+ if (tdiLog.date < tdiLogBeforeUpdate.date) {
153
+ _write(`Restore TDI submodule because it was newer before the repository update:`);
154
+ execGitCommand(`checkout ${tdiLogBeforeUpdate.hash}`, path.join(_paths.repo, _paths.tdi));
155
+ _write(`Submodule checked out: ${[_formatDate(tdiLogBeforeUpdate.date), tdiLogBeforeUpdate.tags, tdiLogBeforeUpdate.hash].filter(v=>v).join(' - ')}\n`);
79
156
  }
80
-
81
- if (argv.reset) {
82
- _info('Reset and clean repo:');
83
- cmdExec([
84
- ['git reset --hard'],
85
- ['git clean -f -d']
86
- ]);
157
+ else {
158
+ _write(`Submodule in repo-commit is checked out\n`);
87
159
  }
88
160
 
89
- if (argv.clone) {
90
- if (typeof argv.clone !== 'string') _error('Pass a repository name!');
91
-
92
- _info('Clone a client repository and do basic project initialization');
93
- cmdExec([
94
- // Clone repository
95
- ['git clone git@bitbucket.org:tangelosoftware/' + argv.clone + '.git']
96
- ])
97
- .then(() => {
98
- // Change directory for the next commands, it should have the same name as the clone argument from commandline at this point
99
- process.chdir('./' + argv.clone);
100
- cmdExec([
101
- // Retrieve TDI submodule for this client
102
- ['git submodule update --init', 'Fetch submodules such as TDI'],
103
- // Create symlinks for TDI
104
- ['tct b -s', 'Create TDI symlinks'],
105
- // Create Oxygen project file and set name equal to repo name
106
- [`tct b -x ${argv.clone}`, 'Create Oxygen project file'],
107
- ]);
108
- })
109
- .catch(e => {throw e;})
110
- ;
111
- }
161
+ const tdiBranch = getTdiBranch();
162
+ _info(`\nTDI branch ${tdiBranch.name} is ${tdiBranch.commitsBehind} commits behind.\nUpdate TDI submodule to latest version with ${'tct git --update-submodule'.cyan}`);
163
+ }
164
+
165
+
166
+ /**
167
+ * Updates a submodule in a Git repository. Allows for custom date ranges (for testing) and branch upgrades, and applies migrations to the repository if necessary.
168
+ *
169
+ * @param {string[]} options.dates - An array of two strings representing the start and end dates for the custom date range.
170
+ * @param {string} options.updateSubmodule - The target branch name for the submodule update.
171
+ */
172
+ function updateSubmodule({dates = [], updateSubmodule: toBranchName}) {
173
+ const [dateCustomFrom, dateCustomTo] = dates;
174
+ const branch = getTdiBranch(typeof toBranchName !== 'boolean' && toBranchName);
175
+ const branchUpgrade = branch.from ? branch.from.name < branch.name : false;
176
+
177
+ if (branchUpgrade) _info(`Current branch '${branch.from.name}' will be updated to '${branch.name}'\nCommon ancestor will be used in selecting TDI commits requiring migration: ${_formatDate(branch.commonAncestor.date)}`);
178
+ _info(`Branch ${branch.name} is ${branch.commitsBehind} commits behind.\n`);
179
+
180
+ // Set branch in .gitmodules file; This ensures submodule update will follow this branch to the latest commit
181
+ const setBranch = execGitCommand(`submodule set-branch -b ${branch.name} tangelo-default-implementation`, _paths.repo);
182
+ if (setBranch.error) _error(`Set branch failed: ${setBranch.error}`);
183
+
184
+ if (branch.commitsBehind > 0 || dateCustomFrom) {
185
+ const dateBeforeUpdate = _git.commitTdi.local().date;
186
+
187
+ // update submodule
188
+ const updateSubmoduleMsg = execGitCommand(`submodule update --remote`, _paths.repo);
189
+ if (updateSubmoduleMsg.error) _error(`Update submodule failed\n${updateSubmoduleMsg.error}`);
190
+ if (updateSubmoduleMsg) _info(`TDI submodule updated:\n${updateSubmoduleMsg}\n`);
191
+
192
+ // tdiMigrationFilePath should exist in latest commits of releases 5.3+; As we updated to the latest version this should work
193
+ const migrations = _modulesTdi.require('git/tdiCommitsRequiringMigration.js');
194
+ const fromDate = dateCustomFrom ? new Date(dateCustomFrom) : branch.commonAncestor?.date ?? dateBeforeUpdate;
195
+ const toDate = dateCustomTo ? new Date(dateCustomTo) : new Date();
196
+
197
+ _info(`TDI commits requiring migration between ${_formatDate(fromDate)} and ${_formatDate(toDate)}`);
198
+ // Filter the migrations that should be applied/considered; Also display older releases migrations
199
+ // For a branch upgrade, all the migrations need to be executed from the beginning of the target branch, not just within the given date range
200
+ const migrationsFiltered = migrations.filter(m => m.releases.find(r => {
201
+ const withinDateRange = fromDate < new Date(r.date) && new Date(r.date) < toDate;
202
+ const isTargetBranch = `release/${r.release}` === branch.name;
203
+ return branchUpgrade ?
204
+ isTargetBranch || (branch.from.name <= `release/${r.release}` && `release/${r.release}` < branch.name && withinDateRange) :
205
+ isTargetBranch && withinDateRange
206
+ ;
207
+ }));
208
+
209
+ let relevantMigrationCount = 0;
210
+ // Apply callback for migrations
211
+ migrationsFiltered
212
+ .sort((a, b) => Date(a.releases[0].date) > Date(b.releases[0].date) ? 1 : -1)
213
+ .forEach(m => {
214
+ const date = _formatDate(new Date(m.releases.filter(r => branch.name >= `release/${r.release}`)[0].date));
215
+ relevantMigrationCount += m.callback(date, relevantMigrationCount+1) === 1 ? 1 : 0;
216
+ });
112
217
 
113
- if (argv.update) {
114
- const [update, tdiFromDateCustom, tdiToDateCustom] = `${argv.update}`.split(/\s/);
115
-
116
- if (update === `tdi`) {
117
-
118
- const tdiBranch = getTdiBranch(argv.branch);
119
- const tdiBranchUpgrade = tdiBranch.from ? (tdiBranch.from.name < tdiBranch.name) : false;
120
-
121
- if (tdiBranchUpgrade) _info(`Current branch '${tdiBranch.from.name}' will be updated to '${tdiBranch.name}'\nCommon ancestor will be used in selecting TDI commits requiring migration: ${_formatDate(tdiBranch.commonAncestor.date)}`);
122
- _info(`Branch ${tdiBranch.name} is ${tdiBranch.commitsBehind} commits behind.`);
123
-
124
- // Set branch in .gitmodules file; This ensures submodule update will follow this branch to the latest commit
125
- const setBranch = execGitCommand(`submodule set-branch -b ${tdiBranch.name} tangelo-default-implementation`, _paths.repo);
126
- if (setBranch.error) _error(`Set branch failed: ${setBranch.error}`);
127
-
128
- if (tdiBranch.commitsBehind > 0 || tdiFromDateCustom) {
129
- // update submodule
130
- const updateSubmoduleMsg = execGitCommand(`submodule update --remote`, _paths.repo);
131
- if (updateSubmoduleMsg.error && !tdiFromDateCustom) _error(`Update submodule failed\n${updateSubmoduleMsg.error}`);
132
-
133
- const tdiDateBeforeUpdate = _git.commitTdi.local().date;
134
- // update local TDI commit data
135
- _git.commitTdi.local(true);
136
-
137
- if (!tdiFromDateCustom) _info(`TDI submodule updated:\n${updateSubmoduleMsg}`);
138
-
139
- // tdiMigrationFilePath should exist in latest commits of releases 5.3+; As we updated to the latest version this should work
140
- const migrations = _modulesTdi.require('git/tdiCommitsRequiringMigration.js');
141
- if (migrations) {
142
- const fromTdiDate = tdiFromDateCustom ? new Date(tdiFromDateCustom) : (tdiBranch.commonAncestor) ? tdiBranch.commonAncestor.date: tdiDateBeforeUpdate;
143
- const toTdiDate = tdiToDateCustom ? new Date(tdiToDateCustom) : new Date();
144
-
145
- _info(`TDI commits requiring migration between ${_formatDate(fromTdiDate)} and ${_formatDate(toTdiDate)}`);
146
- // Filter the migrations that should be applied/considered; Also display older releases migrations
147
- const migrationsFiltered = migrations
148
- .filter((m) => m.releases.find((r) => {
149
- const time = new Date(r.date).getTime();
150
- return ((tdiBranchUpgrade)
151
- ? `release/${r.release}` == tdiBranch.name || (tdiBranch.from.name <= `release/${r.release}` && `release/${r.release}` < tdiBranch.name && (fromTdiDate.getTime() < time && time < toTdiDate.getTime()))
152
- : `release/${r.release}` == tdiBranch.name && (fromTdiDate.getTime() < time && time < toTdiDate.getTime())
153
- );
154
- })
155
- );
156
-
157
- let relevantMigrationCount = 0;
158
- // Apply callback for migrations
159
- migrationsFiltered
160
- .sort((a, b) => Date(a.releases[0].date)>Date(b.releases[0].date)?1:-1)
161
- .forEach((m) => {
162
- const date = _formatDate(new Date(m.releases.filter((r) => tdiBranch.name >= `release/${r.release}`)[0].date));
163
- relevantMigrationCount += (m.callback(date, relevantMigrationCount+1)) === 1 ? 1 : 0;
164
- });
165
-
166
- // Output message based on whether or not migrations are required
167
- if (migrationsFiltered.length === 0) {
168
- _write(`-- No commits require migrations --`);
169
- } else {
170
- _info(`\n${relevantMigrationCount} relevant migration(s) out of ${migrationsFiltered.length} migration(s) are shown.`);
171
- _info(`See confluence page 'TDI commits requiring migration' for more detailed information`);
172
- }
173
-
174
- } else {
175
- _error(`Cannot find required files in TDI submodule.`);
176
- }
177
- } else {
178
- _info(`Your TDI submodule is already up to date`);
179
- }
218
+ // Output message based on whether or not migrations are required
219
+ if (migrationsFiltered.length === 0) {
220
+ _write(`-- No commits require migrations --`);
180
221
  } else {
181
- if (argv.branch) _warn(`Branch argument '${argv.branch}' is ignored for a repository update.`);
182
-
183
- // Fetch all
184
- _info(`Fetch all`);
185
- const cmdFetch = execGitCommand('fetch -pf --all', path.join(_paths.repo));
186
- if (cmdFetch.error) _warn(`Fetch failed\n${cmdFetch.error}`);
187
-
188
- // pull repo
189
- _info(`Pull repository`);
190
- const cmdPull = execGitCommand(`pull`, _paths.repo);
191
- if (cmdPull.error) _warn(`Pull failed\n${cmdPull.error}`);
192
-
193
- _info(`Checked out at commit:`);
194
- const repoLog = _git.commitLocal(true);
195
- _write(`${_formatDate(repoLog.date)} - ${repoLog.tags} - ${repoLog.hash}`);
196
-
197
- _info(`Retrieve submodules that belong to this repo-commit`); // update submodule recursively (set to version that belongs to repo)
198
- _write(execGitCommand(`submodule update --recursive`, path.join(_paths.repo)));
199
- _info(`Submodule checked out at commit:`);
200
- const tdiLog = _git.commitTdi.local(true);
201
- _write(`${_formatDate(tdiLog.date)} - ${tdiLog.tags} - ${tdiLog.hash}`);
202
- const tdiBranch = getTdiBranch();
203
-
204
- _info(`\nTDI branch ${tdiBranch.name} is ${tdiBranch.commitsBehind} commits behind.\nUpdate TDI to latest version with 'tct g -u tdi'`);
222
+ _info(`\n${relevantMigrationCount} relevant migration(s) out of ${migrationsFiltered.length} migration(s) are shown.`);
223
+ _info(`See confluence page 'TDI commits requiring migration' for more detailed information (https://tangelo-software.atlassian.net/wiki/x/qQnE)`);
205
224
  }
225
+
226
+ } else {
227
+ _info(`Your TDI submodule is already up to date`);
206
228
  }
229
+ }
230
+
231
+
232
+ module.exports = function git (argv) {
233
+
234
+ if (argv.init) init();
235
+ if (argv.reset) reset();
236
+ if (argv.clone) clone(argv);
237
+ if (argv.updateRepo) updateRepo();
238
+ if (argv.updateSubmodule) updateSubmodule(argv);
239
+
207
240
  };
@@ -51,7 +51,7 @@ module.exports = function migrate (argv) {
51
51
  a.script = cmdlScript.value;
52
52
  }
53
53
 
54
- let filter = path.join(_paths.apply, argv.filter??'**').toFws(); // Default set filter with filter added to the command argument -f
54
+ let filter = path.join(_paths.apply, argv.filter??'**').toFws; // Default set filter with filter added to the command argument -f
55
55
 
56
56
  if (a.filter === 'projects' && _repoconfig.length > a.projects.length) {
57
57
  // push paths of chosen active documents to projectFolders (only if not all projects are chosen)
@@ -61,10 +61,7 @@ module.exports = function sql (argv) {
61
61
  .prompt([
62
62
  {
63
63
  message: 'Choose projects: ', name: 'projects', type: 'checkbox', validate: v => !!v[0],
64
- choices:
65
- _repoconfig.projects
66
- ? _repoconfig.projects.map(p => ({name: p.name, value: p})) // when using repoconfig file (old)
67
- : _repoconfig.map(p => ({name: `${p.customer_name} ${p.project_name}`, value: p}))
64
+ choices: _repoconfig.map(p => ({name: `${p.customer_name} ${p.project_name}`, value: p}))
68
65
  },
69
66
  {message: 'Database TNS name: ', name: 'db', default: _appconfig.defaultDatabase},
70
67
  {message: 'TANCMS password: ', name: 'pw', default: 'tancms'}
@@ -1,15 +0,0 @@
1
- /* eslint-disable no-control-regex */
2
-
3
- const styles = {bold: 1, underline: 4, black: 30, red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37, lblack: 90, lred: 91, lgreen: 92, lyellow: 93, lblue: 94, lmagenta: 95, lcyan: 96, lwhite: 97};
4
-
5
- for (const name in styles ) {
6
- const style = styles[name];
7
-
8
- String.prototype.__defineGetter__(name, function () {
9
- return `\x1b[${style}m${this}\x1b[0m`;
10
- });
11
- }
12
-
13
- String.prototype.__defineGetter__('nostyle', function () {
14
- return this.replace(/[\u001b\u009b][[()#;?]*(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '');
15
- });