Panda CMS
Better websites, on Rails. 🐼
A modern, modular content management system built for Ruby on Rails. Simple enough for small sites, powerful enough to scale. No bloat, just the features you need.
Panda CMS has been in production since March 2024 and is actively maintained by Otaina.
Website · Pro Features · Managed Hosting
Usage
New applications
To create a new Rails app, run the command below, replacing demo with the name of the application you want to create:
rails new demo $(curl -fsSL https://raw.githubusercontent.com/tastybamboo/generator/main/.railsrc) -m https://raw.githubusercontent.com/tastybamboo/generator/main/template.rb
cd into your directory (e.g. demo), then run bin/dev. A basic website has automatically been created for you at http://localhost:3000/
Visit http://localhost:3000/admin and sign in with GitHub — it works immediately in development using shared dev credentials (no OAuth app setup needed). As the first user, you'll automatically be given an administrator account.
For production, configure your own GitHub OAuth credentials in config/initializers/panda.rb — see Configuration below.
Existing applications
Add the gem to your Gemfile:
gem "panda-cms"Then run:
bundle install
rails generate panda:cms:install
bundle install
rails db:migrate
rails db:seedThe install generator will:
- Create
config/initializers/panda.rb(if it doesn't exist) - Enable GitHub OAuth and Developer Login authentication
- Add
omniauth-githubto your Gemfile (hence the secondbundle install) - Copy all required database migrations
Start your server with bin/dev and visit /admin. In development, GitHub OAuth works immediately using shared dev credentials — just click GitHub and authorize. A Developer Login (name/email form) is also available as a fallback.
For production, add your GitHub OAuth credentials:
rails credentials:editgithub:
client_id: your_client_id
client_secret: your_client_secretThe CMS engine auto-mounts itself — no changes to config/routes.rb are needed.
Configuration
All Panda configuration is managed in config/initializers/panda.rb. The install generator creates this file with sensible defaults:
# config/initializers/panda.rb
Panda::Core.configure do |config|
config.admin_path = "/admin"
config.login_page_title = "Admin"
config.admin_title = "MyApp Admin"
config.authentication_providers = {
github: {
enabled: true,
name: "GitHub",
client_id: if Rails.env.development?
"Ov23liFMGyVvRrpuvyTT" # Shared Panda dev app (localhost:3000 only)
else
Rails.application.credentials.dig(:github, :client_id)
end,
client_secret: if Rails.env.development?
"394a7024d7dd9c0ee0c8540768331d59d9e22477"
else
Rails.application.credentials.dig(:github, :client_secret)
end
},
developer: {
enabled: true,
name: "Developer Login"
}
}
config.session_token_cookie = :panda_session
config.user_class = "Panda::Core::User"
config.user_identity_class = "Panda::Core::UserIdentity"
end
Panda::CMS.configure do |config|
config.require_login_to_view = false
endAuthentication providers
In development, GitHub OAuth works out of the box using shared dev credentials that are restricted to localhost:3000. No OAuth app setup needed — just click "GitHub" and authorize.
The Developer provider is also available in development as a fallback. It shows a simple form to enter a name and email.
For production, replace the dev credentials with your own via rails credentials:edit. Supported providers:
| Provider | Gem | Config key |
|---|---|---|
| GitHub | omniauth-github |
github |
omniauth-google-oauth2 |
google_oauth2 |
|
| Microsoft | omniauth-microsoft_graph |
microsoft_graph |
Providers without valid credentials are automatically skipped at boot.
See the Configuration Documentation for detailed information on all available settings.
Engine Mounting
The Panda CMS engine automatically mounts itself via an after_initialize hook. You do not need to manually add mount Panda::CMS::Engine => "/" to your routes file. The engine will:
- Mount itself at the root path
- Add admin routes under the configured admin path (e.g.,
/admin/cmsor/manage/cms) - Set up a catch-all route for CMS pages (excluding admin paths)
The admin interface structure will be:
-
{admin_path}- Panda Core admin dashboard (authentication, profile) -
{admin_path}/cms- Panda CMS admin (pages, posts, menus, files)
Styling
Panda CMS does not compile or manage its own CSS. All admin interface styling is provided by Panda Core.
The CMS automatically loads Core's compiled stylesheet:
<link rel="stylesheet" href="/panda-core-assets/panda-core.css">Core's Rack middleware serves this file from the gem, so:
- ✅ No CSS copying or compilation needed
- ✅ Styles update automatically when Core updates
- ✅ Consistent design across all Panda gems
For details on customizing styles, development workflows, and troubleshooting, see docs/STYLING.md.
For CSS compilation (when contributing to styling), see Panda Core Asset Compilation Guide.
Gotchas
This is a non-exhaustive list (there will be many more):
- To date, this has only been tested with Rails 7.1, 7.2 and 8
- There may be conflicts if you're not using Tailwind CSS on the frontend. Please report this.
Contributing
We welcome contributions.
See our Contributing Guidelines
Testing
Panda CMS uses RSpec for testing.
Using Fixtures
We encourage using fixtures for tests instead of factories for consistent test data:
- Create fixture files in
spec/fixturesnamed after the model's table (e.g.,panda_cms_pages.yml) - Define records with unique names and their attributes
- Use helper methods to create test templates with mocked file validation
Example fixture format:
# spec/fixtures/panda_cms_pages.yml
home_page:
title: "Home"
path: "/"
panda_cms_template_id: <%= ActiveRecord::FixtureSet.identify(:page_template) %>
status: "active"
created_at: <%= Time.current %>
updated_at: <%= Time.current %>Example test using fixtures:
# Access fixture using table name and record name
page = panda_cms_pages(:home_page)
expect(page.title).to eq("Home")When testing models with file validations or complex callbacks, use the helper methods in spec/models/panda/cms/page_spec.rb as a reference.
🚀 Running CI Locally
This project uses a deterministic CI environment, based on a single Docker
image (panda-cms-test). This ensures:
- identical Ruby/Node/Chrome versions everywhere
- no drift between local / Docker / GitHub Actions / act
- fast, stable, reproducible tests
There are three supported ways to run the full CI suite locally.
1. Run full CI via Docker Compose
bin/ci build # build the local test image
bin/ci local # run full CI stack locallyThis uses docker-compose.ci.yml and reproduces the entire GitHub Actions environment.
2. Run single RSpec execution in the CI container
bin/ci testThis mounts your project into the container and executes RSpec exactly as CI does.
3. Run GitHub Actions locally using act
Install act:
brew install actUse the project’s .actrc:
-P ubuntu-latest=ghcr.io/tastybamboo/panda-cms-test:local
--container-options "--shm-size=2gb"
Then run:
bin/ci actThis executes the real GitHub Actions workflow on your machine.
4. Continuous Integration on GitHub
GitHub Actions uses the same deterministic container image. See:
.github/workflows/ci.yml
5. Code Coverage
Coverage is produced per-suite (models, requests, libs, system) and merged
into a unified coverage/ directory via SimpleCov.
Artifacts are uploaded automatically on CI.
License
The gem is available as open source under the terms of the BSD-3-Clause License.
Copyright © 2024 - 2026, Otaina Limited.