|
| 1 | +# OpenProject Coding Agent Instructions |
| 2 | + |
| 3 | +## Repository Overview |
| 4 | + |
| 5 | +**OpenProject** is a web-based, open-source project management software written in Ruby on Rails. It uses PostgreSQL for data persistence and supports features like project planning, task management, Agile/Scrum, time tracking, wikis, and forums. |
| 6 | + |
| 7 | +- **Size**: Large monorepo (~840MB, ~1M+ lines of code) |
| 8 | +- **History**: Originally forked from Redmine over a decade ago, evolved significantly as an independent project |
| 9 | +- **Backend**: Ruby 3.4.5, Rails ~8.0.3 |
| 10 | +- **Frontend**: Node.js 22.15.0, npm 10.1.0+, TypeScript |
| 11 | +- **Database**: PostgreSQL (required) |
| 12 | +- **Architecture**: Server-rendered HTML with Hotwire (Turbo + Stimulus). Legacy Angular components exist and are being migrated to custom elements. Uses GitHub's Primer Design System via ViewComponent. |
| 13 | +- **Editions**: OpenProject comes in Community and Enterprise editions |
| 14 | + - **Enterprise Edition**: Includes additional features like Single sign-on (OIDC & SAML), LDAP, Nextcloud integration, SCIM API, and more (requires token for development) |
| 15 | + - **BIM Edition**: Tailored for construction industry needs. Code in `modules/bim/`, docs in `docs/bim-guide/`. Existing instances can be switched to BIM edition. |
| 16 | + |
| 17 | +## Critical Setup Requirements |
| 18 | + |
| 19 | +### Ruby and Node Versions |
| 20 | +**ALWAYS verify versions before building:** |
| 21 | +- Ruby: `3.4.5` (see `.ruby-version`) |
| 22 | +- Node: `^22.15.0` (see `package.json` engines) |
| 23 | +- Bundler: Latest 2.x |
| 24 | + |
| 25 | +### Development Environment Options |
| 26 | + |
| 27 | +**Docker (Recommended for Quick Start)** |
| 28 | +```bash |
| 29 | +# ALWAYS run these commands in sequence: |
| 30 | +cp .env.example .env |
| 31 | +cp docker-compose.override.example.yml docker-compose.override.yml |
| 32 | +docker compose run --rm backend setup |
| 33 | +docker compose run --rm frontend npm install |
| 34 | +docker compose up -d backend |
| 35 | +# Access at http://localhost:3000 |
| 36 | +``` |
| 37 | + |
| 38 | +**Local Development Setup** |
| 39 | +```bash |
| 40 | +# Install dependencies (ALWAYS run in this order): |
| 41 | +bundle install # Install Ruby gems |
| 42 | +cd frontend && npm ci && cd .. # Install Node packages (use 'ci' not 'install' for reproducibility) |
| 43 | +bundle exec rake db:migrate # Setup database |
| 44 | +bundle exec rails openproject:plugins:register_frontend assets:export_locales |
| 45 | + |
| 46 | +# Start services (use bin/dev for all-in-one): |
| 47 | +bin/dev # Starts Rails, frontend dev server, and Good Job worker |
| 48 | +# OR manually: |
| 49 | +# Terminal 1: bundle exec rails server |
| 50 | +# Terminal 2: npm run serve |
| 51 | +# Terminal 3: bundle exec good_job start |
| 52 | +``` |
| 53 | + |
| 54 | +**Important**: The `config/database.yml` file MUST NOT exist when using Docker. Delete or rename it if present. |
| 55 | + |
| 56 | +## Building and Testing |
| 57 | + |
| 58 | +### Linting (Run Before Committing) |
| 59 | + |
| 60 | +**Ruby (Rubocop)** |
| 61 | +```bash |
| 62 | +bundle exec rubocop # Check all files |
| 63 | +bin/dirty-rubocop --uncommitted # Check only uncommitted changes |
| 64 | +bin/dirty-rubocop --uncommitted --force-exclusion {files} # Check specific files |
| 65 | +``` |
| 66 | + |
| 67 | +**JavaScript/TypeScript (ESLint)** |
| 68 | +```bash |
| 69 | +cd frontend |
| 70 | +npx eslint src/ # Lint all frontend code |
| 71 | +cd .. |
| 72 | +``` |
| 73 | + |
| 74 | +**ERB Templates (erb_lint)** |
| 75 | +```bash |
| 76 | +erb_lint {files} # Lint ERB template files |
| 77 | +``` |
| 78 | + |
| 79 | +**Install Git Hooks** (optional but recommended): |
| 80 | +```bash |
| 81 | +bundle exec lefthook install # Sets up pre-commit hooks for linting |
| 82 | +``` |
| 83 | + |
| 84 | +### Running Tests |
| 85 | + |
| 86 | +**Backend Tests (RSpec)** |
| 87 | +```bash |
| 88 | +# Run specific tests (ALWAYS preferred over running all tests): |
| 89 | +bundle exec rspec spec/models/user_spec.rb # Single file |
| 90 | +bundle exec rspec spec/models/user_spec.rb:42 # Single line |
| 91 | +bundle exec rspec spec/features # Directory |
| 92 | + |
| 93 | +# Run all tests (slow, ~40 minutes on CI): |
| 94 | +bundle exec rspec |
| 95 | + |
| 96 | +# Parallel execution (faster): |
| 97 | +bundle exec rake parallel:spec |
| 98 | + |
| 99 | +# With Docker: |
| 100 | +docker compose run --rm backend-test "bundle exec rspec spec/features/work_package_show_spec.rb" |
| 101 | +``` |
| 102 | + |
| 103 | +**Frontend Tests (Jasmine/Karma)** |
| 104 | +```bash |
| 105 | +cd frontend |
| 106 | +npm test # Run all frontend unit tests |
| 107 | +npm run test:ci # Run in CI mode (single run) |
| 108 | +cd .. |
| 109 | +``` |
| 110 | + |
| 111 | +**Debugging Failed GitHub Actions Tests** |
| 112 | +```bash |
| 113 | +# Extract and run all failed tests from CI: |
| 114 | +./script/github_pr_errors |
| 115 | +./script/github_pr_errors | xargs bundle exec rspec |
| 116 | + |
| 117 | +# Run flaky tests multiple times: |
| 118 | +./script/bulk_run_rspec spec/path/to/flaky_spec.rb |
| 119 | +``` |
| 120 | + |
| 121 | +### Running the Application Locally |
| 122 | + |
| 123 | +**Development Mode** |
| 124 | +```bash |
| 125 | +bin/dev # Uses Overmind or Foreman to start all services |
| 126 | +# Access at http://localhost:3000 |
| 127 | +``` |
| 128 | + |
| 129 | +**Individual Services** |
| 130 | +```bash |
| 131 | +bundle exec rails server # Rails backend (port 3000) |
| 132 | +npm run serve # Frontend dev server (proxied through Rails) |
| 133 | +bundle exec good_job start # Background job worker |
| 134 | +``` |
| 135 | + |
| 136 | +## Project Structure |
| 137 | + |
| 138 | +### Key Directories |
| 139 | +- `app/` - Rails application code (models, controllers, services, views, components) |
| 140 | + - `app/components/` - ViewComponent-based UI components (Ruby + ERB) |
| 141 | + - `app/contracts/` - Validation and authorization contracts |
| 142 | + - `app/controllers/` - Rails controllers |
| 143 | + - `app/models/` - ActiveRecord models |
| 144 | + - `app/services/` - Service objects (business logic) |
| 145 | + - `app/workers/` - Background job workers |
| 146 | +- `config/` - Rails configuration |
| 147 | + - `config/application.rb` - Application configuration |
| 148 | + - `config/locales/` - I18n translations |
| 149 | + - `config/routes.rb` - Rails routes |
| 150 | +- `db/` - Database migrations and seeds |
| 151 | +- `docker/` - Docker build contexts |
| 152 | +- `frontend/src/` - Frontend |
| 153 | + - `frontend/src/app/` - Angular modules, components, services (legacy Angular code) |
| 154 | + - `frontend/src/main.ts` - Angular Application bootstrap entry point |
| 155 | + - `frontend/src/react` - React components (currently only used for experimental BlockNote integration) |
| 156 | + - `frontend/src/stimulus` - Stimulus controllers, helpers |
| 157 | + - `frontend/src/turbo` - Turbo integration (e.g. custom Turbo Stream actions) |
| 158 | +- `lib/` - Ruby libraries and extensions |
| 159 | +- `lookbook/` - Lookbook component previews for ViewComponents (see https://github.com/lookbook-hq/lookbook) |
| 160 | +- `modules/` - OpenProject plugin modules |
| 161 | +- `spec/` - RSpec test suite |
| 162 | + - `spec/features/` - System/feature tests (Capybara) |
| 163 | + - `spec/models/` - Model unit tests |
| 164 | + - `spec/requests/` - API/integration tests |
| 165 | + - `spec/services/` - Service tests |
| 166 | + |
| 167 | +### Configuration Files |
| 168 | +- `.erb_lint.yml` - ERB template linting |
| 169 | +- `.rubocop.yml` - Ruby linting rules |
| 170 | +- `.ruby-version` - Ruby version (check this file for current version) |
| 171 | +- `docker-compose.yml` - Docker development environment |
| 172 | +- `frontend/eslint.config.mjs` - JavaScript/TypeScript linting |
| 173 | +- `Gemfile` / `Gemfile.lock` - Ruby dependencies |
| 174 | +- `lefthook.yml` - Git hooks configuration |
| 175 | +- `package.json` / `frontend/package.json` - Node.js dependencies |
| 176 | +- `Procfile.dev` - Services for `bin/dev` |
| 177 | + |
| 178 | +## GitHub Actions CI/CD |
| 179 | + |
| 180 | +### Main Workflows |
| 181 | +- **test-core.yml** - Main test suite (units + features, ~40 min, runs on all PRs) |
| 182 | +- **rubocop-core.yml** - Ruby linting (runs on all PRs with Ruby changes) |
| 183 | +- **eslint-core.yml** - JS/TS linting (runs on all PRs with JS/TS changes) |
| 184 | +- **test-frontend-unit.yml** - Frontend unit tests |
| 185 | +- **brakeman-scan-core.yml** - Security scanning |
| 186 | +- **codeql-scan-core.yml** - Code quality/security analysis |
| 187 | + |
| 188 | +### CI Requirements for Merge |
| 189 | +- All linting checks must pass (Rubocop, ESLint, erb_lint) |
| 190 | +- Test suite must be green |
| 191 | +- No security vulnerabilities introduced (Brakeman, CodeQL) |
| 192 | + |
| 193 | +**Skip CI**: Add `[ci skip]` to commit message to skip CI (use sparingly). |
| 194 | + |
| 195 | +## Common Issues and Workarounds |
| 196 | + |
| 197 | +### Database Configuration |
| 198 | +- **Issue**: Docker fails with "database.yml exists" |
| 199 | +- **Fix**: Delete or rename `config/database.yml` when using Docker |
| 200 | + |
| 201 | +### Memory Issues in Docker |
| 202 | +- **Issue**: Frontend container exits with status 137 |
| 203 | +- **Fix**: Increase Docker memory limit to at least 4GB |
| 204 | + |
| 205 | +### Test Failures on CI but Passing Locally |
| 206 | +- Run with `CI=true` environment variable (eager loads app) |
| 207 | +- Check for `OPENPROJECT_*` environment variables |
| 208 | +- Match the random seed: `bundle exec rspec --seed 18352` |
| 209 | +- Use `--bisect` to find order-dependent failures |
| 210 | +- View browser tests with `OPENPROJECT_TESTING_NO_HEADLESS=1` |
| 211 | + |
| 212 | +### Frontend Build Issues |
| 213 | +- **Issue**: "jQuery not defined", frontend asset errors, or blank page |
| 214 | +- **Fix**: Run `bin/setup_dev` to rebuild frontend completely |
| 215 | + |
| 216 | +### Parallel Test Failures |
| 217 | +- Tests run in parallel on CI with different random seeds per group |
| 218 | +- Check `tmp/parallel_runtime.log` for execution times |
| 219 | +- **Flaky specs**: Some tests may fail randomly; see `docs/development/running-tests/` for handling flaky tests |
| 220 | + - Use `script/bulk_run_rspec` to run tests multiple times to identify flaky behavior |
| 221 | + |
| 222 | +## Code Style Guidelines |
| 223 | + |
| 224 | +### Ruby |
| 225 | +- Follow [Ruby community style guide](https://github.com/bbatsov/ruby-style-guide) |
| 226 | +- Use service objects for complex business logic |
| 227 | + - Return results using the `ServiceResult` class (well-documented in codebase) |
| 228 | + - Some services use monads via [dry-monads](https://github.com/dry-rb/dry-monads) for result modeling |
| 229 | +- Use contracts for validation and authorization |
| 230 | +- Keep controllers thin, models focused |
| 231 | +- Document code units and patterns with [YARD](https://yardoc.org/) |
| 232 | +- Write tests for all new features (RSpec) |
| 233 | + - Unit tests for models, services, and other components |
| 234 | + - Feature specs use Capybara (with Cuprite and Selenium WebDriver) |
| 235 | + - Feature specs can use A11y selectors ([capybara_accessible_selectors](https://github.com/citizensadvice/capybara_accessible_selectors)), test IDs, or page objects (in `spec/support/pages/`) |
| 236 | + |
| 237 | +### Database Migrations |
| 238 | +- Follow Rails migration conventions |
| 239 | +- OpenProject implements migration "squashing" between major releases |
| 240 | +- See `docs/development/migrations/` for details on the squashing process |
| 241 | +- Migrations are consolidated to manage database changes across major versions |
| 242 | +- OpenProject does not currently aim for zero downtime migrations |
| 243 | + |
| 244 | +### JavaScript/TypeScript |
| 245 | +- **New development**: Use Hotwire (Turbo + Stimulus) with server-rendered HTML |
| 246 | +- **Legacy code**: Follow ESLint recommended rules (eslint, typescript-eslint, Angular ESLint) |
| 247 | +- Prefer TypeScript over JavaScript |
| 248 | +- **Design system**: Use GitHub's [Primer Design System](https://primer.style/product/) via ViewComponent |
| 249 | + - [primer_view_components](https://github.com/opf/primer_view_components) - OpenProject's fork of Primer Rails/ViewComponent |
| 250 | + - [openproject-octicons](https://github.com/opf/openproject-octicons) - OpenProject's fork of Primer Octicons |
| 251 | + - [commonmark-ckeditor-build](https://github.com/opf/commonmark-ckeditor-build) - Custom CKEditor build with CommonMark Markdown support |
| 252 | +- Write unit tests for components (Jasmine for legacy Angular, RSpec for ViewComponents) |
| 253 | + |
| 254 | +### Templates |
| 255 | +- Use ERB for server-rendered views |
| 256 | +- Use ViewComponents for reusable UI components |
| 257 | + - Document new ViewComponents with API/Yard docs and Lookbook previews |
| 258 | + - Lookbook deployed at: https://qa.openproject-edge.com/lookbook/ |
| 259 | + - See https://github.com/lookbook-hq/lookbook for Lookbook documentation |
| 260 | +- Lint with erb_lint before committing |
| 261 | + |
| 262 | +### Commit Messages |
| 263 | +- First line: < 72 characters |
| 264 | +- Blank line |
| 265 | +- Detailed description wrapped to 72 characters |
| 266 | +- Reference work packages when applicable |
| 267 | +- See [code review guidelines](docs/development/code-review-guidelines/) for more details |
| 268 | +- **Merge strategy**: Use "Merge pull request" (not squash) to retain commit history, except for single-commit PRs which can use "Rebase and merge" |
| 269 | + |
| 270 | +### Translations |
| 271 | +- OpenProject is a multilingual product with officially supported and community-supported languages |
| 272 | +- UI translations are managed via [Crowdin](https://crowdin.com/) |
| 273 | +- Don't modify translation files directly; contributions should go through Crowdin |
| 274 | + - Exception: Source translations in `**/config/locales/en.yml` can be modified directly |
| 275 | +- UI strings should never be hard-coded; always use translation keys for accessibility and internationalization |
| 276 | + |
| 277 | +## Performance Considerations |
| 278 | + |
| 279 | +### CI Timeouts |
| 280 | +- Main test suite: 40 minutes timeout |
| 281 | +- Individual jobs: varies by type |
| 282 | +- Use parallel execution when available |
| 283 | + |
| 284 | +### Build Times |
| 285 | +- Full Docker build: ~10-15 minutes (first time) |
| 286 | +- Bundle install: ~2-5 minutes |
| 287 | +- npm install: ~3-7 minutes |
| 288 | +- Database setup: ~1-2 minutes |
| 289 | +- Asset compilation: ~30-40 seconds |
| 290 | + |
| 291 | +## Important Commands Reference |
| 292 | + |
| 293 | +```bash |
| 294 | +# Setup |
| 295 | +bin/setup # Initial Rails setup (creates DB, runs migrations) |
| 296 | +bin/setup_dev # Full dev environment setup (backend + frontend) |
| 297 | + |
| 298 | +# Database |
| 299 | +bundle exec rake db:migrate # Run pending migrations |
| 300 | +bundle exec rake db:rollback # Rollback last migration |
| 301 | +bundle exec rake db:seed # Seed database with sample data |
| 302 | +bundle exec rake db:migrate:status # Check migration status |
| 303 | + |
| 304 | +# Testing |
| 305 | +bundle exec rspec # Run RSpec tests |
| 306 | +bundle exec rake parallel:spec # Run tests in parallel |
| 307 | +cd frontend && npm test # Run frontend tests |
| 308 | + |
| 309 | +# Linting |
| 310 | +bundle exec rubocop # Ruby linting |
| 311 | +cd frontend && npx eslint src/ # JavaScript/TypeScript linting |
| 312 | +erb_lint {files} # ERB template linting |
| 313 | + |
| 314 | +# Development |
| 315 | +bin/dev # Start all services |
| 316 | +bundle exec rails console # Rails console |
| 317 | +bundle exec rails routes # List all routes |
| 318 | + |
| 319 | +# Docker |
| 320 | +bin/compose setup # Setup Docker environment |
| 321 | +bin/compose start # Start Docker services |
| 322 | +bin/compose run # Run with backend in foreground |
| 323 | +bin/compose rspec {test_file} # Run tests in Docker |
| 324 | +``` |
| 325 | + |
| 326 | +## Trust These Instructions |
| 327 | + |
| 328 | +These instructions are comprehensive and validated. Only search for additional information if: |
| 329 | +1. You encounter an error not documented here |
| 330 | +2. You need specific implementation details for a feature |
| 331 | +3. The instructions appear outdated (e.g., version mismatches) |
| 332 | + |
| 333 | +For any issues, consult: |
| 334 | +- `docs/development/` - Development documentation |
| 335 | +- `docs/development/running-tests/` - Testing guide |
| 336 | +- `docs/development/code-review-guidelines/` - Code review standards |
| 337 | +- `CONTRIBUTING.md` - Contribution workflow |
0 commit comments