specweave 0.32.5 → 0.32.6
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/bin/specweave.js +78 -0
- package/dist/src/cli/commands/docs.d.ts +45 -0
- package/dist/src/cli/commands/docs.d.ts.map +1 -0
- package/dist/src/cli/commands/docs.js +307 -0
- package/dist/src/cli/commands/docs.js.map +1 -0
- package/dist/src/cli/helpers/init/repository-setup.d.ts +14 -2
- package/dist/src/cli/helpers/init/repository-setup.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/repository-setup.js +292 -188
- package/dist/src/cli/helpers/init/repository-setup.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/index.js +9 -3
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts +18 -2
- package/dist/src/cli/helpers/issue-tracker/jira.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/jira.js +325 -25
- package/dist/src/cli/helpers/issue-tracker/jira.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js +61 -1
- package/dist/src/cli/helpers/issue-tracker/sync-config-writer.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/types.d.ts +25 -0
- package/dist/src/cli/helpers/issue-tracker/types.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/types.js.map +1 -1
- package/dist/src/core/living-docs/intelligent-analyzer/organization-synthesizer.js +1 -1
- package/dist/src/utils/docs-preview/config-generator.d.ts.map +1 -1
- package/dist/src/utils/docs-preview/config-generator.js +12 -16
- package/dist/src/utils/docs-preview/config-generator.js.map +1 -1
- package/dist/src/utils/docs-preview/docusaurus-setup.d.ts.map +1 -1
- package/dist/src/utils/docs-preview/docusaurus-setup.js +24 -5
- package/dist/src/utils/docs-preview/docusaurus-setup.js.map +1 -1
- package/dist/src/utils/docs-preview/package-installer.d.ts +3 -0
- package/dist/src/utils/docs-preview/package-installer.d.ts.map +1 -1
- package/dist/src/utils/docs-preview/package-installer.js +18 -9
- package/dist/src/utils/docs-preview/package-installer.js.map +1 -1
- package/dist/src/utils/docs-preview/server-manager.d.ts.map +1 -1
- package/dist/src/utils/docs-preview/server-manager.js +2 -1
- package/dist/src/utils/docs-preview/server-manager.js.map +1 -1
- package/dist/src/utils/validators/jira-validator.d.ts +25 -1
- package/dist/src/utils/validators/jira-validator.d.ts.map +1 -1
- package/dist/src/utils/validators/jira-validator.js +254 -172
- package/dist/src/utils/validators/jira-validator.js.map +1 -1
- package/package.json +1 -1
|
@@ -56,6 +56,110 @@ async function safeParseJsonResponse(response, context) {
|
|
|
56
56
|
` Response preview: ${preview}${responseText.length > 100 ? '...' : ''}`);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Fetch all accessible projects for an ADO organization
|
|
61
|
+
*/
|
|
62
|
+
async function fetchAdoProjects(org, pat) {
|
|
63
|
+
const auth = Buffer.from(`:${pat}`).toString('base64');
|
|
64
|
+
const endpoint = `https://dev.azure.com/${org}/_apis/projects?api-version=7.0`;
|
|
65
|
+
const response = await fetch(endpoint, {
|
|
66
|
+
headers: { 'Authorization': `Basic ${auth}`, 'Accept': 'application/json' }
|
|
67
|
+
});
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
// Read response body to check for HTML error pages
|
|
70
|
+
const errorBody = await response.text();
|
|
71
|
+
const looksLikeHtml = errorBody.trim().startsWith('<!DOCTYPE') || errorBody.trim().startsWith('<html');
|
|
72
|
+
if (looksLikeHtml) {
|
|
73
|
+
throw new Error(`Azure DevOps returned an error page (HTTP ${response.status}).\n` +
|
|
74
|
+
` This usually indicates an authentication or configuration issue.\n` +
|
|
75
|
+
` \n` +
|
|
76
|
+
` Possible causes:\n` +
|
|
77
|
+
` • Invalid or expired Personal Access Token (PAT)\n` +
|
|
78
|
+
` • Incorrect organization name "${org}"\n` +
|
|
79
|
+
` • Corporate firewall or proxy intercepting the request\n` +
|
|
80
|
+
` • SSO/authentication redirect (try accessing Azure DevOps in browser first)`);
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Failed to fetch projects: ${response.status} - ${errorBody.substring(0, 200)}`);
|
|
83
|
+
}
|
|
84
|
+
// Use safe JSON parser to detect HTML responses even on 200 OK
|
|
85
|
+
const data = await safeParseJsonResponse(response, 'Azure DevOps');
|
|
86
|
+
return (data.value || []).map((p) => ({ name: p.name, id: p.id }));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Prompt user for ADO organization, credentials, and project selection
|
|
90
|
+
* For repository cloning in multi-repo setups
|
|
91
|
+
*/
|
|
92
|
+
async function promptAdoProjectSelection(targetDir, strings) {
|
|
93
|
+
// Check for existing credentials in .env or environment
|
|
94
|
+
let existingOrg;
|
|
95
|
+
let existingPat;
|
|
96
|
+
const envContent = readEnvFile(targetDir);
|
|
97
|
+
if (envContent) {
|
|
98
|
+
const parsed = parseEnvFile(envContent);
|
|
99
|
+
existingOrg = parsed.AZURE_DEVOPS_ORG;
|
|
100
|
+
existingPat = parsed.AZURE_DEVOPS_PAT;
|
|
101
|
+
}
|
|
102
|
+
// Fall back to environment variables
|
|
103
|
+
if (!existingOrg || !existingPat) {
|
|
104
|
+
const auth = getAzureDevOpsAuth();
|
|
105
|
+
if (auth) {
|
|
106
|
+
existingOrg = existingOrg || auth.org;
|
|
107
|
+
existingPat = existingPat || auth.pat;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Prompt for organization
|
|
111
|
+
const org = await input({
|
|
112
|
+
message: strings.adoOrgPrompt,
|
|
113
|
+
default: existingOrg,
|
|
114
|
+
validate: (value) => {
|
|
115
|
+
if (!value.trim())
|
|
116
|
+
return 'Organization name is required';
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// Prompt for PAT
|
|
121
|
+
const pat = await password({
|
|
122
|
+
message: strings.adoPatPrompt,
|
|
123
|
+
mask: true,
|
|
124
|
+
validate: (value) => {
|
|
125
|
+
if (!value.trim())
|
|
126
|
+
return 'Personal Access Token is required';
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
// Fetch projects
|
|
131
|
+
const spinner = ora(strings.adoFetchingProjects).start();
|
|
132
|
+
let projects;
|
|
133
|
+
try {
|
|
134
|
+
projects = await fetchAdoProjects(org, pat);
|
|
135
|
+
spinner.succeed();
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
spinner.fail(strings.adoConnectionFailed);
|
|
139
|
+
console.log(chalk.red(` ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
if (projects.length === 0) {
|
|
143
|
+
console.log(chalk.yellow(` ${strings.adoNoProjects}`));
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
// Let user select projects
|
|
147
|
+
const selectedProjects = await checkbox({
|
|
148
|
+
message: strings.adoProjectPrompt,
|
|
149
|
+
choices: projects.map((p, index) => ({
|
|
150
|
+
name: p.name,
|
|
151
|
+
value: p.name,
|
|
152
|
+
checked: index === 0 // First project selected by default
|
|
153
|
+
})),
|
|
154
|
+
required: true
|
|
155
|
+
});
|
|
156
|
+
console.log(chalk.green(` ✓ ${strings.adoProjectsSelected.replace('{count}', String(selectedProjects.length))}`));
|
|
157
|
+
return {
|
|
158
|
+
org,
|
|
159
|
+
pat,
|
|
160
|
+
projects: selectedProjects
|
|
161
|
+
};
|
|
162
|
+
}
|
|
59
163
|
/**
|
|
60
164
|
* Get translated strings for repository setup
|
|
61
165
|
*/
|
|
@@ -77,7 +181,7 @@ function getRepoStrings(language) {
|
|
|
77
181
|
other: 'Other (GitLab, etc - coming soon)',
|
|
78
182
|
adoCloneQuestion: 'Enter repo name pattern to clone (e.g., sw-* or leave empty to skip):',
|
|
79
183
|
adoCloneSkip: 'Skipping repo cloning - you can configure later',
|
|
80
|
-
//
|
|
184
|
+
// Unified selection strings
|
|
81
185
|
adoSelectStrategy: 'How do you want to select repositories to clone?',
|
|
82
186
|
adoAllRepos: 'All',
|
|
83
187
|
adoAllReposDesc: 'Clone all repositories from the project',
|
|
@@ -105,6 +209,13 @@ function getRepoStrings(language) {
|
|
|
105
209
|
adoFetchingProjects: 'Fetching projects...',
|
|
106
210
|
adoNoProjects: 'No projects found in this organization',
|
|
107
211
|
adoConnectionFailed: 'Failed to connect to Azure DevOps',
|
|
212
|
+
// GitHub/Bitbucket multi-repo strings (v0.32.6+)
|
|
213
|
+
githubMultiRepoHeader: '📁 GitHub Multi-Repository Selection',
|
|
214
|
+
githubMultiRepoDesc: 'Select which repositories to work with from your GitHub organization.',
|
|
215
|
+
githubSelectStrategy: 'How do you want to select GitHub repositories?',
|
|
216
|
+
bitbucketMultiRepoHeader: '📁 Bitbucket Multi-Repository Selection',
|
|
217
|
+
bitbucketMultiRepoDesc: 'Select which repositories to work with from your Bitbucket workspace.',
|
|
218
|
+
bitbucketSelectStrategy: 'How do you want to select Bitbucket repositories?',
|
|
108
219
|
},
|
|
109
220
|
ru: {
|
|
110
221
|
header: '📦 Хостинг репозитория',
|
|
@@ -122,7 +233,7 @@ function getRepoStrings(language) {
|
|
|
122
233
|
other: 'Другой (GitLab и т.д. - скоро)',
|
|
123
234
|
adoCloneQuestion: 'Введите шаблон имени репо для клонирования (напр., sw-* или оставьте пустым):',
|
|
124
235
|
adoCloneSkip: 'Пропуск клонирования - можно настроить позже',
|
|
125
|
-
//
|
|
236
|
+
// Unified selection strings
|
|
126
237
|
adoSelectStrategy: 'Как вы хотите выбрать репозитории для клонирования?',
|
|
127
238
|
adoAllRepos: 'Все',
|
|
128
239
|
adoAllReposDesc: 'Клонировать все репозитории из проекта',
|
|
@@ -150,6 +261,13 @@ function getRepoStrings(language) {
|
|
|
150
261
|
adoFetchingProjects: 'Загрузка проектов...',
|
|
151
262
|
adoNoProjects: 'Проекты не найдены в этой организации',
|
|
152
263
|
adoConnectionFailed: 'Не удалось подключиться к Azure DevOps',
|
|
264
|
+
// GitHub/Bitbucket multi-repo strings (v0.32.6+)
|
|
265
|
+
githubMultiRepoHeader: '📁 Выбор репозиториев GitHub',
|
|
266
|
+
githubMultiRepoDesc: 'Выберите репозитории для работы из вашей организации GitHub.',
|
|
267
|
+
githubSelectStrategy: 'Как вы хотите выбрать репозитории GitHub?',
|
|
268
|
+
bitbucketMultiRepoHeader: '📁 Выбор репозиториев Bitbucket',
|
|
269
|
+
bitbucketMultiRepoDesc: 'Выберите репозитории для работы из вашего рабочего пространства Bitbucket.',
|
|
270
|
+
bitbucketSelectStrategy: 'Как вы хотите выбрать репозитории Bitbucket?',
|
|
153
271
|
},
|
|
154
272
|
es: {
|
|
155
273
|
header: '📦 Alojamiento del repositorio',
|
|
@@ -167,7 +285,7 @@ function getRepoStrings(language) {
|
|
|
167
285
|
other: 'Otro (GitLab, etc - próximamente)',
|
|
168
286
|
adoCloneQuestion: 'Ingrese patrón de nombre de repo a clonar (ej., sw-* o deje vacío):',
|
|
169
287
|
adoCloneSkip: 'Omitiendo clonación - puede configurar después',
|
|
170
|
-
//
|
|
288
|
+
// Unified selection strings
|
|
171
289
|
adoSelectStrategy: '¿Cómo quiere seleccionar repositorios para clonar?',
|
|
172
290
|
adoAllRepos: 'Todos',
|
|
173
291
|
adoAllReposDesc: 'Clonar todos los repositorios del proyecto',
|
|
@@ -195,6 +313,13 @@ function getRepoStrings(language) {
|
|
|
195
313
|
adoFetchingProjects: 'Obteniendo proyectos...',
|
|
196
314
|
adoNoProjects: 'No se encontraron proyectos en esta organización',
|
|
197
315
|
adoConnectionFailed: 'Error al conectar con Azure DevOps',
|
|
316
|
+
// GitHub/Bitbucket multi-repo strings (v0.32.6+)
|
|
317
|
+
githubMultiRepoHeader: '📁 Selección de repositorios GitHub',
|
|
318
|
+
githubMultiRepoDesc: 'Seleccione los repositorios de su organización GitHub.',
|
|
319
|
+
githubSelectStrategy: '¿Cómo desea seleccionar los repositorios de GitHub?',
|
|
320
|
+
bitbucketMultiRepoHeader: '📁 Selección de repositorios Bitbucket',
|
|
321
|
+
bitbucketMultiRepoDesc: 'Seleccione los repositorios de su espacio de trabajo Bitbucket.',
|
|
322
|
+
bitbucketSelectStrategy: '¿Cómo desea seleccionar los repositorios de Bitbucket?',
|
|
198
323
|
},
|
|
199
324
|
zh: {
|
|
200
325
|
header: '📦 仓库托管',
|
|
@@ -212,7 +337,7 @@ function getRepoStrings(language) {
|
|
|
212
337
|
other: '其他(GitLab 等 - 即将推出)',
|
|
213
338
|
adoCloneQuestion: '输入要克隆的仓库名称模式(例如 sw-* 或留空跳过):',
|
|
214
339
|
adoCloneSkip: '跳过克隆 - 稍后可以配置',
|
|
215
|
-
//
|
|
340
|
+
// Unified selection strings
|
|
216
341
|
adoSelectStrategy: '您想如何选择要克隆的仓库?',
|
|
217
342
|
adoAllRepos: '全部',
|
|
218
343
|
adoAllReposDesc: '克隆项目中的所有仓库',
|
|
@@ -240,6 +365,13 @@ function getRepoStrings(language) {
|
|
|
240
365
|
adoFetchingProjects: '正在获取项目...',
|
|
241
366
|
adoNoProjects: '在此组织中未找到项目',
|
|
242
367
|
adoConnectionFailed: '无法连接到 Azure DevOps',
|
|
368
|
+
// GitHub/Bitbucket multi-repo strings (v0.32.6+)
|
|
369
|
+
githubMultiRepoHeader: '📁 GitHub 多仓库选择',
|
|
370
|
+
githubMultiRepoDesc: '从您的 GitHub 组织中选择要使用的仓库。',
|
|
371
|
+
githubSelectStrategy: '您想如何选择 GitHub 仓库?',
|
|
372
|
+
bitbucketMultiRepoHeader: '📁 Bitbucket 多仓库选择',
|
|
373
|
+
bitbucketMultiRepoDesc: '从您的 Bitbucket 工作区中选择要使用的仓库。',
|
|
374
|
+
bitbucketSelectStrategy: '您想如何选择 Bitbucket 仓库?',
|
|
243
375
|
},
|
|
244
376
|
de: {
|
|
245
377
|
header: '📦 Repository-Hosting',
|
|
@@ -257,7 +389,7 @@ function getRepoStrings(language) {
|
|
|
257
389
|
other: 'Andere (GitLab, etc - kommt bald)',
|
|
258
390
|
adoCloneQuestion: 'Repo-Namensmuster zum Klonen eingeben (z.B. sw-* oder leer lassen):',
|
|
259
391
|
adoCloneSkip: 'Klonen übersprungen - später konfigurierbar',
|
|
260
|
-
//
|
|
392
|
+
// Unified selection strings
|
|
261
393
|
adoSelectStrategy: 'Wie möchten Sie Repositories zum Klonen auswählen?',
|
|
262
394
|
adoAllRepos: 'Alle',
|
|
263
395
|
adoAllReposDesc: 'Alle Repositories aus dem Projekt klonen',
|
|
@@ -285,6 +417,13 @@ function getRepoStrings(language) {
|
|
|
285
417
|
adoFetchingProjects: 'Projekte werden abgerufen...',
|
|
286
418
|
adoNoProjects: 'Keine Projekte in dieser Organisation gefunden',
|
|
287
419
|
adoConnectionFailed: 'Verbindung zu Azure DevOps fehlgeschlagen',
|
|
420
|
+
// GitHub/Bitbucket multi-repo strings (v0.32.6+)
|
|
421
|
+
githubMultiRepoHeader: '📁 GitHub Multi-Repository Auswahl',
|
|
422
|
+
githubMultiRepoDesc: 'Wählen Sie die Repositories aus Ihrer GitHub-Organisation.',
|
|
423
|
+
githubSelectStrategy: 'Wie möchten Sie GitHub-Repositories auswählen?',
|
|
424
|
+
bitbucketMultiRepoHeader: '📁 Bitbucket Multi-Repository Auswahl',
|
|
425
|
+
bitbucketMultiRepoDesc: 'Wählen Sie die Repositories aus Ihrem Bitbucket-Workspace.',
|
|
426
|
+
bitbucketSelectStrategy: 'Wie möchten Sie Bitbucket-Repositories auswählen?',
|
|
288
427
|
},
|
|
289
428
|
fr: {
|
|
290
429
|
header: '📦 Hébergement du dépôt',
|
|
@@ -302,7 +441,7 @@ function getRepoStrings(language) {
|
|
|
302
441
|
other: 'Autre (GitLab, etc - bientôt)',
|
|
303
442
|
adoCloneQuestion: 'Entrez le modèle de nom de repo à cloner (ex. sw-* ou laissez vide):',
|
|
304
443
|
adoCloneSkip: 'Clonage ignoré - configurable plus tard',
|
|
305
|
-
//
|
|
444
|
+
// Unified selection strings
|
|
306
445
|
adoSelectStrategy: 'Comment voulez-vous sélectionner les repos à cloner?',
|
|
307
446
|
adoAllRepos: 'Tous',
|
|
308
447
|
adoAllReposDesc: 'Cloner tous les repos du projet',
|
|
@@ -330,6 +469,13 @@ function getRepoStrings(language) {
|
|
|
330
469
|
adoFetchingProjects: 'Récupération des projets...',
|
|
331
470
|
adoNoProjects: 'Aucun projet trouvé dans cette organisation',
|
|
332
471
|
adoConnectionFailed: 'Échec de connexion à Azure DevOps',
|
|
472
|
+
// GitHub/Bitbucket multi-repo strings (v0.32.6+)
|
|
473
|
+
githubMultiRepoHeader: '📁 Sélection multi-dépôt GitHub',
|
|
474
|
+
githubMultiRepoDesc: 'Sélectionnez les dépôts de votre organisation GitHub.',
|
|
475
|
+
githubSelectStrategy: 'Comment voulez-vous sélectionner les dépôts GitHub?',
|
|
476
|
+
bitbucketMultiRepoHeader: '📁 Sélection multi-dépôt Bitbucket',
|
|
477
|
+
bitbucketMultiRepoDesc: 'Sélectionnez les dépôts de votre espace de travail Bitbucket.',
|
|
478
|
+
bitbucketSelectStrategy: 'Comment voulez-vous sélectionner les dépôts Bitbucket?',
|
|
333
479
|
},
|
|
334
480
|
ja: {
|
|
335
481
|
header: '📦 リポジトリホスティング',
|
|
@@ -347,7 +493,7 @@ function getRepoStrings(language) {
|
|
|
347
493
|
other: 'その他(GitLabなど - 近日公開)',
|
|
348
494
|
adoCloneQuestion: 'クローンするリポ名パターンを入力(例: sw-* または空白でスキップ):',
|
|
349
495
|
adoCloneSkip: 'クローンをスキップ - 後で設定可能',
|
|
350
|
-
//
|
|
496
|
+
// Unified selection strings
|
|
351
497
|
adoSelectStrategy: 'クローンするリポジトリをどのように選択しますか?',
|
|
352
498
|
adoAllRepos: 'すべて',
|
|
353
499
|
adoAllReposDesc: 'プロジェクトのすべてのリポジトリをクローン',
|
|
@@ -375,6 +521,13 @@ function getRepoStrings(language) {
|
|
|
375
521
|
adoFetchingProjects: 'プロジェクトを取得中...',
|
|
376
522
|
adoNoProjects: 'この組織にプロジェクトが見つかりません',
|
|
377
523
|
adoConnectionFailed: 'Azure DevOps への接続に失敗しました',
|
|
524
|
+
// GitHub/Bitbucket multi-repo strings (v0.32.6+)
|
|
525
|
+
githubMultiRepoHeader: '📁 GitHub マルチリポジトリ選択',
|
|
526
|
+
githubMultiRepoDesc: 'GitHub組織からリポジトリを選択してください。',
|
|
527
|
+
githubSelectStrategy: 'GitHubリポジトリをどのように選択しますか?',
|
|
528
|
+
bitbucketMultiRepoHeader: '📁 Bitbucket マルチリポジトリ選択',
|
|
529
|
+
bitbucketMultiRepoDesc: 'Bitbucketワークスペースからリポジトリを選択してください。',
|
|
530
|
+
bitbucketSelectStrategy: 'Bitbucketリポジトリをどのように選択しますか?',
|
|
378
531
|
},
|
|
379
532
|
ko: {
|
|
380
533
|
header: '📦 저장소 호스팅',
|
|
@@ -392,7 +545,7 @@ function getRepoStrings(language) {
|
|
|
392
545
|
other: '기타 (GitLab 등 - 곧 출시)',
|
|
393
546
|
adoCloneQuestion: '복제할 저장소 이름 패턴 입력 (예: sw-* 또는 비워두기):',
|
|
394
547
|
adoCloneSkip: '복제 건너뜀 - 나중에 설정 가능',
|
|
395
|
-
//
|
|
548
|
+
// Unified selection strings
|
|
396
549
|
adoSelectStrategy: '복제할 저장소를 어떻게 선택하시겠습니까?',
|
|
397
550
|
adoAllRepos: '모두',
|
|
398
551
|
adoAllReposDesc: '프로젝트의 모든 저장소 복제',
|
|
@@ -420,6 +573,13 @@ function getRepoStrings(language) {
|
|
|
420
573
|
adoFetchingProjects: '프로젝트 가져오는 중...',
|
|
421
574
|
adoNoProjects: '이 조직에서 프로젝트를 찾을 수 없습니다',
|
|
422
575
|
adoConnectionFailed: 'Azure DevOps 연결 실패',
|
|
576
|
+
// GitHub/Bitbucket multi-repo strings (v0.32.6+)
|
|
577
|
+
githubMultiRepoHeader: '📁 GitHub 다중 저장소 선택',
|
|
578
|
+
githubMultiRepoDesc: 'GitHub 조직에서 저장소를 선택하세요.',
|
|
579
|
+
githubSelectStrategy: 'GitHub 저장소를 어떻게 선택하시겠습니까?',
|
|
580
|
+
bitbucketMultiRepoHeader: '📁 Bitbucket 다중 저장소 선택',
|
|
581
|
+
bitbucketMultiRepoDesc: 'Bitbucket 워크스페이스에서 저장소를 선택하세요.',
|
|
582
|
+
bitbucketSelectStrategy: 'Bitbucket 저장소를 어떻게 선택하시겠습니까?',
|
|
423
583
|
},
|
|
424
584
|
pt: {
|
|
425
585
|
header: '📦 Hospedagem do repositório',
|
|
@@ -437,7 +597,7 @@ function getRepoStrings(language) {
|
|
|
437
597
|
other: 'Outro (GitLab, etc - em breve)',
|
|
438
598
|
adoCloneQuestion: 'Digite padrão de nome de repo para clonar (ex. sw-* ou deixe vazio):',
|
|
439
599
|
adoCloneSkip: 'Clonagem ignorada - configurável depois',
|
|
440
|
-
//
|
|
600
|
+
// Unified selection strings
|
|
441
601
|
adoSelectStrategy: 'Como você quer selecionar repositórios para clonar?',
|
|
442
602
|
adoAllRepos: 'Todos',
|
|
443
603
|
adoAllReposDesc: 'Clonar todos os repositórios do projeto',
|
|
@@ -465,113 +625,112 @@ function getRepoStrings(language) {
|
|
|
465
625
|
adoFetchingProjects: 'Buscando projetos...',
|
|
466
626
|
adoNoProjects: 'Nenhum projeto encontrado nesta organização',
|
|
467
627
|
adoConnectionFailed: 'Falha ao conectar ao Azure DevOps',
|
|
628
|
+
// GitHub/Bitbucket multi-repo strings (v0.32.6+)
|
|
629
|
+
githubMultiRepoHeader: '📁 Seleção de múltiplos repositórios GitHub',
|
|
630
|
+
githubMultiRepoDesc: 'Selecione os repositórios da sua organização GitHub.',
|
|
631
|
+
githubSelectStrategy: 'Como você quer selecionar os repositórios do GitHub?',
|
|
632
|
+
bitbucketMultiRepoHeader: '📁 Seleção de múltiplos repositórios Bitbucket',
|
|
633
|
+
bitbucketMultiRepoDesc: 'Selecione os repositórios do seu workspace Bitbucket.',
|
|
634
|
+
bitbucketSelectStrategy: 'Como você quer selecionar os repositórios do Bitbucket?',
|
|
468
635
|
},
|
|
469
636
|
};
|
|
470
637
|
return strings[language] || strings.en;
|
|
471
638
|
}
|
|
472
639
|
/**
|
|
473
|
-
*
|
|
640
|
+
* Prompt for multi-repo pattern selection (unified for GitHub, Bitbucket, ADO)
|
|
641
|
+
*
|
|
642
|
+
* @param provider - The git provider ('github' | 'bitbucket' | 'ado')
|
|
643
|
+
* @param strings - Localized strings
|
|
644
|
+
* @returns Selected pattern result
|
|
474
645
|
*/
|
|
475
|
-
async function
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
});
|
|
481
|
-
if (!response.ok) {
|
|
482
|
-
// Read response body to check for HTML error pages
|
|
483
|
-
const errorBody = await response.text();
|
|
484
|
-
const looksLikeHtml = errorBody.trim().startsWith('<!DOCTYPE') || errorBody.trim().startsWith('<html');
|
|
485
|
-
if (looksLikeHtml) {
|
|
486
|
-
throw new Error(`Azure DevOps returned an error page (HTTP ${response.status}).\n` +
|
|
487
|
-
` This usually indicates an authentication or configuration issue.\n` +
|
|
488
|
-
` \n` +
|
|
489
|
-
` Possible causes:\n` +
|
|
490
|
-
` • Invalid or expired Personal Access Token (PAT)\n` +
|
|
491
|
-
` • Incorrect organization name "${org}"\n` +
|
|
492
|
-
` • Corporate firewall or proxy intercepting the request\n` +
|
|
493
|
-
` • SSO/authentication redirect (try accessing Azure DevOps in browser first)`);
|
|
494
|
-
}
|
|
495
|
-
throw new Error(`Failed to fetch projects: ${response.status} - ${errorBody.substring(0, 200)}`);
|
|
646
|
+
async function promptMultiRepoPatternSelection(provider, strings) {
|
|
647
|
+
// Display provider-specific header
|
|
648
|
+
if (provider === 'github') {
|
|
649
|
+
console.log(chalk.blue(`\n${strings.githubMultiRepoHeader}\n`));
|
|
650
|
+
console.log(chalk.gray(` ${strings.githubMultiRepoDesc}\n`));
|
|
496
651
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
501
|
-
/**
|
|
502
|
-
* Prompt user for ADO organization, credentials, and project selection
|
|
503
|
-
* For repository cloning in multi-repo setups
|
|
504
|
-
*/
|
|
505
|
-
async function promptAdoProjectSelection(targetDir, strings) {
|
|
506
|
-
// Check for existing credentials in .env or environment
|
|
507
|
-
let existingOrg;
|
|
508
|
-
let existingPat;
|
|
509
|
-
const envContent = readEnvFile(targetDir);
|
|
510
|
-
if (envContent) {
|
|
511
|
-
const parsed = parseEnvFile(envContent);
|
|
512
|
-
existingOrg = parsed.AZURE_DEVOPS_ORG;
|
|
513
|
-
existingPat = parsed.AZURE_DEVOPS_PAT;
|
|
652
|
+
else if (provider === 'bitbucket') {
|
|
653
|
+
console.log(chalk.blue(`\n${strings.bitbucketMultiRepoHeader}\n`));
|
|
654
|
+
console.log(chalk.gray(` ${strings.bitbucketMultiRepoDesc}\n`));
|
|
514
655
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (auth) {
|
|
519
|
-
existingOrg = existingOrg || auth.org;
|
|
520
|
-
existingPat = existingPat || auth.pat;
|
|
521
|
-
}
|
|
656
|
+
else {
|
|
657
|
+
console.log(chalk.blue('\n📁 Azure DevOps Repository Selection\n'));
|
|
658
|
+
console.log(chalk.gray(' Select which repositories to clone from your ADO project.\n'));
|
|
522
659
|
}
|
|
523
|
-
//
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
660
|
+
// Get provider-specific strategy message
|
|
661
|
+
const strategyMessage = provider === 'github'
|
|
662
|
+
? strings.githubSelectStrategy
|
|
663
|
+
: provider === 'bitbucket'
|
|
664
|
+
? strings.bitbucketSelectStrategy
|
|
665
|
+
: strings.adoSelectStrategy;
|
|
666
|
+
const strategyChoices = [
|
|
667
|
+
{
|
|
668
|
+
name: `${chalk.green('✓')} ${strings.adoAllRepos} ${chalk.gray(`- ${strings.adoAllReposDesc}`)}`,
|
|
669
|
+
value: 'all',
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
name: `${strings.adoPatternGlob} ${chalk.gray(`- ${strings.adoPatternGlobDesc}`)}`,
|
|
673
|
+
value: 'pattern-glob',
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
name: `${strings.adoPatternRegex} ${chalk.gray(`- ${strings.adoPatternRegexDesc}`)}`,
|
|
677
|
+
value: 'pattern-regex',
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
name: `${strings.adoSkipOption} ${chalk.gray(`- ${strings.adoSkipDesc}`)}`,
|
|
681
|
+
value: 'skip',
|
|
682
|
+
},
|
|
683
|
+
];
|
|
684
|
+
const strategy = await select({
|
|
685
|
+
message: strategyMessage,
|
|
686
|
+
choices: strategyChoices,
|
|
687
|
+
default: 'all',
|
|
532
688
|
});
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
689
|
+
switch (strategy) {
|
|
690
|
+
case 'all': {
|
|
691
|
+
console.log(chalk.green(` ✓ ${strings.adoSelectedAll}`));
|
|
692
|
+
return { strategy: 'all', pattern: '*' };
|
|
693
|
+
}
|
|
694
|
+
case 'pattern-glob': {
|
|
695
|
+
console.log(chalk.gray(`\n 💡 ${strings.adoPatternHint}`));
|
|
696
|
+
console.log(chalk.gray(` 💡 ${strings.adoShortcutsHint}\n`));
|
|
697
|
+
const pattern = await input({
|
|
698
|
+
message: strings.adoPatternPrompt,
|
|
699
|
+
validate: (value) => {
|
|
700
|
+
if (!value.trim())
|
|
701
|
+
return strings.adoPatternRequired;
|
|
702
|
+
return true;
|
|
703
|
+
},
|
|
704
|
+
});
|
|
705
|
+
// Parse shortcuts (starts:, ends:, contains:)
|
|
706
|
+
const parsedPattern = parsePatternShortcut(pattern.trim());
|
|
707
|
+
const message = strings.adoSelectedPattern.replace('{pattern}', parsedPattern);
|
|
708
|
+
console.log(chalk.green(` ✓ ${message}`));
|
|
709
|
+
return { strategy: 'pattern-glob', pattern: parsedPattern };
|
|
710
|
+
}
|
|
711
|
+
case 'pattern-regex': {
|
|
712
|
+
console.log(chalk.gray(`\n 💡 ${strings.adoRegexHint}\n`));
|
|
713
|
+
const pattern = await input({
|
|
714
|
+
message: strings.adoRegexPrompt,
|
|
715
|
+
validate: (value) => {
|
|
716
|
+
if (!value.trim())
|
|
717
|
+
return strings.adoPatternRequired;
|
|
718
|
+
const validation = validateRegex(value.trim());
|
|
719
|
+
if (validation !== true) {
|
|
720
|
+
return `${strings.adoInvalidRegex}: ${validation}`;
|
|
721
|
+
}
|
|
722
|
+
return true;
|
|
723
|
+
},
|
|
724
|
+
});
|
|
725
|
+
const message = strings.adoSelectedRegex.replace('{pattern}', pattern.trim());
|
|
726
|
+
console.log(chalk.green(` ✓ ${message}`));
|
|
727
|
+
return { strategy: 'pattern-regex', pattern: pattern.trim(), isRegex: true };
|
|
728
|
+
}
|
|
729
|
+
case 'skip': {
|
|
730
|
+
console.log(chalk.gray(` → ${strings.adoCloneSkip}`));
|
|
731
|
+
return { strategy: 'skip' };
|
|
541
732
|
}
|
|
542
|
-
});
|
|
543
|
-
// Fetch projects
|
|
544
|
-
const spinner = ora(strings.adoFetchingProjects).start();
|
|
545
|
-
let projects;
|
|
546
|
-
try {
|
|
547
|
-
projects = await fetchAdoProjects(org, pat);
|
|
548
|
-
spinner.succeed();
|
|
549
|
-
}
|
|
550
|
-
catch (error) {
|
|
551
|
-
spinner.fail(strings.adoConnectionFailed);
|
|
552
|
-
console.log(chalk.red(` ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
553
|
-
return null;
|
|
554
|
-
}
|
|
555
|
-
if (projects.length === 0) {
|
|
556
|
-
console.log(chalk.yellow(` ${strings.adoNoProjects}`));
|
|
557
|
-
return null;
|
|
558
733
|
}
|
|
559
|
-
// Let user select projects
|
|
560
|
-
const selectedProjects = await checkbox({
|
|
561
|
-
message: strings.adoProjectPrompt,
|
|
562
|
-
choices: projects.map((p, index) => ({
|
|
563
|
-
name: p.name,
|
|
564
|
-
value: p.name,
|
|
565
|
-
checked: index === 0 // First project selected by default
|
|
566
|
-
})),
|
|
567
|
-
required: true
|
|
568
|
-
});
|
|
569
|
-
console.log(chalk.green(` ✓ ${strings.adoProjectsSelected.replace('{count}', String(selectedProjects.length))}`));
|
|
570
|
-
return {
|
|
571
|
-
org,
|
|
572
|
-
pat,
|
|
573
|
-
projects: selectedProjects
|
|
574
|
-
};
|
|
575
734
|
}
|
|
576
735
|
/**
|
|
577
736
|
* Prompt user for repository hosting configuration
|
|
@@ -643,95 +802,40 @@ export async function setupRepositoryHosting(options) {
|
|
|
643
802
|
else {
|
|
644
803
|
repositoryHosting = `${provider}-${structure}`;
|
|
645
804
|
}
|
|
646
|
-
// Step 3: For
|
|
805
|
+
// Step 3: For multi-repo setups, prompt for pattern selection
|
|
806
|
+
// Now unified for ADO, GitHub, and Bitbucket (v0.32.6+)
|
|
647
807
|
let adoClonePattern;
|
|
648
808
|
let adoClonePatternResult;
|
|
649
809
|
let adoProjectSelection;
|
|
650
|
-
if (provider === 'ado'
|
|
651
|
-
// Step 3a:
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
// Step 3b: Show unified strategy selection
|
|
659
|
-
const strategyChoices = [
|
|
660
|
-
{
|
|
661
|
-
name: `${chalk.green('✓')} ${strings.adoAllRepos} ${chalk.gray(`- ${strings.adoAllReposDesc}`)}`,
|
|
662
|
-
value: 'all',
|
|
663
|
-
},
|
|
664
|
-
{
|
|
665
|
-
name: `${strings.adoPatternGlob} ${chalk.gray(`- ${strings.adoPatternGlobDesc}`)}`,
|
|
666
|
-
value: 'pattern-glob',
|
|
667
|
-
},
|
|
668
|
-
{
|
|
669
|
-
name: `${strings.adoPatternRegex} ${chalk.gray(`- ${strings.adoPatternRegexDesc}`)}`,
|
|
670
|
-
value: 'pattern-regex',
|
|
671
|
-
},
|
|
672
|
-
{
|
|
673
|
-
name: `${strings.adoSkipOption} ${chalk.gray(`- ${strings.adoSkipDesc}`)}`,
|
|
674
|
-
value: 'skip',
|
|
675
|
-
},
|
|
676
|
-
];
|
|
677
|
-
const strategy = await select({
|
|
678
|
-
message: strings.adoSelectStrategy,
|
|
679
|
-
choices: strategyChoices,
|
|
680
|
-
default: 'all',
|
|
681
|
-
});
|
|
682
|
-
switch (strategy) {
|
|
683
|
-
case 'all': {
|
|
684
|
-
adoClonePattern = '*';
|
|
685
|
-
adoClonePatternResult = { strategy: 'all', pattern: '*' };
|
|
686
|
-
console.log(chalk.green(` ✓ ${strings.adoSelectedAll}`));
|
|
687
|
-
break;
|
|
688
|
-
}
|
|
689
|
-
case 'pattern-glob': {
|
|
690
|
-
console.log(chalk.gray(`\n 💡 ${strings.adoPatternHint}`));
|
|
691
|
-
console.log(chalk.gray(` 💡 ${strings.adoShortcutsHint}\n`));
|
|
692
|
-
const pattern = await input({
|
|
693
|
-
message: strings.adoPatternPrompt,
|
|
694
|
-
validate: (value) => {
|
|
695
|
-
if (!value.trim())
|
|
696
|
-
return strings.adoPatternRequired;
|
|
697
|
-
return true;
|
|
698
|
-
},
|
|
699
|
-
});
|
|
700
|
-
// Parse shortcuts (starts:, ends:, contains:)
|
|
701
|
-
const parsedPattern = parsePatternShortcut(pattern.trim());
|
|
702
|
-
adoClonePattern = parsedPattern;
|
|
703
|
-
adoClonePatternResult = { strategy: 'pattern-glob', pattern: parsedPattern };
|
|
704
|
-
const message = strings.adoSelectedPattern.replace('{pattern}', parsedPattern);
|
|
705
|
-
console.log(chalk.green(` ✓ ${message}`));
|
|
706
|
-
break;
|
|
707
|
-
}
|
|
708
|
-
case 'pattern-regex': {
|
|
709
|
-
console.log(chalk.gray(`\n 💡 ${strings.adoRegexHint}\n`));
|
|
710
|
-
const pattern = await input({
|
|
711
|
-
message: strings.adoRegexPrompt,
|
|
712
|
-
validate: (value) => {
|
|
713
|
-
if (!value.trim())
|
|
714
|
-
return strings.adoPatternRequired;
|
|
715
|
-
const validation = validateRegex(value.trim());
|
|
716
|
-
if (validation !== true) {
|
|
717
|
-
return `${strings.adoInvalidRegex}: ${validation}`;
|
|
718
|
-
}
|
|
719
|
-
return true;
|
|
720
|
-
},
|
|
721
|
-
});
|
|
722
|
-
// Store as regex: prefix for downstream processing
|
|
723
|
-
adoClonePattern = `regex:${pattern.trim()}`;
|
|
724
|
-
adoClonePatternResult = { strategy: 'pattern-regex', pattern: pattern.trim(), isRegex: true };
|
|
725
|
-
const message = strings.adoSelectedRegex.replace('{pattern}', pattern.trim());
|
|
726
|
-
console.log(chalk.green(` ✓ ${message}`));
|
|
727
|
-
break;
|
|
728
|
-
}
|
|
729
|
-
case 'skip': {
|
|
730
|
-
adoClonePatternResult = { strategy: 'skip' };
|
|
731
|
-
console.log(chalk.gray(` → ${strings.adoCloneSkip}`));
|
|
732
|
-
break;
|
|
810
|
+
if (isMultiRepo && (provider === 'ado' || provider === 'github' || provider === 'bitbucket')) {
|
|
811
|
+
// Step 3a: For ADO only - prompt for organization and project selection
|
|
812
|
+
if (provider === 'ado') {
|
|
813
|
+
console.log(chalk.blue('\n📁 Azure DevOps Project Selection\n'));
|
|
814
|
+
console.log(chalk.gray(' Select which project(s) to clone repositories from.\n'));
|
|
815
|
+
const projectSelection = await promptAdoProjectSelection(options.targetDir, strings);
|
|
816
|
+
if (projectSelection) {
|
|
817
|
+
adoProjectSelection = projectSelection;
|
|
733
818
|
}
|
|
734
819
|
}
|
|
820
|
+
// Step 3b: Show unified strategy selection (ALL providers)
|
|
821
|
+
const patternResult = await promptMultiRepoPatternSelection(provider, strings);
|
|
822
|
+
// Map to ADO-style result for backward compatibility
|
|
823
|
+
adoClonePatternResult = {
|
|
824
|
+
strategy: patternResult.strategy,
|
|
825
|
+
pattern: patternResult.pattern,
|
|
826
|
+
isRegex: patternResult.isRegex,
|
|
827
|
+
};
|
|
828
|
+
// Set the clone pattern string
|
|
829
|
+
if (patternResult.strategy === 'all') {
|
|
830
|
+
adoClonePattern = '*';
|
|
831
|
+
}
|
|
832
|
+
else if (patternResult.strategy === 'pattern-glob') {
|
|
833
|
+
adoClonePattern = patternResult.pattern;
|
|
834
|
+
}
|
|
835
|
+
else if (patternResult.strategy === 'pattern-regex') {
|
|
836
|
+
adoClonePattern = `regex:${patternResult.pattern}`;
|
|
837
|
+
}
|
|
838
|
+
// For 'skip', adoClonePattern remains undefined
|
|
735
839
|
}
|
|
736
840
|
return { hosting: repositoryHosting, isMultiRepo, adoClonePattern, adoClonePatternResult, adoProjectSelection };
|
|
737
841
|
}
|