openxiangda 1.0.172 → 1.0.174
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 +6 -7
- package/lib/change-baseline.js +117 -18
- package/lib/cli.js +984 -125
- package/lib/form-schema-loader.js +166 -0
- package/lib/git-remote-identity.js +89 -0
- package/lib/release-mainline.js +59 -24
- package/lib/release-plan.js +294 -0
- package/lib/runtime-lineage.js +23 -3
- package/lib/sdd.js +246 -56
- package/openxiangda-skills/SKILL.md +8 -6
- package/openxiangda-skills/references/openxiangda-api.md +6 -6
- package/openxiangda-skills/references/pages/page-sdk.md +1 -1
- package/openxiangda-skills/references/permissions-settings.md +4 -3
- package/openxiangda-skills/skills/openxiangda-core/SKILL.md +8 -6
- package/openxiangda-skills/skills/openxiangda-permission-settings/SKILL.md +5 -4
- package/package.json +2 -1
- package/packages/sdk/dist/runtime/react.d.mts +3 -0
- package/packages/sdk/dist/runtime/react.d.ts +3 -0
- package/templates/openxiangda-react-spa/AGENTS.md +5 -11
- package/templates/openxiangda-react-spa/scripts/deploy.mjs +2 -8
- package/templates/sy-lowcode-app-workspace/AGENTS.md +2 -2
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
function uniqueSorted(values = []) {
|
|
4
|
+
return Array.from(
|
|
5
|
+
new Set(
|
|
6
|
+
values
|
|
7
|
+
.map(value => String(value || '').trim())
|
|
8
|
+
.filter(Boolean)
|
|
9
|
+
)
|
|
10
|
+
).sort();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeReleaseTargets(targets = {}, runtimeMode = 'legacy') {
|
|
14
|
+
const logicalTargets = {
|
|
15
|
+
forms: uniqueSorted(targets.forms || []),
|
|
16
|
+
pages: uniqueSorted(targets.pages || []),
|
|
17
|
+
functions: uniqueSorted(targets.functions || []),
|
|
18
|
+
automations: uniqueSorted(targets.automations || []),
|
|
19
|
+
workflows: uniqueSorted(targets.workflows || []),
|
|
20
|
+
jsCodeNodes: uniqueSorted(targets.jsCodeNodes || []),
|
|
21
|
+
resources: Boolean(targets.resources),
|
|
22
|
+
runtime: Boolean(targets.runtime),
|
|
23
|
+
other: uniqueSorted(targets.other || []),
|
|
24
|
+
};
|
|
25
|
+
const activationTargets = {
|
|
26
|
+
...logicalTargets,
|
|
27
|
+
pages:
|
|
28
|
+
runtimeMode === 'react-spa'
|
|
29
|
+
? []
|
|
30
|
+
: logicalTargets.pages,
|
|
31
|
+
runtime:
|
|
32
|
+
runtimeMode === 'react-spa'
|
|
33
|
+
? logicalTargets.runtime || logicalTargets.pages.length > 0
|
|
34
|
+
: logicalTargets.runtime,
|
|
35
|
+
};
|
|
36
|
+
return { logicalTargets, activationTargets };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function buildTargetedResourceReleaseCommands(
|
|
40
|
+
targets,
|
|
41
|
+
profileArg,
|
|
42
|
+
changeId
|
|
43
|
+
) {
|
|
44
|
+
return buildTargetedResourceReleaseSteps(
|
|
45
|
+
targets,
|
|
46
|
+
profileArg,
|
|
47
|
+
changeId
|
|
48
|
+
).map(step => step.command);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function commandFromArgs(args) {
|
|
52
|
+
return ['openxiangda', ...args]
|
|
53
|
+
.map(value => {
|
|
54
|
+
const text = String(value);
|
|
55
|
+
return /^[A-Za-z0-9_./,:@<>=+-]+$/.test(text)
|
|
56
|
+
? text
|
|
57
|
+
: `'${text.replace(/'/g, `'\\''`)}'`;
|
|
58
|
+
})
|
|
59
|
+
.join(' ');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function createStep(id, args, options = {}) {
|
|
63
|
+
return {
|
|
64
|
+
id,
|
|
65
|
+
args,
|
|
66
|
+
command: commandFromArgs(args),
|
|
67
|
+
writes: options.writes !== false,
|
|
68
|
+
stagedKind: options.stagedKind || null,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function appendProfileAndChange(args, profileArg, changeId) {
|
|
73
|
+
args.push('--profile', profileArg);
|
|
74
|
+
if (changeId) args.push('--change', changeId);
|
|
75
|
+
return args;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function buildTargetedResourceReleaseSteps(
|
|
79
|
+
targets,
|
|
80
|
+
profileArg,
|
|
81
|
+
changeId
|
|
82
|
+
) {
|
|
83
|
+
const steps = [];
|
|
84
|
+
const functions = targets.functions || [];
|
|
85
|
+
const automations = targets.automations || [];
|
|
86
|
+
if (functions.length > 0 || automations.length > 0) {
|
|
87
|
+
const resourceTypes = [
|
|
88
|
+
...(functions.length > 0 ? ['function'] : []),
|
|
89
|
+
...(automations.length > 0 ? ['automation'] : []),
|
|
90
|
+
];
|
|
91
|
+
const qualify = resourceTypes.length > 1;
|
|
92
|
+
const selectors = [
|
|
93
|
+
...functions.map(code => (qualify ? `function:${code}` : code)),
|
|
94
|
+
...automations.map(code => (qualify ? `automation:${code}` : code)),
|
|
95
|
+
];
|
|
96
|
+
steps.push(
|
|
97
|
+
createStep(
|
|
98
|
+
'backend-stage',
|
|
99
|
+
appendProfileAndChange(
|
|
100
|
+
[
|
|
101
|
+
'resource',
|
|
102
|
+
'publish',
|
|
103
|
+
resourceTypes.join(','),
|
|
104
|
+
'--only',
|
|
105
|
+
selectors.join(','),
|
|
106
|
+
'--stage-only',
|
|
107
|
+
],
|
|
108
|
+
profileArg,
|
|
109
|
+
changeId
|
|
110
|
+
),
|
|
111
|
+
{ stagedKind: 'BackendRelease' }
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const workflows = targets.workflows || [];
|
|
116
|
+
if (workflows.length > 0) {
|
|
117
|
+
steps.push(
|
|
118
|
+
createStep(
|
|
119
|
+
'workflow-publish',
|
|
120
|
+
appendProfileAndChange(
|
|
121
|
+
[
|
|
122
|
+
'resource',
|
|
123
|
+
'publish',
|
|
124
|
+
'workflow',
|
|
125
|
+
'--only',
|
|
126
|
+
workflows.join(','),
|
|
127
|
+
],
|
|
128
|
+
profileArg,
|
|
129
|
+
changeId
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return steps;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function buildWorkspaceReleaseCommands(
|
|
138
|
+
targetsInput,
|
|
139
|
+
runtimeMode,
|
|
140
|
+
profile,
|
|
141
|
+
changeId
|
|
142
|
+
) {
|
|
143
|
+
return buildWorkspaceReleaseSteps(
|
|
144
|
+
targetsInput,
|
|
145
|
+
runtimeMode,
|
|
146
|
+
profile,
|
|
147
|
+
changeId
|
|
148
|
+
).map(step => step.command);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function buildWorkspaceReleaseSteps(
|
|
152
|
+
targetsInput,
|
|
153
|
+
runtimeMode,
|
|
154
|
+
profile,
|
|
155
|
+
changeId
|
|
156
|
+
) {
|
|
157
|
+
const profileArg = profile || '<profile>';
|
|
158
|
+
const { activationTargets: targets } = normalizeReleaseTargets(
|
|
159
|
+
targetsInput,
|
|
160
|
+
runtimeMode
|
|
161
|
+
);
|
|
162
|
+
const steps = [];
|
|
163
|
+
const hasResourceTargets =
|
|
164
|
+
Boolean(targets.resources) ||
|
|
165
|
+
targets.functions.length > 0 ||
|
|
166
|
+
targets.automations.length > 0 ||
|
|
167
|
+
targets.workflows.length > 0 ||
|
|
168
|
+
targets.jsCodeNodes.length > 0;
|
|
169
|
+
if (runtimeMode === 'react-spa') {
|
|
170
|
+
if (targets.forms.length > 0) {
|
|
171
|
+
steps.push(
|
|
172
|
+
createStep(
|
|
173
|
+
'form-stage',
|
|
174
|
+
appendProfileAndChange(
|
|
175
|
+
[
|
|
176
|
+
'resource',
|
|
177
|
+
'publish',
|
|
178
|
+
'form-setting',
|
|
179
|
+
'--only',
|
|
180
|
+
targets.forms.join(','),
|
|
181
|
+
],
|
|
182
|
+
profileArg,
|
|
183
|
+
changeId
|
|
184
|
+
),
|
|
185
|
+
{ stagedKind: 'FormRelease' }
|
|
186
|
+
)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
if (hasResourceTargets) {
|
|
190
|
+
steps.push(
|
|
191
|
+
...buildTargetedResourceReleaseSteps(
|
|
192
|
+
targets,
|
|
193
|
+
profileArg,
|
|
194
|
+
changeId
|
|
195
|
+
)
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
if (targets.runtime) {
|
|
199
|
+
steps.push(
|
|
200
|
+
createStep(
|
|
201
|
+
'runtime-stage',
|
|
202
|
+
appendProfileAndChange(
|
|
203
|
+
['runtime', 'deploy', '--no-activate'],
|
|
204
|
+
profileArg,
|
|
205
|
+
changeId
|
|
206
|
+
),
|
|
207
|
+
{ stagedKind: 'RuntimeRelease' }
|
|
208
|
+
)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
if (
|
|
212
|
+
targets.forms.length > 0 ||
|
|
213
|
+
targets.functions.length > 0 ||
|
|
214
|
+
targets.automations.length > 0 ||
|
|
215
|
+
targets.runtime
|
|
216
|
+
) {
|
|
217
|
+
steps.push(
|
|
218
|
+
createStep(
|
|
219
|
+
'app-finalize',
|
|
220
|
+
appendProfileAndChange(
|
|
221
|
+
[
|
|
222
|
+
'release',
|
|
223
|
+
'app-finalize',
|
|
224
|
+
'--staged-resources-json',
|
|
225
|
+
`.openxiangda/releases/${changeId || 'change'}/staged-resources.json`,
|
|
226
|
+
],
|
|
227
|
+
profileArg,
|
|
228
|
+
changeId
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
return steps;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const onlyTargets = [
|
|
237
|
+
...targets.forms.map(code => `forms/${code}`),
|
|
238
|
+
...targets.pages.map(code => `pages/${code}`),
|
|
239
|
+
];
|
|
240
|
+
if (onlyTargets.length > 0) {
|
|
241
|
+
steps.push(
|
|
242
|
+
createStep(
|
|
243
|
+
'workspace-publish',
|
|
244
|
+
appendProfileAndChange(
|
|
245
|
+
[
|
|
246
|
+
'workspace',
|
|
247
|
+
'publish',
|
|
248
|
+
'--only',
|
|
249
|
+
onlyTargets.join(','),
|
|
250
|
+
'--skip-resources',
|
|
251
|
+
],
|
|
252
|
+
profileArg,
|
|
253
|
+
changeId
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
if (hasResourceTargets) {
|
|
259
|
+
steps.push(
|
|
260
|
+
...buildTargetedResourceReleaseSteps(targets, profileArg, changeId)
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
return steps;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function releasePlanHash(value) {
|
|
267
|
+
return crypto
|
|
268
|
+
.createHash('sha256')
|
|
269
|
+
.update(canonicalJson(value))
|
|
270
|
+
.digest('hex');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function canonicalJson(value) {
|
|
274
|
+
if (Array.isArray(value)) {
|
|
275
|
+
return `[${value.map(item => canonicalJson(item)).join(',')}]`;
|
|
276
|
+
}
|
|
277
|
+
if (value && typeof value === 'object') {
|
|
278
|
+
return `{${Object.keys(value)
|
|
279
|
+
.sort()
|
|
280
|
+
.map(key => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
|
|
281
|
+
.join(',')}}`;
|
|
282
|
+
}
|
|
283
|
+
return JSON.stringify(value);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
module.exports = {
|
|
287
|
+
buildTargetedResourceReleaseCommands,
|
|
288
|
+
buildTargetedResourceReleaseSteps,
|
|
289
|
+
buildWorkspaceReleaseCommands,
|
|
290
|
+
buildWorkspaceReleaseSteps,
|
|
291
|
+
commandFromArgs,
|
|
292
|
+
normalizeReleaseTargets,
|
|
293
|
+
releasePlanHash,
|
|
294
|
+
};
|
package/lib/runtime-lineage.js
CHANGED
|
@@ -2,6 +2,7 @@ const path = require('path');
|
|
|
2
2
|
const { spawnSync } = require('child_process');
|
|
3
3
|
|
|
4
4
|
const { readGitSourceBase } = require('./change-baseline');
|
|
5
|
+
const { identitiesIntersect } = require('./git-remote-identity');
|
|
5
6
|
|
|
6
7
|
const MIN_ROLLBACK_REASON_LENGTH = 8;
|
|
7
8
|
const PURE_RUNTIME_OUTPUT_PREFIXES = Object.freeze([
|
|
@@ -58,7 +59,13 @@ function prepareRuntimeDeployLineage(options = {}) {
|
|
|
58
59
|
|
|
59
60
|
const legacyBootstrap = Boolean(parentReleaseId && !parentSourceRevision);
|
|
60
61
|
if (parentSourceRevision) {
|
|
61
|
-
const sameRepository =
|
|
62
|
+
const sameRepository = identitiesIntersect(
|
|
63
|
+
[
|
|
64
|
+
parentSourceRevision.repo,
|
|
65
|
+
...(parentSourceRevision.repoAliases || []),
|
|
66
|
+
],
|
|
67
|
+
[sourceRevision.repo, ...(sourceRevision.repoAliases || [])]
|
|
68
|
+
);
|
|
62
69
|
const descendsFromActive =
|
|
63
70
|
sameRepository &&
|
|
64
71
|
isGitAncestor(cwd, parentSourceRevision.baseCommit, sourceRevision.baseCommit);
|
|
@@ -73,7 +80,13 @@ function prepareRuntimeDeployLineage(options = {}) {
|
|
|
73
80
|
);
|
|
74
81
|
}
|
|
75
82
|
return stripUndefinedValues({
|
|
76
|
-
sourceRevision
|
|
83
|
+
sourceRevision: sameRepository
|
|
84
|
+
? {
|
|
85
|
+
...sourceRevision,
|
|
86
|
+
repo: parentSourceRevision.repo,
|
|
87
|
+
repositoryId: parentSourceRevision.repo,
|
|
88
|
+
}
|
|
89
|
+
: sourceRevision,
|
|
77
90
|
parentReleaseId,
|
|
78
91
|
parentSourceRevision,
|
|
79
92
|
lineageBypassReason: rollback.enabled ? rollback.reason : undefined,
|
|
@@ -246,7 +259,14 @@ function normalizeRuntimeSourceRevision(value, options = {}) {
|
|
|
246
259
|
'线上 Runtime source revision 缺少有效的 repo/baseCommit/treeHash'
|
|
247
260
|
);
|
|
248
261
|
}
|
|
249
|
-
|
|
262
|
+
const repoAliases = Array.from(
|
|
263
|
+
new Set(
|
|
264
|
+
[repo, ...(Array.isArray(input.repoAliases) ? input.repoAliases : [])]
|
|
265
|
+
.map(item => String(item || '').trim().toLowerCase())
|
|
266
|
+
.filter(item => /^sha256:[0-9a-f]{64}$/.test(item))
|
|
267
|
+
)
|
|
268
|
+
);
|
|
269
|
+
return { repo, repoAliases, baseCommit, treeHash };
|
|
250
270
|
}
|
|
251
271
|
|
|
252
272
|
function normalizeAppSnapshot(value) {
|