specweave 0.24.6 → 0.24.9
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/CLAUDE.md +64 -0
- package/README.md +34 -0
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +3 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts +5 -2
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +90 -8
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github.d.ts +2 -1
- package/dist/src/cli/helpers/issue-tracker/github.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github.js +4 -3
- package/dist/src/cli/helpers/issue-tracker/github.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 +26 -9
- package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/types.d.ts +1 -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/repo-structure/git-error-handler.d.ts +37 -0
- package/dist/src/core/repo-structure/git-error-handler.d.ts.map +1 -0
- package/dist/src/core/repo-structure/git-error-handler.js +214 -0
- package/dist/src/core/repo-structure/git-error-handler.js.map +1 -0
- package/dist/src/core/repo-structure/git-provider.d.ts +183 -0
- package/dist/src/core/repo-structure/git-provider.d.ts.map +1 -0
- package/dist/src/core/repo-structure/git-provider.js +57 -0
- package/dist/src/core/repo-structure/git-provider.js.map +1 -0
- package/dist/src/core/repo-structure/github-validator.d.ts +1 -0
- package/dist/src/core/repo-structure/github-validator.d.ts.map +1 -1
- package/dist/src/core/repo-structure/github-validator.js +35 -9
- package/dist/src/core/repo-structure/github-validator.js.map +1 -1
- package/dist/src/core/repo-structure/platform-registry.d.ts +114 -0
- package/dist/src/core/repo-structure/platform-registry.d.ts.map +1 -0
- package/dist/src/core/repo-structure/platform-registry.js +206 -0
- package/dist/src/core/repo-structure/platform-registry.js.map +1 -0
- package/dist/src/core/repo-structure/prompt-consolidator.d.ts +42 -0
- package/dist/src/core/repo-structure/prompt-consolidator.d.ts.map +1 -1
- package/dist/src/core/repo-structure/prompt-consolidator.js +102 -0
- package/dist/src/core/repo-structure/prompt-consolidator.js.map +1 -1
- package/dist/src/core/repo-structure/providers/azure-devops-provider.d.ts +64 -0
- package/dist/src/core/repo-structure/providers/azure-devops-provider.d.ts.map +1 -0
- package/dist/src/core/repo-structure/providers/azure-devops-provider.js +263 -0
- package/dist/src/core/repo-structure/providers/azure-devops-provider.js.map +1 -0
- package/dist/src/core/repo-structure/providers/bitbucket-provider.d.ts +55 -0
- package/dist/src/core/repo-structure/providers/bitbucket-provider.d.ts.map +1 -0
- package/dist/src/core/repo-structure/providers/bitbucket-provider.js +238 -0
- package/dist/src/core/repo-structure/providers/bitbucket-provider.js.map +1 -0
- package/dist/src/core/repo-structure/providers/github-provider.d.ts +53 -0
- package/dist/src/core/repo-structure/providers/github-provider.d.ts.map +1 -0
- package/dist/src/core/repo-structure/providers/github-provider.js +239 -0
- package/dist/src/core/repo-structure/providers/github-provider.js.map +1 -0
- package/dist/src/core/repo-structure/providers/gitlab-provider.d.ts +51 -0
- package/dist/src/core/repo-structure/providers/gitlab-provider.d.ts.map +1 -0
- package/dist/src/core/repo-structure/providers/gitlab-provider.js +251 -0
- package/dist/src/core/repo-structure/providers/gitlab-provider.js.map +1 -0
- package/dist/src/core/repo-structure/providers/index.d.ts +35 -0
- package/dist/src/core/repo-structure/providers/index.d.ts.map +1 -0
- package/dist/src/core/repo-structure/providers/index.js +68 -0
- package/dist/src/core/repo-structure/providers/index.js.map +1 -0
- package/dist/src/core/repo-structure/providers/local-provider.d.ts +61 -0
- package/dist/src/core/repo-structure/providers/local-provider.d.ts.map +1 -0
- package/dist/src/core/repo-structure/providers/local-provider.js +148 -0
- package/dist/src/core/repo-structure/providers/local-provider.js.map +1 -0
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts +21 -4
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +380 -113
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/core/repo-structure/url-generator.d.ts +80 -0
- package/dist/src/core/repo-structure/url-generator.d.ts.map +1 -0
- package/dist/src/core/repo-structure/url-generator.js +110 -0
- package/dist/src/core/repo-structure/url-generator.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/hooks/post-task-completion.sh +69 -175
- package/plugins/specweave/lib/hooks/consolidated-sync.js +183 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +64 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +96 -0
|
@@ -23,23 +23,29 @@ import { execSync } from 'child_process';
|
|
|
23
23
|
import { execFileNoThrowSync } from '../../utils/execFileNoThrow.js';
|
|
24
24
|
import { generateRepoIdSmart, ensureUniqueId, validateRepoId, suggestRepoName } from './repo-id-generator.js';
|
|
25
25
|
import { SetupStateManager } from './setup-state-manager.js';
|
|
26
|
-
import { validateRepository, validateOwner } from './github-validator.js';
|
|
27
26
|
import { generateEnvFile } from '../../utils/env-file-generator.js';
|
|
28
27
|
import { generateSetupSummary } from './setup-summary.js';
|
|
29
|
-
import { getArchitecturePrompt, getParentRepoBenefits, getVisibilityPrompt } from './prompt-consolidator.js';
|
|
28
|
+
import { getArchitecturePrompt, getParentRepoBenefits, getVisibilityPrompt, getUrlTypePrompt } from './prompt-consolidator.js';
|
|
30
29
|
import { detectRepositoryHints } from './folder-detector.js';
|
|
31
30
|
import { discoverRepositories } from './repo-bulk-discovery.js';
|
|
32
31
|
import { Octokit } from '@octokit/rest';
|
|
32
|
+
import { initializeProviders } from './providers/index.js';
|
|
33
|
+
import { getPlatformRegistry } from './platform-registry.js';
|
|
34
|
+
import { getPlatformSelectionPrompt } from './prompt-consolidator.js';
|
|
33
35
|
export class RepoStructureManager {
|
|
34
36
|
constructor(projectPath, githubToken) {
|
|
35
37
|
this.projectPath = projectPath;
|
|
36
38
|
this.githubToken = githubToken;
|
|
37
39
|
this.stateManager = new SetupStateManager(projectPath);
|
|
40
|
+
// Initialize Git providers on instantiation
|
|
41
|
+
initializeProviders();
|
|
38
42
|
}
|
|
39
43
|
/**
|
|
40
44
|
* Prompt user for repository structure decisions
|
|
45
|
+
*
|
|
46
|
+
* @param preSelectedArchitecture - Optional pre-selected architecture to skip duplicate prompts
|
|
41
47
|
*/
|
|
42
|
-
async promptStructure() {
|
|
48
|
+
async promptStructure(preSelectedArchitecture) {
|
|
43
49
|
console.log(chalk.cyan.bold('\n🏗️ Repository Architecture Setup\n'));
|
|
44
50
|
console.log(chalk.gray('Let\'s configure your repository structure for optimal organization.\n'));
|
|
45
51
|
// Check for resumed setup
|
|
@@ -62,27 +68,76 @@ export class RepoStructureManager {
|
|
|
62
68
|
await this.stateManager.deleteState();
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
|
-
// Step 1: Ask about architecture type using consolidator
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
// Step 1: Ask about architecture type using consolidator (SKIP if pre-selected)
|
|
72
|
+
let architecture;
|
|
73
|
+
if (preSelectedArchitecture) {
|
|
74
|
+
// Architecture already selected - skip duplicate prompt
|
|
75
|
+
architecture = preSelectedArchitecture;
|
|
76
|
+
console.log(chalk.green(`✓ Architecture: ${this.formatArchitectureForDisplay(architecture)}\n`));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Ask user for architecture choice
|
|
80
|
+
const promptData = getArchitecturePrompt();
|
|
81
|
+
const result = await inquirer.prompt([{
|
|
82
|
+
type: 'list',
|
|
83
|
+
name: 'architecture',
|
|
84
|
+
message: promptData.question,
|
|
85
|
+
choices: promptData.options.map(opt => ({
|
|
86
|
+
name: `${opt.label}\n${chalk.gray(opt.description)}\n${chalk.dim(opt.example)}`,
|
|
87
|
+
value: opt.value,
|
|
88
|
+
short: opt.label
|
|
89
|
+
})),
|
|
90
|
+
default: 'single'
|
|
91
|
+
}]);
|
|
92
|
+
architecture = result.architecture;
|
|
93
|
+
}
|
|
94
|
+
// Step 2: Ask about Git hosting platform
|
|
95
|
+
const registry = getPlatformRegistry();
|
|
96
|
+
const platformOptions = registry.getPlatformOptions(true); // Include unsupported platforms
|
|
97
|
+
const platformPromptData = getPlatformSelectionPrompt();
|
|
98
|
+
console.log(chalk.cyan('\n' + platformPromptData.message));
|
|
99
|
+
const { platform } = await inquirer.prompt([{
|
|
100
|
+
type: 'list',
|
|
101
|
+
name: 'platform',
|
|
102
|
+
message: platformPromptData.question,
|
|
103
|
+
choices: platformOptions.map(opt => ({
|
|
104
|
+
name: opt.disabled
|
|
105
|
+
? `${opt.name}\n${chalk.gray(opt.description)}\n${chalk.yellow('⚠️ ' + opt.disabled)}`
|
|
106
|
+
: `${opt.name}\n${chalk.gray(opt.description)}`,
|
|
107
|
+
value: opt.value,
|
|
108
|
+
short: opt.name,
|
|
109
|
+
disabled: opt.disabled ? opt.disabled : false
|
|
110
|
+
})),
|
|
111
|
+
default: 'github'
|
|
112
|
+
}]);
|
|
113
|
+
// Get provider instance
|
|
114
|
+
const provider = registry.getProvider(platform);
|
|
115
|
+
if (!provider) {
|
|
116
|
+
throw new Error(`Platform ${platform} is not available. This should not happen!`);
|
|
117
|
+
}
|
|
118
|
+
console.log(chalk.green(`\n✓ Using ${provider.config.name} as Git hosting platform\n`));
|
|
119
|
+
// Step 3: Ask about Git remote URL format (SSH vs HTTPS)
|
|
120
|
+
const urlTypePromptData = getUrlTypePrompt();
|
|
121
|
+
const { urlType } = await inquirer.prompt([{
|
|
68
122
|
type: 'list',
|
|
69
|
-
name: '
|
|
70
|
-
message:
|
|
71
|
-
choices:
|
|
72
|
-
name: `${opt.label}\n${chalk.gray(opt.description)}
|
|
123
|
+
name: 'urlType',
|
|
124
|
+
message: urlTypePromptData.question,
|
|
125
|
+
choices: urlTypePromptData.options.map(opt => ({
|
|
126
|
+
name: `${opt.label}\n${chalk.gray(opt.description)}`,
|
|
73
127
|
value: opt.value,
|
|
74
128
|
short: opt.label
|
|
75
129
|
})),
|
|
76
|
-
default:
|
|
130
|
+
default: urlTypePromptData.default
|
|
77
131
|
}]);
|
|
132
|
+
console.log(chalk.green(`\n✓ Using ${urlType.toUpperCase()} remote URLs\n`));
|
|
78
133
|
// Map ArchitectureChoice to internal architecture
|
|
79
134
|
const mappedArch = this.mapArchitectureChoice(architecture);
|
|
80
135
|
switch (mappedArch) {
|
|
81
136
|
case 'single':
|
|
82
|
-
return this.configureSingleRepo();
|
|
137
|
+
return this.configureSingleRepo(urlType, platform, provider);
|
|
83
138
|
case 'parent':
|
|
84
139
|
// GitHub parent repo (pushed to GitHub)
|
|
85
|
-
return this.configureMultiRepo(true, false);
|
|
140
|
+
return this.configureMultiRepo(true, false, urlType, platform, provider);
|
|
86
141
|
default:
|
|
87
142
|
throw new Error(`Unknown architecture: ${architecture}`);
|
|
88
143
|
}
|
|
@@ -100,13 +155,38 @@ export class RepoStructureManager {
|
|
|
100
155
|
return 'single';
|
|
101
156
|
}
|
|
102
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Format architecture choice for display
|
|
160
|
+
*
|
|
161
|
+
* @param choice - Architecture choice
|
|
162
|
+
* @returns Human-readable format
|
|
163
|
+
*/
|
|
164
|
+
formatArchitectureForDisplay(choice) {
|
|
165
|
+
switch (choice) {
|
|
166
|
+
case 'single':
|
|
167
|
+
return 'Single repository';
|
|
168
|
+
case 'github-parent':
|
|
169
|
+
return 'Parent repo + nested repos (GitHub)';
|
|
170
|
+
default:
|
|
171
|
+
return choice;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
103
174
|
/**
|
|
104
175
|
* Resume setup from saved state
|
|
105
176
|
*/
|
|
106
177
|
async resumeSetup(state) {
|
|
178
|
+
// Default to GitHub platform for resumed setups (backward compatibility)
|
|
179
|
+
const registry = getPlatformRegistry();
|
|
180
|
+
const provider = registry.getProvider('github');
|
|
181
|
+
if (!provider) {
|
|
182
|
+
throw new Error('GitHub provider not available. This should not happen!');
|
|
183
|
+
}
|
|
107
184
|
// Convert saved state back to config format
|
|
108
185
|
const config = {
|
|
109
186
|
architecture: state.architecture,
|
|
187
|
+
urlType: 'ssh', // Default to SSH for resumed setups
|
|
188
|
+
platform: 'github', // Default to GitHub for backward compatibility
|
|
189
|
+
provider: provider,
|
|
110
190
|
parentRepo: state.parentRepo,
|
|
111
191
|
repositories: state.repos.map(r => ({
|
|
112
192
|
id: r.id,
|
|
@@ -126,7 +206,7 @@ export class RepoStructureManager {
|
|
|
126
206
|
/**
|
|
127
207
|
* Configure single repository
|
|
128
208
|
*/
|
|
129
|
-
async configureSingleRepo() {
|
|
209
|
+
async configureSingleRepo(urlType = 'ssh', platform = 'github', provider) {
|
|
130
210
|
console.log(chalk.cyan('\n📦 Single Repository Configuration\n'));
|
|
131
211
|
// Check if repo already exists
|
|
132
212
|
const hasGit = existsSync(path.join(this.projectPath, '.git'));
|
|
@@ -172,6 +252,9 @@ export class RepoStructureManager {
|
|
|
172
252
|
}
|
|
173
253
|
return {
|
|
174
254
|
architecture: 'single',
|
|
255
|
+
urlType,
|
|
256
|
+
platform,
|
|
257
|
+
provider,
|
|
175
258
|
repositories: [{
|
|
176
259
|
id: 'main',
|
|
177
260
|
name: repo,
|
|
@@ -237,6 +320,9 @@ export class RepoStructureManager {
|
|
|
237
320
|
}
|
|
238
321
|
return {
|
|
239
322
|
architecture: 'single',
|
|
323
|
+
urlType,
|
|
324
|
+
platform,
|
|
325
|
+
provider,
|
|
240
326
|
repositories: [{
|
|
241
327
|
id: 'main',
|
|
242
328
|
name: answers.repo,
|
|
@@ -253,12 +339,15 @@ export class RepoStructureManager {
|
|
|
253
339
|
* Configure multi-repository architecture
|
|
254
340
|
* @param useParent - Whether to use parent repository/folder
|
|
255
341
|
* @param isLocalParent - If true, parent folder is local only (NOT pushed to GitHub)
|
|
342
|
+
* @param urlType - Git remote URL format (ssh or https)
|
|
343
|
+
* @param platform - Git hosting platform type
|
|
344
|
+
* @param provider - Git provider instance for API operations
|
|
256
345
|
*
|
|
257
346
|
* NOTE: This is primarily user-facing output (console.log/console.error).
|
|
258
347
|
* All console.* calls in this method are legitimate user-facing exceptions
|
|
259
348
|
* as defined in CONTRIBUTING.md (colored messages, confirmations, formatted output).
|
|
260
349
|
*/
|
|
261
|
-
async configureMultiRepo(useParent = true, isLocalParent = false) {
|
|
350
|
+
async configureMultiRepo(useParent = true, isLocalParent = false, urlType = 'ssh', platform = 'github', provider) {
|
|
262
351
|
console.log(chalk.cyan('\n🎯 Multi-Repository Configuration\n'));
|
|
263
352
|
console.log(chalk.gray('This creates separate repositories for each service/component.\n'));
|
|
264
353
|
// Show parent repo benefits if using parent approach
|
|
@@ -268,6 +357,9 @@ export class RepoStructureManager {
|
|
|
268
357
|
}
|
|
269
358
|
const config = {
|
|
270
359
|
architecture: useParent ? 'parent' : 'multi-repo',
|
|
360
|
+
urlType,
|
|
361
|
+
platform,
|
|
362
|
+
provider,
|
|
271
363
|
repositories: []
|
|
272
364
|
};
|
|
273
365
|
// Save state: architecture selected
|
|
@@ -280,8 +372,155 @@ export class RepoStructureManager {
|
|
|
280
372
|
timestamp: new Date().toISOString(),
|
|
281
373
|
envCreated: false
|
|
282
374
|
});
|
|
283
|
-
//
|
|
284
|
-
|
|
375
|
+
// ========== NEW FLOW: Ask discovery strategy FIRST (before parent questions!) ==========
|
|
376
|
+
// Step 1: Ask how to configure implementation repositories (BEFORE parent questions)
|
|
377
|
+
let discoveryStrategy = 'manual';
|
|
378
|
+
let discoveredRepos = [];
|
|
379
|
+
let owner = '';
|
|
380
|
+
if (!isLocalParent && this.githubToken && useParent) {
|
|
381
|
+
console.log(chalk.cyan('\n🚀 Repository Configuration\n'));
|
|
382
|
+
console.log(chalk.gray('You can discover repositories automatically or enter them manually.\n'));
|
|
383
|
+
const { configMethod } = await inquirer.prompt([
|
|
384
|
+
{
|
|
385
|
+
type: 'list',
|
|
386
|
+
name: 'configMethod',
|
|
387
|
+
message: 'How do you want to configure these repositories?',
|
|
388
|
+
choices: [
|
|
389
|
+
{
|
|
390
|
+
name: `${chalk.bold('Manual entry')} - Enter parent and each repository one by one ${chalk.gray('(full control)')}`,
|
|
391
|
+
value: 'manual'
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: `${chalk.bold('Pattern matching')} - Discover repositories, then select parent ${chalk.gray('(faster for many repos)')}`,
|
|
395
|
+
value: 'pattern-first'
|
|
396
|
+
}
|
|
397
|
+
],
|
|
398
|
+
default: 'manual'
|
|
399
|
+
}
|
|
400
|
+
]);
|
|
401
|
+
discoveryStrategy = configMethod;
|
|
402
|
+
}
|
|
403
|
+
// Step 2: If pattern/all, discover repos FIRST, then ask which is parent
|
|
404
|
+
if (discoveryStrategy === 'pattern-first') {
|
|
405
|
+
// Get owner FIRST (needed for discovery)
|
|
406
|
+
console.log(chalk.cyan('\n👤 Repository Owner\n'));
|
|
407
|
+
const ownerPrompt = await inquirer.prompt([
|
|
408
|
+
{
|
|
409
|
+
type: 'input',
|
|
410
|
+
name: 'owner',
|
|
411
|
+
message: `${provider.config.name} owner/organization:`,
|
|
412
|
+
validate: async (input) => {
|
|
413
|
+
if (!input.trim())
|
|
414
|
+
return 'Owner is required';
|
|
415
|
+
// Validate owner exists on the platform
|
|
416
|
+
if (this.githubToken) {
|
|
417
|
+
const result = await provider.validateOwner(input, this.githubToken);
|
|
418
|
+
if (!result.valid) {
|
|
419
|
+
return result.error || `Invalid ${provider.config.name} owner`;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
]);
|
|
426
|
+
owner = ownerPrompt.owner;
|
|
427
|
+
// Discover repositories via pattern matching
|
|
428
|
+
const octokit = new Octokit({ auth: this.githubToken });
|
|
429
|
+
const isOrg = await provider.isOrganization(owner, this.githubToken);
|
|
430
|
+
// Retry loop for pattern adjustment
|
|
431
|
+
let discoveryResult = null;
|
|
432
|
+
while (discoveryResult === null) {
|
|
433
|
+
discoveryResult = await discoverRepositories(octokit, owner, isOrg, 0); // Pass 0, we'll count later
|
|
434
|
+
// If null, user selected "go back and adjust pattern", loop will retry
|
|
435
|
+
// If user selected "manual", discoveryResult will be { repositories: [], strategy: 'manual' }
|
|
436
|
+
}
|
|
437
|
+
if (discoveryResult && discoveryResult.strategy !== 'manual') {
|
|
438
|
+
discoveredRepos = discoveryResult.repositories;
|
|
439
|
+
// Now ask: Which repo is the parent?
|
|
440
|
+
console.log(chalk.cyan('\n🏠 Select Parent Repository\n'));
|
|
441
|
+
console.log(chalk.gray('Choose which repository will be the parent (contains .specweave/ structure)\n'));
|
|
442
|
+
const parentChoices = [
|
|
443
|
+
...discoveredRepos.map((repo, index) => ({
|
|
444
|
+
name: `${chalk.bold(repo.name)}\n${chalk.gray(repo.description || '(no description)')}`,
|
|
445
|
+
value: index.toString(),
|
|
446
|
+
short: repo.name
|
|
447
|
+
})),
|
|
448
|
+
{
|
|
449
|
+
name: `${chalk.yellow('✏️ Enter parent manually')} ${chalk.gray('(not in discovered list)')}`,
|
|
450
|
+
value: 'manual',
|
|
451
|
+
short: 'Enter manually'
|
|
452
|
+
}
|
|
453
|
+
];
|
|
454
|
+
const { parentSelection } = await inquirer.prompt([
|
|
455
|
+
{
|
|
456
|
+
type: 'list',
|
|
457
|
+
name: 'parentSelection',
|
|
458
|
+
message: 'Which repository is the parent?',
|
|
459
|
+
choices: parentChoices,
|
|
460
|
+
pageSize: 15
|
|
461
|
+
}
|
|
462
|
+
]);
|
|
463
|
+
if (parentSelection === 'manual') {
|
|
464
|
+
// User wants to enter parent manually - fall back to old flow
|
|
465
|
+
discoveryStrategy = 'manual';
|
|
466
|
+
discoveredRepos = []; // Clear discovered repos
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
// User selected a parent from discovered list
|
|
470
|
+
const parentIndex = parseInt(parentSelection);
|
|
471
|
+
const selectedParent = discoveredRepos[parentIndex];
|
|
472
|
+
// Fetch full repo details from GitHub API
|
|
473
|
+
let description = selectedParent.description || 'SpecWeave parent repository - specs, docs, and architecture';
|
|
474
|
+
let existingVisibility = selectedParent.private ? 'private' : 'public';
|
|
475
|
+
try {
|
|
476
|
+
const response = await fetch(`https://api.github.com/repos/${owner}/${selectedParent.name}`, {
|
|
477
|
+
headers: {
|
|
478
|
+
'Authorization': `Bearer ${this.githubToken}`,
|
|
479
|
+
'Accept': 'application/vnd.github+json'
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
if (response.ok) {
|
|
483
|
+
const data = await response.json();
|
|
484
|
+
description = data.description || description;
|
|
485
|
+
existingVisibility = data.private ? 'private' : 'public';
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
catch {
|
|
489
|
+
// Use defaults if fetch fails
|
|
490
|
+
}
|
|
491
|
+
// Set parent config
|
|
492
|
+
config.parentRepo = {
|
|
493
|
+
name: selectedParent.name,
|
|
494
|
+
owner: owner,
|
|
495
|
+
description: description,
|
|
496
|
+
visibility: existingVisibility,
|
|
497
|
+
createOnGitHub: false // Already exists!
|
|
498
|
+
};
|
|
499
|
+
// Remove parent from discovered repos (implementation repos = discovered - parent)
|
|
500
|
+
discoveredRepos.splice(parentIndex, 1);
|
|
501
|
+
console.log(chalk.green(`\n✓ Using existing repository: ${owner}/${selectedParent.name}\n`));
|
|
502
|
+
console.log(chalk.gray(`✓ Implementation repositories: ${discoveredRepos.length}\n`));
|
|
503
|
+
// Save state: parent repo configured
|
|
504
|
+
await this.saveSetupState({
|
|
505
|
+
version: '1.0.0',
|
|
506
|
+
architecture: useParent ? 'parent' : 'multi-repo',
|
|
507
|
+
parentRepo: config.parentRepo,
|
|
508
|
+
repos: [],
|
|
509
|
+
currentStep: 'parent-repo-configured',
|
|
510
|
+
timestamp: new Date().toISOString(),
|
|
511
|
+
envCreated: false
|
|
512
|
+
});
|
|
513
|
+
// Skip to repository configuration (lines 794+)
|
|
514
|
+
// We'll continue below with discoveredRepos
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
// User selected manual - fall back to old flow
|
|
519
|
+
discoveryStrategy = 'manual';
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// Step 3: Manual flow (existing logic) - only runs if discoveryStrategy === 'manual'
|
|
523
|
+
if (discoveryStrategy === 'manual' && useParent) {
|
|
285
524
|
let parentAnswers;
|
|
286
525
|
if (isLocalParent) {
|
|
287
526
|
// Local parent: Skip GitHub questions, just ask for folder name
|
|
@@ -302,15 +541,15 @@ export class RepoStructureManager {
|
|
|
302
541
|
{
|
|
303
542
|
type: 'input',
|
|
304
543
|
name: 'owner',
|
|
305
|
-
message:
|
|
544
|
+
message: `${provider.config.name} owner/organization for IMPLEMENTATION repos:`,
|
|
306
545
|
validate: async (input) => {
|
|
307
546
|
if (!input.trim())
|
|
308
547
|
return 'Owner is required';
|
|
309
|
-
// Validate owner exists on
|
|
548
|
+
// Validate owner exists on the platform
|
|
310
549
|
if (this.githubToken) {
|
|
311
|
-
const result = await validateOwner(input, this.githubToken);
|
|
550
|
+
const result = await provider.validateOwner(input, this.githubToken);
|
|
312
551
|
if (!result.valid) {
|
|
313
|
-
return result.error ||
|
|
552
|
+
return result.error || `Invalid ${provider.config.name} owner`;
|
|
314
553
|
}
|
|
315
554
|
}
|
|
316
555
|
return true;
|
|
@@ -351,15 +590,15 @@ export class RepoStructureManager {
|
|
|
351
590
|
{
|
|
352
591
|
type: 'input',
|
|
353
592
|
name: 'owner',
|
|
354
|
-
message:
|
|
593
|
+
message: `${provider.config.name} owner/organization:`,
|
|
355
594
|
validate: async (input) => {
|
|
356
595
|
if (!input.trim())
|
|
357
596
|
return 'Owner is required';
|
|
358
|
-
// Validate owner exists on
|
|
597
|
+
// Validate owner exists on the platform
|
|
359
598
|
if (this.githubToken) {
|
|
360
|
-
const result = await validateOwner(input, this.githubToken);
|
|
599
|
+
const result = await provider.validateOwner(input, this.githubToken);
|
|
361
600
|
if (!result.valid) {
|
|
362
|
-
return result.error ||
|
|
601
|
+
return result.error || `Invalid ${provider.config.name} owner`;
|
|
363
602
|
}
|
|
364
603
|
}
|
|
365
604
|
return true;
|
|
@@ -376,11 +615,11 @@ export class RepoStructureManager {
|
|
|
376
615
|
validate: async (input) => {
|
|
377
616
|
if (!input.trim())
|
|
378
617
|
return 'Repository name is required';
|
|
379
|
-
// Validate repository EXISTS on
|
|
618
|
+
// Validate repository EXISTS on the platform
|
|
380
619
|
if (this.githubToken && ownerPrompt.owner) {
|
|
381
|
-
const result = await validateRepository(ownerPrompt.owner, input, this.githubToken);
|
|
620
|
+
const result = await provider.validateRepository(ownerPrompt.owner, input, this.githubToken);
|
|
382
621
|
if (!result.exists) {
|
|
383
|
-
return `Repository ${ownerPrompt.owner}/${input} not found on
|
|
622
|
+
return `Repository ${ownerPrompt.owner}/${input} not found on ${provider.config.name}. Please check the name or choose 'Create new'.`;
|
|
384
623
|
}
|
|
385
624
|
}
|
|
386
625
|
return true;
|
|
@@ -425,15 +664,15 @@ export class RepoStructureManager {
|
|
|
425
664
|
{
|
|
426
665
|
type: 'input',
|
|
427
666
|
name: 'owner',
|
|
428
|
-
message:
|
|
667
|
+
message: `${provider.config.name} owner/organization for ALL repos:`,
|
|
429
668
|
validate: async (input) => {
|
|
430
669
|
if (!input.trim())
|
|
431
670
|
return 'Owner is required';
|
|
432
|
-
// Validate owner exists on
|
|
671
|
+
// Validate owner exists on the platform
|
|
433
672
|
if (this.githubToken) {
|
|
434
|
-
const result = await validateOwner(input, this.githubToken);
|
|
673
|
+
const result = await provider.validateOwner(input, this.githubToken);
|
|
435
674
|
if (!result.valid) {
|
|
436
|
-
return result.error ||
|
|
675
|
+
return result.error || `Invalid ${provider.config.name} owner`;
|
|
437
676
|
}
|
|
438
677
|
}
|
|
439
678
|
return true;
|
|
@@ -452,7 +691,7 @@ export class RepoStructureManager {
|
|
|
452
691
|
return 'Repository name is required';
|
|
453
692
|
// Validate repository DOESN'T exist
|
|
454
693
|
if (this.githubToken && ownerPrompt.owner) {
|
|
455
|
-
const result = await validateRepository(ownerPrompt.owner, input, this.githubToken);
|
|
694
|
+
const result = await provider.validateRepository(ownerPrompt.owner, input, this.githubToken);
|
|
456
695
|
if (result.exists) {
|
|
457
696
|
return `Repository ${ownerPrompt.owner}/${input} already exists at ${result.url}. Please choose 'Use existing' or pick a different name.`;
|
|
458
697
|
}
|
|
@@ -517,73 +756,88 @@ export class RepoStructureManager {
|
|
|
517
756
|
envCreated: false
|
|
518
757
|
});
|
|
519
758
|
}
|
|
520
|
-
//
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
759
|
+
// Step 4: Repository count and discovery (skip count question if using pattern-first)
|
|
760
|
+
let repoCount;
|
|
761
|
+
let bulkDiscoveryStrategy = discoveryStrategy === 'pattern-first' ? 'pattern' : 'manual';
|
|
762
|
+
if (discoveryStrategy === 'pattern-first' && discoveredRepos.length > 0) {
|
|
763
|
+
// Pattern discovery: repos already discovered, skip count question
|
|
764
|
+
repoCount = discoveredRepos.length;
|
|
765
|
+
console.log(chalk.green(`\n✓ Total repositories: ${repoCount + 1} (1 parent + ${repoCount} implementation)\n`));
|
|
526
766
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if (
|
|
532
|
-
console.log(chalk.
|
|
533
|
-
console.log(chalk.
|
|
534
|
-
|
|
535
|
-
else {
|
|
536
|
-
console.log(chalk.white(' • 1 parent repository (specs, docs, increments)'));
|
|
537
|
-
console.log(chalk.white(' • N implementation repositories (your services/apps)'));
|
|
767
|
+
else {
|
|
768
|
+
// Manual entry: ask for count
|
|
769
|
+
// Auto-detect existing folders
|
|
770
|
+
const hints = await detectRepositoryHints(this.projectPath);
|
|
771
|
+
if (hints.detectedFolders.length > 0) {
|
|
772
|
+
console.log(chalk.green(`\n✓ Detected ${hints.detectedFolders.length} service folder(s):`));
|
|
773
|
+
hints.detectedFolders.forEach(f => console.log(chalk.gray(` • ${f}`)));
|
|
774
|
+
console.log('');
|
|
538
775
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
? '📦 How many IMPLEMENTATION repositories? (not counting parent)'
|
|
547
|
-
: 'How many repositories?',
|
|
548
|
-
default: hints.suggestedCount, // Use auto-detected count
|
|
549
|
-
validate: (input) => {
|
|
550
|
-
if (input < 1)
|
|
551
|
-
return useParent
|
|
552
|
-
? 'Need at least 1 implementation repository'
|
|
553
|
-
: 'Need at least 2 repositories';
|
|
554
|
-
if (input > 10)
|
|
555
|
-
return 'Maximum 10 repositories supported';
|
|
556
|
-
return true;
|
|
776
|
+
// Show repository count clarification BEFORE asking
|
|
777
|
+
if (useParent && config.parentRepo) {
|
|
778
|
+
console.log(chalk.cyan('\n📊 Repository Count\n'));
|
|
779
|
+
console.log(chalk.gray('You will create:'));
|
|
780
|
+
if (isLocalParent) {
|
|
781
|
+
console.log(chalk.white(' • 1 parent FOLDER (local only, .specweave/ gitignored)'));
|
|
782
|
+
console.log(chalk.white(' • N implementation repositories (your services/apps on GitHub)'));
|
|
557
783
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
console.log(chalk.
|
|
563
|
-
console.log(chalk.gray(` (Parent folder is local only, not counted)\n`));
|
|
784
|
+
else {
|
|
785
|
+
console.log(chalk.white(' • 1 parent repository (specs, docs, increments)'));
|
|
786
|
+
console.log(chalk.white(' • N implementation repositories (your services/apps)'));
|
|
787
|
+
}
|
|
788
|
+
console.log(chalk.gray('\nNext question asks for: IMPLEMENTATION repositories ONLY (not counting parent)\n'));
|
|
564
789
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
790
|
+
// Ask how many implementation repositories
|
|
791
|
+
const promptResult = await inquirer.prompt([{
|
|
792
|
+
type: 'number',
|
|
793
|
+
name: 'repoCount',
|
|
794
|
+
message: useParent
|
|
795
|
+
? '📦 How many IMPLEMENTATION repositories? (not counting parent)'
|
|
796
|
+
: 'How many repositories?',
|
|
797
|
+
default: hints.suggestedCount, // Use auto-detected count
|
|
798
|
+
validate: (input) => {
|
|
799
|
+
if (input < 1)
|
|
800
|
+
return useParent
|
|
801
|
+
? 'Need at least 1 implementation repository'
|
|
802
|
+
: 'Need at least 2 repositories';
|
|
803
|
+
if (input > 10)
|
|
804
|
+
return 'Maximum 10 repositories supported';
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
}]);
|
|
808
|
+
repoCount = promptResult.repoCount;
|
|
809
|
+
// Show summary AFTER for confirmation
|
|
810
|
+
if (useParent && config.parentRepo) {
|
|
811
|
+
if (isLocalParent) {
|
|
812
|
+
console.log(chalk.green(`\n✓ Total repositories to create: ${repoCount} implementation repos`));
|
|
813
|
+
console.log(chalk.gray(` (Parent folder is local only, not counted)\n`));
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
const totalRepos = 1 + repoCount;
|
|
817
|
+
console.log(chalk.green(`\n✓ Total repositories to create: ${totalRepos} (1 parent + ${repoCount} implementation)\n`));
|
|
818
|
+
}
|
|
568
819
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
if (
|
|
584
|
-
|
|
585
|
-
//
|
|
586
|
-
|
|
820
|
+
// Bulk repository discovery for manual flow (old behavior)
|
|
821
|
+
if (this.githubToken && config.parentRepo && discoveryStrategy === 'manual') {
|
|
822
|
+
const octokit = new Octokit({ auth: this.githubToken });
|
|
823
|
+
const owner = config.parentRepo.owner;
|
|
824
|
+
const isOrg = await provider.isOrganization(owner, this.githubToken);
|
|
825
|
+
// Retry loop for pattern adjustment
|
|
826
|
+
let discoveryResult = null;
|
|
827
|
+
while (discoveryResult === null) {
|
|
828
|
+
discoveryResult = await discoverRepositories(octokit, owner, isOrg, repoCount);
|
|
829
|
+
// If null, user selected "go back and adjust pattern", loop will retry
|
|
830
|
+
// If user selected "manual", discoveryResult will be { repositories: [], strategy: 'manual' }
|
|
831
|
+
}
|
|
832
|
+
if (discoveryResult) {
|
|
833
|
+
bulkDiscoveryStrategy = discoveryResult.strategy;
|
|
834
|
+
if (discoveryResult.strategy !== 'manual') {
|
|
835
|
+
discoveredRepos = discoveryResult.repositories;
|
|
836
|
+
// Update repoCount to match discovered repos
|
|
837
|
+
if (discoveredRepos.length !== repoCount) {
|
|
838
|
+
console.log(chalk.yellow(`\n📝 Adjusting repository count from ${repoCount} to ${discoveredRepos.length} (based on discovery)\n`));
|
|
839
|
+
repoCount = discoveredRepos.length;
|
|
840
|
+
}
|
|
587
841
|
}
|
|
588
842
|
}
|
|
589
843
|
}
|
|
@@ -646,7 +900,7 @@ export class RepoStructureManager {
|
|
|
646
900
|
return 'Repository name is required';
|
|
647
901
|
// Validate repository doesn't exist (skip for discovered repos)
|
|
648
902
|
if (!isDiscovered && this.githubToken && config.parentRepo) {
|
|
649
|
-
const result = await validateRepository(config.parentRepo.owner, input, this.githubToken);
|
|
903
|
+
const result = await provider.validateRepository(config.parentRepo.owner, input, this.githubToken);
|
|
650
904
|
if (result.exists) {
|
|
651
905
|
return `Repository ${config.parentRepo.owner}/${input} already exists at ${result.url}`;
|
|
652
906
|
}
|
|
@@ -772,7 +1026,7 @@ export class RepoStructureManager {
|
|
|
772
1026
|
/**
|
|
773
1027
|
* Configure monorepo
|
|
774
1028
|
*/
|
|
775
|
-
async configureMonorepo() {
|
|
1029
|
+
async configureMonorepo(urlType = 'ssh', platform = 'github', provider) {
|
|
776
1030
|
console.log(chalk.cyan('\n📚 Monorepo Configuration\n'));
|
|
777
1031
|
console.log(chalk.gray('Single repository with multiple projects/packages.\n'));
|
|
778
1032
|
const answers = await inquirer.prompt([
|
|
@@ -834,6 +1088,9 @@ export class RepoStructureManager {
|
|
|
834
1088
|
const projects = answers.projects.split(',').map((p) => p.trim());
|
|
835
1089
|
return {
|
|
836
1090
|
architecture: 'monorepo',
|
|
1091
|
+
urlType,
|
|
1092
|
+
platform,
|
|
1093
|
+
provider,
|
|
837
1094
|
repositories: [{
|
|
838
1095
|
id: 'main',
|
|
839
1096
|
name: answers.repo,
|
|
@@ -848,28 +1105,33 @@ export class RepoStructureManager {
|
|
|
848
1105
|
};
|
|
849
1106
|
}
|
|
850
1107
|
/**
|
|
851
|
-
* Create repositories on
|
|
1108
|
+
* Create repositories on Git hosting platform via API
|
|
852
1109
|
*/
|
|
853
|
-
async
|
|
1110
|
+
async createRepositories(config) {
|
|
854
1111
|
if (!this.githubToken) {
|
|
855
|
-
console.log(chalk.yellow(
|
|
856
|
-
console.log(chalk.gray(
|
|
1112
|
+
console.log(chalk.yellow(`\n⚠️ No ${config.provider.config.name} token available`));
|
|
1113
|
+
console.log(chalk.gray(` Skipping ${config.provider.config.name} repository creation`));
|
|
857
1114
|
console.log(chalk.gray(' You can create repositories manually later\n'));
|
|
858
1115
|
return;
|
|
859
1116
|
}
|
|
860
|
-
const spinner = ora(
|
|
1117
|
+
const spinner = ora(`Creating ${config.provider.config.name} repositories...`).start();
|
|
861
1118
|
const created = [];
|
|
862
1119
|
const failed = [];
|
|
863
1120
|
// Create parent repository if needed
|
|
864
1121
|
if (config.parentRepo?.createOnGitHub) {
|
|
865
1122
|
try {
|
|
866
|
-
await
|
|
1123
|
+
await config.provider.createRepository({
|
|
1124
|
+
owner: config.parentRepo.owner,
|
|
1125
|
+
name: config.parentRepo.name,
|
|
1126
|
+
description: config.parentRepo.description,
|
|
1127
|
+
visibility: config.parentRepo.visibility
|
|
1128
|
+
}, this.githubToken);
|
|
867
1129
|
created.push(`${config.parentRepo.owner}/${config.parentRepo.name}`);
|
|
868
1130
|
// Save state: parent repo created
|
|
869
1131
|
await this.saveSetupState({
|
|
870
1132
|
version: '1.0.0',
|
|
871
1133
|
architecture: config.architecture,
|
|
872
|
-
parentRepo: { ...config.parentRepo, url:
|
|
1134
|
+
parentRepo: { ...config.parentRepo, url: config.provider.getRemoteUrl(config.parentRepo.owner, config.parentRepo.name, config.urlType) },
|
|
873
1135
|
repos: [],
|
|
874
1136
|
currentStep: 'parent-repo-created',
|
|
875
1137
|
timestamp: new Date().toISOString(),
|
|
@@ -884,7 +1146,12 @@ export class RepoStructureManager {
|
|
|
884
1146
|
for (const repo of config.repositories) {
|
|
885
1147
|
if (repo.createOnGitHub) {
|
|
886
1148
|
try {
|
|
887
|
-
await
|
|
1149
|
+
await config.provider.createRepository({
|
|
1150
|
+
owner: repo.owner,
|
|
1151
|
+
name: repo.name,
|
|
1152
|
+
description: repo.description,
|
|
1153
|
+
visibility: repo.visibility
|
|
1154
|
+
}, this.githubToken);
|
|
888
1155
|
created.push(`${repo.owner}/${repo.name}`);
|
|
889
1156
|
}
|
|
890
1157
|
catch (error) {
|
|
@@ -973,7 +1240,7 @@ export class RepoStructureManager {
|
|
|
973
1240
|
path: r.path,
|
|
974
1241
|
visibility: r.visibility,
|
|
975
1242
|
displayName: r.name,
|
|
976
|
-
url:
|
|
1243
|
+
url: config.provider.getRemoteUrl(r.owner, r.name, config.urlType),
|
|
977
1244
|
created: false
|
|
978
1245
|
})),
|
|
979
1246
|
currentStep: 'complete',
|
|
@@ -1066,14 +1333,14 @@ export class RepoStructureManager {
|
|
|
1066
1333
|
}
|
|
1067
1334
|
/**
|
|
1068
1335
|
* Clone or initialize a repository
|
|
1069
|
-
* If the repo exists on
|
|
1336
|
+
* If the repo exists on the platform, clone it; otherwise, init + add remote
|
|
1070
1337
|
*/
|
|
1071
|
-
async cloneOrInitRepository(repoPath, owner, name, createOnGitHub) {
|
|
1338
|
+
async cloneOrInitRepository(repoPath, owner, name, createOnGitHub, urlType = 'ssh', provider) {
|
|
1072
1339
|
// If .git already exists, skip
|
|
1073
1340
|
if (existsSync(path.join(repoPath, '.git'))) {
|
|
1074
1341
|
return;
|
|
1075
1342
|
}
|
|
1076
|
-
const remoteUrl =
|
|
1343
|
+
const remoteUrl = provider.getRemoteUrl(owner, name, urlType);
|
|
1077
1344
|
// Check if repository exists on GitHub
|
|
1078
1345
|
const repoExists = await this.repositoryExistsOnGitHub(owner, name);
|
|
1079
1346
|
if (repoExists) {
|
|
@@ -1131,7 +1398,7 @@ export class RepoStructureManager {
|
|
|
1131
1398
|
if (!existsSync(path.join(this.projectPath, '.git'))) {
|
|
1132
1399
|
execFileNoThrowSync('git', ['init'], { cwd: this.projectPath });
|
|
1133
1400
|
if (config.parentRepo) {
|
|
1134
|
-
const remoteUrl =
|
|
1401
|
+
const remoteUrl = config.provider.getRemoteUrl(config.parentRepo.owner, config.parentRepo.name, config.urlType);
|
|
1135
1402
|
execFileNoThrowSync('git', ['remote', 'add', 'origin', remoteUrl], { cwd: this.projectPath });
|
|
1136
1403
|
}
|
|
1137
1404
|
}
|
|
@@ -1139,7 +1406,7 @@ export class RepoStructureManager {
|
|
|
1139
1406
|
for (const repo of config.repositories) {
|
|
1140
1407
|
const repoPath = path.join(this.projectPath, repo.path);
|
|
1141
1408
|
// Clone or initialize repository
|
|
1142
|
-
await this.cloneOrInitRepository(repoPath, repo.owner, repo.name, repo.createOnGitHub);
|
|
1409
|
+
await this.cloneOrInitRepository(repoPath, repo.owner, repo.name, repo.createOnGitHub, config.urlType, config.provider);
|
|
1143
1410
|
// Create basic structure (only if repo was just initialized, not cloned)
|
|
1144
1411
|
if (!repo.createOnGitHub || !await this.repositoryExistsOnGitHub(repo.owner, repo.name)) {
|
|
1145
1412
|
this.createBasicRepoStructure(repoPath, repo.id);
|
|
@@ -1151,7 +1418,7 @@ export class RepoStructureManager {
|
|
|
1151
1418
|
for (const repo of config.repositories) {
|
|
1152
1419
|
const repoPath = path.join(this.projectPath, repo.path);
|
|
1153
1420
|
// Clone or initialize repository
|
|
1154
|
-
await this.cloneOrInitRepository(repoPath, repo.owner, repo.name, repo.createOnGitHub);
|
|
1421
|
+
await this.cloneOrInitRepository(repoPath, repo.owner, repo.name, repo.createOnGitHub, config.urlType, config.provider);
|
|
1155
1422
|
// Create basic structure (only if repo was just initialized, not cloned)
|
|
1156
1423
|
if (!repo.createOnGitHub || !await this.repositoryExistsOnGitHub(repo.owner, repo.name)) {
|
|
1157
1424
|
this.createBasicRepoStructure(repoPath, repo.id);
|
|
@@ -1164,7 +1431,7 @@ export class RepoStructureManager {
|
|
|
1164
1431
|
execFileNoThrowSync('git', ['init'], { cwd: this.projectPath });
|
|
1165
1432
|
const repo = config.repositories[0];
|
|
1166
1433
|
if (repo) {
|
|
1167
|
-
const remoteUrl =
|
|
1434
|
+
const remoteUrl = config.provider.getRemoteUrl(repo.owner, repo.name, config.urlType);
|
|
1168
1435
|
execFileNoThrowSync('git', ['remote', 'add', 'origin', remoteUrl], { cwd: this.projectPath });
|
|
1169
1436
|
}
|
|
1170
1437
|
}
|