openxiangda 1.0.234 → 1.0.236
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/lib/delivery-v2-executor.js +216 -3
- package/lib/delivery-v2-package.js +3 -1
- package/lib/release-plan.js +39 -6
- package/package.json +1 -1
|
@@ -232,6 +232,11 @@ function createDeliveryV2Executor(dependencies) {
|
|
|
232
232
|
activeRun,
|
|
233
233
|
input.targets
|
|
234
234
|
);
|
|
235
|
+
restoreDeliveryCheckpointFiles(
|
|
236
|
+
input.executionRoot,
|
|
237
|
+
activeRun,
|
|
238
|
+
executionTarget
|
|
239
|
+
);
|
|
235
240
|
activeRun = await checkpoint(
|
|
236
241
|
input.config,
|
|
237
242
|
executionTarget,
|
|
@@ -297,7 +302,11 @@ function createDeliveryV2Executor(dependencies) {
|
|
|
297
302
|
completed: index + 1,
|
|
298
303
|
total: steps.length,
|
|
299
304
|
},
|
|
300
|
-
result: compactStepResult(step.id, result
|
|
305
|
+
result: compactStepResult(step.id, result, {
|
|
306
|
+
executionRoot: input.executionRoot,
|
|
307
|
+
run: activeRun,
|
|
308
|
+
target: executionTarget,
|
|
309
|
+
}),
|
|
301
310
|
}
|
|
302
311
|
);
|
|
303
312
|
}
|
|
@@ -800,7 +809,7 @@ function packageSummary(manifest, previousManifest) {
|
|
|
800
809
|
};
|
|
801
810
|
}
|
|
802
811
|
|
|
803
|
-
function compactStepResult(stepId, result) {
|
|
812
|
+
function compactStepResult(stepId, result, context = {}) {
|
|
804
813
|
if (!result || typeof result !== 'object') return { ok: true };
|
|
805
814
|
if (stepId === 'app-finalize') {
|
|
806
815
|
return {
|
|
@@ -811,10 +820,212 @@ function compactStepResult(stepId, result) {
|
|
|
811
820
|
null,
|
|
812
821
|
};
|
|
813
822
|
}
|
|
814
|
-
|
|
823
|
+
const compact = {
|
|
815
824
|
ok: true,
|
|
816
825
|
id: result.id || result.release?.id || null,
|
|
817
826
|
};
|
|
827
|
+
const stagedResources = normalizeCheckpointStagedResources(
|
|
828
|
+
Array.isArray(result.stagedResources)
|
|
829
|
+
? result.stagedResources
|
|
830
|
+
: result.stagedResource
|
|
831
|
+
? [result.stagedResource]
|
|
832
|
+
: []
|
|
833
|
+
);
|
|
834
|
+
if (stagedResources.length > 0) {
|
|
835
|
+
compact.stagedResources = stagedResources;
|
|
836
|
+
}
|
|
837
|
+
const satisfiedSelectors = uniqueStrings(result.satisfiedSelectors);
|
|
838
|
+
if (satisfiedSelectors.length > 0) {
|
|
839
|
+
compact.satisfiedSelectors = satisfiedSelectors;
|
|
840
|
+
}
|
|
841
|
+
if (stepId === 'backend-stage') {
|
|
842
|
+
const bindingContracts = readDeliverySupportFile(
|
|
843
|
+
context.executionRoot,
|
|
844
|
+
context.run,
|
|
845
|
+
context.target,
|
|
846
|
+
'backend-binding-contracts.json'
|
|
847
|
+
);
|
|
848
|
+
if (bindingContracts) {
|
|
849
|
+
compact.backendBindingContracts = bindingContracts;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return compact;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function restoreDeliveryCheckpointFiles(executionRoot, run, target) {
|
|
856
|
+
const changeId = String(run?.control?.changeId || '').trim();
|
|
857
|
+
if (!changeId) return { stagedResourceCount: 0, restored: false };
|
|
858
|
+
const checkpointResults = Object.values(run?.result || {});
|
|
859
|
+
const staged = new Map();
|
|
860
|
+
const satisfiedSelectors = [];
|
|
861
|
+
let backendBindingContracts = null;
|
|
862
|
+
for (const result of checkpointResults) {
|
|
863
|
+
for (const resource of normalizeCheckpointStagedResources(
|
|
864
|
+
result?.stagedResources || []
|
|
865
|
+
)) {
|
|
866
|
+
staged.set(checkpointStagedResourceKey(resource), resource);
|
|
867
|
+
}
|
|
868
|
+
satisfiedSelectors.push(...uniqueStrings(result?.satisfiedSelectors));
|
|
869
|
+
if (result?.backendBindingContracts) {
|
|
870
|
+
backendBindingContracts = cloneJson(result.backendBindingContracts);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (staged.size === 0 && !backendBindingContracts) {
|
|
874
|
+
return { stagedResourceCount: 0, restored: false };
|
|
875
|
+
}
|
|
876
|
+
const releaseDir = deliveryReleaseDirectory(
|
|
877
|
+
executionRoot,
|
|
878
|
+
changeId,
|
|
879
|
+
target?.deploymentId
|
|
880
|
+
);
|
|
881
|
+
fs.mkdirSync(releaseDir, { recursive: true });
|
|
882
|
+
const resources = Array.from(staged.values()).sort((left, right) =>
|
|
883
|
+
checkpointStagedResourceKey(left).localeCompare(
|
|
884
|
+
checkpointStagedResourceKey(right)
|
|
885
|
+
)
|
|
886
|
+
);
|
|
887
|
+
if (resources.length > 0) {
|
|
888
|
+
writeJsonAtomic(
|
|
889
|
+
path.join(releaseDir, 'staged-resources.json'),
|
|
890
|
+
resources
|
|
891
|
+
);
|
|
892
|
+
writeJsonAtomic(
|
|
893
|
+
path.join(releaseDir, 'staged-resources.context.json'),
|
|
894
|
+
{
|
|
895
|
+
contractVersion: 'staged_resources_context_v1',
|
|
896
|
+
appType: target.appType,
|
|
897
|
+
profile: target.profileName,
|
|
898
|
+
changeId,
|
|
899
|
+
deploymentId: target.deploymentId || null,
|
|
900
|
+
baselineId: run.control?.baselineId || null,
|
|
901
|
+
clientSessionId: run.control?.clientSessionId || null,
|
|
902
|
+
initializedAt: new Date().toISOString(),
|
|
903
|
+
restoredFromDeliveryRunId: run.id,
|
|
904
|
+
...(uniqueStrings(satisfiedSelectors).length > 0
|
|
905
|
+
? { satisfiedSelectors: uniqueStrings(satisfiedSelectors) }
|
|
906
|
+
: {}),
|
|
907
|
+
}
|
|
908
|
+
);
|
|
909
|
+
}
|
|
910
|
+
if (backendBindingContracts) {
|
|
911
|
+
writeJsonAtomic(
|
|
912
|
+
path.join(releaseDir, 'backend-binding-contracts.json'),
|
|
913
|
+
backendBindingContracts
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
return {
|
|
917
|
+
stagedResourceCount: resources.length,
|
|
918
|
+
restored: true,
|
|
919
|
+
backendBindingContracts: Boolean(backendBindingContracts),
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function readDeliverySupportFile(
|
|
924
|
+
executionRoot,
|
|
925
|
+
run,
|
|
926
|
+
target,
|
|
927
|
+
fileName
|
|
928
|
+
) {
|
|
929
|
+
const changeId = String(run?.control?.changeId || '').trim();
|
|
930
|
+
if (!changeId || !executionRoot) return null;
|
|
931
|
+
const file = path.join(
|
|
932
|
+
deliveryReleaseDirectory(
|
|
933
|
+
executionRoot,
|
|
934
|
+
changeId,
|
|
935
|
+
target?.deploymentId
|
|
936
|
+
),
|
|
937
|
+
fileName
|
|
938
|
+
);
|
|
939
|
+
if (!fs.existsSync(file)) return null;
|
|
940
|
+
try {
|
|
941
|
+
return cloneJson(JSON.parse(fs.readFileSync(file, 'utf8')));
|
|
942
|
+
} catch {
|
|
943
|
+
throw deliveryError(
|
|
944
|
+
'DELIVERY_CHECKPOINT_FILE_INVALID',
|
|
945
|
+
`${fileName} 不是合法 JSON,不能创建可恢复检查点`,
|
|
946
|
+
false
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
function deliveryReleaseDirectory(executionRoot, changeId, deploymentId) {
|
|
952
|
+
const normalizedChangeId = String(changeId || '').trim();
|
|
953
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(normalizedChangeId)) {
|
|
954
|
+
throw deliveryError(
|
|
955
|
+
'DELIVERY_CONTROL_INVALID',
|
|
956
|
+
'ReleaseRun changeId 不能用于恢复暂存资源',
|
|
957
|
+
false
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
const base = path.join(
|
|
961
|
+
path.resolve(executionRoot),
|
|
962
|
+
'.openxiangda',
|
|
963
|
+
'releases',
|
|
964
|
+
normalizedChangeId
|
|
965
|
+
);
|
|
966
|
+
return deploymentId ? path.join(base, String(deploymentId)) : base;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
function normalizeCheckpointStagedResources(values) {
|
|
970
|
+
if (!Array.isArray(values)) return [];
|
|
971
|
+
const supportedKinds = new Set([
|
|
972
|
+
'RuntimeRelease',
|
|
973
|
+
'PageRelease',
|
|
974
|
+
'BackendRelease',
|
|
975
|
+
'WorkflowRelease',
|
|
976
|
+
'FormRelease',
|
|
977
|
+
]);
|
|
978
|
+
return values
|
|
979
|
+
.filter(value => value && typeof value === 'object' && !Array.isArray(value))
|
|
980
|
+
.filter(value => supportedKinds.has(String(value.kind || '').trim()))
|
|
981
|
+
.filter(value => {
|
|
982
|
+
const releaseId = String(
|
|
983
|
+
value.identity?.releaseId || value.releaseId || ''
|
|
984
|
+
).trim();
|
|
985
|
+
const hash = String(value.hash || value.resourceHash || '')
|
|
986
|
+
.trim()
|
|
987
|
+
.toLowerCase();
|
|
988
|
+
const formUuid = String(
|
|
989
|
+
value.identity?.formUuid || value.formUuid || ''
|
|
990
|
+
).trim();
|
|
991
|
+
return (
|
|
992
|
+
Boolean(releaseId) &&
|
|
993
|
+
/^[a-f0-9]{64}$/.test(hash) &&
|
|
994
|
+
(value.kind !== 'FormRelease' || Boolean(formUuid))
|
|
995
|
+
);
|
|
996
|
+
})
|
|
997
|
+
.map(cloneJson);
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
function checkpointStagedResourceKey(resource) {
|
|
1001
|
+
if (resource.kind === 'FormRelease') {
|
|
1002
|
+
return `FormRelease:${
|
|
1003
|
+
resource.identity?.formUuid || resource.formUuid
|
|
1004
|
+
}`;
|
|
1005
|
+
}
|
|
1006
|
+
return String(resource.kind);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
function uniqueStrings(values) {
|
|
1010
|
+
return Array.from(
|
|
1011
|
+
new Set(
|
|
1012
|
+
(Array.isArray(values) ? values : [])
|
|
1013
|
+
.map(value => String(value || '').trim())
|
|
1014
|
+
.filter(Boolean)
|
|
1015
|
+
)
|
|
1016
|
+
).sort();
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function writeJsonAtomic(file, value) {
|
|
1020
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
1021
|
+
fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, {
|
|
1022
|
+
mode: 0o600,
|
|
1023
|
+
});
|
|
1024
|
+
fs.renameSync(temporary, file);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
function cloneJson(value) {
|
|
1028
|
+
return JSON.parse(JSON.stringify(value));
|
|
818
1029
|
}
|
|
819
1030
|
|
|
820
1031
|
function structuredFailure(error, runId, stage, component) {
|
|
@@ -949,7 +1160,9 @@ function assertDependencies(value) {
|
|
|
949
1160
|
|
|
950
1161
|
module.exports = {
|
|
951
1162
|
CONFIG_TARGET_BY_RESOURCE_TYPE,
|
|
1163
|
+
compactStepResult,
|
|
952
1164
|
createDeliveryV2Executor,
|
|
953
1165
|
releaseTargets,
|
|
1166
|
+
restoreDeliveryCheckpointFiles,
|
|
954
1167
|
structuredFailure,
|
|
955
1168
|
};
|
|
@@ -13,6 +13,7 @@ const ROOT_SOURCE_FILES = new Set([
|
|
|
13
13
|
'package.json',
|
|
14
14
|
'package-lock.json',
|
|
15
15
|
'pnpm-lock.yaml',
|
|
16
|
+
'pnpm-workspace.yaml',
|
|
16
17
|
'yarn.lock',
|
|
17
18
|
'postcss.config.cjs',
|
|
18
19
|
'postcss.config.js',
|
|
@@ -111,10 +112,11 @@ function shouldIncludeSourceFile(relativePath) {
|
|
|
111
112
|
const segments = normalized.split('/');
|
|
112
113
|
if (segments.some(segment => IGNORED_SEGMENTS.has(segment))) return false;
|
|
113
114
|
if (ROOT_SOURCE_FILES.has(normalized)) return true;
|
|
115
|
+
if (/^tsconfig(?:\.[^/]+)?\.json$/.test(normalized)) return true;
|
|
114
116
|
return (
|
|
115
117
|
normalized.startsWith('src/') ||
|
|
116
118
|
normalized.startsWith('public/') ||
|
|
117
|
-
normalized
|
|
119
|
+
normalized.startsWith('scripts/')
|
|
118
120
|
);
|
|
119
121
|
}
|
|
120
122
|
|
package/lib/release-plan.js
CHANGED
|
@@ -251,11 +251,14 @@ function buildTargetedResourceReleaseSteps(
|
|
|
251
251
|
function buildDirectConfigurationReleaseSteps(
|
|
252
252
|
targets,
|
|
253
253
|
profileArg,
|
|
254
|
-
changeId
|
|
254
|
+
changeId,
|
|
255
|
+
options = {}
|
|
255
256
|
) {
|
|
257
|
+
const excludedTypes = new Set(options.excludeTypes || []);
|
|
256
258
|
return Object.entries(targets.resourceSelectors || {})
|
|
257
259
|
.filter(
|
|
258
260
|
([type, selectors]) =>
|
|
261
|
+
!excludedTypes.has(type) &&
|
|
259
262
|
directResourceTypeForKey(type) &&
|
|
260
263
|
uniqueSorted(selectors || []).length > 0
|
|
261
264
|
)
|
|
@@ -318,11 +321,24 @@ function buildWorkspaceReleaseSteps(
|
|
|
318
321
|
Object.values(targets.resourceSelectors || {}).some(
|
|
319
322
|
selectors => (selectors || []).length > 0
|
|
320
323
|
);
|
|
324
|
+
const formPermissionGroups = uniqueSorted(
|
|
325
|
+
targets.resourceSelectors?.formPermissionGroups || []
|
|
326
|
+
);
|
|
327
|
+
const stagesFormRelease =
|
|
328
|
+
runtimeMode === 'react-spa' &&
|
|
329
|
+
(targets.forms.length > 0 || formPermissionGroups.length > 0);
|
|
321
330
|
const directConfigurationSteps =
|
|
322
331
|
buildDirectConfigurationReleaseSteps(
|
|
323
332
|
targets,
|
|
324
333
|
profileArg,
|
|
325
|
-
changeId
|
|
334
|
+
changeId,
|
|
335
|
+
{
|
|
336
|
+
// A FormRelease is a single immutable snapshot. Publishing form
|
|
337
|
+
// settings and permission groups as sibling steps from the same live
|
|
338
|
+
// parent lets the second descriptor replace the first and discard its
|
|
339
|
+
// schema. Stage both resource types in one bundle instead.
|
|
340
|
+
excludeTypes: stagesFormRelease ? ['formPermissionGroups'] : [],
|
|
341
|
+
}
|
|
326
342
|
);
|
|
327
343
|
if (runtimeMode === 'react-spa') {
|
|
328
344
|
if (targets.forms.length > 0) {
|
|
@@ -336,6 +352,23 @@ function buildWorkspaceReleaseSteps(
|
|
|
336
352
|
)
|
|
337
353
|
)
|
|
338
354
|
);
|
|
355
|
+
}
|
|
356
|
+
if (stagesFormRelease) {
|
|
357
|
+
const resourceTypes = [
|
|
358
|
+
...(targets.forms.length > 0 ? ['form-setting'] : []),
|
|
359
|
+
...(formPermissionGroups.length > 0
|
|
360
|
+
? ['form-permission-group']
|
|
361
|
+
: []),
|
|
362
|
+
];
|
|
363
|
+
const qualify = resourceTypes.length > 1;
|
|
364
|
+
const selectors = [
|
|
365
|
+
...targets.forms.map(code =>
|
|
366
|
+
qualify ? `form-setting:${code}` : code
|
|
367
|
+
),
|
|
368
|
+
...formPermissionGroups.map(code =>
|
|
369
|
+
qualify ? `form-permission-group:${code}` : code
|
|
370
|
+
),
|
|
371
|
+
];
|
|
339
372
|
steps.push(
|
|
340
373
|
createStep(
|
|
341
374
|
'form-stage',
|
|
@@ -343,9 +376,9 @@ function buildWorkspaceReleaseSteps(
|
|
|
343
376
|
[
|
|
344
377
|
'resource',
|
|
345
378
|
'publish',
|
|
346
|
-
'
|
|
379
|
+
resourceTypes.join(','),
|
|
347
380
|
'--only',
|
|
348
|
-
|
|
381
|
+
selectors.join(','),
|
|
349
382
|
],
|
|
350
383
|
profileArg,
|
|
351
384
|
changeId
|
|
@@ -378,7 +411,7 @@ function buildWorkspaceReleaseSteps(
|
|
|
378
411
|
);
|
|
379
412
|
}
|
|
380
413
|
if (
|
|
381
|
-
|
|
414
|
+
stagesFormRelease ||
|
|
382
415
|
targets.functions.length > 0 ||
|
|
383
416
|
targets.automations.length > 0 ||
|
|
384
417
|
targets.workflows.length > 0 ||
|
|
@@ -386,7 +419,7 @@ function buildWorkspaceReleaseSteps(
|
|
|
386
419
|
directConfigurationSteps.length > 0
|
|
387
420
|
) {
|
|
388
421
|
const hasStagedChildren =
|
|
389
|
-
|
|
422
|
+
stagesFormRelease ||
|
|
390
423
|
targets.functions.length > 0 ||
|
|
391
424
|
targets.automations.length > 0 ||
|
|
392
425
|
targets.workflows.length > 0 ||
|