{"id":35163914,"url":"https://github.com/ducks/denver","last_synced_at":"2026-05-21T02:31:54.670Z","repository":{"id":323421813,"uuid":"1093166146","full_name":"ducks/denver","owner":"ducks","description":"Discourse ENV managER - A CLI tool for managing multiple isolated Discourse development environments. Create, switch between, and test different plugin combinations without breaking your main setup.","archived":false,"fork":false,"pushed_at":"2025-11-15T01:07:14.000Z","size":63,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-31T07:04:08.367Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ducks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-10T02:05:50.000Z","updated_at":"2025-11-15T01:07:18.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ducks/denver","commit_stats":null,"previous_names":["ducks/denver"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ducks/denver","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdenver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdenver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdenver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdenver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ducks","download_url":"https://codeload.github.com/ducks/denver/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fdenver/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33286014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-20T15:12:43.734Z","status":"online","status_checked_at":"2026-05-21T02:00:07.181Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-28T19:03:27.871Z","updated_at":"2026-05-21T02:31:54.652Z","avatar_url":"https://github.com/ducks.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Denver\n\n**D**iscourse **ENV**ironments manag**ER**\n\nA CLI tool for managing multiple isolated Discourse development environments.\n\n## The Problem\n\nTesting Discourse core changes with different plugin combinations is painful.\nExisting tools (discourse-cp, dev containers) make it hard to maintain\nmultiple separate instances with different plugin sets.\n\n## The Solution\n\nDenver makes it easy to create, manage, and switch between multiple isolated\nDiscourse development environments. Uses git worktrees for instant environment\ncreation (less than 1 second after initial setup). Each environment gets its\nown branch, plugin set, and isolated working directory.\n\n## Prerequisites\n\nDenver requires PostgreSQL and Redis to be running locally. You can install and run them however you prefer (system packages, Docker, Homebrew, etc.).\n\n**PostgreSQL**: Must be running and accessible (default port 5432)\n**Redis**: Must be running and accessible (default port 6379)\n\nExample with Docker:\n```bash\ndocker run -d --name postgres -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust postgres:16\ndocker run -d --name redis -p 6379:6379 redis:7\n```\n\nOr use your system package manager, Homebrew, etc.\n\n## Installation\n\n**Download pre-built binary** (recommended):\n\nVisit the [releases page](https://github.com/ducks/denver/releases) and download the binary for your platform:\n- Linux (amd64, arm64)\n- macOS (Intel, Apple Silicon)\n\nMake it executable and move to your PATH:\n```bash\nchmod +x denver-*\nsudo mv denver-* /usr/local/bin/denver\n```\n\n**Or install with Go**:\n\n```bash\ngo install github.com/ducks/denver@latest\n```\n\n**Or build from source**:\n\n```bash\ngit clone https://github.com/ducks/denver.git\ncd denver\ngo build -o denver\n```\n\n## Quick Start\n\n```bash\n# Check prerequisites\ndenver doctor\n\n# Create a minimal environment (first run clones bare repo, takes a few minutes)\ndenver create minimal --profile base\n\n# Setup dependencies (one-time per environment)\ndenver setup minimal\n\n# Start Rails and Ember servers\ndenver start minimal\n\n# View logs\ndenver logs              # Rails logs\ndenver logs --ember      # Ember logs\n\n# Check status\ndenver status\n\n# List all environments\ndenver list\n\n# Create and switch to another environment\ndenver create yaks --profile base --plugin discourse-yaks\ndenver setup yaks\ndenver switch yaks          # Stops minimal, starts yaks\n\n# Stop servers\ndenver stop\n\n# Destroy environments when done\ndenver destroy minimal\ndenver destroy yaks\n```\n\n## How It Works\n\n**Git Worktrees**: Denver uses git worktrees to share a single bare repository\nacross all environments. The first `create` command clones discourse to\n`~/.denver/discourse.git` (takes a few minutes). Subsequent environments are\ncreated instantly as worktrees.\n\n**Benefits**:\n- First environment: ~3 minutes (one-time bare repo clone)\n- Additional environments: \u003c1 second\n- Disk savings: ~400MB per environment (shared .git)\n- Each environment gets its own branch (named after the environment)\n\n## Profiles\n\nProfiles define the base configuration for an environment. They live in\n`~/.denver/profiles/` and are written in YAML.\n\n### Example: base.yml\n\n```yaml\nname: Base\ndescription: Minimal Discourse environment with no plugins\n\nplugins: []\n\nsite_settings:\n  title: \"Discourse Local\"\n\nseed:\n  admin: true\n  sample_users: 5\n  sample_topics: 10\n```\n\n### Example: full.yml\n\n```yaml\nname: Full\ndescription: Full-featured Discourse with common plugins\n\nplugins:\n  - name: discourse-chat\n    repo: discourse/discourse-chat\n  - name: discourse-automation\n    repo: discourse/discourse-automation\n  - name: discourse-voting\n    repo: discourse/discourse-voting\n\nsite_settings:\n  title: \"Discourse Local (Full)\"\n  chat_enabled: true\n\nseed:\n  admin: true\n  sample_users: 20\n  sample_topics: 50\n```\n\n## Commands\n\n### create\n\nCreate a new environment from a profile.\n\n```bash\ndenver create \u003cname\u003e --profile \u003cprofile\u003e [flags]\n```\n\nFlags:\n- `--profile, -p`: Profile to use (required)\n- `--base`: Base branch to branch from (default: main)\n- `--plugin`: Add plugins beyond profile (repeatable)\n  - Simple name: `discourse-chat` (uses discourse org)\n  - Full path: `ducks/discourse-invite-stats` (custom org)\n  - With branch: `discourse-yaks:feature/new-stuff`\n- `--local`: Use local plugin path for development (repeatable)\n  - Format: `plugin-name:~/path/to/plugin`\n  - Symlinks directly to your local plugin directory\n  - Changes appear instantly without git push/pull\n\nExamples:\n\n```bash\n# Basic environment (creates branch \"yaks\" from main)\ndenver create yaks --profile base\n\n# Test a core PR (creates branch \"test-buttons\" from fix/button-refactor)\ndenver create test-buttons --profile base --base fix/button-refactor\n\n# Add plugins to base profile\ndenver create yaks-dev --profile base --plugin discourse-yaks\n\n# Add plugins from custom GitHub org\ndenver create invite-stats --profile base --plugin ducks/discourse-invite-stats\n\n# Use local plugin for active development\ndenver create frndr --profile base --local discourse-frndr:~/dev/discourse-frndr\n\n# Mix cloned and local plugins\ndenver create test --profile base --plugin discourse-chat --local discourse-yaks:~/dev/discourse-yaks\n\n# Full environment with core branch\ndenver create test-epic --profile epic-games --base my-pr\n```\n\n**Note**: Each environment gets a unique git branch named after the environment.\nThe `--base` flag specifies which branch to branch from (default: main).\n\n### setup\n\nInstall dependencies for an environment (run once after creating).\n\n```bash\ndenver setup \u003cname\u003e\n```\n\nThis runs:\n- `bundle install` (Ruby gems)\n- `pnpm install` (JavaScript dependencies)\n- `bundle exec rake db:create db:migrate` (database setup)\n\n### start\n\nStart Rails and Ember servers for an environment.\n\n```bash\ndenver start \u003cname\u003e\n```\n\nServers run on:\n- Rails: http://localhost:3000\n- Ember: http://localhost:4200\n\nOnly one environment can run at a time.\n\n### stop\n\nStop the currently running environment.\n\n```bash\ndenver stop\n```\n\n### sync\n\nUpdate discourse core and/or plugins by pulling latest changes from git.\n\n```bash\ndenver sync \u003cname\u003e [target]\n```\n\nTargets:\n- (none): Update discourse core and all remote plugins\n- `discourse`: Update only discourse core\n- `\u003cplugin-name\u003e`: Update only the specified plugin\n\nLocal plugins (created with `--local`) are skipped since they're symlinks to your development directories.\n\nExamples:\n\n```bash\n# Update everything (discourse + all remote plugins)\ndenver sync frndr\n\n# Update only discourse core\ndenver sync frndr discourse\n\n# Update specific plugin\ndenver sync frndr discourse-chat\n\n# Local plugin (will be skipped)\ndenver sync frndr discourse-yaks  # \"Plugin is local, skipping sync\"\n```\n\n### switch\n\nSwitch to a different environment (stops current, starts new).\n\n```bash\ndenver switch yaks\n```\n\nConvenience command that combines `denver stop` and `denver start`. If the target environment is already running, does nothing.\n\n### status\n\nShow the currently running environment.\n\n```bash\ndenver status\n```\n\n### logs\n\nView server logs for the running environment.\n\n```bash\ndenver logs           # Rails logs (last 100 lines)\ndenver logs -f        # Follow Rails logs\ndenver logs --ember   # Ember logs\ndenver logs --ember -f  # Follow Ember logs\n```\n\n### list\n\nList all environments.\n\n```bash\ndenver list\n```\n\nShows environment names, modification times, and which is currently running.\n\n### doctor\n\nCheck that all prerequisites are installed and running.\n\n```bash\ndenver doctor\n```\n\nChecks for:\n- git, ruby, bundler, node, pnpm\n- PostgreSQL and Redis availability\n\n### destroy\n\nDestroy an existing environment.\n\n```bash\ndenver destroy \u003cname\u003e\n```\n\nThis removes the environment directory, git worktree, and git branch. If the branch has uncommitted changes, you'll be prompted before deletion.\n\n## Directory Structure\n\n```\n~/.denver/\n├── discourse.git/          # Bare repo (shared across environments)\n├── profiles/\n│   ├── base.yml\n│   ├── full.yml\n│   └── epic-games.yml\n└── environments/\n    ├── minimal/\n    │   ├── discourse/      # Worktree (branch: minimal)\n    │   ├── plugins/        # Cloned plugins\n    │   │   └── discourse-chat/\n    │   └── .denver.yml     # Environment config\n    └── yaks/\n        ├── discourse/      # Worktree (branch: yaks)\n        │   └── plugins/\n        │       └── discourse-yaks -\u003e ~/dev/discourse-yaks  # Local plugin symlink\n        ├── plugins/\n        │   └── discourse-chat/  # Cloned plugin\n        └── .denver.yml\n```\n\n**Plugin Storage:**\n- **Cloned plugins**: Stored in `environments/\u003cname\u003e/plugins/` and symlinked into `discourse/plugins/`\n- **Local plugins** (`--local`): Symlinked directly from your local dev directory to `discourse/plugins/`\n\nThe `.denver.yml` file tracks which plugins are local vs cloned, so future\ncommands (like `denver sync`) can handle them appropriately.\n\n## Status\n\nVersion 20251109\n\n**Implemented:**\n- ✅ Profile loading from YAML\n- ✅ Git worktrees for fast environment creation (\u003c1 second)\n- ✅ Bare repo sharing (~400MB saved per environment)\n- ✅ Plugin cloning with symlinks\n- ✅ Command-line plugin additions (`--plugin`)\n- ✅ Custom GitHub org support (e.g., `ducks/discourse-invite-stats`)\n- ✅ Local plugin development (`--local` flag for instant updates)\n- ✅ Branch selection for discourse core\n- ✅ Environment setup (bundle, pnpm, database)\n- ✅ Server management (start, stop, status)\n- ✅ Log viewing (Rails and Ember)\n- ✅ List environments\n- ✅ Doctor command (prerequisite checking)\n- ✅ Smart environment destruction (prompts if branch has changes)\n\n**Coming soon:**\n- ⏳ Plugin branch management (`denver plugin` command)\n- ⏳ Database cloning from staging environments\n- ⏳ Nix shell auto-detection and wrapping\n- ⏳ Multi-environment support (auto port allocation)\n\n## Use Cases\n\n**Testing Core PRs**: Create environment with specific plugin set to test core\nchanges.\n\n```bash\ndenver create test-pr --profile epic-games --base fix/my-feature\n```\n\n**Plugin Development**: Work on plugins in isolation without affecting your\nmain dev environment.\n\n```bash\n# Clone plugin from GitHub\ndenver create yaks --profile base --plugin discourse-yaks\n\n# Or use local plugin for active development (instant updates)\ndenver create yaks --profile base --local discourse-yaks:~/dev/discourse-yaks\n```\n\nThe `--local` flag symlinks directly to your local plugin directory, so changes\nappear instantly without needing to commit/push to GitHub. Perfect for active\nplugin development.\n\n**Multiple Projects**: Maintain separate environments for different plugins or\nfeatures.\n\n```bash\ndenver create yaks --profile base --plugin discourse-yaks\ndenver create transit --profile base --plugin discourse-transit-tracker\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fducks%2Fdenver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fducks%2Fdenver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fducks%2Fdenver/lists"}