@hyperdrive.bot/cli 1.0.2
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 +1598 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +3 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/commands/account/add.d.ts +16 -0
- package/dist/commands/account/add.js +185 -0
- package/dist/commands/account/list.d.ts +6 -0
- package/dist/commands/account/list.js +37 -0
- package/dist/commands/account/remove.d.ts +11 -0
- package/dist/commands/account/remove.js +57 -0
- package/dist/commands/auth/login.d.ts +16 -0
- package/dist/commands/auth/login.js +178 -0
- package/dist/commands/auth/logout.d.ts +6 -0
- package/dist/commands/auth/logout.js +39 -0
- package/dist/commands/auth/refresh.d.ts +6 -0
- package/dist/commands/auth/refresh.js +66 -0
- package/dist/commands/auth/status.d.ts +6 -0
- package/dist/commands/auth/status.js +63 -0
- package/dist/commands/ci/account/create.d.ts +16 -0
- package/dist/commands/ci/account/create.js +158 -0
- package/dist/commands/ci/account/delete.d.ts +14 -0
- package/dist/commands/ci/account/delete.js +88 -0
- package/dist/commands/ci/account/list.d.ts +10 -0
- package/dist/commands/ci/account/list.js +65 -0
- package/dist/commands/config/get.d.ts +9 -0
- package/dist/commands/config/get.js +37 -0
- package/dist/commands/config/set.d.ts +10 -0
- package/dist/commands/config/set.js +48 -0
- package/dist/commands/config/show.d.ts +6 -0
- package/dist/commands/config/show.js +10 -0
- package/dist/commands/deployment/create.d.ts +30 -0
- package/dist/commands/deployment/create.js +188 -0
- package/dist/commands/deployment/get.d.ts +13 -0
- package/dist/commands/deployment/get.js +101 -0
- package/dist/commands/deployment/launch.d.ts +15 -0
- package/dist/commands/deployment/launch.js +105 -0
- package/dist/commands/deployment/list.d.ts +11 -0
- package/dist/commands/deployment/list.js +91 -0
- package/dist/commands/domain/current.d.ts +6 -0
- package/dist/commands/domain/current.js +18 -0
- package/dist/commands/domain/list.d.ts +6 -0
- package/dist/commands/domain/list.js +42 -0
- package/dist/commands/domain/switch.d.ts +9 -0
- package/dist/commands/domain/switch.js +40 -0
- package/dist/commands/example.d.ts +13 -0
- package/dist/commands/example.js +24 -0
- package/dist/commands/git/connect.d.ts +10 -0
- package/dist/commands/git/connect.js +56 -0
- package/dist/commands/git/disconnect.d.ts +11 -0
- package/dist/commands/git/disconnect.js +93 -0
- package/dist/commands/git/list.d.ts +10 -0
- package/dist/commands/git/list.js +53 -0
- package/dist/commands/git/sync.d.ts +18 -0
- package/dist/commands/git/sync.js +235 -0
- package/dist/commands/init.d.ts +188 -0
- package/dist/commands/init.js +817 -0
- package/dist/commands/jira/connect.d.ts +9 -0
- package/dist/commands/jira/connect.js +141 -0
- package/dist/commands/jira/status.d.ts +9 -0
- package/dist/commands/jira/status.js +118 -0
- package/dist/commands/module/analyze.d.ts +29 -0
- package/dist/commands/module/analyze.js +201 -0
- package/dist/commands/module/create.d.ts +42 -0
- package/dist/commands/module/create.js +498 -0
- package/dist/commands/module/destroy.d.ts +11 -0
- package/dist/commands/module/destroy.js +77 -0
- package/dist/commands/module/get.d.ts +10 -0
- package/dist/commands/module/get.js +43 -0
- package/dist/commands/module/link.d.ts +15 -0
- package/dist/commands/module/link.js +175 -0
- package/dist/commands/module/list.d.ts +9 -0
- package/dist/commands/module/list.js +51 -0
- package/dist/commands/module/reanalyze.d.ts +30 -0
- package/dist/commands/module/reanalyze.js +206 -0
- package/dist/commands/module/update.d.ts +27 -0
- package/dist/commands/module/update.js +102 -0
- package/dist/commands/parameter/add.d.ts +15 -0
- package/dist/commands/parameter/add.js +99 -0
- package/dist/commands/parameter/backfill.d.ts +12 -0
- package/dist/commands/parameter/backfill.js +113 -0
- package/dist/commands/parameter/clear.d.ts +14 -0
- package/dist/commands/parameter/clear.js +95 -0
- package/dist/commands/parameter/list.d.ts +14 -0
- package/dist/commands/parameter/list.js +92 -0
- package/dist/commands/parameter/pull.d.ts +14 -0
- package/dist/commands/parameter/pull.js +124 -0
- package/dist/commands/parameter/remove.d.ts +15 -0
- package/dist/commands/parameter/remove.js +90 -0
- package/dist/commands/parameter/sync.d.ts +14 -0
- package/dist/commands/parameter/sync.js +153 -0
- package/dist/commands/parameter/update.d.ts +15 -0
- package/dist/commands/parameter/update.js +100 -0
- package/dist/commands/stage/create.d.ts +28 -0
- package/dist/commands/stage/create.js +312 -0
- package/dist/commands/stage/list.d.ts +9 -0
- package/dist/commands/stage/list.js +63 -0
- package/dist/commands/test-api.d.ts +9 -0
- package/dist/commands/test-api.js +40 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/services/auth-service.d.ts +84 -0
- package/dist/services/auth-service.js +240 -0
- package/dist/services/git.d.ts +46 -0
- package/dist/services/git.js +409 -0
- package/dist/services/hyperdrive-sigv4.d.ts +449 -0
- package/dist/services/hyperdrive-sigv4.js +375 -0
- package/dist/services/hyperdrive.d.ts +87 -0
- package/dist/services/hyperdrive.js +108 -0
- package/dist/services/log-tailer.d.ts +95 -0
- package/dist/services/log-tailer.js +242 -0
- package/dist/services/tenant-service.d.ts +106 -0
- package/dist/services/tenant-service.js +332 -0
- package/dist/utils/account-flow.d.ts +74 -0
- package/dist/utils/account-flow.js +228 -0
- package/dist/utils/auth-flow.d.ts +146 -0
- package/dist/utils/auth-flow.js +477 -0
- package/dist/utils/git-flow.d.ts +72 -0
- package/dist/utils/git-flow.js +232 -0
- package/dist/utils/jira-flow.d.ts +71 -0
- package/dist/utils/jira-flow.js +120 -0
- package/dist/utils/summary-display.d.ts +59 -0
- package/dist/utils/summary-display.js +140 -0
- package/dist/utils/validation.d.ts +15 -0
- package/dist/utils/validation.js +32 -0
- package/oclif.manifest.json +2819 -0
- package/package.json +112 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Example extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
file: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
|
+
name: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
+
export default class Example extends Command {
|
|
3
|
+
static args = {
|
|
4
|
+
file: Args.string({ description: 'file to read' }),
|
|
5
|
+
};
|
|
6
|
+
static description = 'describe the command here';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> <%= command.id %>',
|
|
9
|
+
];
|
|
10
|
+
static flags = {
|
|
11
|
+
// flag with no value (-f, --force)
|
|
12
|
+
force: Flags.boolean({ char: 'f' }),
|
|
13
|
+
// flag with a value (-n, --name=VALUE)
|
|
14
|
+
name: Flags.string({ char: 'n', description: 'name to print' }),
|
|
15
|
+
};
|
|
16
|
+
async run() {
|
|
17
|
+
const { args, flags } = await this.parse(Example);
|
|
18
|
+
const name = flags.name ?? 'world';
|
|
19
|
+
this.log(`hello ${name} from /Users/marcelomarra/Developer/devsquad/hyperdrive/cli/src/commands/example.ts`);
|
|
20
|
+
if (args.file && flags.force) {
|
|
21
|
+
this.log(`you input --force and --file: ${args.file}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class GitConnect extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
provider: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { executeGitConnect } from '../../utils/git-flow.js';
|
|
4
|
+
export default class GitConnect extends Command {
|
|
5
|
+
static description = 'Connect a GitHub or GitLab account to Hyperdrive';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %> --provider=github',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --provider=gitlab',
|
|
9
|
+
];
|
|
10
|
+
static flags = {
|
|
11
|
+
domain: Flags.string({
|
|
12
|
+
char: 'd',
|
|
13
|
+
description: 'Tenant domain (for multi-domain setups)',
|
|
14
|
+
}),
|
|
15
|
+
provider: Flags.string({
|
|
16
|
+
char: 'p',
|
|
17
|
+
description: 'Git provider to connect',
|
|
18
|
+
options: ['github', 'gitlab'],
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
async run() {
|
|
22
|
+
const { flags } = await this.parse(GitConnect);
|
|
23
|
+
const provider = flags.provider;
|
|
24
|
+
if (provider) {
|
|
25
|
+
this.log(chalk.blue(`\nConnecting to ${provider === 'github' ? 'GitHub' : 'GitLab'}...`));
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const result = await executeGitConnect({
|
|
29
|
+
logger: (message) => this.log(chalk.gray(message)),
|
|
30
|
+
provider,
|
|
31
|
+
});
|
|
32
|
+
if (result.success) {
|
|
33
|
+
const providerName = result.provider === 'github' ? 'GitHub' : 'GitLab';
|
|
34
|
+
this.log(chalk.green(`\nâ
Successfully connected to ${providerName}!`));
|
|
35
|
+
// Display connected accounts
|
|
36
|
+
if (result.installations && result.installations.length > 0) {
|
|
37
|
+
this.log(chalk.blue('\nConnected accounts:'));
|
|
38
|
+
for (const installation of result.installations) {
|
|
39
|
+
const accountName = installation.accountLogin || installation.gitlabUsername || 'Unknown';
|
|
40
|
+
this.log(chalk.white(` - ${accountName} (${installation.provider})`));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (result.skipped) {
|
|
45
|
+
this.log(chalk.yellow('\nâ Git provider connection skipped.'));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.log(chalk.red(`\nâ Failed to connect: ${result.error || 'Unknown error'}`));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
53
|
+
this.error(`Failed to connect: ${errorMessage}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class GitDisconnect extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
installationId: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
provider: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { HyperdriveSigV4Service } from '../../services/hyperdrive-sigv4.js';
|
|
5
|
+
export default class GitDisconnect extends Command {
|
|
6
|
+
static description = 'Disconnect a Git account from Hyperdrive';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> <%= command.id %>',
|
|
9
|
+
'<%= config.bin %> <%= command.id %> --provider=github --installation-id=12345',
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
domain: Flags.string({
|
|
13
|
+
char: 'd',
|
|
14
|
+
description: 'Tenant domain (for multi-domain setups)',
|
|
15
|
+
}),
|
|
16
|
+
installationId: Flags.string({
|
|
17
|
+
char: 'i',
|
|
18
|
+
description: 'Installation ID to disconnect',
|
|
19
|
+
}),
|
|
20
|
+
provider: Flags.string({
|
|
21
|
+
char: 'p',
|
|
22
|
+
description: 'Git provider',
|
|
23
|
+
options: ['github', 'gitlab'],
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
async run() {
|
|
27
|
+
const { flags } = await this.parse(GitDisconnect);
|
|
28
|
+
const service = new HyperdriveSigV4Service(flags.domain);
|
|
29
|
+
try {
|
|
30
|
+
// List installations to let user select
|
|
31
|
+
const response = await service.gitListInstallations();
|
|
32
|
+
if (response.installations.length === 0) {
|
|
33
|
+
this.log(chalk.yellow('\nNo Git accounts connected.'));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let provider = flags.provider;
|
|
37
|
+
let installationId = flags.installationId;
|
|
38
|
+
if (!installationId) {
|
|
39
|
+
// Show list and let user select
|
|
40
|
+
const choices = response.installations.map((installation) => ({
|
|
41
|
+
name: `${installation.provider === 'github' ? 'đ' : 'đĻ'} ${installation.accountLogin || installation.gitlabUsername || 'Unknown'} (${installation.provider})`,
|
|
42
|
+
value: {
|
|
43
|
+
installationId: installation.installationId,
|
|
44
|
+
provider: installation.provider,
|
|
45
|
+
},
|
|
46
|
+
}));
|
|
47
|
+
const selection = await inquirer.prompt([{
|
|
48
|
+
choices,
|
|
49
|
+
message: chalk.yellow('Which Git account would you like to disconnect?'),
|
|
50
|
+
name: 'installation',
|
|
51
|
+
type: 'list',
|
|
52
|
+
}]);
|
|
53
|
+
provider = selection.installation.provider;
|
|
54
|
+
installationId = selection.installation.installationId;
|
|
55
|
+
}
|
|
56
|
+
if (!provider) {
|
|
57
|
+
const providerResponse = await inquirer.prompt([{
|
|
58
|
+
choices: [
|
|
59
|
+
{ name: 'GitHub', value: 'github' },
|
|
60
|
+
{ name: 'GitLab', value: 'gitlab' },
|
|
61
|
+
],
|
|
62
|
+
message: chalk.yellow('Which Git provider?'),
|
|
63
|
+
name: 'provider',
|
|
64
|
+
type: 'list',
|
|
65
|
+
}]);
|
|
66
|
+
provider = providerResponse.provider;
|
|
67
|
+
}
|
|
68
|
+
// Confirm disconnection
|
|
69
|
+
const confirm = await inquirer.prompt([{
|
|
70
|
+
default: false,
|
|
71
|
+
message: chalk.red('Are you sure you want to disconnect this Git account? This cannot be undone.'),
|
|
72
|
+
name: 'confirm',
|
|
73
|
+
type: 'confirm',
|
|
74
|
+
}]);
|
|
75
|
+
if (!confirm.confirm) {
|
|
76
|
+
this.log(chalk.gray('Disconnection cancelled.'));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Delete the installation
|
|
80
|
+
const result = await service.gitDeleteInstallation(provider, installationId);
|
|
81
|
+
if (result.success) {
|
|
82
|
+
this.log(chalk.green(`\nâ
${result.message}`));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
this.log(chalk.red(`\nâ Failed to disconnect: ${result.message}`));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
90
|
+
this.error(`Failed to disconnect Git account: ${errorMessage}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class GitList extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
provider: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { HyperdriveSigV4Service } from '../../services/hyperdrive-sigv4.js';
|
|
4
|
+
export default class GitList extends Command {
|
|
5
|
+
static description = 'List connected Git accounts';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> <%= command.id %>',
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --provider=github',
|
|
9
|
+
];
|
|
10
|
+
static flags = {
|
|
11
|
+
domain: Flags.string({
|
|
12
|
+
char: 'd',
|
|
13
|
+
description: 'Tenant domain (for multi-domain setups)',
|
|
14
|
+
}),
|
|
15
|
+
provider: Flags.string({
|
|
16
|
+
char: 'p',
|
|
17
|
+
description: 'Filter by Git provider',
|
|
18
|
+
options: ['github', 'gitlab'],
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
async run() {
|
|
22
|
+
const { flags } = await this.parse(GitList);
|
|
23
|
+
const service = new HyperdriveSigV4Service(flags.domain);
|
|
24
|
+
try {
|
|
25
|
+
const provider = flags.provider;
|
|
26
|
+
const response = await service.gitListInstallations(provider);
|
|
27
|
+
if (response.installations.length === 0) {
|
|
28
|
+
this.log(chalk.yellow('\nNo Git accounts connected.'));
|
|
29
|
+
this.log(chalk.gray('Run "hd git connect" to connect a GitHub or GitLab account.'));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.log(chalk.blue('\nConnected Git Accounts:'));
|
|
33
|
+
this.log('');
|
|
34
|
+
for (const installation of response.installations) {
|
|
35
|
+
const providerIcon = installation.provider === 'github' ? 'đ' : 'đĻ';
|
|
36
|
+
const accountName = installation.accountLogin || installation.gitlabUsername || 'Unknown';
|
|
37
|
+
const accountType = installation.accountType || '';
|
|
38
|
+
const status = installation.status === 'active'
|
|
39
|
+
? chalk.green('active')
|
|
40
|
+
: chalk.yellow(installation.status);
|
|
41
|
+
this.log(`${providerIcon} ${chalk.white.bold(accountName)} ${accountType ? chalk.gray(`(${accountType})`) : ''}`);
|
|
42
|
+
this.log(` Provider: ${installation.provider}`);
|
|
43
|
+
this.log(` Status: ${status}`);
|
|
44
|
+
this.log(` Connected: ${new Date(installation.installedAt).toLocaleString()}`);
|
|
45
|
+
this.log('');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
50
|
+
this.error(`Failed to list Git accounts: ${errorMessage}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Sync extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
all: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
7
|
+
branch: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
|
+
'merge-strategy': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
10
|
+
remote: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
11
|
+
'source-branch': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
12
|
+
verbose: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
13
|
+
};
|
|
14
|
+
run(): Promise<void>;
|
|
15
|
+
private displayConfiguration;
|
|
16
|
+
private displayResults;
|
|
17
|
+
private formatDuration;
|
|
18
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { Command, Flags, ux } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import * as ora from 'ora';
|
|
4
|
+
import { GitService } from '../../services/git.js';
|
|
5
|
+
export default class Sync extends Command {
|
|
6
|
+
static description = 'Sync specific branches or all open branches with a source branch (default: master)';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> <%= command.id %> --branch feature-branch',
|
|
9
|
+
'<%= config.bin %> <%= command.id %> --branch feature-1 --branch feature-2',
|
|
10
|
+
'<%= config.bin %> <%= command.id %> --all',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> --branch feature-branch --source-branch main',
|
|
12
|
+
'<%= config.bin %> <%= command.id %> --all --remote upstream --merge-strategy no-ff',
|
|
13
|
+
];
|
|
14
|
+
static flags = {
|
|
15
|
+
all: Flags.boolean({
|
|
16
|
+
char: 'a',
|
|
17
|
+
default: false,
|
|
18
|
+
description: 'Sync all remote branches'
|
|
19
|
+
}),
|
|
20
|
+
branch: Flags.string({
|
|
21
|
+
char: 'b',
|
|
22
|
+
description: 'Specific branch(es) to sync (can be used multiple times)',
|
|
23
|
+
multiple: true
|
|
24
|
+
}),
|
|
25
|
+
domain: Flags.string({
|
|
26
|
+
char: 'd',
|
|
27
|
+
description: 'Tenant domain (for multi-domain setups)',
|
|
28
|
+
}),
|
|
29
|
+
'merge-strategy': Flags.string({
|
|
30
|
+
char: 'm',
|
|
31
|
+
default: 'no-ff',
|
|
32
|
+
description: 'Git merge strategy',
|
|
33
|
+
options: ['no-ff', '--ff-only', '--no-ff', '--squash']
|
|
34
|
+
}),
|
|
35
|
+
remote: Flags.string({
|
|
36
|
+
char: 'r',
|
|
37
|
+
default: 'origin',
|
|
38
|
+
description: 'Remote repository name'
|
|
39
|
+
}),
|
|
40
|
+
'source-branch': Flags.string({
|
|
41
|
+
char: 's',
|
|
42
|
+
default: 'master',
|
|
43
|
+
description: 'Source branch to merge from'
|
|
44
|
+
}),
|
|
45
|
+
verbose: Flags.boolean({
|
|
46
|
+
char: 'v',
|
|
47
|
+
default: false,
|
|
48
|
+
description: 'Show detailed progress information'
|
|
49
|
+
})
|
|
50
|
+
};
|
|
51
|
+
async run() {
|
|
52
|
+
const startTime = Date.now();
|
|
53
|
+
const { flags } = await this.parse(Sync);
|
|
54
|
+
// Validate arguments
|
|
55
|
+
if (!flags.all && (!flags.branch || flags.branch.length === 0)) {
|
|
56
|
+
this.error('Either specify branches with --branch or use --all flag', {
|
|
57
|
+
suggestions: [
|
|
58
|
+
'Use --all to sync all branches: hd git sync --all',
|
|
59
|
+
'Specify one or more branches: hd git sync --branch feature-1 --branch feature-2',
|
|
60
|
+
'Specify a single branch: hd git sync --branch feature-branch'
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (flags.all && flags.branch && flags.branch.length > 0) {
|
|
65
|
+
this.error('Cannot specify both --branch and --all flag', {
|
|
66
|
+
suggestions: [
|
|
67
|
+
'Use only --all: hd git sync --all',
|
|
68
|
+
'Use only branch names: hd git sync --branch feature-1 --branch feature-2'
|
|
69
|
+
]
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const syncOptions = {
|
|
73
|
+
all: flags.all,
|
|
74
|
+
mergeStrategy: flags['merge-strategy'],
|
|
75
|
+
remote: flags.remote,
|
|
76
|
+
sourceBranch: flags['source-branch'],
|
|
77
|
+
targetBranches: flags.branch,
|
|
78
|
+
verbose: flags.verbose
|
|
79
|
+
};
|
|
80
|
+
// Display configuration
|
|
81
|
+
this.displayConfiguration(syncOptions);
|
|
82
|
+
// Initialize git service
|
|
83
|
+
const gitService = new GitService();
|
|
84
|
+
let spinner;
|
|
85
|
+
try {
|
|
86
|
+
// Start operation with spinner
|
|
87
|
+
if (flags.verbose) {
|
|
88
|
+
this.log(chalk.blue('đ Initializing git service...'));
|
|
89
|
+
}
|
|
90
|
+
spinner = ora.default({
|
|
91
|
+
color: 'blue',
|
|
92
|
+
spinner: 'dots',
|
|
93
|
+
text: 'Starting branch sync operation...'
|
|
94
|
+
}).start();
|
|
95
|
+
if (flags.verbose) {
|
|
96
|
+
spinner.text = 'Creating temporary workspace...';
|
|
97
|
+
}
|
|
98
|
+
// Execute sync operation
|
|
99
|
+
const results = await gitService.syncBranches(syncOptions, (emoji, color, step) => {
|
|
100
|
+
if (spinner) {
|
|
101
|
+
spinner.text = `${emoji} ${step}`;
|
|
102
|
+
}
|
|
103
|
+
if (flags.verbose) {
|
|
104
|
+
// Use chalk to apply the color to the step text
|
|
105
|
+
const coloredStep = color === 'blue' ? chalk.blue(step) :
|
|
106
|
+
color === 'cyan' ? chalk.cyan(step) :
|
|
107
|
+
color === 'yellow' ? chalk.yellow(step) :
|
|
108
|
+
color === 'magenta' ? chalk.magenta(step) :
|
|
109
|
+
color === 'green' ? chalk.green(step) :
|
|
110
|
+
color === 'red' ? chalk.red(step) :
|
|
111
|
+
color === 'gray' ? chalk.gray(step) :
|
|
112
|
+
step;
|
|
113
|
+
this.log(`${emoji} ${coloredStep}`);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
spinner.succeed(chalk.green('Sync operation completed!'));
|
|
117
|
+
// Display results
|
|
118
|
+
this.displayResults(results, this.formatDuration(startTime));
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (spinner) {
|
|
122
|
+
spinner.fail(chalk.red('Sync operation failed'));
|
|
123
|
+
}
|
|
124
|
+
this.error(chalk.red(`Failed to sync branches: ${error instanceof Error ? error.message : String(error)}`), {
|
|
125
|
+
suggestions: [
|
|
126
|
+
'Check that you are in a git repository',
|
|
127
|
+
'Verify remote repository access',
|
|
128
|
+
'Ensure git is installed and configured'
|
|
129
|
+
]
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
displayConfiguration(syncOptions) {
|
|
134
|
+
ux.styledHeader('đ§ Sync Configuration');
|
|
135
|
+
const configTable = {
|
|
136
|
+
'Merge Strategy': chalk.cyan(syncOptions.mergeStrategy),
|
|
137
|
+
'Mode': syncOptions.all
|
|
138
|
+
? chalk.yellow('Sync all remote branches')
|
|
139
|
+
: chalk.yellow(`Sync specific branches: ${syncOptions.targetBranches?.join(', ')}`),
|
|
140
|
+
'Remote': chalk.cyan(syncOptions.remote),
|
|
141
|
+
'Source Branch': chalk.cyan(syncOptions.sourceBranch)
|
|
142
|
+
};
|
|
143
|
+
Object.entries(configTable).forEach(([key, value]) => {
|
|
144
|
+
this.log(`${chalk.gray('âĸ')} ${chalk.bold(key)}: ${value}`);
|
|
145
|
+
});
|
|
146
|
+
this.log(); // Empty line for spacing
|
|
147
|
+
}
|
|
148
|
+
displayResults(results, duration) {
|
|
149
|
+
this.log(); // Empty line for spacing
|
|
150
|
+
ux.styledHeader('đ Sync Results');
|
|
151
|
+
// Count results by type
|
|
152
|
+
const successCount = results.filter(r => r.success).length;
|
|
153
|
+
const conflictCount = results.filter(r => r.conflicts).length;
|
|
154
|
+
const errorCount = results.filter(r => !r.success && !r.conflicts).length;
|
|
155
|
+
// Display summary statistics
|
|
156
|
+
const summaryData = [
|
|
157
|
+
{ metric: 'Total Branches', status: 'info', value: results.length.toString() },
|
|
158
|
+
{ metric: 'Successful', status: 'success', value: successCount.toString() },
|
|
159
|
+
{ metric: 'Conflicts', status: 'warning', value: conflictCount.toString() },
|
|
160
|
+
{ metric: 'Errors', status: 'error', value: errorCount.toString() },
|
|
161
|
+
{ metric: 'Duration', status: 'info', value: duration }
|
|
162
|
+
];
|
|
163
|
+
ux.table(summaryData, {
|
|
164
|
+
metric: { header: 'Metric' },
|
|
165
|
+
status: {
|
|
166
|
+
get: (row) => {
|
|
167
|
+
switch (row.status) {
|
|
168
|
+
case 'success': return chalk.green('â
');
|
|
169
|
+
case 'warning': return chalk.yellow('â ī¸');
|
|
170
|
+
case 'error': return chalk.red('â');
|
|
171
|
+
default: return chalk.blue('âšī¸');
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
header: 'Status'
|
|
175
|
+
},
|
|
176
|
+
value: { header: 'Count' }
|
|
177
|
+
}, { 'no-truncate': true });
|
|
178
|
+
if (results.length > 0) {
|
|
179
|
+
this.log(); // Empty line for spacing
|
|
180
|
+
ux.styledHeader('đ Branch Details');
|
|
181
|
+
// Convert results to proper table format
|
|
182
|
+
const tableData = results.map(result => ({
|
|
183
|
+
branch: result.branch,
|
|
184
|
+
conflicts: result.conflicts,
|
|
185
|
+
error: result.error,
|
|
186
|
+
success: result.success
|
|
187
|
+
}));
|
|
188
|
+
// Display detailed results
|
|
189
|
+
ux.table(tableData, {
|
|
190
|
+
branch: {
|
|
191
|
+
get: (row) => chalk.cyan(row.branch),
|
|
192
|
+
header: 'Branch'
|
|
193
|
+
},
|
|
194
|
+
message: {
|
|
195
|
+
get: (row) => {
|
|
196
|
+
if (row.success)
|
|
197
|
+
return chalk.gray('Successfully merged and pushed');
|
|
198
|
+
if (row.conflicts)
|
|
199
|
+
return chalk.yellow('Merge conflicts detected - manual resolution required');
|
|
200
|
+
return chalk.red(row.error || 'Unknown error');
|
|
201
|
+
},
|
|
202
|
+
header: 'Message'
|
|
203
|
+
},
|
|
204
|
+
status: {
|
|
205
|
+
get: (row) => {
|
|
206
|
+
if (row.success)
|
|
207
|
+
return chalk.green('â
Synced');
|
|
208
|
+
if (row.conflicts)
|
|
209
|
+
return chalk.yellow('â ī¸ Conflicts');
|
|
210
|
+
return chalk.red('â Failed');
|
|
211
|
+
},
|
|
212
|
+
header: 'Status'
|
|
213
|
+
}
|
|
214
|
+
}, { 'no-truncate': true });
|
|
215
|
+
}
|
|
216
|
+
// Display actionable insights
|
|
217
|
+
this.log(); // Empty line for spacing
|
|
218
|
+
if (conflictCount > 0) {
|
|
219
|
+
ux.warn(`${conflictCount} branch(es) have merge conflicts and need manual resolution`);
|
|
220
|
+
this.log(chalk.gray(' đĄ Tip: Resolve conflicts manually and push the changes'));
|
|
221
|
+
}
|
|
222
|
+
if (errorCount > 0) {
|
|
223
|
+
ux.warn(`${errorCount} branch(es) failed to sync`);
|
|
224
|
+
this.log(chalk.gray(' đĄ Tip: Check branch permissions and network connectivity'));
|
|
225
|
+
}
|
|
226
|
+
if (successCount === results.length && results.length > 0) {
|
|
227
|
+
ux.info(chalk.green('đ All branches synced successfully!'));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
formatDuration(startTime) {
|
|
231
|
+
const duration = Date.now() - startTime;
|
|
232
|
+
const seconds = Math.round(duration / 1000);
|
|
233
|
+
return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
234
|
+
}
|
|
235
|
+
}
|