initial commit
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
---
|
||||
description: GitHub workflow skills for managing repositories, pull requests, code reviews, issues, and CI/CD pipelines using the gh CLI and git via terminal.
|
||||
---
|
||||
@@ -0,0 +1,335 @@
|
||||
---
|
||||
name: github
|
||||
description: Complete GitHub workflow — auth setup, repo management, PR lifecycle, code review, and issue triage via gh CLI or git+curl fallback.
|
||||
version: 1.0.0
|
||||
author: Hermes Agent
|
||||
license: MIT
|
||||
metadata:
|
||||
hermes:
|
||||
tags: [GitHub, Authentication, Code-Review, Pull-Requests, Issues, Repositories, CI/CD, Git]
|
||||
related_skills: []
|
||||
---
|
||||
|
||||
# GitHub — Complete Workflow Guide
|
||||
|
||||
Single reference for all GitHub operations: authentication, repository management, PR lifecycle, code review, and issue triage. Every section shows the `gh` CLI way first, then the `git` + `curl` fallback.
|
||||
|
||||
## Quick Auth Detection
|
||||
|
||||
```bash
|
||||
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
|
||||
AUTH="gh"
|
||||
else
|
||||
AUTH="git"
|
||||
if [ -z "$GITHUB_TOKEN" ]; then
|
||||
if [ -f ~/.hermes/.env ] && grep -q "^GITHUB_TOKEN=" ~/.hermes/.env; then
|
||||
GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" ~/.hermes/.env | head -1 | cut -d= -f2 | tr -d '\n\r')
|
||||
elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
|
||||
GITHUB_TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Extract owner/repo from git remote
|
||||
REMOTE_URL=$(git remote get-url origin 2>/dev/null || echo "")
|
||||
if [ -n "$REMOTE_URL" ]; then
|
||||
OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's|.*github\.com[:/]||; s|\.git$||')
|
||||
OWNER=$(echo "$OWNER_REPO" | cut -d/ -f1)
|
||||
REPO=$(echo "$OWNER_REPO" | cut -d/ -f2)
|
||||
fi
|
||||
```
|
||||
|
||||
Script reference: `scripts/gh-env.sh` (sourced in workflows that need persistent env).
|
||||
|
||||
---
|
||||
|
||||
## Section A — Authentication Setup
|
||||
|
||||
Two reliable paths:
|
||||
|
||||
### A1. Git-Only (HTTPS + Personal Access Token)
|
||||
|
||||
```bash
|
||||
# Configure credential helper
|
||||
git config --global credential.helper store
|
||||
git config --global user.name "Your Name"
|
||||
git config --global user.email "your@email.com"
|
||||
|
||||
# Use the token as password when prompted
|
||||
git ls-remote https://github.com/<username>/<repo>.git
|
||||
```
|
||||
|
||||
**Alternative — cache helper** (credentials expire from memory):
|
||||
```bash
|
||||
git config --global credential.helper 'cache --timeout=28800'
|
||||
```
|
||||
|
||||
### A2. SSH Key Auth
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -C "your@email.com" -f ~/.ssh/id_ed25519 -N ""
|
||||
cat ~/.ssh/id_ed25519.pub # Add to https://github.com/settings/keys
|
||||
ssh -T git@github.com
|
||||
git config --global url."git@github.com:".insteadOf "https://github.com/"
|
||||
```
|
||||
|
||||
### A3. gh CLI Auth
|
||||
|
||||
```bash
|
||||
# Interactive browser login
|
||||
gh auth login
|
||||
|
||||
# Token-based (headless)
|
||||
echo "<token>" | gh auth login --with-token
|
||||
gh auth setup-git
|
||||
```
|
||||
|
||||
### A4. API Access Without gh
|
||||
|
||||
```bash
|
||||
export GITHUB_TOKEN="<token>"
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user
|
||||
```
|
||||
|
||||
See `references/github-api-cheatsheet.md` for common API endpoint patterns.
|
||||
|
||||
---
|
||||
|
||||
## Section B — Repository Management
|
||||
|
||||
### Cloning
|
||||
|
||||
```bash
|
||||
git clone https://github.com/owner/repo.git
|
||||
gh repo clone owner/repo
|
||||
```
|
||||
|
||||
### Creating Repos
|
||||
|
||||
```bash
|
||||
# gh
|
||||
gh repo create my-project --public --clone
|
||||
|
||||
# curl
|
||||
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/user/repos \
|
||||
-d '{"name": "my-project", "private": false}'
|
||||
```
|
||||
|
||||
### Forking & Sync
|
||||
|
||||
```bash
|
||||
gh repo fork owner/repo --clone
|
||||
|
||||
# Keep fork in sync
|
||||
git fetch upstream && git merge upstream/main && git push
|
||||
gh repo sync <user>/<repo>
|
||||
```
|
||||
|
||||
### Releases
|
||||
|
||||
```bash
|
||||
gh release create v1.0.0 --title "v1.0.0" --generate-notes
|
||||
|
||||
# curl
|
||||
curl -X POST -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/releases \
|
||||
-d '{"tag_name": "v1.0.0", "name": "v1.0.0"}'
|
||||
```
|
||||
|
||||
### Secrets (GitHub Actions)
|
||||
|
||||
```bash
|
||||
gh secret set API_KEY --body "value"
|
||||
|
||||
# curl requires encryption with the repo's public key (much more complex)
|
||||
# Prefer gh for secrets.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Section C — Pull Request Lifecycle
|
||||
|
||||
### Branch + Commit
|
||||
|
||||
```bash
|
||||
git checkout -b feat/description
|
||||
git add <files>
|
||||
git commit -m "feat: add feature description"
|
||||
git push -u origin HEAD
|
||||
```
|
||||
|
||||
### Create PR
|
||||
|
||||
```bash
|
||||
# gh
|
||||
gh pr create --title "feat: ..." --body "## Summary..." --label enhancement
|
||||
|
||||
# curl
|
||||
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls \
|
||||
-d '{"title": "feat: ...", "head": "'$(git branch --show-current)'", "base": "main"}'
|
||||
```
|
||||
|
||||
### CI Monitoring
|
||||
|
||||
```bash
|
||||
gh pr checks --watch
|
||||
|
||||
git log --oneline --format="%H %s" | head -1 | xargs -I{} \
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/commits/{}/status
|
||||
```
|
||||
|
||||
### Merging
|
||||
|
||||
```bash
|
||||
gh pr merge --squash --delete-branch
|
||||
|
||||
curl -X PUT -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$N/merge \
|
||||
-d '{"merge_method": "squash"}'
|
||||
```
|
||||
|
||||
See `references/conventional-commits.md` for commit message format.
|
||||
|
||||
---
|
||||
|
||||
## Section D — Code Review
|
||||
|
||||
### Local Changes Review (Pre-Push)
|
||||
|
||||
```bash
|
||||
git diff main...HEAD --stat # scope of changes
|
||||
git diff main...HEAD # full diff
|
||||
git diff main...HEAD -- src/ # file-specific
|
||||
|
||||
# Check for common issues
|
||||
git diff main...HEAD | grep -n "TODO\|FIXME\|debugger\|console\.log"
|
||||
git diff main...HEAD | grep -in "password\|secret\|api_key"
|
||||
git diff main...HEAD --stat | sort -t'|' -k2 -rn | head -10
|
||||
```
|
||||
|
||||
### PR Review
|
||||
|
||||
```bash
|
||||
gh pr view $N && gh pr diff $N
|
||||
git fetch origin pull/$N/head:pr-$N && git checkout pr-$N
|
||||
gh pr review $N --approve --body "LGTM"
|
||||
gh pr review $N --request-changes --body "See comments"
|
||||
```
|
||||
|
||||
### Inline Comments (curl)
|
||||
|
||||
```bash
|
||||
HEAD_SHA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$N \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin)['head']['sha'])")
|
||||
|
||||
curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/pulls/$N/reviews \
|
||||
-d '{
|
||||
"commit_id": "'$HEAD_SHA'",
|
||||
"event": "COMMENT",
|
||||
"body": "Code review from Hermes Agent",
|
||||
"comments": [
|
||||
{"path": "src/auth.py", "line": 45, "body": "Use parameterized queries."}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
See `references/review-output-template.md` for the structured review format.
|
||||
|
||||
---
|
||||
|
||||
## Section E — Issues Management
|
||||
|
||||
### Viewing
|
||||
|
||||
```bash
|
||||
gh issue list
|
||||
gh issue list --label bug --state open
|
||||
gh issue view $N
|
||||
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/$OWNER/$REPO/issues?state=open"
|
||||
```
|
||||
|
||||
### Creating
|
||||
|
||||
```bash
|
||||
gh issue create --title "Bug" --body "Steps..." --label bug
|
||||
|
||||
curl -X POST -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues \
|
||||
-d '{"title": "Bug", "body": "Steps...", "labels": ["bug"]}'
|
||||
```
|
||||
|
||||
### Managing (Labels, Assign, Close)
|
||||
|
||||
```bash
|
||||
gh issue edit $N --add-label "priority:high"
|
||||
gh issue edit $N --add-assignee username
|
||||
gh issue close $N
|
||||
gh issue comment $N --body "Fixed in PR #99"
|
||||
|
||||
curl -X PATCH -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/issues/$N \
|
||||
-d '{"state": "closed"}'
|
||||
```
|
||||
|
||||
See `templates/bug-report.md` and `templates/feature-request.md` for issue body templates.
|
||||
|
||||
---
|
||||
|
||||
## Section F — CI/CD & Actions
|
||||
|
||||
```bash
|
||||
gh workflow list
|
||||
gh run list --limit 10
|
||||
gh run view <ID> --log-failed
|
||||
gh run rerun <ID> --failed
|
||||
gh workflow run deploy.yml -f environment=staging
|
||||
|
||||
# curl
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$OWNER/$REPO/actions/workflows
|
||||
```
|
||||
|
||||
See `references/ci-troubleshooting.md` for common CI failure patterns and fixes.
|
||||
|
||||
---
|
||||
|
||||
## Pitfalls
|
||||
|
||||
1. **Username vs numeric ID** — Not relevant for GitHub (unlike Telegram), but tokens expire. Set appropriate expiration (90 days default).
|
||||
2. **`gh` not available** — Every operation has a `git` + `curl` fallback. Prefer it for CI/automation where `gh` may not be installed.
|
||||
3. **Token in config.yaml** — Never put secrets in config.yaml. Use `.env` tokens only.
|
||||
4. **Branch protection** — `gh pr merge` will fail on protected branches without approval. Use `gh pr merge --auto` for auto-merge when checks pass.
|
||||
5. **Large repos** — Use `--depth 1` shallow clones to save time and disk.
|
||||
6. **Conflicting workflow names** — GitHub Actions workflow filenames must be unique within a repo.
|
||||
7. **Secrets via curl** — Requires encryption with the repo's public key. Always prefer `gh secret set`.
|
||||
8. **Rate limits** — Unauthenticated API calls are limited to 60/hour. Authenticated: 5000/hour.
|
||||
|
||||
## Verification Checklist
|
||||
|
||||
- [ ] Auth method detected (gh or curl) before operations
|
||||
- [ ] `GITHUB_TOKEN` or `gh auth status` confirmed functional
|
||||
- [ ] git identity configured (`user.name` + `user.email`)
|
||||
- [ ] PR body references issues with `Closes #N` for auto-close
|
||||
- [ ] CI checks monitored and passing before merge
|
||||
- [ ] Stale review branches cleaned up after merge
|
||||
|
||||
## Support Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `scripts/gh-env.sh` | Persistent auth detection script (source in workflows) |
|
||||
| `references/review-output-template.md` | Structured code review output format |
|
||||
| `references/ci-troubleshooting.md` | Common CI failure patterns and fixes |
|
||||
| `references/conventional-commits.md` | Commit message format guide |
|
||||
| `references/github-api-cheatsheet.md` | Common REST API endpoint reference |
|
||||
| `templates/bug-report.md` | Bug report issue body template |
|
||||
| `templates/feature-request.md` | Feature request issue body template |
|
||||
| `templates/pr-body-feature.md` | Feature PR body template |
|
||||
| `templates/pr-body-bugfix.md` | Bugfix PR body template |
|
||||
@@ -0,0 +1,183 @@
|
||||
# CI Troubleshooting Quick Reference
|
||||
|
||||
Common CI failure patterns and how to diagnose them from the logs.
|
||||
|
||||
## Reading CI Logs
|
||||
|
||||
```bash
|
||||
# With gh
|
||||
gh run view <RUN_ID> --log-failed
|
||||
|
||||
# With curl — download and extract
|
||||
curl -sL -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/actions/runs/<RUN_ID>/logs \
|
||||
-o /tmp/ci-logs.zip && unzip -o /tmp/ci-logs.zip -d /tmp/ci-logs
|
||||
```
|
||||
|
||||
## Common Failure Patterns
|
||||
|
||||
### Test Failures
|
||||
|
||||
**Signatures in logs:**
|
||||
```
|
||||
FAILED tests/test_foo.py::test_bar - AssertionError
|
||||
E assert 42 == 43
|
||||
ERROR tests/test_foo.py - ModuleNotFoundError
|
||||
```
|
||||
|
||||
**Diagnosis:**
|
||||
1. Find the test file and line number from the traceback
|
||||
2. Use `read_file` to read the failing test
|
||||
3. Check if it's a logic error in the code or a stale test assertion
|
||||
4. Look for `ModuleNotFoundError` — usually a missing dependency in CI
|
||||
|
||||
**Common fixes:**
|
||||
- Update assertion to match new expected behavior
|
||||
- Add missing dependency to requirements.txt / pyproject.toml
|
||||
- Fix flaky test (add retry, mock external service, fix race condition)
|
||||
|
||||
---
|
||||
|
||||
### Lint / Formatting Failures
|
||||
|
||||
**Signatures in logs:**
|
||||
```
|
||||
src/auth.py:45:1: E302 expected 2 blank lines, got 1
|
||||
src/models.py:12:80: E501 line too long (95 > 88 characters)
|
||||
error: would reformat src/utils.py
|
||||
```
|
||||
|
||||
**Diagnosis:**
|
||||
1. Read the specific file:line numbers mentioned
|
||||
2. Check which linter is complaining (flake8, ruff, black, isort, mypy)
|
||||
|
||||
**Common fixes:**
|
||||
- Run the formatter locally: `black .`, `isort .`, `ruff check --fix .`
|
||||
- Fix the specific style violation by editing the file
|
||||
- If using `patch`, make sure to match existing indentation style
|
||||
|
||||
---
|
||||
|
||||
### Type Check Failures (mypy / pyright)
|
||||
|
||||
**Signatures in logs:**
|
||||
```
|
||||
src/api.py:23: error: Argument 1 to "process" has incompatible type "str"; expected "int"
|
||||
src/models.py:45: error: Missing return statement
|
||||
```
|
||||
|
||||
**Diagnosis:**
|
||||
1. Read the file at the mentioned line
|
||||
2. Check the function signature and what's being passed
|
||||
|
||||
**Common fixes:**
|
||||
- Add type cast or conversion
|
||||
- Fix the function signature
|
||||
- Add `# type: ignore` comment as last resort (with explanation)
|
||||
|
||||
---
|
||||
|
||||
### Build / Compilation Failures
|
||||
|
||||
**Signatures in logs:**
|
||||
```
|
||||
ModuleNotFoundError: No module named 'some_package'
|
||||
ERROR: Could not find a version that satisfies the requirement foo==1.2.3
|
||||
npm ERR! Could not resolve dependency
|
||||
```
|
||||
|
||||
**Diagnosis:**
|
||||
1. Check requirements.txt / package.json for the missing or incompatible dependency
|
||||
2. Compare local vs CI Python/Node version
|
||||
|
||||
**Common fixes:**
|
||||
- Add missing dependency to requirements file
|
||||
- Pin compatible version
|
||||
- Update lockfile (`pip freeze`, `npm install`)
|
||||
|
||||
---
|
||||
|
||||
### Permission / Auth Failures
|
||||
|
||||
**Signatures in logs:**
|
||||
```
|
||||
fatal: could not read Username for 'https://github.com': No such device or address
|
||||
Error: Resource not accessible by integration
|
||||
403 Forbidden
|
||||
```
|
||||
|
||||
**Diagnosis:**
|
||||
1. Check if the workflow needs special permissions (token scopes)
|
||||
2. Check if secrets are configured (missing `GITHUB_TOKEN` or custom secrets)
|
||||
|
||||
**Common fixes:**
|
||||
- Add `permissions:` block to workflow YAML
|
||||
- Verify secrets exist: `gh secret list` or check repo settings
|
||||
- For fork PRs: some secrets aren't available by design
|
||||
|
||||
---
|
||||
|
||||
### Timeout Failures
|
||||
|
||||
**Signatures in logs:**
|
||||
```
|
||||
Error: The operation was canceled.
|
||||
The job running on runner ... has exceeded the maximum execution time
|
||||
```
|
||||
|
||||
**Diagnosis:**
|
||||
1. Check which step timed out
|
||||
2. Look for infinite loops, hung processes, or slow network calls
|
||||
|
||||
**Common fixes:**
|
||||
- Add timeout to the specific step: `timeout-minutes: 10`
|
||||
- Fix the underlying performance issue
|
||||
- Split into parallel jobs
|
||||
|
||||
---
|
||||
|
||||
### Docker / Container Failures
|
||||
|
||||
**Signatures in logs:**
|
||||
```
|
||||
docker: Error response from daemon
|
||||
failed to solve: ... not found
|
||||
COPY failed: file not found in build context
|
||||
```
|
||||
|
||||
**Diagnosis:**
|
||||
1. Check Dockerfile for the failing step
|
||||
2. Verify the referenced files exist in the repo
|
||||
|
||||
**Common fixes:**
|
||||
- Fix path in COPY/ADD command
|
||||
- Update base image tag
|
||||
- Add missing file to `.dockerignore` exclusion or remove from it
|
||||
|
||||
---
|
||||
|
||||
## Auto-Fix Decision Tree
|
||||
|
||||
```
|
||||
CI Failed
|
||||
├── Test failure
|
||||
│ ├── Assertion mismatch → update test or fix logic
|
||||
│ └── Import/module error → add dependency
|
||||
├── Lint failure → run formatter, fix style
|
||||
├── Type error → fix types
|
||||
├── Build failure
|
||||
│ ├── Missing dep → add to requirements
|
||||
│ └── Version conflict → update pins
|
||||
├── Permission error → update workflow permissions (needs user)
|
||||
└── Timeout → investigate perf (may need user input)
|
||||
```
|
||||
|
||||
## Re-running After Fix
|
||||
|
||||
```bash
|
||||
git add <fixed_files> && git commit -m "fix: resolve CI failure" && git push
|
||||
|
||||
# Then monitor
|
||||
gh pr checks --watch 2>/dev/null || \
|
||||
echo "Poll with: curl -s -H 'Authorization: token ...' https://api.github.com/repos/.../commits/$(git rev-parse HEAD)/status"
|
||||
```
|
||||
@@ -0,0 +1,71 @@
|
||||
# Conventional Commits Quick Reference
|
||||
|
||||
Format: `type(scope): description`
|
||||
|
||||
## Types
|
||||
|
||||
| Type | When to use | Example |
|
||||
|------|------------|---------|
|
||||
| `feat` | New feature or capability | `feat(auth): add OAuth2 login flow` |
|
||||
| `fix` | Bug fix | `fix(api): handle null response from /users endpoint` |
|
||||
| `refactor` | Code restructuring, no behavior change | `refactor(db): extract query builder into separate module` |
|
||||
| `docs` | Documentation only | `docs: update API usage examples in README` |
|
||||
| `test` | Adding or updating tests | `test(auth): add integration tests for token refresh` |
|
||||
| `ci` | CI/CD configuration | `ci: add Python 3.12 to test matrix` |
|
||||
| `chore` | Maintenance, dependencies, tooling | `chore: upgrade pytest to 8.x` |
|
||||
| `perf` | Performance improvement | `perf(search): add index on users.email column` |
|
||||
| `style` | Formatting, whitespace, semicolons | `style: run black formatter on src/` |
|
||||
| `build` | Build system or external deps | `build: switch from setuptools to hatch` |
|
||||
| `revert` | Reverts a previous commit | `revert: revert "feat(auth): add OAuth2 login flow"` |
|
||||
|
||||
## Scope (optional)
|
||||
|
||||
Short identifier for the area of the codebase: `auth`, `api`, `db`, `ui`, `cli`, etc.
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
Add `!` after type or `BREAKING CHANGE:` in footer:
|
||||
|
||||
```
|
||||
feat(api)!: change authentication to use bearer tokens
|
||||
|
||||
BREAKING CHANGE: API endpoints now require Bearer token instead of API key header.
|
||||
Migration guide: https://docs.example.com/migrate-auth
|
||||
```
|
||||
|
||||
## Multi-line Body
|
||||
|
||||
Wrap at 72 characters. Use bullet points for multiple changes:
|
||||
|
||||
```
|
||||
feat(auth): add JWT-based user authentication
|
||||
|
||||
- Add login/register endpoints with input validation
|
||||
- Add User model with argon2 password hashing
|
||||
- Add auth middleware for protected routes
|
||||
- Add token refresh endpoint with rotation
|
||||
|
||||
Closes #42
|
||||
```
|
||||
|
||||
## Linking Issues
|
||||
|
||||
In the commit body or footer:
|
||||
|
||||
```
|
||||
Closes #42 ← closes the issue when merged
|
||||
Fixes #42 ← same effect
|
||||
Refs #42 ← references without closing
|
||||
Co-authored-by: Name <email>
|
||||
```
|
||||
|
||||
## Quick Decision Guide
|
||||
|
||||
- Added something new? → `feat`
|
||||
- Something was broken and you fixed it? → `fix`
|
||||
- Changed how code is organized but not what it does? → `refactor`
|
||||
- Only touched tests? → `test`
|
||||
- Only touched docs? → `docs`
|
||||
- Updated CI/CD pipelines? → `ci`
|
||||
- Updated dependencies or tooling? → `chore`
|
||||
- Made something faster? → `perf`
|
||||
@@ -0,0 +1,161 @@
|
||||
# GitHub REST API Cheatsheet
|
||||
|
||||
Base URL: `https://api.github.com`
|
||||
|
||||
All requests need: `-H "Authorization: token $GITHUB_TOKEN"`
|
||||
|
||||
Use the `gh-env.sh` helper to set `$GITHUB_TOKEN`, `$GH_OWNER`, `$GH_REPO` automatically:
|
||||
```bash
|
||||
source "${HERMES_HOME:-$HOME/.hermes}/skills/github/github-auth/scripts/gh-env.sh"
|
||||
```
|
||||
|
||||
## Repositories
|
||||
|
||||
| Action | Method | Endpoint |
|
||||
|--------|--------|----------|
|
||||
| Get repo info | GET | `/repos/{owner}/{repo}` |
|
||||
| Create repo (user) | POST | `/user/repos` |
|
||||
| Create repo (org) | POST | `/orgs/{org}/repos` |
|
||||
| Update repo | PATCH | `/repos/{owner}/{repo}` |
|
||||
| Delete repo | DELETE | `/repos/{owner}/{repo}` |
|
||||
| List your repos | GET | `/user/repos?per_page=30&sort=updated` |
|
||||
| List org repos | GET | `/orgs/{org}/repos` |
|
||||
| Fork repo | POST | `/repos/{owner}/{repo}/forks` |
|
||||
| Create from template | POST | `/repos/{owner}/{template}/generate` |
|
||||
| Get topics | GET | `/repos/{owner}/{repo}/topics` |
|
||||
| Set topics | PUT | `/repos/{owner}/{repo}/topics` |
|
||||
|
||||
## Pull Requests
|
||||
|
||||
| Action | Method | Endpoint |
|
||||
|--------|--------|----------|
|
||||
| List PRs | GET | `/repos/{owner}/{repo}/pulls?state=open` |
|
||||
| Create PR | POST | `/repos/{owner}/{repo}/pulls` |
|
||||
| Get PR | GET | `/repos/{owner}/{repo}/pulls/{number}` |
|
||||
| Update PR | PATCH | `/repos/{owner}/{repo}/pulls/{number}` |
|
||||
| List PR files | GET | `/repos/{owner}/{repo}/pulls/{number}/files` |
|
||||
| Merge PR | PUT | `/repos/{owner}/{repo}/pulls/{number}/merge` |
|
||||
| Request reviewers | POST | `/repos/{owner}/{repo}/pulls/{number}/requested_reviewers` |
|
||||
| Create review | POST | `/repos/{owner}/{repo}/pulls/{number}/reviews` |
|
||||
| Inline comment | POST | `/repos/{owner}/{repo}/pulls/{number}/comments` |
|
||||
|
||||
### PR Merge Body
|
||||
|
||||
```json
|
||||
{"merge_method": "squash", "commit_title": "feat: description (#N)"}
|
||||
```
|
||||
|
||||
Merge methods: `"merge"`, `"squash"`, `"rebase"`
|
||||
|
||||
### PR Review Events
|
||||
|
||||
`"APPROVE"`, `"REQUEST_CHANGES"`, `"COMMENT"`
|
||||
|
||||
## Issues
|
||||
|
||||
| Action | Method | Endpoint |
|
||||
|--------|--------|----------|
|
||||
| List issues | GET | `/repos/{owner}/{repo}/issues?state=open` |
|
||||
| Create issue | POST | `/repos/{owner}/{repo}/issues` |
|
||||
| Get issue | GET | `/repos/{owner}/{repo}/issues/{number}` |
|
||||
| Update issue | PATCH | `/repos/{owner}/{repo}/issues/{number}` |
|
||||
| Add comment | POST | `/repos/{owner}/{repo}/issues/{number}/comments` |
|
||||
| Add labels | POST | `/repos/{owner}/{repo}/issues/{number}/labels` |
|
||||
| Remove label | DELETE | `/repos/{owner}/{repo}/issues/{number}/labels/{name}` |
|
||||
| Add assignees | POST | `/repos/{owner}/{repo}/issues/{number}/assignees` |
|
||||
| List labels | GET | `/repos/{owner}/{repo}/labels` |
|
||||
| Search issues | GET | `/search/issues?q={query}+repo:{owner}/{repo}` |
|
||||
|
||||
Note: The Issues API also returns PRs. Filter with `"pull_request" not in item` when parsing.
|
||||
|
||||
## CI / GitHub Actions
|
||||
|
||||
| Action | Method | Endpoint |
|
||||
|--------|--------|----------|
|
||||
| List workflows | GET | `/repos/{owner}/{repo}/actions/workflows` |
|
||||
| List runs | GET | `/repos/{owner}/{repo}/actions/runs?per_page=10` |
|
||||
| List runs (branch) | GET | `/repos/{owner}/{repo}/actions/runs?branch={branch}` |
|
||||
| Get run | GET | `/repos/{owner}/{repo}/actions/runs/{run_id}` |
|
||||
| Download logs | GET | `/repos/{owner}/{repo}/actions/runs/{run_id}/logs` |
|
||||
| Re-run | POST | `/repos/{owner}/{repo}/actions/runs/{run_id}/rerun` |
|
||||
| Re-run failed | POST | `/repos/{owner}/{repo}/actions/runs/{run_id}/rerun-failed-jobs` |
|
||||
| Trigger dispatch | POST | `/repos/{owner}/{repo}/actions/workflows/{id}/dispatches` |
|
||||
| Commit status | GET | `/repos/{owner}/{repo}/commits/{sha}/status` |
|
||||
| Check runs | GET | `/repos/{owner}/{repo}/commits/{sha}/check-runs` |
|
||||
|
||||
## Releases
|
||||
|
||||
| Action | Method | Endpoint |
|
||||
|--------|--------|----------|
|
||||
| List releases | GET | `/repos/{owner}/{repo}/releases` |
|
||||
| Create release | POST | `/repos/{owner}/{repo}/releases` |
|
||||
| Get release | GET | `/repos/{owner}/{repo}/releases/{id}` |
|
||||
| Delete release | DELETE | `/repos/{owner}/{repo}/releases/{id}` |
|
||||
| Upload asset | POST | `https://uploads.github.com/repos/{owner}/{repo}/releases/{id}/assets?name={filename}` |
|
||||
|
||||
## Secrets
|
||||
|
||||
| Action | Method | Endpoint |
|
||||
|--------|--------|----------|
|
||||
| List secrets | GET | `/repos/{owner}/{repo}/actions/secrets` |
|
||||
| Get public key | GET | `/repos/{owner}/{repo}/actions/secrets/public-key` |
|
||||
| Set secret | PUT | `/repos/{owner}/{repo}/actions/secrets/{name}` |
|
||||
| Delete secret | DELETE | `/repos/{owner}/{repo}/actions/secrets/{name}` |
|
||||
|
||||
## Branch Protection
|
||||
|
||||
| Action | Method | Endpoint |
|
||||
|--------|--------|----------|
|
||||
| Get protection | GET | `/repos/{owner}/{repo}/branches/{branch}/protection` |
|
||||
| Set protection | PUT | `/repos/{owner}/{repo}/branches/{branch}/protection` |
|
||||
| Delete protection | DELETE | `/repos/{owner}/{repo}/branches/{branch}/protection` |
|
||||
|
||||
## User / Auth
|
||||
|
||||
| Action | Method | Endpoint |
|
||||
|--------|--------|----------|
|
||||
| Get current user | GET | `/user` |
|
||||
| List user repos | GET | `/user/repos` |
|
||||
| List user gists | GET | `/gists` |
|
||||
| Create gist | POST | `/gists` |
|
||||
| Search repos | GET | `/search/repositories?q={query}` |
|
||||
|
||||
## Pagination
|
||||
|
||||
Most list endpoints support:
|
||||
- `?per_page=100` (max 100)
|
||||
- `?page=2` for next page
|
||||
- Check `Link` header for `rel="next"` URL
|
||||
|
||||
## Rate Limits
|
||||
|
||||
- Authenticated: 5,000 requests/hour
|
||||
- Check remaining: `curl -s -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/rate_limit`
|
||||
|
||||
## Common curl Patterns
|
||||
|
||||
```bash
|
||||
# GET
|
||||
curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO
|
||||
|
||||
# POST with JSON body
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/issues \
|
||||
-d '{"title": "...", "body": "..."}'
|
||||
|
||||
# PATCH (update)
|
||||
curl -s -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/issues/42 \
|
||||
-d '{"state": "closed"}'
|
||||
|
||||
# DELETE
|
||||
curl -s -X DELETE \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/repos/$GH_OWNER/$GH_REPO/issues/42/labels/bug
|
||||
|
||||
# Parse JSON response with python3
|
||||
curl -s ... | python3 -c "import sys,json; data=json.load(sys.stdin); print(data['field'])"
|
||||
```
|
||||
@@ -0,0 +1,74 @@
|
||||
# Review Output Template
|
||||
|
||||
Use this as the structure for PR review summary comments. Copy and fill in the sections.
|
||||
|
||||
## For PR Summary Comment
|
||||
|
||||
```markdown
|
||||
## Code Review Summary
|
||||
|
||||
**Verdict: [Approved ✅ | Changes Requested 🔴 | Reviewed 💬]** ([N] issues, [N] suggestions)
|
||||
|
||||
**PR:** #[number] — [title]
|
||||
**Author:** @[username]
|
||||
**Files changed:** [N] (+[additions] -[deletions])
|
||||
|
||||
### 🔴 Critical
|
||||
<!-- Issues that MUST be fixed before merge -->
|
||||
- **file.py:line** — [description]. Suggestion: [fix].
|
||||
|
||||
### ⚠️ Warnings
|
||||
<!-- Issues that SHOULD be fixed, but not strictly blocking -->
|
||||
- **file.py:line** — [description].
|
||||
|
||||
### 💡 Suggestions
|
||||
<!-- Non-blocking improvements, style preferences, future considerations -->
|
||||
- **file.py:line** — [description].
|
||||
|
||||
### ✅ Looks Good
|
||||
<!-- Call out things done well — positive reinforcement -->
|
||||
- [aspect that was done well]
|
||||
|
||||
---
|
||||
*Reviewed by Hermes Agent*
|
||||
```
|
||||
|
||||
## Severity Guide
|
||||
|
||||
| Level | Icon | When to use | Blocks merge? |
|
||||
|-------|------|-------------|---------------|
|
||||
| Critical | 🔴 | Security vulnerabilities, data loss risk, crashes, broken core functionality | Yes |
|
||||
| Warning | ⚠️ | Bugs in non-critical paths, missing error handling, missing tests for new code | Usually yes |
|
||||
| Suggestion | 💡 | Style improvements, refactoring ideas, performance hints, documentation gaps | No |
|
||||
| Looks Good | ✅ | Clean patterns, good test coverage, clear naming, smart design decisions | N/A |
|
||||
|
||||
## Verdict Decision
|
||||
|
||||
- **Approved ✅** — Zero critical/warning items. Only suggestions or all clear.
|
||||
- **Changes Requested 🔴** — Any critical or warning item exists.
|
||||
- **Reviewed 💬** — Observations only (draft PRs, uncertain findings, informational).
|
||||
|
||||
## For Inline Comments
|
||||
|
||||
Prefix inline comments with the severity icon so they're scannable:
|
||||
|
||||
```
|
||||
🔴 **Critical:** User input passed directly to SQL query — use parameterized queries to prevent injection.
|
||||
```
|
||||
|
||||
```
|
||||
⚠️ **Warning:** This error is silently swallowed. At minimum, log it.
|
||||
```
|
||||
|
||||
```
|
||||
💡 **Suggestion:** This could be simplified with a dict comprehension:
|
||||
`{k: v for k, v in items if v is not None}`
|
||||
```
|
||||
|
||||
```
|
||||
✅ **Nice:** Good use of context manager here — ensures cleanup on exceptions.
|
||||
```
|
||||
|
||||
## For Local (Pre-Push) Review
|
||||
|
||||
When reviewing locally before push, use the same structure but present it as a message to the user instead of a PR comment. Skip the PR metadata header and just start with the severity sections.
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# GitHub environment detection helper for Hermes Agent skills.
|
||||
#
|
||||
# Usage (via terminal tool):
|
||||
# source skills/github/github-auth/scripts/gh-env.sh
|
||||
#
|
||||
# After sourcing, these variables are set:
|
||||
# GH_AUTH_METHOD - "gh", "curl", or "none"
|
||||
# GITHUB_TOKEN - personal access token (set if method is "curl")
|
||||
# GH_USER - GitHub username
|
||||
# GH_OWNER - repo owner (only if inside a git repo with a github remote)
|
||||
# GH_REPO - repo name (only if inside a git repo with a github remote)
|
||||
# GH_OWNER_REPO - owner/repo (only if inside a git repo with a github remote)
|
||||
|
||||
# --- Auth detection ---
|
||||
|
||||
GH_AUTH_METHOD="none"
|
||||
GITHUB_TOKEN="${GITHUB_TOKEN:-}"
|
||||
GH_USER=""
|
||||
|
||||
if command -v gh &>/dev/null && gh auth status &>/dev/null 2>&1; then
|
||||
GH_AUTH_METHOD="gh"
|
||||
GH_USER=$(gh api user --jq '.login' 2>/dev/null)
|
||||
elif [ -n "$GITHUB_TOKEN" ]; then
|
||||
GH_AUTH_METHOD="curl"
|
||||
elif [ -f "$HOME/.hermes/.env" ] && grep -q "^GITHUB_TOKEN=" "$HOME/.hermes/.env" 2>/dev/null; then
|
||||
GITHUB_TOKEN=$(grep "^GITHUB_TOKEN=" "$HOME/.hermes/.env" | head -1 | cut -d= -f2 | tr -d '\n\r')
|
||||
if [ -n "$GITHUB_TOKEN" ]; then
|
||||
GH_AUTH_METHOD="curl"
|
||||
fi
|
||||
elif [ -f "$HOME/.git-credentials" ] && grep -q "github.com" "$HOME/.git-credentials" 2>/dev/null; then
|
||||
GITHUB_TOKEN=$(grep "github.com" "$HOME/.git-credentials" | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
|
||||
if [ -n "$GITHUB_TOKEN" ]; then
|
||||
GH_AUTH_METHOD="curl"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Resolve username for curl method
|
||||
if [ "$GH_AUTH_METHOD" = "curl" ] && [ -z "$GH_USER" ]; then
|
||||
GH_USER=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
https://api.github.com/user 2>/dev/null \
|
||||
| python3 -c "import sys,json; print(json.load(sys.stdin).get('login',''))" 2>/dev/null)
|
||||
fi
|
||||
|
||||
# --- Repo detection (if inside a git repo with a GitHub remote) ---
|
||||
|
||||
GH_OWNER=""
|
||||
GH_REPO=""
|
||||
GH_OWNER_REPO=""
|
||||
|
||||
_remote_url=$(git remote get-url origin 2>/dev/null)
|
||||
if [ -n "$_remote_url" ] && echo "$_remote_url" | grep -q "github.com"; then
|
||||
GH_OWNER_REPO=$(echo "$_remote_url" | sed -E 's|.*github\.com[:/]||; s|\.git$||')
|
||||
GH_OWNER=$(echo "$GH_OWNER_REPO" | cut -d/ -f1)
|
||||
GH_REPO=$(echo "$GH_OWNER_REPO" | cut -d/ -f2)
|
||||
fi
|
||||
unset _remote_url
|
||||
|
||||
# --- Summary ---
|
||||
|
||||
echo "GitHub Auth: $GH_AUTH_METHOD"
|
||||
[ -n "$GH_USER" ] && echo "User: $GH_USER"
|
||||
[ -n "$GH_OWNER_REPO" ] && echo "Repo: $GH_OWNER_REPO"
|
||||
[ "$GH_AUTH_METHOD" = "none" ] && echo "⚠ Not authenticated — see github-auth skill"
|
||||
|
||||
export GH_AUTH_METHOD GITHUB_TOKEN GH_USER GH_OWNER GH_REPO GH_OWNER_REPO
|
||||
@@ -0,0 +1,35 @@
|
||||
## Bug Description
|
||||
|
||||
<!-- Clear, concise description of the bug -->
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!-- What should happen -->
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
<!-- What actually happens -->
|
||||
|
||||
## Environment
|
||||
|
||||
- OS:
|
||||
- Version/Commit:
|
||||
- Python version:
|
||||
- Browser (if applicable):
|
||||
|
||||
## Error Output
|
||||
|
||||
<!-- Paste relevant error messages, stack traces, or logs -->
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
## Additional Context
|
||||
|
||||
<!-- Screenshots, related issues, workarounds discovered, etc. -->
|
||||
@@ -0,0 +1,31 @@
|
||||
## Feature Description
|
||||
|
||||
<!-- What do you want? -->
|
||||
|
||||
## Motivation
|
||||
|
||||
<!-- Why would this be useful? What problem does it solve? -->
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
<!-- How could it work? Include API sketches, CLI examples, or mockups if helpful -->
|
||||
|
||||
```
|
||||
# Example usage
|
||||
```
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
<!-- Other approaches and why they're less ideal -->
|
||||
|
||||
-
|
||||
|
||||
## Scope / Effort Estimate
|
||||
|
||||
<!-- How big is this? What areas of the codebase would it touch? -->
|
||||
|
||||
Small / Medium / Large — <!-- explanation -->
|
||||
|
||||
## Additional Context
|
||||
|
||||
<!-- Links to similar features in other tools, relevant discussions, etc. -->
|
||||
@@ -0,0 +1,35 @@
|
||||
## Bug Description
|
||||
|
||||
<!-- What was happening? -->
|
||||
|
||||
Fixes #
|
||||
|
||||
## Root Cause
|
||||
|
||||
<!-- What was causing the bug? -->
|
||||
|
||||
## Fix
|
||||
|
||||
<!-- What does this PR change to fix it? -->
|
||||
|
||||
-
|
||||
|
||||
## How to Verify
|
||||
|
||||
<!-- Steps a reviewer can follow to confirm the fix -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Test Plan
|
||||
|
||||
- [ ] Added regression test for this bug
|
||||
- [ ] Existing tests still pass
|
||||
- [ ] Manual verification of the fix
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
<!-- Could this fix break anything else? What's the blast radius? -->
|
||||
|
||||
Low / Medium / High — <!-- explanation -->
|
||||
@@ -0,0 +1,33 @@
|
||||
## Summary
|
||||
|
||||
<!-- 1-3 bullet points describing what this PR does -->
|
||||
|
||||
-
|
||||
|
||||
## Motivation
|
||||
|
||||
<!-- Why is this change needed? Link to issue if applicable -->
|
||||
|
||||
Closes #
|
||||
|
||||
## Changes
|
||||
|
||||
<!-- Detailed list of changes made -->
|
||||
|
||||
-
|
||||
|
||||
## Test Plan
|
||||
|
||||
<!-- How was this tested? Checklist of verification steps -->
|
||||
|
||||
- [ ] Unit tests pass (`pytest`)
|
||||
- [ ] Manual testing of new functionality
|
||||
- [ ] No regressions in existing behavior
|
||||
|
||||
## Screenshots / Examples
|
||||
|
||||
<!-- If UI changes or new output, show before/after -->
|
||||
|
||||
## Notes for Reviewers
|
||||
|
||||
<!-- Anything reviewers should pay special attention to -->
|
||||
Reference in New Issue
Block a user