@burgan-tech/vnext-workflow-cli 1.0.1 → 1.0.3
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/README.md +413 -80
- package/bin/workflow.js +47 -13
- package/package.json +5 -5
- package/src/commands/check.js +62 -22
- package/src/commands/config.js +15 -9
- package/src/commands/csx.js +125 -44
- package/src/commands/domain.js +161 -0
- package/src/commands/reset.js +198 -80
- package/src/commands/sync.js +189 -107
- package/src/commands/update.js +217 -99
- package/src/lib/api.js +54 -36
- package/src/lib/config.js +231 -21
- package/src/lib/csx.js +130 -57
- package/src/lib/db.js +10 -10
- package/src/lib/discover.js +131 -29
- package/src/lib/vnextConfig.js +124 -0
- package/src/lib/workflow.js +86 -39
package/src/lib/config.js
CHANGED
|
@@ -1,29 +1,239 @@
|
|
|
1
1
|
const Conf = require('conf');
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
// Default config values for a domain
|
|
4
|
+
const DEFAULT_DOMAIN_CONFIG = {
|
|
5
|
+
AUTO_DISCOVER: true,
|
|
6
|
+
API_BASE_URL: 'http://localhost:4201',
|
|
7
|
+
API_VERSION: 'v1',
|
|
8
|
+
DB_HOST: 'localhost',
|
|
9
|
+
DB_PORT: 5432,
|
|
10
|
+
DB_NAME: 'vNext_WorkflowDb',
|
|
11
|
+
DB_USER: 'postgres',
|
|
12
|
+
DB_PASSWORD: 'postgres',
|
|
13
|
+
USE_DOCKER: false,
|
|
14
|
+
DOCKER_POSTGRES_CONTAINER: 'vnext-postgres',
|
|
15
|
+
DEBUG_MODE: false
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const DOMAIN_CONFIG_KEYS = Object.keys(DEFAULT_DOMAIN_CONFIG);
|
|
3
19
|
|
|
4
20
|
const config = new Conf({
|
|
5
|
-
projectName: 'vnext-workflow-cli'
|
|
6
|
-
|
|
21
|
+
projectName: 'vnext-workflow-cli'
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Migrates old flat config to new domain-aware format.
|
|
26
|
+
* Old: { API_BASE_URL: "...", DB_NAME: "..." }
|
|
27
|
+
* New: { ACTIVE_DOMAIN: "default", DOMAINS: [{ DOMAIN_NAME: "default", ... }] }
|
|
28
|
+
*/
|
|
29
|
+
function migrateConfig() {
|
|
30
|
+
const store = config.store;
|
|
31
|
+
|
|
32
|
+
// Already in new format - skip
|
|
33
|
+
if (store.ACTIVE_DOMAIN !== undefined && Array.isArray(store.DOMAINS)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Collect existing values from old flat config
|
|
38
|
+
const existingValues = {};
|
|
39
|
+
for (const key of DOMAIN_CONFIG_KEYS) {
|
|
40
|
+
if (store[key] !== undefined) {
|
|
41
|
+
existingValues[key] = store[key];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Create default domain: defaults + any existing overrides
|
|
46
|
+
const defaultDomain = {
|
|
47
|
+
DOMAIN_NAME: 'default',
|
|
48
|
+
...DEFAULT_DOMAIN_CONFIG,
|
|
49
|
+
...existingValues
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Clear old keys and set new format via conf API
|
|
53
|
+
for (const key of DOMAIN_CONFIG_KEYS) {
|
|
54
|
+
config.delete(key);
|
|
55
|
+
}
|
|
56
|
+
config.set('ACTIVE_DOMAIN', 'default');
|
|
57
|
+
config.set('DOMAINS', [defaultDomain]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Run migration on module load
|
|
61
|
+
migrateConfig();
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Returns the active domain config object.
|
|
65
|
+
* @returns {Object} Active domain config
|
|
66
|
+
*/
|
|
67
|
+
function getActiveDomainConfig() {
|
|
68
|
+
const activeDomain = config.get('ACTIVE_DOMAIN');
|
|
69
|
+
const domains = config.get('DOMAINS') || [];
|
|
70
|
+
const domain = domains.find(d => d.DOMAIN_NAME === activeDomain);
|
|
71
|
+
|
|
72
|
+
if (!domain) {
|
|
73
|
+
throw new Error(`Active domain "${activeDomain}" not found in config.`);
|
|
74
|
+
}
|
|
75
|
+
return domain;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Gets a config value from the active domain.
|
|
80
|
+
* PROJECT_ROOT always returns process.cwd().
|
|
81
|
+
* @param {string} key - Config key
|
|
82
|
+
* @returns {any} Config value
|
|
83
|
+
*/
|
|
84
|
+
function get(key) {
|
|
85
|
+
if (key === 'PROJECT_ROOT') {
|
|
86
|
+
return process.cwd();
|
|
87
|
+
}
|
|
88
|
+
if (key === 'ACTIVE_DOMAIN') {
|
|
89
|
+
return config.get('ACTIVE_DOMAIN');
|
|
90
|
+
}
|
|
91
|
+
const domainConfig = getActiveDomainConfig();
|
|
92
|
+
return domainConfig[key];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Sets a config value on the active domain.
|
|
97
|
+
* PROJECT_ROOT cannot be set (always uses cwd).
|
|
98
|
+
* @param {string} key - Config key
|
|
99
|
+
* @param {any} value - Config value
|
|
100
|
+
*/
|
|
101
|
+
function set(key, value) {
|
|
102
|
+
const RESERVED_KEYS = {
|
|
103
|
+
PROJECT_ROOT: 'PROJECT_ROOT is always the current working directory and cannot be changed.',
|
|
104
|
+
DOMAIN_NAME: 'DOMAIN_NAME cannot be changed directly. Use "wf domain add/remove" instead.',
|
|
105
|
+
ACTIVE_DOMAIN: 'ACTIVE_DOMAIN cannot be changed directly. Use "wf domain use <name>" instead.'
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
if (RESERVED_KEYS[key]) {
|
|
109
|
+
throw new Error(RESERVED_KEYS[key]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const activeDomainName = config.get('ACTIVE_DOMAIN');
|
|
113
|
+
const domains = config.get('DOMAINS') || [];
|
|
114
|
+
const idx = domains.findIndex(d => d.DOMAIN_NAME === activeDomainName);
|
|
115
|
+
|
|
116
|
+
if (idx === -1) {
|
|
117
|
+
throw new Error(`Active domain "${activeDomainName}" not found.`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
domains[idx][key] = value;
|
|
121
|
+
config.set('DOMAINS', domains);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Gets all config values from the active domain.
|
|
126
|
+
* @returns {Object} All config values including PROJECT_ROOT and ACTIVE_DOMAIN
|
|
127
|
+
*/
|
|
128
|
+
function getAll() {
|
|
129
|
+
const domainConfig = getActiveDomainConfig();
|
|
130
|
+
return {
|
|
7
131
|
PROJECT_ROOT: process.cwd(),
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
132
|
+
ACTIVE_DOMAIN: config.get('ACTIVE_DOMAIN'),
|
|
133
|
+
...domainConfig
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Resets config to default state.
|
|
139
|
+
*/
|
|
140
|
+
function clear() {
|
|
141
|
+
config.clear();
|
|
142
|
+
config.set('ACTIVE_DOMAIN', 'default');
|
|
143
|
+
config.set('DOMAINS', [{
|
|
144
|
+
DOMAIN_NAME: 'default',
|
|
145
|
+
...DEFAULT_DOMAIN_CONFIG
|
|
146
|
+
}]);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Adds a new domain. Missing values are inherited from the default domain.
|
|
151
|
+
* @param {string} name - Domain name
|
|
152
|
+
* @param {Object} options - Domain config overrides
|
|
153
|
+
* @returns {Object} Created domain config
|
|
154
|
+
*/
|
|
155
|
+
function addDomain(name, options) {
|
|
156
|
+
const domains = config.get('DOMAINS') || [];
|
|
157
|
+
|
|
158
|
+
if (domains.find(d => d.DOMAIN_NAME === name)) {
|
|
159
|
+
throw new Error(`Domain "${name}" already exists.`);
|
|
19
160
|
}
|
|
20
|
-
|
|
161
|
+
|
|
162
|
+
// Use default domain as base, fall back to DEFAULT_DOMAIN_CONFIG
|
|
163
|
+
const defaultDomain = domains.find(d => d.DOMAIN_NAME === 'default');
|
|
164
|
+
const base = defaultDomain
|
|
165
|
+
? { ...defaultDomain }
|
|
166
|
+
: { ...DEFAULT_DOMAIN_CONFIG };
|
|
167
|
+
delete base.DOMAIN_NAME;
|
|
168
|
+
|
|
169
|
+
const newDomain = {
|
|
170
|
+
DOMAIN_NAME: name,
|
|
171
|
+
...base,
|
|
172
|
+
...options
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
domains.push(newDomain);
|
|
176
|
+
config.set('DOMAINS', domains);
|
|
177
|
+
return newDomain;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Switches the active domain.
|
|
182
|
+
* @param {string} name - Domain name to activate
|
|
183
|
+
*/
|
|
184
|
+
function useDomain(name) {
|
|
185
|
+
const domains = config.get('DOMAINS') || [];
|
|
186
|
+
if (!domains.find(d => d.DOMAIN_NAME === name)) {
|
|
187
|
+
throw new Error(`Domain "${name}" not found.`);
|
|
188
|
+
}
|
|
189
|
+
config.set('ACTIVE_DOMAIN', name);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Lists all domains with active domain info.
|
|
194
|
+
* @returns {Object} { activeDomain, domains }
|
|
195
|
+
*/
|
|
196
|
+
function listDomains() {
|
|
197
|
+
return {
|
|
198
|
+
activeDomain: config.get('ACTIVE_DOMAIN'),
|
|
199
|
+
domains: config.get('DOMAINS') || []
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Removes a domain. Cannot remove the default domain.
|
|
205
|
+
* If the removed domain was active, switches to default.
|
|
206
|
+
* @param {string} name - Domain name to remove
|
|
207
|
+
*/
|
|
208
|
+
function removeDomain(name) {
|
|
209
|
+
if (name === 'default') {
|
|
210
|
+
throw new Error('Cannot remove the default domain.');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const domains = config.get('DOMAINS') || [];
|
|
214
|
+
const filtered = domains.filter(d => d.DOMAIN_NAME !== name);
|
|
215
|
+
|
|
216
|
+
if (filtered.length === domains.length) {
|
|
217
|
+
throw new Error(`Domain "${name}" not found.`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
config.set('DOMAINS', filtered);
|
|
221
|
+
|
|
222
|
+
if (config.get('ACTIVE_DOMAIN') === name) {
|
|
223
|
+
config.set('ACTIVE_DOMAIN', 'default');
|
|
224
|
+
}
|
|
225
|
+
}
|
|
21
226
|
|
|
22
227
|
module.exports = {
|
|
23
|
-
get
|
|
24
|
-
set
|
|
25
|
-
getAll
|
|
26
|
-
clear
|
|
27
|
-
path: config.path
|
|
228
|
+
get,
|
|
229
|
+
set,
|
|
230
|
+
getAll,
|
|
231
|
+
clear,
|
|
232
|
+
path: config.path,
|
|
233
|
+
addDomain,
|
|
234
|
+
useDomain,
|
|
235
|
+
listDomains,
|
|
236
|
+
removeDomain,
|
|
237
|
+
getActiveDomainConfig,
|
|
238
|
+
DEFAULT_DOMAIN_CONFIG
|
|
28
239
|
};
|
|
29
|
-
|
package/src/lib/csx.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
const fs = require('fs').promises;
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { glob } = require('glob');
|
|
4
|
+
const { discoverComponents, findAllJsonFiles } = require('./discover');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* CSX
|
|
7
|
+
* Encodes CSX file to Base64
|
|
8
|
+
* @param {string} csxPath - CSX file path
|
|
9
|
+
* @returns {Promise<string>} Base64 encoded content
|
|
7
10
|
*/
|
|
8
11
|
async function encodeToBase64(csxPath) {
|
|
9
12
|
const content = await fs.readFile(csxPath, 'utf8');
|
|
@@ -11,28 +14,32 @@ async function encodeToBase64(csxPath) {
|
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
|
-
*
|
|
17
|
+
* Finds JSON files that reference the CSX file
|
|
18
|
+
* Only searches in paths defined in vnext.config.json
|
|
19
|
+
* @param {string} csxPath - CSX file path
|
|
20
|
+
* @param {string} projectRoot - Project root folder
|
|
21
|
+
* @returns {Promise<string[]>} Matching JSON file paths
|
|
15
22
|
*/
|
|
16
23
|
async function findJsonFilesForCsx(csxPath, projectRoot) {
|
|
17
24
|
const csxBaseName = path.basename(csxPath);
|
|
18
25
|
|
|
19
|
-
//
|
|
20
|
-
const
|
|
21
|
-
const jsonFiles = await glob(pattern, {
|
|
22
|
-
ignore: ['**/node_modules/**', '**/dist/**', '**/package*.json', '**/*config*.json']
|
|
23
|
-
});
|
|
26
|
+
// Get discovered components (only paths defined in vnext.config.json)
|
|
27
|
+
const discovered = await discoverComponents(projectRoot);
|
|
24
28
|
|
|
25
|
-
//
|
|
29
|
+
// Get all JSON files from discovered components only
|
|
30
|
+
const jsonFileInfos = await findAllJsonFiles(discovered);
|
|
31
|
+
|
|
32
|
+
// Filter JSONs that reference the CSX
|
|
26
33
|
const matchingJsons = [];
|
|
27
34
|
|
|
28
|
-
for (const
|
|
35
|
+
for (const jsonInfo of jsonFileInfos) {
|
|
29
36
|
try {
|
|
30
|
-
const content = await fs.readFile(
|
|
37
|
+
const content = await fs.readFile(jsonInfo.path, 'utf8');
|
|
31
38
|
if (content.includes(csxBaseName)) {
|
|
32
|
-
matchingJsons.push(
|
|
39
|
+
matchingJsons.push(jsonInfo.path);
|
|
33
40
|
}
|
|
34
41
|
} catch (error) {
|
|
35
|
-
//
|
|
42
|
+
// Skip unreadable files
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
|
|
@@ -40,11 +47,14 @@ async function findJsonFilesForCsx(csxPath, projectRoot) {
|
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/**
|
|
43
|
-
* CSX location path
|
|
50
|
+
* Calculates CSX location path
|
|
51
|
+
* @param {string} csxPath - CSX file path
|
|
52
|
+
* @param {string} projectRoot - Project root folder
|
|
53
|
+
* @returns {string} Location path
|
|
44
54
|
*/
|
|
45
|
-
function getCsxLocation(csxPath) {
|
|
46
|
-
// ./src/Rules/MyRule.csx
|
|
47
|
-
const parts = csxPath.split(
|
|
55
|
+
function getCsxLocation(csxPath, projectRoot) {
|
|
56
|
+
// Convert to ./src/Rules/MyRule.csx format
|
|
57
|
+
const parts = csxPath.split(path.sep);
|
|
48
58
|
const srcIndex = parts.lastIndexOf('src');
|
|
49
59
|
|
|
50
60
|
if (srcIndex !== -1) {
|
|
@@ -56,91 +66,154 @@ function getCsxLocation(csxPath) {
|
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
/**
|
|
59
|
-
*
|
|
69
|
+
* Updates ALL CSX codes in a JSON file
|
|
70
|
+
* Updates all references with the same location
|
|
71
|
+
* Supports encoding types: NAT (native/plain text), B64 (Base64, default)
|
|
72
|
+
* @param {string} jsonPath - JSON file path
|
|
73
|
+
* @param {string} csxLocation - CSX location path
|
|
74
|
+
* @param {string} base64Code - Base64 encoded CSX content
|
|
75
|
+
* @param {string} nativeCode - Native (plain text) CSX content
|
|
76
|
+
* @returns {Promise<number>} Number of updated references
|
|
60
77
|
*/
|
|
61
|
-
async function updateCodeInJson(jsonPath, csxLocation, base64Code) {
|
|
78
|
+
async function updateCodeInJson(jsonPath, csxLocation, base64Code, nativeCode) {
|
|
62
79
|
const content = await fs.readFile(jsonPath, 'utf8');
|
|
63
80
|
const data = JSON.parse(content);
|
|
64
81
|
|
|
65
|
-
|
|
82
|
+
let updateCount = 0;
|
|
83
|
+
|
|
84
|
+
// Recursively find and update ALL location matches
|
|
66
85
|
function updateRecursive(obj) {
|
|
67
|
-
if (typeof obj !== 'object' || obj === null) return
|
|
86
|
+
if (typeof obj !== 'object' || obj === null) return;
|
|
87
|
+
|
|
88
|
+
// Skip if location is missing
|
|
89
|
+
if (!obj.location) {
|
|
90
|
+
// Continue scanning children
|
|
91
|
+
for (const key in obj) {
|
|
92
|
+
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
93
|
+
updateRecursive(obj[key]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
68
98
|
|
|
69
99
|
if (obj.location === csxLocation && 'code' in obj) {
|
|
70
|
-
|
|
71
|
-
|
|
100
|
+
// Check encoding type: NAT = Native (plain text), B64 or empty = Base64 (default)
|
|
101
|
+
const encoding = obj.encoding ? obj.encoding.toUpperCase() : 'B64';
|
|
102
|
+
|
|
103
|
+
if (encoding === 'NAT') {
|
|
104
|
+
// Native encoding - write plain text content
|
|
105
|
+
obj.code = nativeCode;
|
|
106
|
+
} else {
|
|
107
|
+
// B64 or default - write Base64 encoded content
|
|
108
|
+
obj.code = base64Code;
|
|
109
|
+
}
|
|
110
|
+
updateCount++;
|
|
72
111
|
}
|
|
73
112
|
|
|
113
|
+
// Scan all elements in array or object
|
|
74
114
|
for (const key in obj) {
|
|
75
|
-
if (
|
|
76
|
-
|
|
115
|
+
if (typeof obj[key] === 'object' && obj[key] !== null) {
|
|
116
|
+
updateRecursive(obj[key]);
|
|
77
117
|
}
|
|
78
118
|
}
|
|
79
|
-
|
|
80
|
-
return false;
|
|
81
119
|
}
|
|
82
120
|
|
|
83
|
-
|
|
121
|
+
updateRecursive(data);
|
|
84
122
|
|
|
85
|
-
if (
|
|
123
|
+
if (updateCount > 0) {
|
|
86
124
|
await fs.writeFile(jsonPath, JSON.stringify(data, null, 2), 'utf8');
|
|
87
|
-
return true;
|
|
88
125
|
}
|
|
89
126
|
|
|
90
|
-
return
|
|
127
|
+
return updateCount;
|
|
91
128
|
}
|
|
92
129
|
|
|
93
130
|
/**
|
|
94
|
-
*
|
|
131
|
+
* Reads CSX file content as plain text (native)
|
|
132
|
+
* @param {string} csxPath - CSX file path
|
|
133
|
+
* @returns {Promise<string>} Plain text content
|
|
134
|
+
*/
|
|
135
|
+
async function readNativeContent(csxPath) {
|
|
136
|
+
return await fs.readFile(csxPath, 'utf8');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Processes a single CSX file
|
|
141
|
+
* Updates ALL referencing JSON files
|
|
142
|
+
* Supports both NAT (native) and B64 (Base64) encoding
|
|
143
|
+
* @param {string} csxPath - CSX file path
|
|
144
|
+
* @param {string} projectRoot - Project root folder
|
|
145
|
+
* @returns {Promise<Object>} Process result
|
|
95
146
|
*/
|
|
96
147
|
async function processCsxFile(csxPath, projectRoot) {
|
|
97
|
-
//
|
|
98
|
-
const
|
|
148
|
+
// Read native content
|
|
149
|
+
const nativeCode = await readNativeContent(csxPath);
|
|
99
150
|
|
|
100
|
-
//
|
|
151
|
+
// Convert to Base64
|
|
152
|
+
const base64Code = Buffer.from(nativeCode).toString('base64');
|
|
153
|
+
|
|
154
|
+
// Find ALL related JSONs (only in paths defined in vnext.config.json)
|
|
101
155
|
const jsonFiles = await findJsonFilesForCsx(csxPath, projectRoot);
|
|
102
156
|
|
|
103
157
|
if (jsonFiles.length === 0) {
|
|
104
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
message: 'No related JSON found',
|
|
161
|
+
updatedJsonCount: 0,
|
|
162
|
+
totalUpdates: 0,
|
|
163
|
+
jsonFiles: []
|
|
164
|
+
};
|
|
105
165
|
}
|
|
106
166
|
|
|
107
|
-
// CSX location
|
|
108
|
-
const csxLocation = getCsxLocation(csxPath);
|
|
167
|
+
// Calculate CSX location
|
|
168
|
+
const csxLocation = getCsxLocation(csxPath, projectRoot);
|
|
169
|
+
|
|
170
|
+
// Update each JSON
|
|
171
|
+
let updatedJsonCount = 0;
|
|
172
|
+
let totalUpdates = 0;
|
|
173
|
+
const updatedFiles = [];
|
|
109
174
|
|
|
110
|
-
// Her JSON'u güncelle
|
|
111
|
-
let updatedCount = 0;
|
|
112
175
|
for (const jsonFile of jsonFiles) {
|
|
113
176
|
try {
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
|
|
177
|
+
const updates = await updateCodeInJson(jsonFile, csxLocation, base64Code, nativeCode);
|
|
178
|
+
if (updates > 0) {
|
|
179
|
+
updatedJsonCount++;
|
|
180
|
+
totalUpdates += updates;
|
|
181
|
+
updatedFiles.push({
|
|
182
|
+
file: path.basename(jsonFile),
|
|
183
|
+
updates: updates
|
|
184
|
+
});
|
|
117
185
|
}
|
|
118
186
|
} catch (error) {
|
|
119
|
-
// Continue with next file
|
|
187
|
+
// Continue with next file on error
|
|
120
188
|
}
|
|
121
189
|
}
|
|
122
190
|
|
|
123
191
|
return {
|
|
124
|
-
success:
|
|
125
|
-
|
|
126
|
-
|
|
192
|
+
success: updatedJsonCount > 0,
|
|
193
|
+
message: updatedJsonCount > 0 ? 'Updated' : 'No references to update',
|
|
194
|
+
updatedJsonCount,
|
|
195
|
+
totalUpdates,
|
|
196
|
+
jsonFiles: updatedFiles
|
|
127
197
|
};
|
|
128
198
|
}
|
|
129
199
|
|
|
130
200
|
/**
|
|
131
|
-
*
|
|
201
|
+
* Finds changed CSX files in Git
|
|
202
|
+
* @param {string} projectRoot - Project root folder
|
|
203
|
+
* @returns {Promise<string[]>} Changed CSX file paths
|
|
132
204
|
*/
|
|
133
205
|
async function getGitChangedCsx(projectRoot) {
|
|
134
206
|
const { exec } = require('child_process');
|
|
135
207
|
const util = require('util');
|
|
136
208
|
const execPromise = util.promisify(exec);
|
|
209
|
+
const fsSync = require('fs');
|
|
137
210
|
|
|
138
211
|
try {
|
|
139
|
-
//
|
|
212
|
+
// Find git root
|
|
140
213
|
const { stdout: gitRoot } = await execPromise('git rev-parse --show-toplevel', { cwd: projectRoot });
|
|
141
214
|
const gitRootDir = gitRoot.trim();
|
|
142
215
|
|
|
143
|
-
//
|
|
216
|
+
// Run git status from git root
|
|
144
217
|
const { stdout } = await execPromise('git status --porcelain', { cwd: gitRootDir });
|
|
145
218
|
const lines = stdout.split('\n').filter(Boolean);
|
|
146
219
|
|
|
@@ -158,7 +231,7 @@ async function getGitChangedCsx(projectRoot) {
|
|
|
158
231
|
.filter(file => {
|
|
159
232
|
// Only .csx files that exist and are in our project
|
|
160
233
|
return file.endsWith('.csx') &&
|
|
161
|
-
|
|
234
|
+
fsSync.existsSync(file) &&
|
|
162
235
|
file.startsWith(path.normalize(projectRoot));
|
|
163
236
|
});
|
|
164
237
|
|
|
@@ -169,18 +242,19 @@ async function getGitChangedCsx(projectRoot) {
|
|
|
169
242
|
}
|
|
170
243
|
|
|
171
244
|
/**
|
|
172
|
-
*
|
|
245
|
+
* Finds all CSX files in discovered components ONLY
|
|
246
|
+
* Does NOT scan folders outside of paths definition
|
|
247
|
+
* @param {string} projectRoot - Project root folder
|
|
248
|
+
* @returns {Promise<string[]>} CSX file paths
|
|
173
249
|
*/
|
|
174
250
|
async function findAllCsx(projectRoot) {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
ignore: ['**/node_modules/**', '**/dist/**']
|
|
178
|
-
});
|
|
179
|
-
return files;
|
|
251
|
+
const { findAllCsxInComponents } = require('./discover');
|
|
252
|
+
return findAllCsxInComponents(projectRoot);
|
|
180
253
|
}
|
|
181
254
|
|
|
182
255
|
module.exports = {
|
|
183
256
|
encodeToBase64,
|
|
257
|
+
readNativeContent,
|
|
184
258
|
findJsonFilesForCsx,
|
|
185
259
|
getCsxLocation,
|
|
186
260
|
updateCodeInJson,
|
|
@@ -188,4 +262,3 @@ module.exports = {
|
|
|
188
262
|
getGitChangedCsx,
|
|
189
263
|
findAllCsx
|
|
190
264
|
};
|
|
191
|
-
|
package/src/lib/db.js
CHANGED
|
@@ -5,11 +5,11 @@ const util = require('util');
|
|
|
5
5
|
const execPromise = util.promisify(exec);
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Tests the DB connection
|
|
9
9
|
*/
|
|
10
10
|
async function testDbConnection(dbConfig) {
|
|
11
11
|
if (dbConfig.useDocker) {
|
|
12
|
-
//
|
|
12
|
+
// Test via Docker
|
|
13
13
|
try {
|
|
14
14
|
const cmd = `docker exec ${dbConfig.dockerContainer} psql -U ${dbConfig.user} -d ${dbConfig.database} -c "SELECT 1;"`;
|
|
15
15
|
await execPromise(cmd);
|
|
@@ -18,7 +18,7 @@ async function testDbConnection(dbConfig) {
|
|
|
18
18
|
return false;
|
|
19
19
|
}
|
|
20
20
|
} else {
|
|
21
|
-
//
|
|
21
|
+
// Direct connection
|
|
22
22
|
const client = new Client({
|
|
23
23
|
host: dbConfig.host,
|
|
24
24
|
port: dbConfig.port,
|
|
@@ -39,15 +39,15 @@ async function testDbConnection(dbConfig) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
42
|
+
* Finds the workflow's ID in the DB
|
|
43
|
+
* NOTE: We don't check version, only the Key (like the bash script)
|
|
44
44
|
*/
|
|
45
45
|
async function getInstanceId(dbConfig, schema, key, version) {
|
|
46
46
|
const dbSchema = schema.replace(/-/g, '_');
|
|
47
47
|
const query = `SELECT "Id" FROM "${dbSchema}"."Instances" WHERE "Key" = $1 ORDER BY "CreatedAt" DESC LIMIT 1`;
|
|
48
48
|
|
|
49
49
|
if (dbConfig.useDocker) {
|
|
50
|
-
// Docker
|
|
50
|
+
// Via Docker - escape double quotes in SQL with backslash
|
|
51
51
|
const cmd = `docker exec ${dbConfig.dockerContainer} psql -U ${dbConfig.user} -d ${dbConfig.database} -t -c "SELECT \\"Id\\" FROM \\"${dbSchema}\\".\\"Instances\\" WHERE \\"Key\\" = '${key}' ORDER BY \\"CreatedAt\\" DESC LIMIT 1"`;
|
|
52
52
|
try {
|
|
53
53
|
const { stdout } = await execPromise(cmd);
|
|
@@ -57,7 +57,7 @@ async function getInstanceId(dbConfig, schema, key, version) {
|
|
|
57
57
|
return null;
|
|
58
58
|
}
|
|
59
59
|
} else {
|
|
60
|
-
//
|
|
60
|
+
// Direct connection
|
|
61
61
|
const client = new Client({
|
|
62
62
|
host: dbConfig.host,
|
|
63
63
|
port: dbConfig.port,
|
|
@@ -78,13 +78,13 @@ async function getInstanceId(dbConfig, schema, key, version) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
81
|
+
* Deletes a workflow from the DB
|
|
82
82
|
*/
|
|
83
83
|
async function deleteWorkflow(dbConfig, schema, instanceId) {
|
|
84
84
|
const dbSchema = schema.replace(/-/g, '_');
|
|
85
85
|
|
|
86
86
|
if (dbConfig.useDocker) {
|
|
87
|
-
// Docker
|
|
87
|
+
// Via Docker - escape double quotes
|
|
88
88
|
const cmd = `docker exec ${dbConfig.dockerContainer} psql -U ${dbConfig.user} -d ${dbConfig.database} -c "DELETE FROM \\"${dbSchema}\\".\\"Instances\\" WHERE \\"Id\\" = '${instanceId}'"`;
|
|
89
89
|
try {
|
|
90
90
|
await execPromise(cmd);
|
|
@@ -93,7 +93,7 @@ async function deleteWorkflow(dbConfig, schema, instanceId) {
|
|
|
93
93
|
return false;
|
|
94
94
|
}
|
|
95
95
|
} else {
|
|
96
|
-
//
|
|
96
|
+
// Direct connection
|
|
97
97
|
const query = `DELETE FROM "${dbSchema}"."Instances" WHERE "Id" = $1`;
|
|
98
98
|
const client = new Client({
|
|
99
99
|
host: dbConfig.host,
|