Skip to content

Commit db1c84f

Browse files
committed
save my work
2 parents ced30be + 0b11d77 commit db1c84f

3 files changed

Lines changed: 67 additions & 37 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,26 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.3.2] - 2025-09-25
9+
10+
### Changed
11+
- **Internationalization:** Refactored the language switching logic to be more stable and prevent dynamic re-initialization.
12+
13+
### Fixed
14+
- **SSH Agent Fallback:** Fixed a bug where key import or deployment would fail if a system key was present in the database but incorrect for the host, instead of correctly falling back to using the SSH agent.
15+
- **TUI Navigation:** Restored `j/k` and `up/down` navigation in the "Deploy to Single Account" view when a filter is active.
16+
17+
---
18+
819
## [1.3.1] - 2025-09-24
920
### Added
1021
- **Project Governance:** Added standard open-source project files including `LICENSE` (MIT), `CODE_OF_CONDUCT.md`, and `CONTRIBUTING.md` to clarify contribution guidelines and project standards.
1122
- **Code Documentation:** Added a lot of comments to clarify how things work.
1223
### Fixed
1324
- **Key Assignment:** Fixed a critical bug where assigning or unassigning a key to an account would fail due to swapped database parameters.
1425

26+
---
27+
1528
## [1.3.0] - 2025-09-23
1629

1730
### Added

internal/deploy/ssh.go

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -71,36 +71,32 @@ func NewDeployer(host, user, privateKey string) (*Deployer, error) {
7171
// for deployment and auditing with a Keymaster system key.
7272
if privateKey != "" {
7373
signer, err := ssh.ParsePrivateKey([]byte(privateKey))
74-
if err != nil {
75-
return nil, fmt.Errorf("unable to parse private key: %w", err)
76-
}
77-
78-
config := &ssh.ClientConfig{
79-
User: user,
80-
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
81-
HostKeyCallback: hostKeyCallback,
82-
Timeout: 10 * time.Second,
83-
}
84-
client, err = ssh.Dial("tcp", addr, config)
8574
if err == nil {
86-
// Success! We connected with the system key.
87-
sftpClient, sftpErr := sftp.NewClient(client)
88-
if sftpErr != nil {
89-
client.Close()
90-
return nil, fmt.Errorf("failed to create sftp client: %w", sftpErr)
75+
config := &ssh.ClientConfig{
76+
User: user,
77+
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
78+
HostKeyCallback: hostKeyCallback,
79+
Timeout: 10 * time.Second,
80+
}
81+
client, err = ssh.Dial("tcp", addr, config)
82+
if err == nil {
83+
// Success! We connected with the system key.
84+
sftpClient, sftpErr := sftp.NewClient(client)
85+
if sftpErr != nil {
86+
client.Close()
87+
return nil, fmt.Errorf("failed to create sftp client: %w", sftpErr)
88+
}
89+
return &Deployer{client: client, sftp: sftpClient}, nil
9190
}
92-
return &Deployer{client: client, sftp: sftpClient}, nil
91+
// If we provided a key and it failed, we will fall through to try the agent.
9392
}
94-
// If we provided a key and it failed, we return the error immediately
95-
// without falling back to the agent.
96-
return nil, fmt.Errorf("connection with system key failed: %w", err)
9793
}
9894

9995
// If no private key was provided, attempt to use the SSH agent.
10096
// This is used for bootstrapping/importing keys.
10197
agentClient := getSSHAgent()
10298
if agentClient == nil {
103-
return nil, fmt.Errorf("no authentication method available (no system key provided and no ssh agent found)")
99+
return nil, fmt.Errorf("no authentication method available (system key failed and no ssh agent found)")
104100
}
105101

106102
config := &ssh.ClientConfig{

internal/tui/deploy.go

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,25 @@ func (m deployModel) updateAccountSelection(msg tea.Msg) (tea.Model, tea.Cmd) {
234234
m.accountFilter = ""
235235
m.status = ""
236236
return m, nil
237+
case "up", "k":
238+
filteredAccounts := m.getFilteredAccounts()
239+
if m.accountCursor > 0 {
240+
m.accountCursor--
241+
} else if len(filteredAccounts) > 0 {
242+
// Wrap around to the bottom
243+
m.accountCursor = len(filteredAccounts) - 1
244+
}
245+
return m, nil
246+
case "down", "j":
247+
filteredAccounts := m.getFilteredAccounts()
248+
if len(filteredAccounts) > 0 {
249+
if m.accountCursor < len(filteredAccounts)-1 {
250+
m.accountCursor++
251+
} else {
252+
m.accountCursor = 0 // Wrap around to the top
253+
}
254+
}
255+
return m, nil
237256
case "esc":
238257
if m.isFilteringAccount {
239258
m.isFilteringAccount = false
@@ -263,10 +282,11 @@ func (m deployModel) updateAccountSelection(msg tea.Msg) (tea.Model, tea.Cmd) {
263282
m.isFilteringAccount = false
264283
return m, nil
265284
}
266-
if len(m.accounts) == 0 {
285+
filteredAccounts := m.getFilteredAccounts()
286+
if len(filteredAccounts) == 0 {
267287
return m, nil
268288
}
269-
m.selectedAccount = m.accounts[m.accountCursor]
289+
m.selectedAccount = filteredAccounts[m.accountCursor]
270290

271291
switch m.action {
272292
case actionGetKeys:
@@ -288,10 +308,6 @@ func (m deployModel) updateAccountSelection(msg tea.Msg) (tea.Model, tea.Cmd) {
288308
m.accountFilter += msg.String()
289309
return m, nil
290310
}
291-
if m.accountFilter != "" && !m.isFilteringAccount {
292-
// Navigation keys etc. handled above
293-
return m, nil
294-
}
295311
}
296312
case startFilteringMsg:
297313
// no-op, just to trigger filter mode
@@ -300,6 +316,20 @@ func (m deployModel) updateAccountSelection(msg tea.Msg) (tea.Model, tea.Cmd) {
300316
return m, nil
301317
}
302318

319+
// getFilteredAccounts is a helper to get the list of accounts based on the current filter.
320+
func (m *deployModel) getFilteredAccounts() []model.Account {
321+
if m.accountFilter == "" {
322+
return m.accounts
323+
}
324+
var filteredAccounts []model.Account
325+
for _, acc := range m.accounts {
326+
if strings.Contains(strings.ToLower(acc.String()), strings.ToLower(m.accountFilter)) {
327+
filteredAccounts = append(filteredAccounts, acc)
328+
}
329+
}
330+
return filteredAccounts
331+
}
332+
303333
// updateSelectTag handles input when the user is selecting a tag.
304334
func (m deployModel) updateSelectTag(msg tea.Msg) (tea.Model, tea.Cmd) {
305335
switch msg := msg.(type) {
@@ -447,16 +477,7 @@ func (m deployModel) View() string {
447477
case deployStateSelectAccount:
448478
title := titleStyle.Render(i18n.T("deploy.select_account"))
449479
var listItems []string
450-
var filteredAccounts []model.Account
451-
if m.accountFilter != "" {
452-
for _, acc := range m.accounts {
453-
if strings.Contains(strings.ToLower(acc.String()), strings.ToLower(m.accountFilter)) {
454-
filteredAccounts = append(filteredAccounts, acc)
455-
}
456-
}
457-
} else {
458-
filteredAccounts = m.accounts
459-
}
480+
filteredAccounts := m.getFilteredAccounts()
460481
if m.accountCursor >= len(filteredAccounts) {
461482
m.accountCursor = 0
462483
}

0 commit comments

Comments
 (0)