Skip to content

fix(ggh): guard dry-run key deletion, make org config idempotent, write Linux IdentityAgent (#67) - #101

Draft
AH-Merii wants to merge 1 commit into
mainfrom
fix/ggh-dry-run-idempotency
Draft

AH-Merii wants to merge 1 commit into
mainfrom
fix/ggh-dry-run-idempotency

Conversation

@AH-Merii

@AH-Merii AH-Merii commented Sep 7, 2026

Copy link
Copy Markdown
Owner

--dry-run could delete a key pair. generate_key ran all the way to the Overwrite prompt, and answering it unlinked the existing key — so the one mode that promises to change nothing could destroy the identity it was asked to inspect.

add and op add were not idempotent: they compared the whole config-<org> file against a two-line template, so a file carrying anything else was reported as differing and rewritten on every run.

On Linux, ggh wrote no 1Password routing at all. The Include it emits is macOS-only, and everything else depended on the shell having exported SSH_AUTH_SOCK, so signing worked from a fish shell that had set it and nowhere else. ggh now writes IdentityAgent itself and stops trusting SSH_AUTH_SOCK when it points somewhere that is not the 1Password socket.

Closes #67

…te Linux IdentityAgent (#67)

- --dry-run returns before the Overwrite/Use-existing prompt, so the unlink branch is unreachable
- config-<org> is written key by key through git_config_set; re-runs report nothing and change nothing
- OnePassword host blocks carry IdentityAgent ~/.1password/agent.sock on Linux; the macOS-only
  Include ~/.ssh/1Password/config is skipped there
- gh auth login exit code is checked and gh auth status re-read before continuing
- Host-block regex anchored so github-acme no longer matches github-acme-old
- dry-run no longer mkdirs/touches; 1Password read-only lookups run under dry-run instead of a placeholder key
- agent socket check ignores SSH_AUTH_SOCK (warns when it points elsewhere)
- validate --org / --host / --key-name before they reach paths, Host lines and agent.toml
- third includeIf for https://github.com/<org>/**, case rule documented
- pin typer>=0.15,<1; verify checks the allowed_signers principal
- drop the unused op-ssh-sign wrapper: gpg.ssh.program already gets the per-platform path

@AH-Merii AH-Merii left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few things worth a look before this goes in​

Comment thread base/git/README.md
| `gpg.format` | `ssh` | Use SSH keys instead of GPG |
| `user.signingkey` | `<path>` | File path to public key, set by `ggh` |
| `gpg.ssh.program` | (platform-specific) | Direct path to op-ssh-sign binary (1Password only) |
| `gpg.ssh.program` | (platform-specific) | 1Password's `op-ssh-sign` binary; `ggh` writes the macOS or Linux path (1Password only) |

@AH-Merii AH-Merii Sep 9, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coinflip: I deleted base/git/.local/bin/op-ssh-sign rather than keeping it as the cross-platform entry point.

nothing referenced it any more. resolve_op_ssh_sign_path() in ggh already returns the macOS or Linux binary path directly, and that absolute path is what ggh writes into gpg.ssh.program in config.local, so the wrapper had been dead since ggh took over signing config. it dates from 23a364d, when you set that key by hand.

the one case that breaks: a machine whose untracked config.local still names ~/.local/bin/op-ssh-sign, which I can't see from here. re-running ggh op init there rewrites the key, or restoring this file is the smaller revert.

Comment thread base/git/.local/bin/ggh
f"IdentityFile {self.pub_key_path}",
"IdentitiesOnly yes",
]
# macOS reaches the agent through the 1Password-managed Include (Host * → IdentityAgent).

@AH-Merii AH-Merii Sep 9, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yolo: the IdentityAgent line this adds on linux is unrun against a real 1Password agent.

I only pointed it at a fake ~/.1password/agent.sock, which shows ggh writes the line but not that ssh actually signs through it. to check it properly: turn on the SSH agent in 1Password on linux, run ggh op init, then ssh -T git@github.com from a shell where SSH_AUTH_SOCK is unset or points at gnome-keyring. if 1Password prompts, the Host block is doing the routing and not the environment, which is the whole point of the line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ggh: --dry-run can delete a key pair; org config not idempotent; Linux IdentityAgent not written; input validation

1 participant