gitshift 2.0.0 → 2.1.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitshift",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "GitHub Account Switcher CLI",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -0,0 +1,51 @@
1
+
2
+ import {
3
+ getFolderMappings,
4
+ getProfile,
5
+ setCurrentProfile,
6
+ } from "../services/profile.js";
7
+
8
+ import {
9
+ setGitUser,
10
+ } from "../services/git.js";
11
+
12
+ import {
13
+ success,
14
+ } from "../utils/logger.js";
15
+
16
+ export async function autoCommand() {
17
+ const cwd = process.cwd();
18
+
19
+ const mappings = getFolderMappings();
20
+
21
+ const matched =
22
+ mappings
23
+ .sort(
24
+ (a, b) =>
25
+ b.path.length -
26
+ a.path.length
27
+ )
28
+ .find((mapping) =>
29
+ cwd.startsWith(
30
+ mapping.path
31
+ )
32
+ );
33
+
34
+ if (!matched) {
35
+ console.log("\nNo matching profile\n");
36
+ return;
37
+ }
38
+
39
+ const profile =
40
+ getProfile(matched.profile);
41
+
42
+ if (!profile) {
43
+ return;
44
+ }
45
+
46
+ await setGitUser(profile.username, profile.email);
47
+
48
+ setCurrentProfile(profile.name);
49
+
50
+ success(`Switched to ${profile.name}`);
51
+ }
@@ -0,0 +1,146 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+
4
+ import {
5
+ confirm,
6
+ input,
7
+ select,
8
+ } from "@inquirer/prompts";
9
+
10
+ import {
11
+ addFolderMapping,
12
+ getProfiles,
13
+ saveProfile,
14
+ } from "../services/profile.js";
15
+
16
+ import {
17
+ generateSSHKey,
18
+ } from "../services/ssh.js";
19
+
20
+ import ora from "ora";
21
+ import {
22
+ error,
23
+ success,
24
+ } from "../utils/logger.js";
25
+
26
+ export async function linkCommand(
27
+ folder
28
+ ) {
29
+ const fullPath = path.resolve(folder);
30
+
31
+ const exists = await fs.pathExists(fullPath);
32
+
33
+ if (!exists) {
34
+ error("Folder does not exist");
35
+ return;
36
+ }
37
+
38
+ let profiles = getProfiles();
39
+
40
+ let selectedProfile;
41
+
42
+ if (profiles.length === 0) {
43
+ console.log("\nNo profiles found.\n");
44
+
45
+ selectedProfile = await createProfile();
46
+ } else {
47
+ const choice =
48
+ await select({
49
+ message: "Select Profile",
50
+ choices: [
51
+ ...profiles.map(
52
+ (
53
+ profile
54
+ ) => ({
55
+ name: `${profile.name} (${profile.username})`,
56
+ value:
57
+ profile.name,
58
+ })
59
+ ),
60
+ {
61
+ name: "+ Create New Profile",
62
+ value: "__create__",
63
+ },
64
+ ],
65
+ });
66
+
67
+ if (choice === "__create__") {
68
+ selectedProfile = await createProfile();
69
+ } else {
70
+ selectedProfile = choice;
71
+ }
72
+ }
73
+
74
+ try {
75
+ addFolderMapping(selectedProfile, fullPath);
76
+
77
+ success(`Linked ${fullPath}`);
78
+
79
+ success(`Profile: ${selectedProfile}`);
80
+ } catch (err) {
81
+ error(err.message);
82
+ }
83
+ }
84
+
85
+ async function createProfile() {
86
+ const name =
87
+ await input({
88
+ message: "Profile Name",
89
+ });
90
+
91
+ const username =
92
+ await input({
93
+ message: "GitHub Username",
94
+ });
95
+
96
+ const email =
97
+ await input({
98
+ message: "Email",
99
+ });
100
+
101
+ const shouldCreateSSH =
102
+ await confirm({
103
+ message: "Generate SSH Key?",
104
+ default: true,
105
+ });
106
+
107
+ let sshKey = null;
108
+
109
+ if (shouldCreateSSH) {
110
+ const spinner = ora("Generating SSH key...").start();
111
+
112
+ try {
113
+ sshKey = await generateSSHKey(name, email);
114
+
115
+ spinner.succeed("SSH key generated");
116
+ } catch (err) {
117
+ spinner.fail("Unable to generate SSH key");
118
+
119
+ error("Could not create SSH key. Ensure OpenSSH is installed and available.");
120
+
121
+ if (
122
+ err &&
123
+ typeof err === "object" &&
124
+ "shortMessage" in err &&
125
+ err.shortMessage
126
+ ) {
127
+ error(String(err.shortMessage));
128
+ }
129
+
130
+ process.exitCode = 1;
131
+ return;
132
+ }
133
+ }
134
+
135
+ saveProfile({
136
+ name,
137
+ username,
138
+ email,
139
+ sshKey,
140
+ source: "manual",
141
+ });
142
+
143
+ success(`Profile "${name}" created`);
144
+
145
+ return name;
146
+ }
@@ -0,0 +1,20 @@
1
+ import {
2
+ getFolderMappings,
3
+ } from "../services/profile.js";
4
+
5
+ export async function linksCommand() {
6
+ const mappings = getFolderMappings();
7
+
8
+ if (!mappings.length) {
9
+ console.log("\nNo folder mappings\n");
10
+ return;
11
+ }
12
+
13
+ console.log();
14
+
15
+ mappings.forEach((mapping) => {
16
+ console.log(`${mapping.profile} → ${mapping.path}`);
17
+ });
18
+
19
+ console.log();
20
+ }
@@ -0,0 +1,17 @@
1
+ import path from "path";
2
+
3
+ import {
4
+ removeFolderMapping,
5
+ } from "../services/profile.js";
6
+
7
+ import { success } from "../utils/logger.js";
8
+
9
+ export async function unlinkCommand(
10
+ folder
11
+ ) {
12
+ const fullPath = path.resolve(folder);
13
+
14
+ removeFolderMapping(fullPath);
15
+
16
+ success(`Removed ${fullPath}`);
17
+ }
package/src/server.js CHANGED
@@ -5,11 +5,15 @@ import chalk from "chalk";
5
5
  import { Command } from "commander";
6
6
  import { createRequire } from "node:module";
7
7
  import { addCommand } from "./commands/add.js";
8
+ import { autoCommand } from "./commands/auto.js";
8
9
  import { currentCommand } from "./commands/current.js";
9
10
  import { doctorCommand } from "./commands/doctor.js";
11
+ import { linkCommand } from "./commands/link.js";
12
+ import { linksCommand } from "./commands/links.js";
10
13
  import { listCommand } from "./commands/list.js";
11
14
  import { removeCommand } from "./commands/remove.js";
12
15
  import { scanCommand } from "./commands/scan.js";
16
+ import { unlinkCommand } from "./commands/unlink.js";
13
17
  import { useCommand } from "./commands/use.js";
14
18
 
15
19
  const require = createRequire(import.meta.url);
@@ -113,7 +117,7 @@ async function main() {
113
117
  program
114
118
  .command("remove <profile>")
115
119
  .description(
116
- "Delete profile"
120
+ "Remove profile"
117
121
  )
118
122
  .action(removeCommand);
119
123
 
@@ -124,6 +128,36 @@ async function main() {
124
128
  )
125
129
  .action(scanCommand);
126
130
 
131
+ program
132
+ .command(
133
+ "link <folder>"
134
+ )
135
+ .description(
136
+ "Link folder to profile"
137
+ )
138
+ .action(linkCommand);
139
+
140
+ program
141
+ .command("unlink <folder>")
142
+ .description(
143
+ "Remove folder mapping"
144
+ )
145
+ .action(unlinkCommand);
146
+
147
+ program
148
+ .command("links")
149
+ .description(
150
+ "List folder mappings"
151
+ )
152
+ .action(linksCommand);
153
+
154
+ program
155
+ .command("auto")
156
+ .description(
157
+ "Auto switch profile"
158
+ )
159
+ .action(autoCommand);
160
+
127
161
  program
128
162
  .command("doctor")
129
163
  .description(
@@ -57,4 +57,39 @@ export function setCurrentProfile(
57
57
 
58
58
  export function getCurrentProfile() {
59
59
  return config.get("current", null);
60
+ }
61
+
62
+ export function getFolderMappings() {
63
+ return config.get("folderMappings", []);
64
+ }
65
+
66
+ export function addFolderMapping(
67
+ profile,
68
+ folderPath
69
+ ) {
70
+ const mappings = getFolderMappings();
71
+
72
+ const exists = mappings.find((item) => item.path === folderPath);
73
+
74
+ if (exists) {
75
+ throw new Error("Folder already mapped");
76
+ }
77
+
78
+ mappings.push({ profile, path: folderPath, });
79
+
80
+ config.set("folderMappings", mappings);
81
+ }
82
+
83
+ export function removeFolderMapping(
84
+ folderPath
85
+ ) {
86
+ const mappings = getFolderMappings();
87
+
88
+ config.set(
89
+ "folderMappings",
90
+ mappings.filter(
91
+ (item) =>
92
+ item.path !== folderPath
93
+ )
94
+ );
60
95
  }