Corral
Automatically clone and organise GitHub repositories by visibility and language.
Install
git clone https://github.com/sebastienrousseau/corral.git
cd corral
gh auth login
./corral <owner>
Then verify the output:
ls ~/Code/
Requires gh, git, and Go 1.25+. Works on macOS, Ubuntu/Debian, Fedora/RHEL, Arch, and WSL2.
Platform-specific prerequisites
macOS:
brew install go git gh
Ubuntu / Debian / WSL2:
sudo apt install golang git
Install gh separately — see the GitHub CLI install guide for your distribution.
Fedora / RHEL:
sudo dnf install golang git gh
WSL2 users: Run all commands inside your Linux distribution, not from PowerShell or CMD. The script works identically to native Linux.
Automation and cron
The tool is idempotent and non-interactive. Safe to run on a schedule:
# crontab -e
0 2 * * * /path/to/corral my-username
Existing repositories are skipped. Only new ones are cloned.
Overview
Most cloning tools dump every repository into a single flat directory. Finding anything means scrolling through hundreds of folders with no structure. Corral creates a clean, navigable local mirror — sorted by visibility and language — in one command. No config files.
~/Code/
├── Public/
│ ├── rust/
│ │ └── my-crate/
│ ├── typescript/
│ │ └── my-app/
│ └── other/
│ └── dotfiles/
└── Private/
└── python/
└── internal-tool/
- One command to clone and organise every repository from a user or organisation
- Safe to re-run at any time — new repos are cloned, and existing ones are pulled to their latest changes (unless
--no-sync is passed)
- Automatic migration from flat
~/Code/<Language>/ layouts to the new visibility-based structure
- Tested on macOS and Ubuntu with automated tests, 100% coverage, and signed commits.
Architecture
Run once or a hundred times, the directory tree converges on the same state.
graph TD
A[User Shell] --> B{Corral}
B --> C[Pre-flight: Git / GitHub API]
C -- Missing --> Z1[Exit: dependency error]
C -- OK --> D[GitHub API → Fetch Repos]
D -- Fails --> Z2[Exit: API error]
D -- OK --> E[Loop: each repo]
E --> F{Already cloned?}
F -- "Yes (default)" --> G1[git pull --rebase --autostash]
F -- "Yes + --no-sync" --> G2[Skip]
F -- No --> H{Legacy directory?}
H -- Yes --> I[Migrate to new layout]
H -- No --> J[git clone]
G1 & G2 & I & J --> E
E -- Done --> K[Remove empty legacy directories]
K --> L[Print summary]
Features
|
|
| Structured |
The only tool that sorts repositories into Public/ and Private/ trees, grouped by primary language |
| Idempotent |
Safe to re-run at any time — already-cloned repositories are skipped, only new ones are fetched |
| Migratory |
Flat ~/Code/<Language>/ layouts from earlier runs move into the new structure automatically |
| Platforms |
First-class support for macOS, Ubuntu/Debian, Fedora/RHEL, Arch, and Windows via WSL2 |
| Zero-config |
No YAML, no .env, no config files — pass the owner name and run |
| Fail-safe |
Pre-flight checks for git and API connectivity with clear error messages on failure |
| Production-grade |
100% test coverage, CI on Ubuntu and macOS, signed commits |
| Security |
All commits cryptographically signed (ED25519), CI actions pinned to immutable SHAs, Gitleaks secret scanning |
Usage
| Parameter |
Required |
Default |
Description |
owner |
Yes |
— |
GitHub username or organisation |
base_dir |
No |
$HOME/Code |
Root directory for the cloned tree |
limit |
No |
1000 |
Maximum repositories to fetch |
| Option |
Short |
Default |
Description |
--dry-run |
-n |
off |
Preview actions without making changes |
--help |
-h |
— |
Show help message |
--protocol |
-p |
https |
Clone protocol — ssh or https |
--no-sync |
— |
off |
Skip pulling latest changes for already-cloned repos |
Clone a personal account:
./corral my-username
Clone an organisation into a custom directory:
./corral my-org ~/Projects 500
Clone via SSH (key-based auth):
./corral --protocol ssh my-username
Skip pulling updates for existing clones:
./corral --no-sync my-username
Preview what would happen without making changes:
./corral --dry-run my-org
Private repositories require a gh token with appropriate access. Public repositories from any account are always available.
What's Included
Organisation and Layout
- Visibility sorting separates repositories into
Public/ and Private/ trees based on GitHub metadata. GitHub Enterprise INTERNAL repositories are securely routed to the Private/ tree.
- Language grouping places each repository under its primary language directory, normalised to lowercase
- Special characters are handled cleanly — C# becomes
csharp, C++ becomes cpp, spaces and slashes become underscores
- Null languages default to
other/ so every repository has a home
Legacy Migration
- Flat layouts from earlier versions (
~/Code/<Language>/<repo>) are detected and moved into the new <Visibility>/<Language>/<repo> structure
- Empty directories left behind after migration are removed automatically
- Existing clones are never overwritten or deleted — the script only adds, never subtracts
Troubleshooting
| Message |
Cause |
Solution |
ERROR: Bash 4+ is required |
macOS includes Bash 3.2 by default |
brew install bash |
ERROR: Required command 'gh' not found |
GitHub CLI is not installed |
See Install above |
ERROR: gh repo list failed |
Not authenticated, or the owner does not exist |
Run gh auth login and verify the owner name |
FAILED: owner/repo |
Network issue or private repo without token access |
Check connectivity and verify gh auth status |
| Script reports 0 repos |
No repositories visible to the current token |
Run gh repo list <owner> --limit 5 to verify |
\r: command not found (WSL2) |
Windows line endings in the script |
Run dos2unix corral.sh or re-clone with git config core.autocrlf input |
Frequently Asked Questions
- Can it back up private repositories? Yes. Any repository visible to the authenticated
gh token is cloned. PRIVATE and GitHub Enterprise INTERNAL repositories land in the Private/ tree.
- Does it work with organisations? Yes. Pass the organisation name as the first argument. Both user accounts and organisations are supported.
- What happens if a repository is deleted on GitHub? The local clone remains untouched. The script never deletes existing directories.
- What happens if I have uncommitted changes when it syncs? Your local uncommitted work is kept safe. Git automatically stashes your changes (
--autostash), rebases the latest commits from the remote branch, and then reapplies your stash. If there is a direct conflict, the sync is cleanly aborted for that specific repository.
- Does it work on Windows? Yes, through WSL2. Install a Linux distribution from the Microsoft Store, open its terminal, and run the script there. It behaves identically to native Linux.
- Does it work on macOS with the default shell? macOS ships with Bash 3.2. Run
brew install bash to get Bash 4+, then invoke the script with the Homebrew-installed Bash or add it to your $PATH.
- Is it safe to run on a schedule? Yes. The script is idempotent — existing repos are skipped, only new ones are cloned. No interactive prompts.
How It Compares
| Feature |
Corral |
ghorg |
ghcloneall |
Gist scripts |
| Organises by language |
Yes |
No |
No |
No |
| Organises by visibility |
Yes |
No |
No |
No |
| macOS, Linux, and WSL2 |
Yes (CI on both) |
Yes |
Linux only |
Varies |
| Zero install |
Yes (single bash file) |
Go binary or Docker |
Python + pip |
Copy-paste |
| Idempotent re-runs |
Yes |
Yes |
Yes |
No |
| Concurrent operations |
Yes |
Yes |
Yes |
No |
| Legacy layout migration |
Yes |
No |
No |
No |
| Test suite |
39 tests, CI on 2 OS |
Yes |
Limited |
None |
| Config required |
None |
YAML + env vars |
CLI flags + rc file |
Manual edits |
For security policy and vulnerability reporting, see SECURITY.md.
THE ARCHITECT ᛫ Sebastien Rousseau
THE ENGINE ᛞ EUXIS ᛫ Enterprise Unified Execution Intelligence System
License
Licensed under the GNU General Public License v3.0.
Back to Top
align="right">
Back to Top
-
License
Licensed under the GNU General Public License v3.0.
Back to Top
align="right">
Back to Top