@savaryna/git-add-account 2.1.0 → 2.2.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 +25 -62
- package/bin/main.js +29 -21
- package/package.json +18 -21
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @savaryna/add-git-account
|
|
2
2
|
|
|
3
|
-
🔐 A small CLI app that allows you to easily add multiple
|
|
3
|
+
🔐 A small CLI app that allows you to easily add multiple Git accounts on one machine. It switches between accounts automatically based on the workspace you are in.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## CLI usage
|
|
6
6
|
|
|
7
7
|
Run the command direcly with
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@ Run the command direcly with
|
|
|
10
10
|
npx @savaryna/git-add-account
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
or if you
|
|
13
|
+
or if you installed it globally with
|
|
14
14
|
|
|
15
15
|
```shell
|
|
16
16
|
npm i -g @savaryna/git-add-account
|
|
@@ -20,69 +20,32 @@ then you can run it using
|
|
|
20
20
|
|
|
21
21
|
```shell
|
|
22
22
|
git-add-account
|
|
23
|
+
# or the shorter version
|
|
24
|
+
gaa
|
|
23
25
|
```
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
For usage and command details run it with the `--help` option.
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
After going through all the steps, you will be presented with your public SSH key so you can copy, and add it to your Git provider. For example GitHub[^1]:
|
|
30
|
+
|
|
31
|
+
1. Copy the public SSH key.
|
|
32
|
+
1. Go to the [SSH keys settings page](https://github.com/settings/keys).
|
|
33
|
+
1. Click on `New SSH key`.
|
|
34
|
+
1. Add the key as an `Authentication Key`.
|
|
35
|
+
1. Click on `Add SSH key`.
|
|
36
|
+
1. Add the same key again as a `Signing Key` if you chose to sign your work[^2].
|
|
37
|
+
|
|
38
|
+
Done! Any `git` command you run from the workspace you chose **(and its subdirectories)**, will now use this new account automatically.
|
|
39
|
+
|
|
40
|
+
## How it works
|
|
41
|
+
|
|
42
|
+
A simple way to use multiple Git accounts on one machine is to use different SSH configs based on the directory you are in. The way [@savaryna/add-git-account](https://www.npmjs.com/package/@savaryna/git-add-account) works is, it asks you for some basic information and then it creates files under `.config/` in the workspace directory you specified.
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
1.
|
|
34
|
-
1.
|
|
35
|
-
1.
|
|
36
|
-
1. Choose `Authentication Key` for key type
|
|
37
|
-
1. Paste in the public SSH key copied earlier in the key field
|
|
38
|
-
1. Click on `Add SSH key`
|
|
39
|
-
1. Repeat steps **2 through 6** to add a `Signing Key` key type, if you chose to sign your work (Commits, Tags, Pushes)[^2]
|
|
40
|
-
1. Done! Now, you can go to the workspace you chose for the account, ex: `cd /Users/john/code/work`, and all the `git`
|
|
41
|
-
commands issued from this, **or any other subdirectory**, will automatically use the correct account/ssh keys.
|
|
42
|
-
|
|
43
|
-
## Example of how it works
|
|
44
|
-
|
|
45
|
-
A simple way to use multiple git accounts on one machine is to use different SSH configs based on the directory you are in. The way [@savaryna/add-git-account](https://www.npmjs.com/package/@savaryna/git-add-account) works is, it asks you for some basic information and then it creates some files under `.config` in the workspace directory you specified. Ex:
|
|
46
|
-
|
|
47
|
-
1. It creates a _(private/public)_ SSH keypair using `ssh-keygen -t ed25519 -C "john@github.com" -f /Users/john/code/work/.config/id_ed25519_git_github_com`. [See code](https://github.com/savaryna/git-add-account/blob/main/src/index.ts#L29-L30).
|
|
48
|
-
1. It creates a `sshconfig` file. [See code](https://github.com/savaryna/git-add-account/blob/main/src/index.ts#L40-L48).
|
|
49
|
-
|
|
50
|
-
```ini
|
|
51
|
-
# File at /Users/john/code/work/.config/sshconfig
|
|
52
|
-
# Config for GIT account john@github.com
|
|
53
|
-
Host github.com
|
|
54
|
-
HostName github.com
|
|
55
|
-
User git
|
|
56
|
-
AddKeysToAgent yes
|
|
57
|
-
UseKeychain yes
|
|
58
|
-
IdentitiesOnly yes
|
|
59
|
-
IdentityFile /Users/john/code/work/.config/id_ed25519_git_github_com
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
1. It creates a `gitconfig` file. [See code](https://github.com/savaryna/git-add-account/blob/main/src/index.ts#L50-L58).
|
|
63
|
-
|
|
64
|
-
```ini
|
|
65
|
-
# File at /Users/john/code/work/.config/gitconfig
|
|
66
|
-
# Config for GIT account john@github.com
|
|
67
|
-
[user]
|
|
68
|
-
name = John Doe
|
|
69
|
-
email = john@github.com
|
|
70
|
-
[core]
|
|
71
|
-
sshCommand = ssh -F /Users/john/code/work/.config/sshconfig
|
|
72
|
-
[gpg]
|
|
73
|
-
format = ssh
|
|
74
|
-
[commit]
|
|
75
|
-
gpgsign = true
|
|
76
|
-
[push]
|
|
77
|
-
gpgsign = if-asked
|
|
78
|
-
[tag]
|
|
79
|
-
gpgsign = true
|
|
80
|
-
[user]
|
|
81
|
-
signingkey = /Users/john/code/work/.config/id_ed25519_git_github_com
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
1. It runs `git config --global includeIf.gitdir:/Users/john/code/work/.path /Users/john/code/work/.config/gitconfig`, this makes sure that as long as you are in the workspace created earlier, **or any other subdirectory**, git will use the config from step **3** automatically[^3]. [See code](https://github.com/savaryna/git-add-account/blob/main/src/index.ts#L60-L61).
|
|
85
|
-
1. And finally, it presents you with your public SSH key so you can copy it and add it to your GIT provider of choice.
|
|
44
|
+
1. It creates a private/public `ed25519` SSH keypair using `ssh-keygen` ([see code](https://github.com/savaryna/git-add-account/blob/main/src/helpers/config.ts#L28-L29)).
|
|
45
|
+
1. It creates a `sshconfig` file based on this [template](https://github.com/savaryna/git-add-account/blob/main/src/templates/sshconfig.mustache).
|
|
46
|
+
1. It creates a `gitconfig` file based on this [template](https://github.com/savaryna/git-add-account/blob/main/src/templates/gitconfig.mustache).
|
|
47
|
+
1. It appends a conditional include to your global Git config based on this [template](https://github.com/savaryna/git-add-account/blob/main/src/templates/gitconfig.global.mustache). This makes sure that any `git` command you run from the workspace you chose **(and its subdirectories)**, will now use this new account automatically[^3].
|
|
48
|
+
1. Finally, it presents you with your public SSH key so you can copy, and add it to your Git provider.
|
|
86
49
|
|
|
87
50
|
## License
|
|
88
51
|
|
package/bin/main.js
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
AddKeysToAgent yes{{#useKeychain}}
|
|
7
|
-
UseKeychain yes{{/useKeychain}}
|
|
8
|
-
IdentitiesOnly yes
|
|
9
|
-
IdentityFile {{{workspace.privateKey}}}
|
|
10
|
-
`;var w=`# Config for GIT account {{{email}}}
|
|
2
|
+
"use strict";var T=Object.create;var u=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var I=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var j=(e,i,n,s)=>{if(i&&typeof i=="object"||typeof i=="function")for(let t of O(i))!Y.call(e,t)&&t!==n&&u(e,t,{get:()=>i[t],enumerable:!(s=N(i,t))||s.enumerable});return e};var d=(e,i,n)=>(n=e!=null?T(I(e)):{},j(i||!e||!e.__esModule?u(n,"default",{value:e,enumerable:!0}):n,e));var $=d(require("yargs")),S=require("yargs/helpers");var x=require("os"),A=require("path"),D=d(require("prompts"));var b=require("child_process"),w=require("fs"),a=require("fs/promises"),l=require("os"),r=require("path"),k=require("util"),m=d(require("mustache"));var h=`
|
|
3
|
+
[includeIf "gitdir:{{{paths.workspace}}}/"]
|
|
4
|
+
path = {{{paths.gitConfig}}}
|
|
5
|
+
`;var y=`# Config for GIT account {{{configDetails.email}}}
|
|
11
6
|
[user]
|
|
12
|
-
name = {{{name
|
|
13
|
-
email = {{{email}}}
|
|
7
|
+
name = {{{configDetails.name}}}
|
|
8
|
+
email = {{{configDetails.email}}}
|
|
14
9
|
[core]
|
|
15
|
-
sshCommand = ssh -F {{{
|
|
16
|
-
{{#signYourWork}}
|
|
10
|
+
sshCommand = ssh -F {{{paths.sshConfig}}}
|
|
11
|
+
{{#configDetails.signYourWork}}
|
|
17
12
|
[gpg]
|
|
18
13
|
format = ssh
|
|
19
14
|
[commit]
|
|
@@ -23,12 +18,25 @@ Host {{{host.value}}}
|
|
|
23
18
|
[tag]
|
|
24
19
|
gpgsign = true
|
|
25
20
|
[user]
|
|
26
|
-
signingkey = {{{
|
|
27
|
-
{{/signYourWork}}
|
|
28
|
-
`;var
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
signingkey = {{{paths.privateKey}}}
|
|
22
|
+
{{/configDetails.signYourWork}}
|
|
23
|
+
`;var C=`# Config for GIT account {{{configDetails.email}}}
|
|
24
|
+
Host {{{configDetails.host}}}
|
|
25
|
+
IgnoreUnknown AddKeysToAgent
|
|
26
|
+
HostName {{{configDetails.host}}}
|
|
27
|
+
User git
|
|
28
|
+
AddKeysToAgent yes
|
|
29
|
+
IdentitiesOnly yes
|
|
30
|
+
IdentityFile {{{paths.privateKey}}}
|
|
31
|
+
`;var c=require("zod"),E=(e,i)=>{let{success:n,error:s}=e.safeParse(i);return!n&&s.errors.map(({message:t})=>t).join(`
|
|
32
|
+
`)},g=E;var L=(0,k.promisify)(b.exec);async function R(e){let i=`id_ed25519_git_${e.host}`,n=i,s=`${i}.pub`,t=(0,r.resolve)((0,l.tmpdir)(),n),o=(0,r.resolve)((0,l.tmpdir)(),s);await(0,a.rm)(t,{force:!0}),await(0,a.rm)(o,{force:!0}),await L(`ssh-keygen -t ed25519 -C "${e.email}" -f ${t} -N ""`);let f=await(0,a.readFile)(t,"utf8"),F=await(0,a.readFile)(o,"utf8");return await(0,a.rm)(t,{force:!0}),await(0,a.rm)(o,{force:!0}),{privateKeyName:n,publicKeyName:s,privateKey:f,publicKey:F}}var p=c.z.object({name:c.z.string().min(1),email:c.z.string().email(),host:c.z.string().refine(e=>e.includes(".")&&URL.canParse(`https://${e}`),{message:"Invalid host, must be a valid domain (e.g., github.com)"}),workspace:c.z.string().min(1).refine(e=>!(0,w.existsSync)(e),"Path already exists"),signYourWork:c.z.boolean()});async function v(e){let i=g(p,e);if(i)throw new Error(i);let n=await R(e),s=(0,r.resolve)(e.workspace),t=(0,r.resolve)(s,".config"),o={workspace:s,config:t,gitConfigGlobal:(0,r.resolve)((0,l.homedir)(),".gitconfig"),gitConfig:(0,r.resolve)(t,"gitconfig"),sshConfig:(0,r.resolve)(t,"sshconfig"),privateKey:(0,r.resolve)(t,n.privateKeyName),publicKey:(0,r.resolve)(t,n.publicKeyName)};return{gitConfigGlobal:{path:o.gitConfigGlobal,content:m.default.render(h,{configDetails:e,paths:o})},gitConfig:{path:o.gitConfig,content:m.default.render(y,{configDetails:e,paths:o})},sshConfig:{path:o.sshConfig,content:m.default.render(C,{configDetails:e,paths:o})},privateKey:{path:o.privateKey,content:n.privateKey,secret:!0},publicKey:{path:o.publicKey,content:n.publicKey}}}async function K(e,i){let n=(0,r.resolve)(e.workspace,".config");await(0,a.mkdir)(n,{recursive:!0});let s=Object.values(i).map(({path:t,content:o,secret:f})=>(0,a.appendFile)(t,o,{encoding:"utf8",mode:f?384:420}));await Promise.all(s)}var G={command:["$0","add"],describe:"Add a new Git account",builder:e=>e.option("dry-run",{type:"boolean",describe:"Preview the generated config files without writing them to disk"}),handler:async e=>{console.log("Let's add a new Git account.");let s=await(0,D.default)([{type:"text",name:"name",message:"Full name for Git commits (e.g., John Doe):",validate:o=>g(p.shape.name,o)||!0},{type:"text",name:"email",message:"Email for this Git account:",validate:o=>g(p.shape.email,o)||!0},{type:"text",name:"host",message:"Git provider host (e.g., github.com, gitlab.com):",initial:o=>o.split("@")[1],validate:o=>g(p.shape.host,o)||!0},{type:"text",name:"workspace",message:"Absolute path to the workspace for this account:",initial:o=>(0,A.resolve)((0,x.homedir)(),"code",o),validate:o=>g(p.shape.workspace,o)||!0},{type:"toggle",name:"signYourWork",message:"Sign commits and tags with your SSH key?",initial:!0,active:"yes",inactive:"no"}],{onCancel:()=>{throw new Error("Operation cancelled by the user.")}}),t=await v(s);if(e!=null&&e.dryRun){console.log(`
|
|
33
|
+
Config files that would be generated/updated:`);for(let o of Object.values(t))console.log(`
|
|
34
|
+
Path:`,o.path),console.log(o.secret?"[Content hidden for security]":o.content)}else await K(s,t),console.log(`
|
|
32
35
|
Your public SSH key is:
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
${t.publicKey.content}`),console.log(`You can also find it here:
|
|
37
|
+
${t.publicKey.path}`),console.log(`
|
|
38
|
+
Next, add the key to ${s.host}:`),console.log("1. Copy the public SSH key above."),console.log(`2. Go to the SSH keys settings page on ${s.host}.`),console.log("3. Add the key as an 'Authentication Key'."),s.signYourWork&&console.log("4. Add the same key again as a 'Signing Key'."),console.log(`
|
|
39
|
+
Done! Any git command you run from '${s.workspace}',`),console.log("(and its subdirectories), will now use this new account.")}};var P=[G];(0,$.default)((0,S.hideBin)(process.argv)).scriptName("git-add-account").usage("$0 [cmd] <options>").command(P).demandCommand(0,1).help("h").alias("h","help").strict().fail((e,i,n)=>{e&&console.log(n.help()),console.log(`
|
|
40
|
+
${e||(i==null?void 0:i.message)}
|
|
41
|
+
`),console.log(`\u{1F635} Exited! Thanks for using @savaryna/git-add-account!
|
|
42
|
+
`),process.exit(1)}).parse();
|
package/package.json
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savaryna/git-add-account",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "🔐 A small CLI app that allows you to easily add multiple
|
|
3
|
+
"version": "2.2.2",
|
|
4
|
+
"description": "🔐 A small CLI app that allows you to easily add multiple Git accounts on one machine. It switches between accounts automatically based on the workspace you are in.",
|
|
5
5
|
"homepage": "https://github.com/savaryna/git-add-account#readme",
|
|
6
6
|
"keywords": [
|
|
7
|
-
"add",
|
|
8
|
-
"multiple",
|
|
9
7
|
"git",
|
|
10
|
-
"
|
|
11
|
-
"account",
|
|
12
|
-
"
|
|
13
|
-
"users",
|
|
14
|
-
"on",
|
|
15
|
-
"one",
|
|
16
|
-
"machine",
|
|
17
|
-
"mac",
|
|
18
|
-
"linux",
|
|
19
|
-
"windows",
|
|
8
|
+
"multiple-accounts",
|
|
9
|
+
"account-switching",
|
|
10
|
+
"cli",
|
|
20
11
|
"ssh",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
12
|
+
"git-config",
|
|
13
|
+
"workspace",
|
|
14
|
+
"developer-tools",
|
|
15
|
+
"automation",
|
|
16
|
+
"productivity",
|
|
17
|
+
"github",
|
|
18
|
+
"gitlab"
|
|
25
19
|
],
|
|
26
20
|
"author": "Alex Tofan",
|
|
27
21
|
"license": "MIT",
|
|
@@ -43,21 +37,24 @@
|
|
|
43
37
|
"scripts": {
|
|
44
38
|
"link": "npm unlink -g && npm link",
|
|
45
39
|
"dev": "tsup --watch",
|
|
40
|
+
"check": "biome check --write",
|
|
46
41
|
"build": "tsup",
|
|
47
42
|
"pretest": "npm run build",
|
|
48
43
|
"test": "npm publish --dry-run"
|
|
49
44
|
},
|
|
50
|
-
"source": "src/index.ts",
|
|
51
45
|
"main": "bin/main.js",
|
|
52
46
|
"dependencies": {
|
|
53
47
|
"mustache": "^4.2.0",
|
|
54
48
|
"prompts": "^2.4.2",
|
|
55
|
-
"
|
|
49
|
+
"yargs": "^18.0.0",
|
|
50
|
+
"zod": "^3.25.76"
|
|
56
51
|
},
|
|
57
52
|
"devDependencies": {
|
|
53
|
+
"@biomejs/biome": "2.1.2",
|
|
58
54
|
"@types/mustache": "^4.2.5",
|
|
59
|
-
"@types/node": "^22.5
|
|
55
|
+
"@types/node": "^22.16.5",
|
|
60
56
|
"@types/prompts": "^2.4.2",
|
|
57
|
+
"@types/yargs": "^17.0.33",
|
|
61
58
|
"tsup": "^8.2.4",
|
|
62
59
|
"typescript": "^5.5.4"
|
|
63
60
|
}
|