README
ΒΆ
Smyklot
GitHub App for automated PR approvals and merges based on CODEOWNERS
Overview
Smyklot is a GitHub App that automates pull request approvals and merges by validating permissions against your repository's CODEOWNERS file. Simply comment on a PR with commands or add emoji reactions, and Smyklot handles the rest.
Features
- CODEOWNERS-based permissions - Only repository owners defined in
.github/CODEOWNERScan approve/merge - Multiple command formats - Supports slash commands (
/approve), mentions (@smyklot approve), and bare commands (lgtm,merge) - Merge method control - Explicit
squashandrebasecommands with intelligent fallback formerge - Reaction-based commands - Use π to approve, π to merge, or β€οΈ to cleanup
- Cleanup command - Remove all bot reactions, approvals, and comments with a single command
- Approval deduplication - Prevents duplicate approvals with smart reaction handling
- Flexible configuration - Configure via
SMYKLOT_CONFIGJSON, individual variables, or environment variables - Emoji feedback - Get instant visual confirmation with β (success), β (error), or β οΈ (warning)
- Comment edit/delete handling - Reacts to command edits and removes reactions when commands are deleted
- Reaction removal tracking - Automatically removes approvals/merges when reactions are removed
- Multi-command support - Execute multiple commands in a single comment
- Security-first design - Follows GitHub Actions best practices with minimal permissions
- Zero external dependencies - Runs entirely on GitHub Actions
- TDD implementation - 130+ passing tests
Quick Start
Prerequisites
- GitHub repository with Actions enabled
.github/CODEOWNERSfile in your repository
Installation
Option 1: GitHub App (Recommended)
-
Install the Smyklot GitHub App on your repository
-
Create
.github/CODEOWNERS:* @username -
Comment on PRs with commands or add reactions
Option 2: Manual Workflow
Copy the workflow file to your repository:
cp .github/workflows/pr-commands.yaml your-repo/.github/workflows/
Usage
Comment Commands
Smyklot responds to these commands in PR comments:
| Command | Aliases | Format | Description |
|---|---|---|---|
approve |
lgtm, accept |
/approve, @smyklot approve, approve |
Approve the pull request |
merge |
- | /merge, @smyklot merge, merge |
Merge the pull request (with fallback) |
squash |
- | /squash, @smyklot squash, squash |
Squash merge the pull request |
rebase |
- | /rebase, @smyklot rebase, rebase |
Rebase merge the pull request |
unapprove |
- | /unapprove, @smyklot unapprove |
Remove approval |
cleanup |
- | /cleanup, @smyklot cleanup, cleanup |
Remove all bot reactions, approvals, and comments |
help |
- | /help, @smyklot help |
Show help information |
Command Formats:
- Slash commands:
/approve,/merge,/squash,/rebase,/unapprove,/cleanup,/help - Mention commands:
@smyklot approve,@smyklot merge,@smyklot squash,@smyklot rebase,@smyklot unapprove,@smyklot cleanup - Bare commands:
approve,accept,lgtm,merge,squash,rebase,cleanup(exact match only)
All commands are case-insensitive.
Reaction Commands
| Reaction | Action |
|---|---|
| π | Approve the pull request |
| π | Merge the pull request |
| β€οΈ | Cleanup (remove all bot reactions, approvals, and comments) |
Note: Reactions must be added to the PR description, not to comments. Removing a reaction will automatically undo the corresponding action (remove approval/merge labels).
Multiple Commands
You can use multiple non-contradicting commands in a single comment:
/approve
/merge
or
lgtm merge
Commands will be executed in order: approve first, then merge.
Examples
Approving a PR
Any of these will approve the PR:
/approve
@smyklot approve
lgtm
Or add a π reaction to the PR description.
Merging a PR
Merge with default method (with fallback to squash/rebase if merge commits disallowed):
/merge
Or add a π reaction to the PR description.
Squash Merging
/squash
Rebase Merging
/rebase
Removing Approval
/unapprove
Or remove your π reaction from the PR description.
Cleanup
Remove all bot reactions, approvals, and comments:
/cleanup
Or add a β€οΈ reaction to the PR description.
Note: Cleanup cannot be combined with other commands.
Configuration
CODEOWNERS Setup
Create .github/CODEOWNERS in your repository:
# Global owners can approve/merge any PR
* @username1 @username2
Currently only global owners (* pattern) are supported. Path-specific owners will be added in Phase 2.
Bot Configuration
Smyklot can be configured via repository variables (Settings β Secrets and variables β Actions β Variables).
Configuration Precedence: CLI flags > Environment variables > Repository variables > Defaults
Option 1: Full JSON Configuration (Recommended)
Set a SMYKLOT_CONFIG repository variable with your complete configuration:
{
"quiet_success": false,
"quiet_reactions": false,
"quiet_pending": false,
"allowed_commands": ["approve", "merge"],
"command_aliases": {
"ok": "approve",
"ship": "merge"
},
"command_prefix": "/",
"disable_mentions": false,
"disable_bare_commands": false,
"disable_unapprove": false,
"disable_reactions": false,
"disable_deleted_comments": false,
"allow_self_approval": false
}
Option 2: Individual Variables
Configure individual settings via repository variables or environment variables with SMYKLOT_ prefix:
| Variable | Type | Default | Description |
|---|---|---|---|
SMYKLOT_QUIET_SUCCESS |
boolean | false |
Disable success feedback comments |
SMYKLOT_QUIET_REACTIONS |
boolean | false |
Disable reaction-based approval/merge comments |
SMYKLOT_QUIET_PENDING |
boolean | false |
Disable pending CI comments (reactions only for "merge after CI") |
SMYKLOT_ALLOWED_COMMANDS |
list | all | Limit which commands are allowed |
SMYKLOT_COMMAND_ALIASES |
map | default | Define custom command aliases |
SMYKLOT_COMMAND_PREFIX |
string | / |
Custom command prefix |
SMYKLOT_DISABLE_MENTIONS |
boolean | false |
Disable mention commands |
SMYKLOT_DISABLE_BARE_COMMANDS |
boolean | false |
Disable bare commands |
SMYKLOT_DISABLE_UNAPPROVE |
boolean | false |
Disable unapprove command |
SMYKLOT_DISABLE_REACTIONS |
boolean | false |
Disable reaction-based approvals/merges |
SMYKLOT_DISABLE_DELETED_COMMENTS |
boolean | false |
Disable handling of deleted comments |
SMYKLOT_ALLOW_SELF_APPROVAL |
boolean | false |
Allow PR authors to approve their own PRs |
SMYKLOT_BOT_USERNAME |
string | smyklot[bot] |
Bot username for cleanup operations (GitHub App format: {app-slug}[bot]) |
Configuration Examples
Only show emoji reactions, no success comments:
# In workflow or as repository variable
env:
SMYKLOT_QUIET_SUCCESS: "true"
Result: User sees only β reaction, no "PR Approved" comment.
Use ! instead of / for commands:
env:
SMYKLOT_COMMAND_PREFIX: "!"
Users can now use !approve and !merge.
Create shortcuts for commands:
env:
SMYKLOT_COMMAND_ALIASES: '{"app":"approve","a":"approve","m":"merge"}'
Users can use /app, /a, or /m as shortcuts.
Disable comment-based commands, only allow reactions:
{
"disable_mentions": true,
"disable_bare_commands": true,
"command_prefix": "disabled"
}
Only π and π reactions will work.
Don't remove approvals/merges when reactions are removed:
env:
SMYKLOT_DISABLE_DELETED_COMMENTS: "true"
β οΈ Not recommended for production - Allow PR authors to approve their own PRs:
env:
SMYKLOT_ALLOW_SELF_APPROVAL: "true"
or via JSON:
{
"allow_self_approval": true
}
Security Note: By default, Smyklot prevents self-approval to enforce separation of duties. Only enable this in development/testing environments.
Architecture
How It Works
- User comments a command or adds a reaction on a PR
- GitHub triggers
issue_commentwebhook pr-commands.yamlworkflow starts- Smyklot:
- Parses the command (supports slash, mention, and bare formats)
- Or processes reactions (π for approve, π for merge, β€οΈ for cleanup)
- Validates command combinations (cleanup cannot be combined with others)
- Checks for duplicate approvals (prevents re-approving for both commands and reactions)
- Fetches
.github/CODEOWNERSvia GitHub API - Checks user permissions
- Calls GitHub API to approve/merge/cleanup
- For merge: tries specified method or falls back (merge β squash β rebase)
- Posts reactions and feedback
- On comment edit/delete or reaction removal, updates accordingly
Permission System
Phase 1 (Current):
- Only global owners (
* @username) are supported - Global owners can approve/merge any PR
- Reaction-based approvals/merges with tracking
- Self-approval prevention (configurable, disabled by default)
- Fail-closed CODEOWNERS parsing (returns error if file is corrupted)
Phase 2 (Planned):
- Path-specific ownership patterns
- Scoped permissions based on changed files
- Team support (
@org/team-name) - Required approvals count
Security
Smyklot implements defense-in-depth security practices:
Input Validation:
- Comment body length validation (max 10KB) - prevents DoS attacks
- Repository owner/name format validation - prevents path traversal
- All inputs passed via environment variables (no shell interpolation)
API Security:
- Parameterized GraphQL queries - prevents injection attacks
- HTTP client timeout (30s) - prevents hung requests
- Exponential backoff retry logic - handles rate limiting gracefully
- Connection pooling - optimizes resource usage
Access Control:
- CODEOWNERS-based authorization with fail-closed parsing
- Self-approval prevention (configurable, disabled by default)
- Minimal workflow permissions (contents: read, pull-requests: write)
- Token-based authentication via GitHub App
Data Protection:
- Sensitive data sanitization in logs (tokens, keys, secrets redacted)
- Maximum CODEOWNERS file size (1MB) - prevents memory exhaustion
- No repository checkout required (CODEOWNERS fetched via API)
Supply Chain Security:
- Actions pinned by commit digest
- Go dependencies verified (
go mod verify) - Docker images use minimal base (
FROM scratch)
Development
Requirements
Setup
# Clone repository
git clone https://github.com/smykla-labs/smyklot.git
cd smyklot
# Install tools
mise install
# Download dependencies
go mod download
# Run tests
task test
Project Structure
smyklot/
βββ cmd/
β βββ github-action/ # GitHub Actions entrypoint
βββ pkg/
β βββ commands/ # Command parser (slash, mention, bare)
β βββ config/ # Configuration management (Viper)
β βββ feedback/ # User feedback system (reactions, comments)
β βββ github/ # GitHub API client
β βββ permissions/ # CODEOWNERS parser & permission checker
βββ .github/workflows/ # GitHub Actions workflows
βββ .goreleaser.yml # GoReleaser config for releases
βββ .mise.toml # Tool versions
βββ Dockerfile # Docker image for GitHub Actions
βββ Taskfile.yaml # Task automation
βββ go.mod # Go module definition
Available Tasks
task # Show available tasks
task test # Run all tests with coverage
task test:unit # Run unit tests only
task lint # Run all linters
task build # Build binaries
task clean # Clean build artifacts
Testing
All tests use Ginkgo/Gomega BDD framework:
# Run all tests
task test
# Run specific package
ginkgo -r pkg/commands/
# Watch mode for TDD
ginkgo watch -r
Current test coverage: 130+ tests passing
- 52+ command parser tests
- 12 CODEOWNERS parser tests
- 30 permission checker tests
- 30 feedback system tests
- 18+ GitHub client tests
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feat/amazing-feature) - Write tests first (TDD)
- Implement the feature
- Ensure all checks pass:
task lint && task test - Commit with conventional commits (
feat:,fix:,docs:, etc.) - Push to your fork
- Open a pull request
Roadmap
Phase 1: GitHub Actions Bot β
- Command parser (slash, mention, bare)
- Multi-command support
- Merge method commands (merge, squash, rebase)
- Merge method fallback (merge β squash β rebase)
- Cleanup command (remove all bot reactions, approvals, comments)
- Approval deduplication (prevent duplicate approvals)
- Reaction-based approvals/merges/cleanup (π, π, β€οΈ)
- Reaction removal tracking
- Comment edit/delete handling
- CODEOWNERS parser (global owners)
- Permission checker
- GitHub API client
- Feedback system (emoji + comments)
- Configuration system (Viper)
- GitHub Actions workflows
- Docker-based GitHub Action
- Documentation
Phase 2: Enhanced Permissions (Planned)
- Path-specific ownership patterns
- Scoped approval requirements based on changed files
- Team support in CODEOWNERS (
@org/team-name) - Self-approval prevention (configurable)
- Required approvals count
Phase 3: Kubernetes Deployment (Future)
Prerequisites (Security Hardening):
- GraphQL injection prevention
- HTTP client timeout and connection pooling
- Rate limiting and retry logic
- Input validation
- Fail-closed CODEOWNERS parsing
Remaining Work:
- Refactor global mutable state to request-scoped parameters
- Add context.Context propagation throughout
- Implement HTTP webhook server
- Add concurrency tests with
-raceflag - Implement comprehensive audit logging
- Kubernetes deployment (Helm chart)
- Prometheus metrics
- Migration strategy
Estimated Effort: 18-30 days
Phase 4: Discord Integration (Future)
- Discord bot
- Unified command system
- Cross-platform notifications
- Status synchronization
License
MIT License - see LICENSE for details
Acknowledgments
Built with:
- Ginkgo - BDD testing framework
- Gomega - Matcher library
- Viper - Configuration management
- Cobra - CLI framework
- mise - Tool version manager
- Task - Task runner
- GoReleaser - Release automation
Made with β€οΈ by @bartsmykla
Directories
ΒΆ
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
github-action
command
Package main provides the GitHub Actions entrypoint for Smyklot bot.
|
Package main provides the GitHub Actions entrypoint for Smyklot bot. |
|
pkg
|
|
|
commands
Package commands provides command parsing for Smyklot PR comments
|
Package commands provides command parsing for Smyklot PR comments |
|
config
Package config provides configuration management for Smyklot using Viper
|
Package config provides configuration management for Smyklot using Viper |
|
feedback
Package feedback provides user-facing messages for Smyklot operations.
|
Package feedback provides user-facing messages for Smyklot operations. |
|
github
Package github provides a GitHub API client for Smyklot operations.
|
Package github provides a GitHub API client for Smyklot operations. |
|
permissions
Package permissions provides CODEOWNERS-based authorization for Smyklot.
|
Package permissions provides CODEOWNERS-based authorization for Smyklot. |