Skip to content

Commit c08a24e

Browse files
committed
refactor: improve path handling for SSH keys and refine interactive shell exit behavior
1 parent e9d2c91 commit c08a24e

15 files changed

Lines changed: 178 additions & 44 deletions

File tree

LICENSE

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
MIT License
2-
3-
Copyright (c) 2026 wuyue
4-
5-
Permission is hereby granted, free of charge, to any person obtaining a copy
6-
of this software and associated documentation files (the "Software"), to deal
7-
in the Software without restriction, including without limitation the rights
8-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9-
copies of the Software, and to permit persons to whom the Software is
10-
furnished to do so, subject to the following conditions:
11-
12-
The above copyright notice and this permission notice shall be included in all
13-
copies or substantial portions of the Software.
14-
15-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21-
SOFTWARE.
1+
MIT License
2+
3+
Copyright (c) 2026 wuyue
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

cmd/exec.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ func (o *ExecOptions) execCreateNewNode(provider config.ConfigProvider, host, us
383383
}
384384

385385
if keyPath != "" {
386-
identity.KeyPath = keyPath
386+
identity.KeyPath = utils.ToAbsolutePath(keyPath)
387387
identity.Passphrase = keyPass
388388
identity.AuthType = "key"
389389
}
@@ -421,8 +421,9 @@ func (o *ExecOptions) updateNodeFromHostInfo(nodeID string, provider config.Conf
421421
updated = true
422422
}
423423
} else if addr.KeyPath != "" {
424-
if identity.KeyPath != addr.KeyPath || identity.Passphrase != addr.Passphrase || identity.AuthType != "key" {
425-
identity.KeyPath = addr.KeyPath
424+
absKeyPath := utils.ToAbsolutePath(addr.KeyPath)
425+
if identity.KeyPath != absKeyPath || identity.Passphrase != addr.Passphrase || identity.AuthType != "key" {
426+
identity.KeyPath = absKeyPath
426427
identity.Passphrase = addr.Passphrase
427428
identity.AuthType = "key"
428429
updated = true

cmd/host/add.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func NewCmdInventoryAdd() *cobra.Command {
5757
}
5858
identity = models.Identity{User: user}
5959
if keyPath != "" {
60-
identity.KeyPath, identity.Passphrase, identity.AuthType = keyPath, keyPass, "key"
60+
identity.KeyPath, identity.Passphrase, identity.AuthType = utils.ToAbsolutePath(keyPath), keyPass, "key"
6161
} else if password != "" {
6262
identity.Password, identity.AuthType = password, "password"
6363
} else {

cmd/host/edit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func applyNodeUpdates(cmd *cobra.Command, host *models.Host, identity *models.Id
8989
identity.User, updated, nameChanged = flags.user, true, true
9090
}
9191
if flags.keyPath != "" {
92-
identity.KeyPath, identity.AuthType, identity.Password, updated = flags.keyPath, "key", "", true
92+
identity.KeyPath, identity.AuthType, identity.Password, updated = utils.ToAbsolutePath(flags.keyPath), "key", "", true
9393
} else if flags.password != "" {
9494
identity.Password, identity.AuthType, identity.KeyPath, updated = flags.password, "password", "", true
9595
}

cmd/host/load.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func getOrCreateNode(provider config.ConfigProvider, addr utils.HostInfo) (strin
144144
identity.Password = addr.Password
145145
identity.AuthType = "password"
146146
} else if addr.KeyPath != "" {
147-
identity.KeyPath = addr.KeyPath
147+
identity.KeyPath = utils.ToAbsolutePath(addr.KeyPath)
148148
identity.Passphrase = addr.Passphrase
149149
identity.AuthType = "key"
150150
}
@@ -169,8 +169,9 @@ func updateNodeFromHostInfo(nodeID string, provider config.ConfigProvider, addr
169169
updated = true
170170
}
171171
} else if addr.KeyPath != "" {
172-
if identity.KeyPath != addr.KeyPath || identity.Passphrase != addr.Passphrase || identity.AuthType != "key" {
173-
identity.KeyPath = addr.KeyPath
172+
absKeyPath := utils.ToAbsolutePath(addr.KeyPath)
173+
if identity.KeyPath != absKeyPath || identity.Passphrase != addr.Passphrase || identity.AuthType != "key" {
174+
identity.KeyPath = absKeyPath
174175
identity.Passphrase = addr.Passphrase
175176
identity.AuthType = "key"
176177
updated = true

cmd/identity.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func NewCmdIdentityEdit() *cobra.Command {
6666
}
6767

6868
if keyPath != "" {
69-
identity.KeyPath = keyPath
69+
identity.KeyPath = utils.ToAbsolutePath(keyPath)
7070
identity.AuthType = "key"
7171
identity.Password = "" // 切换到密钥时清空密码
7272
updated = true
@@ -195,7 +195,7 @@ func NewCmdIdentityAdd() *cobra.Command {
195195
}
196196

197197
if keyPath != "" {
198-
identity.KeyPath = keyPath
198+
identity.KeyPath = utils.ToAbsolutePath(keyPath)
199199
identity.Passphrase = keyPass
200200
identity.AuthType = "key"
201201
} else if password != "" {

cmd/scp.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ func (o *ScpOptions) createNewNode(provider config.ConfigProvider, host, user st
486486
identity.Password = password
487487
identity.AuthType = "password"
488488
} else if o.KeyFile != "" {
489-
identity.KeyPath = o.KeyFile
489+
identity.KeyPath = cmdutils.ToAbsolutePath(o.KeyFile)
490490
identity.Passphrase = o.KeyPass
491491
identity.AuthType = "key"
492492
}
@@ -515,8 +515,9 @@ func (o *ScpOptions) updateNode(nodeID string, provider config.ConfigProvider, s
515515
updated = true
516516
}
517517
} else if o.KeyFile != "" {
518-
if identity.KeyPath != o.KeyFile || identity.AuthType != "key" {
519-
identity.KeyPath = o.KeyFile
518+
absKeyPath := cmdutils.ToAbsolutePath(o.KeyFile)
519+
if identity.KeyPath != absKeyPath || identity.AuthType != "key" {
520+
identity.KeyPath = absKeyPath
520521
identity.AuthType = "key"
521522
updated = true
522523
}

cmd/sftp.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func (o *SftpOptions) createNewNode(provider config.ConfigProvider) (string, err
148148
identity.Password = o.Password
149149
identity.AuthType = "password"
150150
} else if o.KeyFile != "" {
151-
identity.KeyPath = o.KeyFile
151+
identity.KeyPath = utils.ToAbsolutePath(o.KeyFile)
152152
identity.Passphrase = o.KeyPass
153153
identity.AuthType = "key"
154154
}

cmd/ssh.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ func updateIdentityFields(identity *models.Identity, o *SshOptions) bool {
186186
identity.AuthType = "password"
187187
identityUpdated = true
188188
} else if o.KeyFile != "" {
189-
identity.KeyPath = o.KeyFile
189+
identity.KeyPath = utils.ToAbsolutePath(o.KeyFile)
190190
identity.AuthType = "key"
191191
identityUpdated = true
192192
}
@@ -234,7 +234,7 @@ func (o *SshOptions) createNewNode(provider config.ConfigProvider) (string, erro
234234
identity.Password = o.Password
235235
identity.AuthType = "password"
236236
} else if o.KeyFile != "" {
237-
identity.KeyPath = o.KeyFile
237+
identity.KeyPath = utils.ToAbsolutePath(o.KeyFile)
238238
identity.Passphrase = o.KeyPass
239239
identity.AuthType = "key"
240240
}

cmd/utils/utils.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,40 @@ func IsValidCIDR(cidrStr string) bool {
137137
_, _, err := net.ParseCIDR(cidrStr)
138138
return err == nil
139139
}
140+
141+
// ToAbsolutePath 将路径转换为绝对路径
142+
// 支持 ~ 展开和相对路径转绝对路径
143+
// 如果路径已经是绝对路径,直接返回
144+
func ToAbsolutePath(path string) string {
145+
if path == "" {
146+
return path
147+
}
148+
149+
// 处理 ~ 开头的路径
150+
if len(path) > 0 && path[0] == '~' {
151+
home, err := os.UserHomeDir()
152+
if err == nil {
153+
// 使用 filepath.Join 确保路径分隔符正确
154+
rest := path[1:]
155+
if len(rest) > 0 && (rest[0] == '/' || rest[0] == '\\') {
156+
rest = rest[1:]
157+
}
158+
if rest == "" {
159+
return home
160+
}
161+
return filepath.Join(home, rest)
162+
}
163+
}
164+
165+
// 如果已经是绝对路径,直接返回
166+
if filepath.IsAbs(path) {
167+
return path
168+
}
169+
170+
// 将相对路径转换为绝对路径
171+
absPath, err := filepath.Abs(path)
172+
if err != nil {
173+
return path
174+
}
175+
return absPath
176+
}

0 commit comments

Comments
 (0)