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,166 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { builtinModules, createRequire } = require('module');
|
|
4
|
+
const esbuild = require('esbuild');
|
|
5
|
+
|
|
6
|
+
function resolveWorkspaceAlias(workspaceRoot, importPath) {
|
|
7
|
+
const raw = path.join(
|
|
8
|
+
workspaceRoot,
|
|
9
|
+
'src',
|
|
10
|
+
String(importPath || '').replace(/^@\//, '')
|
|
11
|
+
);
|
|
12
|
+
const candidates = [
|
|
13
|
+
raw,
|
|
14
|
+
`${raw}.ts`,
|
|
15
|
+
`${raw}.tsx`,
|
|
16
|
+
`${raw}.js`,
|
|
17
|
+
`${raw}.mjs`,
|
|
18
|
+
`${raw}.cjs`,
|
|
19
|
+
path.join(raw, 'index.ts'),
|
|
20
|
+
path.join(raw, 'index.tsx'),
|
|
21
|
+
path.join(raw, 'index.js'),
|
|
22
|
+
];
|
|
23
|
+
return candidates.find(candidate => fs.existsSync(candidate)) || raw;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function loadLocalFormSchema(workspaceRoot, formCode) {
|
|
27
|
+
const schemaPath = path.join(
|
|
28
|
+
workspaceRoot,
|
|
29
|
+
'src',
|
|
30
|
+
'forms',
|
|
31
|
+
formCode,
|
|
32
|
+
'schema.ts'
|
|
33
|
+
);
|
|
34
|
+
if (!fs.existsSync(schemaPath)) return null;
|
|
35
|
+
const external = Array.from(
|
|
36
|
+
new Set([...builtinModules, ...builtinModules.map(name => `node:${name}`)])
|
|
37
|
+
);
|
|
38
|
+
const built = await esbuild.build({
|
|
39
|
+
entryPoints: [schemaPath],
|
|
40
|
+
bundle: true,
|
|
41
|
+
format: 'cjs',
|
|
42
|
+
platform: 'node',
|
|
43
|
+
target: ['node20'],
|
|
44
|
+
write: false,
|
|
45
|
+
logLevel: 'silent',
|
|
46
|
+
external,
|
|
47
|
+
plugins: [
|
|
48
|
+
{
|
|
49
|
+
name: 'openxiangda-form-schema-workspace-alias',
|
|
50
|
+
setup(build) {
|
|
51
|
+
build.onResolve({ filter: /^@\// }, args => ({
|
|
52
|
+
path: resolveWorkspaceAlias(workspaceRoot, args.path),
|
|
53
|
+
}));
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
const code = built.outputFiles?.[0]?.text;
|
|
59
|
+
if (!code) {
|
|
60
|
+
throw new Error(`FORM_SCHEMA_BUILD_FAILED: ${formCode} 未生成 schema bundle`);
|
|
61
|
+
}
|
|
62
|
+
const schemaModule = { exports: {} };
|
|
63
|
+
const localRequire = createRequire(schemaPath);
|
|
64
|
+
const evaluate = new Function(
|
|
65
|
+
'module',
|
|
66
|
+
'exports',
|
|
67
|
+
'require',
|
|
68
|
+
'__filename',
|
|
69
|
+
'__dirname',
|
|
70
|
+
code
|
|
71
|
+
);
|
|
72
|
+
evaluate(
|
|
73
|
+
schemaModule,
|
|
74
|
+
schemaModule.exports,
|
|
75
|
+
localRequire,
|
|
76
|
+
schemaPath,
|
|
77
|
+
path.dirname(schemaPath)
|
|
78
|
+
);
|
|
79
|
+
const exported = schemaModule.exports?.default || schemaModule.exports;
|
|
80
|
+
return normalizeLocalFormSchema(exported, formCode);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function normalizeLocalFormSchema(schema, formCode) {
|
|
84
|
+
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
|
|
85
|
+
throw new Error(`FORM_SCHEMA_INVALID: ${formCode} 默认导出必须是对象`);
|
|
86
|
+
}
|
|
87
|
+
if (!schema.formMeta || typeof schema.formMeta !== 'object') {
|
|
88
|
+
throw new Error(`FORM_SCHEMA_INVALID: ${formCode}.formMeta 缺失`);
|
|
89
|
+
}
|
|
90
|
+
if (!Array.isArray(schema.fields) || schema.fields.length === 0) {
|
|
91
|
+
throw new Error(`FORM_SCHEMA_INVALID: ${formCode}.fields 不能为空`);
|
|
92
|
+
}
|
|
93
|
+
const seen = new Set();
|
|
94
|
+
const components = schema.fields.map((field, index) => {
|
|
95
|
+
const fieldId = String(field?.fieldId || '').trim();
|
|
96
|
+
const componentName = normalizeComponentName(field?.componentName);
|
|
97
|
+
if (!fieldId || !componentName) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`FORM_SCHEMA_INVALID: ${formCode}.fields[${index}] 缺少 fieldId/componentName`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (seen.has(fieldId)) {
|
|
103
|
+
throw new Error(`FORM_SCHEMA_INVALID: ${formCode} fieldId 重复: ${fieldId}`);
|
|
104
|
+
}
|
|
105
|
+
seen.add(fieldId);
|
|
106
|
+
return {
|
|
107
|
+
componentName,
|
|
108
|
+
id: field.id || `${fieldId}_${index + 1}`,
|
|
109
|
+
title: field.label || fieldId,
|
|
110
|
+
hidden: false,
|
|
111
|
+
isLocked: false,
|
|
112
|
+
condition: true,
|
|
113
|
+
conditionGroup: '',
|
|
114
|
+
props: {
|
|
115
|
+
...field,
|
|
116
|
+
componentName,
|
|
117
|
+
isFormComponent: true,
|
|
118
|
+
fieldId,
|
|
119
|
+
label: field.label || fieldId,
|
|
120
|
+
tips: field.tips || '',
|
|
121
|
+
value: field.value || '',
|
|
122
|
+
placeholder: field.placeholder || '',
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
const pageSchema = {
|
|
127
|
+
version: '2.0',
|
|
128
|
+
componentsTree: [
|
|
129
|
+
{
|
|
130
|
+
componentName: 'Page',
|
|
131
|
+
id: `${schema.formMeta.formUuid || formCode}_page`,
|
|
132
|
+
props: {},
|
|
133
|
+
children: components,
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
return {
|
|
138
|
+
name: schema.formMeta.title || formCode,
|
|
139
|
+
schema: JSON.stringify(pageSchema),
|
|
140
|
+
packages: JSON.stringify({}),
|
|
141
|
+
formType: normalizeFormType(
|
|
142
|
+
schema.formMeta.formType || schema.template?.formType
|
|
143
|
+
),
|
|
144
|
+
fieldCount: components.length,
|
|
145
|
+
sourcePath: `src/forms/${formCode}/schema.ts`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeComponentName(value) {
|
|
150
|
+
const name = String(value || '').trim();
|
|
151
|
+
return name === 'TextAreaField' ? 'TextareaField' : name;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function normalizeFormType(value) {
|
|
155
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
156
|
+
return ['process', 'workflow', 'flow', 'flowform', 'processform'].includes(
|
|
157
|
+
normalized
|
|
158
|
+
)
|
|
159
|
+
? 'process'
|
|
160
|
+
: 'receipt';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = {
|
|
164
|
+
loadLocalFormSchema,
|
|
165
|
+
normalizeLocalFormSchema,
|
|
166
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
function legacyRemoteIdentity(value) {
|
|
4
|
+
const remote = String(value || '').trim();
|
|
5
|
+
try {
|
|
6
|
+
const url = new URL(remote);
|
|
7
|
+
url.username = '';
|
|
8
|
+
url.password = '';
|
|
9
|
+
url.search = '';
|
|
10
|
+
url.hash = '';
|
|
11
|
+
return url.toString();
|
|
12
|
+
} catch {
|
|
13
|
+
return remote
|
|
14
|
+
.replace(/^[^@/]+@([^:]+):/, '$1:')
|
|
15
|
+
.replace(/[?#].*$/, '');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function toggleDotGit(value) {
|
|
20
|
+
const normalized = String(value || '').replace(/\/+$/, '');
|
|
21
|
+
return /\.git$/i.test(normalized)
|
|
22
|
+
? normalized.replace(/\.git$/i, '')
|
|
23
|
+
: `${normalized}.git`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function canonicalRemoteIdentity(value) {
|
|
27
|
+
const legacy = legacyRemoteIdentity(value).replace(/\/+$/, '');
|
|
28
|
+
try {
|
|
29
|
+
const url = new URL(legacy);
|
|
30
|
+
const host = url.hostname.toLowerCase();
|
|
31
|
+
const port = url.port ? `:${url.port}` : '';
|
|
32
|
+
const pathname = url.pathname
|
|
33
|
+
.replace(/^\/+|\/+$/g, '')
|
|
34
|
+
.replace(/\.git$/i, '');
|
|
35
|
+
return `${host}${port}/${pathname}`.replace(/\/+$/, '');
|
|
36
|
+
} catch {
|
|
37
|
+
return legacy
|
|
38
|
+
.replace(/^([^:]+):/, '$1/')
|
|
39
|
+
.replace(/\.git$/i, '')
|
|
40
|
+
.replace(/\/+$/, '');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function remoteIdentityVariants(value) {
|
|
45
|
+
const legacy = legacyRemoteIdentity(value).replace(/\/+$/, '');
|
|
46
|
+
return Array.from(
|
|
47
|
+
new Set(
|
|
48
|
+
[
|
|
49
|
+
legacy,
|
|
50
|
+
toggleDotGit(legacy),
|
|
51
|
+
canonicalRemoteIdentity(legacy),
|
|
52
|
+
].filter(Boolean)
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function sha256(value) {
|
|
58
|
+
return `sha256:${crypto
|
|
59
|
+
.createHash('sha256')
|
|
60
|
+
.update(String(value))
|
|
61
|
+
.digest('hex')}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function remoteRepositoryHashes(value) {
|
|
65
|
+
return remoteIdentityVariants(value).map(identity =>
|
|
66
|
+
sha256(`remote:${identity}`)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function remoteUrlHashes(value) {
|
|
71
|
+
return remoteIdentityVariants(value).map(sha256);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function identitiesIntersect(left, right) {
|
|
75
|
+
const leftSet = new Set(
|
|
76
|
+
(Array.isArray(left) ? left : [left]).filter(Boolean)
|
|
77
|
+
);
|
|
78
|
+
return (Array.isArray(right) ? right : [right]).some(value =>
|
|
79
|
+
leftSet.has(value)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = {
|
|
84
|
+
identitiesIntersect,
|
|
85
|
+
legacyRemoteIdentity,
|
|
86
|
+
remoteIdentityVariants,
|
|
87
|
+
remoteRepositoryHashes,
|
|
88
|
+
remoteUrlHashes,
|
|
89
|
+
};
|
package/lib/release-mainline.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
const crypto = require('crypto');
|
|
2
1
|
const path = require('path');
|
|
3
2
|
const { spawnSync } = require('child_process');
|
|
4
3
|
|
|
5
4
|
const { readGitSourceBase } = require('./change-baseline');
|
|
5
|
+
const {
|
|
6
|
+
identitiesIntersect,
|
|
7
|
+
remoteUrlHashes,
|
|
8
|
+
} = require('./git-remote-identity');
|
|
6
9
|
const { listRuntimeBuildInputChanges } = require('./runtime-lineage');
|
|
7
10
|
|
|
8
11
|
const GIT_OUTPUT_LIMIT = 16 * 1024 * 1024;
|
|
@@ -68,6 +71,7 @@ function prepareReleaseSourceRevision(options = {}) {
|
|
|
68
71
|
worktreeRoot: readGit(cwd, ['rev-parse', '--show-toplevel']).stdout,
|
|
69
72
|
remoteName: mainline.remoteName,
|
|
70
73
|
remoteUrlHash: mainline.remoteUrlHash,
|
|
74
|
+
remoteUrlHashAliases: mainline.remoteUrlHashAliases,
|
|
71
75
|
mainBranch: mainline.branchName,
|
|
72
76
|
mainTipCommit: mainline.tipCommit,
|
|
73
77
|
mainlineSource: mainline.source,
|
|
@@ -82,7 +86,10 @@ function assertReleaseSourceRevisionStable(sourceRevision, options = {}) {
|
|
|
82
86
|
const current = readGitSourceBase(cwd, 'HEAD');
|
|
83
87
|
const dirtyPaths = listRuntimeBuildInputChanges(cwd);
|
|
84
88
|
if (
|
|
85
|
-
|
|
89
|
+
!identitiesIntersect(
|
|
90
|
+
[expected.repo, ...(expected.repoAliases || [])],
|
|
91
|
+
[current.repo, ...(current.repoAliases || [])]
|
|
92
|
+
) ||
|
|
86
93
|
expected.baseCommit !== current.baseCommit ||
|
|
87
94
|
expected.treeHash !== current.treeHash ||
|
|
88
95
|
dirtyPaths.length > 0
|
|
@@ -109,7 +116,12 @@ function inspectReleaseSourceIntegration(sourceRevision, options = {}) {
|
|
|
109
116
|
const cwd = path.resolve(options.cwd || process.cwd());
|
|
110
117
|
const source = normalizeSourceRevision(sourceRevision);
|
|
111
118
|
const currentRepository = readGitSourceBase(cwd, 'HEAD');
|
|
112
|
-
if (
|
|
119
|
+
if (
|
|
120
|
+
!identitiesIntersect(
|
|
121
|
+
[source.repo, ...(source.repoAliases || [])],
|
|
122
|
+
[currentRepository.repo, ...(currentRepository.repoAliases || [])]
|
|
123
|
+
)
|
|
124
|
+
) {
|
|
113
125
|
throw releaseMainlineError(
|
|
114
126
|
'RELEASE_SOURCE_REPOSITORY_MISMATCH',
|
|
115
127
|
'冻结的发布提交与当前 Git 仓库不一致,不能验证主分支回合状态'
|
|
@@ -120,7 +132,10 @@ function inspectReleaseSourceIntegration(sourceRevision, options = {}) {
|
|
|
120
132
|
if (
|
|
121
133
|
source.remoteName !== mainline.remoteName ||
|
|
122
134
|
source.mainBranch !== mainline.branchName ||
|
|
123
|
-
|
|
135
|
+
!identitiesIntersect(
|
|
136
|
+
[source.remoteUrlHash, ...(source.remoteUrlHashAliases || [])],
|
|
137
|
+
[mainline.remoteUrlHash, ...(mainline.remoteUrlHashAliases || [])]
|
|
138
|
+
)
|
|
124
139
|
) {
|
|
125
140
|
throw releaseMainlineError(
|
|
126
141
|
'RELEASE_MAINLINE_TARGET_CHANGED',
|
|
@@ -204,7 +219,8 @@ function resolveAuthoritativeMainline(options = {}) {
|
|
|
204
219
|
}
|
|
205
220
|
|
|
206
221
|
function resolveRemoteMainline({ cwd, remoteName }) {
|
|
207
|
-
const
|
|
222
|
+
const remoteUrlHashAliases = readRemoteUrlHashes(cwd, remoteName);
|
|
223
|
+
const remoteUrlHash = remoteUrlHashAliases[0];
|
|
208
224
|
const remoteHead = readGit(
|
|
209
225
|
cwd,
|
|
210
226
|
['ls-remote', '--symref', remoteName, 'HEAD'],
|
|
@@ -217,6 +233,7 @@ function resolveRemoteMainline({ cwd, remoteName }) {
|
|
|
217
233
|
return {
|
|
218
234
|
remoteName,
|
|
219
235
|
remoteUrlHash,
|
|
236
|
+
remoteUrlHashAliases,
|
|
220
237
|
branchName: parsed.branchName,
|
|
221
238
|
tipCommit: parsed.tipCommit,
|
|
222
239
|
source: 'remote-head',
|
|
@@ -236,6 +253,7 @@ function resolveRemoteMainline({ cwd, remoteName }) {
|
|
|
236
253
|
return {
|
|
237
254
|
remoteName,
|
|
238
255
|
remoteUrlHash,
|
|
256
|
+
remoteUrlHashAliases,
|
|
239
257
|
...candidates[0],
|
|
240
258
|
source: 'remote-main-master-fallback',
|
|
241
259
|
};
|
|
@@ -246,6 +264,12 @@ function resolveRemoteMainline({ cwd, remoteName }) {
|
|
|
246
264
|
`远端 ${remoteName} 同时存在 main 与 master 且无法读取 remote HEAD;请先修复远端默认分支 HEAD`
|
|
247
265
|
);
|
|
248
266
|
}
|
|
267
|
+
if (isGitAuthenticationError(remoteHead.stderr)) {
|
|
268
|
+
throw releaseMainlineError(
|
|
269
|
+
'RELEASE_GIT_AUTH_REQUIRED',
|
|
270
|
+
`无法认证权威远端 ${remoteName}。请先解锁系统钥匙串或配置可用的 Git credential helper,并确认 \`git ls-remote --symref ${remoteName} HEAD\` 可成功执行;发布会在获取平台租约前停止,不会留下远端发布状态`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
249
273
|
const detail = remoteHead.stderr
|
|
250
274
|
? `: ${sanitizeGitError(remoteHead.stderr)}`
|
|
251
275
|
: '';
|
|
@@ -255,6 +279,12 @@ function resolveRemoteMainline({ cwd, remoteName }) {
|
|
|
255
279
|
);
|
|
256
280
|
}
|
|
257
281
|
|
|
282
|
+
function isGitAuthenticationError(value) {
|
|
283
|
+
return /(?:authentication failed|could not read (?:username|password)|terminal prompts? disabled|credential|keychain|access denied|http basic: access denied|response:\s*(?:401|403)|returned error:\s*(?:401|403))/i.test(
|
|
284
|
+
String(value || '')
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
258
288
|
function readRemoteBranchTip(cwd, remoteName, branchName, options = {}) {
|
|
259
289
|
const result = readGit(
|
|
260
290
|
cwd,
|
|
@@ -422,6 +452,25 @@ function normalizeSourceRevision(value) {
|
|
|
422
452
|
const remoteName = normalizeOptionalName(value.remoteName, 'remote');
|
|
423
453
|
const mainBranch = String(value.mainBranch || '').trim();
|
|
424
454
|
const remoteUrlHash = String(value.remoteUrlHash || '').trim().toLowerCase();
|
|
455
|
+
const remoteUrlHashAliases = Array.from(
|
|
456
|
+
new Set(
|
|
457
|
+
[
|
|
458
|
+
remoteUrlHash,
|
|
459
|
+
...(Array.isArray(value.remoteUrlHashAliases)
|
|
460
|
+
? value.remoteUrlHashAliases
|
|
461
|
+
: []),
|
|
462
|
+
]
|
|
463
|
+
.map(item => String(item || '').trim().toLowerCase())
|
|
464
|
+
.filter(item => /^sha256:[0-9a-f]{64}$/.test(item))
|
|
465
|
+
)
|
|
466
|
+
);
|
|
467
|
+
const repoAliases = Array.from(
|
|
468
|
+
new Set(
|
|
469
|
+
[repo, ...(Array.isArray(value.repoAliases) ? value.repoAliases : [])]
|
|
470
|
+
.map(item => String(item || '').trim().toLowerCase())
|
|
471
|
+
.filter(item => /^sha256:[0-9a-f]{64}$/.test(item))
|
|
472
|
+
)
|
|
473
|
+
);
|
|
425
474
|
if (
|
|
426
475
|
!/^sha256:[0-9a-f]{64}$/i.test(repo) ||
|
|
427
476
|
!isObjectId(baseCommit) ||
|
|
@@ -439,15 +488,17 @@ function normalizeSourceRevision(value) {
|
|
|
439
488
|
...value,
|
|
440
489
|
repo,
|
|
441
490
|
repositoryId: repo,
|
|
491
|
+
repoAliases,
|
|
442
492
|
baseCommit,
|
|
443
493
|
treeHash,
|
|
444
494
|
remoteName,
|
|
445
495
|
remoteUrlHash,
|
|
496
|
+
remoteUrlHashAliases,
|
|
446
497
|
mainBranch,
|
|
447
498
|
};
|
|
448
499
|
}
|
|
449
500
|
|
|
450
|
-
function
|
|
501
|
+
function readRemoteUrlHashes(cwd, remoteName) {
|
|
451
502
|
const result = readGit(cwd, ['remote', 'get-url', remoteName]);
|
|
452
503
|
if (!result.stdout) {
|
|
453
504
|
throw releaseMainlineError(
|
|
@@ -455,24 +506,7 @@ function readRemoteUrlHash(cwd, remoteName) {
|
|
|
455
506
|
`无法读取权威远端地址: ${remoteName}`
|
|
456
507
|
);
|
|
457
508
|
}
|
|
458
|
-
|
|
459
|
-
return `sha256:${crypto.createHash('sha256').update(identity).digest('hex')}`;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
function normalizeRemoteUrlIdentity(value) {
|
|
463
|
-
const remote = String(value || '').trim();
|
|
464
|
-
try {
|
|
465
|
-
const url = new URL(remote);
|
|
466
|
-
url.username = '';
|
|
467
|
-
url.password = '';
|
|
468
|
-
url.search = '';
|
|
469
|
-
url.hash = '';
|
|
470
|
-
return url.toString();
|
|
471
|
-
} catch {
|
|
472
|
-
return remote
|
|
473
|
-
.replace(/^[^@/]+@([^:]+):/, '$1:')
|
|
474
|
-
.replace(/[?#].*$/, '');
|
|
475
|
-
}
|
|
509
|
+
return remoteUrlHashes(result.stdout);
|
|
476
510
|
}
|
|
477
511
|
|
|
478
512
|
function assertSafeDefaultBranch(remoteName, branchName) {
|
|
@@ -555,6 +589,7 @@ module.exports = {
|
|
|
555
589
|
assertReleaseSourceIntegrated,
|
|
556
590
|
assertReleaseSourceRevisionStable,
|
|
557
591
|
inspectReleaseSourceIntegration,
|
|
592
|
+
isGitAuthenticationError,
|
|
558
593
|
prepareReleaseSourceRevision,
|
|
559
594
|
resolveAuthoritativeMainline,
|
|
560
595
|
};
|