gitshift 1.0.0 → 1.0.1
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/package.json +2 -2
- package/src/commands/add.js +66 -0
- package/src/commands/current.js +45 -0
- package/src/commands/doctor.js +44 -0
- package/src/commands/list.js +21 -0
- package/src/commands/remove.js +37 -0
- package/src/commands/use.js +57 -0
- package/src/server.js +69 -0
- package/src/services/git.js +20 -0
- package/src/services/profile.js +60 -0
- package/src/services/ssh.js +36 -0
- package/src/utils/logger.js +10 -0
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
confirm,
|
|
3
|
+
input,
|
|
4
|
+
} from "@inquirer/prompts";
|
|
5
|
+
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
saveProfile,
|
|
10
|
+
} from "../services/profile.js";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
generateSSHKey,
|
|
14
|
+
} from "../services/ssh.js";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
success,
|
|
18
|
+
} from "../utils/logger.js";
|
|
19
|
+
|
|
20
|
+
export async function addCommand() {
|
|
21
|
+
const name = await input({
|
|
22
|
+
message: "Profile Name",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const username = await input({
|
|
26
|
+
message: "GitHub Username",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const email = await input({
|
|
30
|
+
message: "Email",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const createSSH = await confirm({
|
|
34
|
+
message:
|
|
35
|
+
"Generate SSH key automatically?",
|
|
36
|
+
default: true,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let sshKey = null;
|
|
40
|
+
|
|
41
|
+
if (createSSH) {
|
|
42
|
+
const spinner = ora(
|
|
43
|
+
"Generating SSH key..."
|
|
44
|
+
).start();
|
|
45
|
+
|
|
46
|
+
sshKey = await generateSSHKey(
|
|
47
|
+
name,
|
|
48
|
+
email
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
spinner.succeed(
|
|
52
|
+
"SSH key generated"
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
saveProfile({
|
|
57
|
+
name,
|
|
58
|
+
username,
|
|
59
|
+
email,
|
|
60
|
+
sshKey,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
success(
|
|
64
|
+
`Profile "${name}" created`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCurrentProfile,
|
|
3
|
+
getProfile,
|
|
4
|
+
} from "../services/profile.js";
|
|
5
|
+
|
|
6
|
+
import { info } from "../utils/logger.js";
|
|
7
|
+
|
|
8
|
+
export async function currentCommand() {
|
|
9
|
+
const current =
|
|
10
|
+
getCurrentProfile();
|
|
11
|
+
|
|
12
|
+
if (!current) {
|
|
13
|
+
info("No active profile");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const profile =
|
|
18
|
+
getProfile(current);
|
|
19
|
+
|
|
20
|
+
if (!profile) {
|
|
21
|
+
info("Profile not found");
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log();
|
|
26
|
+
|
|
27
|
+
console.log(
|
|
28
|
+
`Profile : ${profile.name}`
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
console.log(
|
|
32
|
+
`Username: ${profile.username}`
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
console.log(
|
|
36
|
+
`Email : ${profile.email}`
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
console.log(
|
|
40
|
+
`SSH Key : ${profile.sshKey || "Not configured"
|
|
41
|
+
}`
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
console.log();
|
|
45
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
error,
|
|
5
|
+
success,
|
|
6
|
+
} from "../utils/logger.js";
|
|
7
|
+
|
|
8
|
+
async function check(
|
|
9
|
+
name,
|
|
10
|
+
command,
|
|
11
|
+
args = []
|
|
12
|
+
) {
|
|
13
|
+
try {
|
|
14
|
+
await execa(command, args);
|
|
15
|
+
|
|
16
|
+
success(`${name} installed`);
|
|
17
|
+
|
|
18
|
+
return true;
|
|
19
|
+
} catch {
|
|
20
|
+
error(`${name} missing`);
|
|
21
|
+
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function doctorCommand() {
|
|
27
|
+
console.log();
|
|
28
|
+
|
|
29
|
+
await check("Git", "git", [
|
|
30
|
+
"--version",
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
await check("SSH", "ssh", [
|
|
34
|
+
"-V",
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
await check(
|
|
38
|
+
"GitHub CLI",
|
|
39
|
+
"gh",
|
|
40
|
+
["--version"]
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
console.log();
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getProfiles } from "../services/profile.js";
|
|
2
|
+
import { info } from "../utils/logger.js";
|
|
3
|
+
|
|
4
|
+
export async function listCommand() {
|
|
5
|
+
const profiles = getProfiles();
|
|
6
|
+
|
|
7
|
+
if (!profiles.length) {
|
|
8
|
+
info("No profiles found");
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
console.log();
|
|
13
|
+
|
|
14
|
+
profiles.forEach((profile) => {
|
|
15
|
+
console.log(
|
|
16
|
+
`• ${profile.name} (${profile.username})`
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
console.log();
|
|
21
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCurrentProfile,
|
|
3
|
+
getProfile,
|
|
4
|
+
removeProfile,
|
|
5
|
+
setCurrentProfile,
|
|
6
|
+
} from "../services/profile.js";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
error,
|
|
10
|
+
success,
|
|
11
|
+
} from "../utils/logger.js";
|
|
12
|
+
|
|
13
|
+
export async function removeCommand(
|
|
14
|
+
profileName
|
|
15
|
+
) {
|
|
16
|
+
const profile =
|
|
17
|
+
getProfile(profileName);
|
|
18
|
+
|
|
19
|
+
if (!profile) {
|
|
20
|
+
error("Profile not found");
|
|
21
|
+
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
removeProfile(profileName);
|
|
26
|
+
|
|
27
|
+
if (
|
|
28
|
+
getCurrentProfile() ===
|
|
29
|
+
profileName
|
|
30
|
+
) {
|
|
31
|
+
setCurrentProfile(null);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
success(
|
|
35
|
+
`Profile "${profileName}" removed`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getProfile,
|
|
5
|
+
setCurrentProfile,
|
|
6
|
+
} from "../services/profile.js";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
setGitUser,
|
|
10
|
+
} from "../services/git.js";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
error,
|
|
14
|
+
success,
|
|
15
|
+
} from "../utils/logger.js";
|
|
16
|
+
|
|
17
|
+
export async function useCommand(
|
|
18
|
+
profileName
|
|
19
|
+
) {
|
|
20
|
+
const profile =
|
|
21
|
+
getProfile(profileName);
|
|
22
|
+
|
|
23
|
+
if (!profile) {
|
|
24
|
+
error(
|
|
25
|
+
`Profile "${profileName}" not found`
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const spinner = ora(
|
|
32
|
+
"Switching profile..."
|
|
33
|
+
).start();
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
await setGitUser(
|
|
37
|
+
profile.username,
|
|
38
|
+
profile.email
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
setCurrentProfile(profile.name);
|
|
42
|
+
|
|
43
|
+
spinner.succeed(
|
|
44
|
+
"Profile switched"
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
success(
|
|
48
|
+
`Current profile: ${profile.name}`
|
|
49
|
+
);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
spinner.fail(
|
|
52
|
+
"Unable to switch profile"
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
error(err.message);
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/server.js
CHANGED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
|
|
5
|
+
import { addCommand } from "./commands/add.js";
|
|
6
|
+
|
|
7
|
+
import { listCommand } from "./commands/list.js";
|
|
8
|
+
|
|
9
|
+
import { currentCommand } from "./commands/current.js";
|
|
10
|
+
|
|
11
|
+
import { useCommand } from "./commands/use.js";
|
|
12
|
+
|
|
13
|
+
import { removeCommand } from "./commands/remove.js";
|
|
14
|
+
|
|
15
|
+
import { doctorCommand } from "./commands/doctor.js";
|
|
16
|
+
|
|
17
|
+
const program =
|
|
18
|
+
new Command();
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.name("gitshift")
|
|
22
|
+
.description(
|
|
23
|
+
"GitHub Account Switcher"
|
|
24
|
+
)
|
|
25
|
+
.version("1.0.0");
|
|
26
|
+
|
|
27
|
+
program
|
|
28
|
+
.command("add")
|
|
29
|
+
.description(
|
|
30
|
+
"Create profile"
|
|
31
|
+
)
|
|
32
|
+
.action(addCommand);
|
|
33
|
+
|
|
34
|
+
program
|
|
35
|
+
.command("list")
|
|
36
|
+
.description(
|
|
37
|
+
"List profiles"
|
|
38
|
+
)
|
|
39
|
+
.action(listCommand);
|
|
40
|
+
|
|
41
|
+
program
|
|
42
|
+
.command("current")
|
|
43
|
+
.description(
|
|
44
|
+
"Show active profile"
|
|
45
|
+
)
|
|
46
|
+
.action(currentCommand);
|
|
47
|
+
|
|
48
|
+
program
|
|
49
|
+
.command("use <profile>")
|
|
50
|
+
.description(
|
|
51
|
+
"Switch profile"
|
|
52
|
+
)
|
|
53
|
+
.action(useCommand);
|
|
54
|
+
|
|
55
|
+
program
|
|
56
|
+
.command("remove <profile>")
|
|
57
|
+
.description(
|
|
58
|
+
"Delete profile"
|
|
59
|
+
)
|
|
60
|
+
.action(removeCommand);
|
|
61
|
+
|
|
62
|
+
program
|
|
63
|
+
.command("doctor")
|
|
64
|
+
.description(
|
|
65
|
+
"System health check"
|
|
66
|
+
)
|
|
67
|
+
.action(doctorCommand);
|
|
68
|
+
|
|
69
|
+
program.parse();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
|
|
3
|
+
export async function setGitUser(
|
|
4
|
+
name,
|
|
5
|
+
email
|
|
6
|
+
) {
|
|
7
|
+
await execa("git", [
|
|
8
|
+
"config",
|
|
9
|
+
"--global",
|
|
10
|
+
"user.name",
|
|
11
|
+
name,
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
await execa("git", [
|
|
15
|
+
"config",
|
|
16
|
+
"--global",
|
|
17
|
+
"user.email",
|
|
18
|
+
email,
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import Conf from "conf";
|
|
2
|
+
|
|
3
|
+
const config = new Conf({
|
|
4
|
+
projectName: "ghswitch",
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
export function getProfiles() {
|
|
8
|
+
return config.get("profiles", []);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function saveProfile(profile) {
|
|
12
|
+
const profiles = getProfiles();
|
|
13
|
+
|
|
14
|
+
const exists = profiles.find(
|
|
15
|
+
(item) => item.name === profile.name
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
if (exists) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Profile "${profile.name}" already exists`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
profiles.push(profile);
|
|
25
|
+
|
|
26
|
+
config.set("profiles", profiles);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getProfile(name) {
|
|
30
|
+
const profiles = getProfiles();
|
|
31
|
+
|
|
32
|
+
return profiles.find(
|
|
33
|
+
(profile) => profile.name === name
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function removeProfile(name) {
|
|
38
|
+
const profiles = getProfiles();
|
|
39
|
+
|
|
40
|
+
const filtered = profiles.filter(
|
|
41
|
+
(profile) => profile.name !== name
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
config.set("profiles", filtered);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function setCurrentProfile(
|
|
48
|
+
name
|
|
49
|
+
) {
|
|
50
|
+
if (!name) {
|
|
51
|
+
config.delete("current");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
config.set("current", name);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getCurrentProfile() {
|
|
59
|
+
return config.get("current", null);
|
|
60
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
export async function generateSSHKey(
|
|
7
|
+
profileName,
|
|
8
|
+
email
|
|
9
|
+
) {
|
|
10
|
+
const keyPath = path.join(
|
|
11
|
+
os.homedir(),
|
|
12
|
+
".ssh",
|
|
13
|
+
`ghswitch-${profileName}`
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const exists = await fs.pathExists(
|
|
17
|
+
keyPath
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (exists) {
|
|
21
|
+
return keyPath;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await execa("ssh-keygen", [
|
|
25
|
+
"-t",
|
|
26
|
+
"ed25519",
|
|
27
|
+
"-C",
|
|
28
|
+
email,
|
|
29
|
+
"-f",
|
|
30
|
+
keyPath,
|
|
31
|
+
"-N",
|
|
32
|
+
"",
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
return keyPath;
|
|
36
|
+
}
|