Skip to content

Commit 4506227

Browse files
committed
init
0 parents  commit 4506227

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+5644
-0
lines changed

.github/workflows/release.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
release:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
19+
- uses: actions/setup-go@v5
20+
with:
21+
go-version-file: go.mod
22+
23+
- name: Run tests
24+
run: go test ./...
25+
26+
- uses: goreleaser/goreleaser-action@v6
27+
with:
28+
version: "~> v2"
29+
args: release --clean
30+
env:
31+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32+
HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}

.gitignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Binaries
2+
/build/
3+
4+
# IDE / tools
5+
.idea/
6+
.vscode/
7+
.claude/
8+
*.swp
9+
10+
# OS
11+
.DS_Store
12+
Thumbs.db
13+
14+
# Local docs/research
15+
/docs/
16+
/research/
17+
18+
# Go
19+
/dist/

.goreleaser.yaml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
version: 2
2+
3+
project_name: ghapp
4+
5+
builds:
6+
- id: ghapp
7+
main: ./cmd/ghapp/
8+
binary: ghapp
9+
ldflags:
10+
- -s -w
11+
- -X main.version={{.Version}}
12+
- -X main.commit={{.Commit}}
13+
- -X main.date={{.Date}}
14+
goos:
15+
- linux
16+
- darwin
17+
- windows
18+
goarch:
19+
- amd64
20+
- arm64
21+
ignore:
22+
- goos: windows
23+
goarch: arm64
24+
- id: ghapp-gh
25+
main: ./cmd/gh-wrapper/
26+
binary: ghapp-gh
27+
ldflags:
28+
- -s -w
29+
goos:
30+
- linux
31+
- darwin
32+
- windows
33+
goarch:
34+
- amd64
35+
- arm64
36+
ignore:
37+
- goos: windows
38+
goarch: arm64
39+
40+
archives:
41+
- formats:
42+
- tar.gz
43+
format_overrides:
44+
- goos: windows
45+
formats:
46+
- zip
47+
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
48+
49+
checksum:
50+
name_template: checksums.txt
51+
52+
release:
53+
draft: true
54+
55+
changelog:
56+
sort: asc
57+
filters:
58+
exclude:
59+
- "^docs:"
60+
- "^test:"
61+
- "^chore:"
62+
63+
brews:
64+
- repository:
65+
owner: operator-kit
66+
name: homebrew-tap
67+
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
68+
homepage: "https://github.com/operator-kit/ghapp-cli"
69+
description: "GitHub App authentication for git and gh"
70+
install: |
71+
bin.install "ghapp"
72+
bin.install "ghapp-gh"
73+
test: |
74+
system "#{bin}/ghapp", "version"

README.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# ghapp — GitHub App Auth for git/gh
2+
3+
CLI tool that authenticates as a GitHub App, generates installation tokens, and configures `git` and `gh` to use them transparently.
4+
5+
## Install
6+
7+
```bash
8+
# Homebrew (macOS/Linux)
9+
brew tap operator-kit/tap
10+
brew install ghapp
11+
12+
# One-liner (Linux/macOS)
13+
curl -sSL https://raw.githubusercontent.com/operator-kit/ghapp-cli/main/install.sh | bash
14+
15+
# PowerShell (Windows)
16+
irm https://raw.githubusercontent.com/operator-kit/ghapp-cli/main/install.ps1 | iex
17+
18+
# Specific version
19+
curl -sSL https://raw.githubusercontent.com/operator-kit/ghapp-cli/main/install.sh | GHAPP_VERSION=v0.1.0 bash
20+
21+
# From source (requires Go)
22+
go install github.com/operator-kit/ghapp-cli/cmd/ghapp@latest
23+
24+
# Build from cloned repo
25+
go build -o build/ ./cmd/ghapp/
26+
go build -o build/ ./cmd/gh-wrapper/ # optional: gh wrapper binary
27+
28+
# Cross-compile
29+
GOOS=linux GOARCH=arm64 go build -o build/ ./cmd/ghapp/
30+
```
31+
32+
## Prerequisite — Create a GitHub App
33+
34+
You need three values for `ghapp setup`: **App ID**, **Installation ID**, and a **private key** (.pem file).
35+
36+
### 1. Create the app
37+
- Go to **Settings → Developer settings → GitHub Apps → New GitHub App** ([docs](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app))
38+
- For org-owned apps: **Org settings → Developer settings → GitHub Apps**
39+
- **Name**: anything unique (e.g., `Mr Fox`) - this will be used everywhere your App interacts.
40+
- **Homepage URL**: can be any URL (e.g., your org's GitHub page)
41+
- **Webhooks**: uncheck **Active** (not needed)
42+
The other settings are not needed.
43+
44+
### 2. Set permissions
45+
46+
Select **Repository permissions** based on what you need:
47+
48+
| Use case | Required permissions |
49+
|----------|---------------------|
50+
| `git` clone/push/pull | **Contents**: Read & write |
51+
| `gh` PRs, issues, etc. | **Contents**: Read & write, **Pull requests**: Read & write, **Issues**: Read & write, **Metadata**: Read |
52+
53+
> Add more as needed. `gh pr list` silently returns empty without Issues read permission.
54+
55+
### 3. Create & note your App ID
56+
- Click **Create GitHub App**
57+
- **App ID** is shown at the top of the app's settings page
58+
59+
### 4. Generate a private key
60+
- On the app settings page, scroll to **Private keys → Generate a private key**
61+
- A `.pem` file downloads — store it securely (GitHub won't show it again)
62+
63+
### 5. Install the app & note your Installation ID
64+
- On the app settings page, click **Install App** in the left sidebar
65+
- Select your account/org and choose which repos to grant access to
66+
- After installing, the URL will be `github.com/settings/installations/12345678` — the number at the end is your **Installation ID**
67+
68+
## Quick Start
69+
70+
```bash
71+
# 1. Setup — enter App ID, Installation ID, key path
72+
# (optionally configures git + gh auth at the end)
73+
ghapp setup
74+
75+
# 2. Use git/gh normally — auth is transparent
76+
git clone https://github.com/org/repo.git
77+
gh pr list
78+
```
79+
80+
> If you skipped auth configuration during setup, run `ghapp auth configure` separately.
81+
82+
## Commands
83+
84+
| Command | Description |
85+
|---------|-------------|
86+
| `ghapp setup [--import-key]` | Interactive setup — App ID, Installation ID, PEM key |
87+
| `ghapp token [--no-cache]` | Print an installation token (cached; `--no-cache` forces fresh) |
88+
| `ghapp auth configure [--gh-auth MODE]` | Configure git credential helper, gh CLI, and git identity |
89+
| `ghapp auth status` | Show current auth configuration and diagnostics |
90+
| `ghapp auth reset [--remove-key]` | Remove all auth config and restore previous git identity |
91+
| `ghapp update` | Self-update to the latest release |
92+
| `ghapp version` | Print version info |
93+
94+
### `--gh-auth` modes
95+
96+
During `auth configure`, you're prompted to choose how `gh` CLI gets authenticated. You can also pass it non-interactively:
97+
98+
| Mode | Flag value | Description |
99+
|------|-----------|-------------|
100+
| Shell function | `--gh-auth shell-function` | Wraps `gh` with a shell function that injects a fresh token per invocation |
101+
| PATH binary | `--gh-auth path-shim` | Installs `ghapp-gh` wrapper binary as `gh` earlier in PATH |
102+
| None | `--gh-auth none` | Only writes `hosts.yml` (token expires in ~1hr) |
103+
104+
## How It Works
105+
106+
### git auth
107+
108+
`ghapp` registers itself as a git credential helper. On every git network operation, git calls `ghapp credential-helper get`, which returns a fresh installation token. Tokens are cached locally so repeated operations within the same session are fast.
109+
110+
`auth configure` also sets `url."https://github.com/".insteadOf "git@github.com:"` so that SSH-style URLs (`git@github.com:org/repo.git`) are transparently rewritten to HTTPS. This means copy-pasted SSH clone URLs and submodules that reference `git@github.com:...` will work automatically.
111+
112+
### git identity
113+
114+
`auth configure` sets `user.name` and `user.email` to the app's bot account (e.g., `myapp[bot]`), so commits are attributed to the app with its icon on GitHub. If you already have a git identity, it will ask before overwriting and backs up your previous identity for `auth reset`.
115+
116+
### gh auth — shell function (recommended)
117+
118+
`auth configure` injects a managed block into your shell's rc file that wraps `gh` with a function. Every `gh` invocation automatically gets a fresh token via `GH_TOKEN`:
119+
120+
```bash
121+
# What gets added to your .bashrc / .zshrc (managed automatically):
122+
eval "$(ghapp auth shell-init)"
123+
```
124+
125+
Under the hood, this defines a `gh()` function that calls `ghapp token`, sets `GH_TOKEN`, and delegates to the real `gh`. Tokens are cached so the overhead is negligible after the first call.
126+
127+
**Supported shells:** bash, zsh, fish, PowerShell
128+
129+
### gh auth — PATH binary (CI / non-shell)
130+
131+
For environments without shell rc files (CI, containers, cron), the `ghapp-gh` wrapper binary can be placed on PATH as `gh`. It resolves the real `gh`, generates/caches a token, and execs with `GH_TOKEN` set. Falls through to plain `gh` if config is missing.
132+
133+
### Token caching
134+
135+
All token paths (credential helper, `ghapp token`, shell function, wrapper binary) share a local cache file. Tokens are reused until they're within 5 minutes of expiry, then automatically refreshed. This means:
136+
137+
- `ghapp token` returns in <10ms on cache hit
138+
- Back-to-back `git` operations don't re-generate tokens
139+
- The shell function adds negligible latency to `gh` commands
140+
141+
## Config
142+
143+
Stored at `~/.config/ghapp/config.yaml`:
144+
145+
```yaml
146+
app_id: 123456
147+
installation_id: 789012
148+
private_key_path: /path/to/key.pem
149+
key_in_keyring: false
150+
app_slug: myapp # cached after first auth configure
151+
bot_user_id: 149130343 # cached after first auth configure
152+
```
153+
154+
Environment overrides: `GHAPP_APP_ID`, `GHAPP_INSTALLATION_ID`, `GHAPP_PRIVATE_KEY_PATH`, `GHAPP_NO_UPDATE_CHECK=1` (disable daily update notice)
155+
156+
## Private Key Storage
157+
158+
- **File** (default): path stored in config, key stays on disk
159+
- **OS Keyring** (`--import-key`): key imported into Windows Credential Manager / macOS Keychain / Linux Secret Service

0 commit comments

Comments
 (0)