From 1ceecd567a4bb34f134ca59e8df42ea8433d16b7 Mon Sep 17 00:00:00 2001 From: Adrian Lansdown Date: Thu, 29 Jan 2026 17:54:19 +0000 Subject: [PATCH 1/4] Add AGENTS.md guidance Document repo setup, workflows, and guardrails for AI agents. --- AGENTS.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..f71534dea --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,76 @@ +# Project Overview +- Rails 7.1 monolith for the Raspberry Pi Code Editor API (REST + GraphQL), served at + `editor-api.raspberrypi.org`. Provides auth, project storage, and education features. +- Primary runtime via Docker; API listens on port 3009 in containers. + +## Repository Structure +- `app/` Rails application code (REST, GraphQL, jobs, views, admin). +- `config/` environment, initializers, Puma, CORS, credentials. +- `db/` migrations, seeds, schema helpers; `bin/db-sync/` for pulling Heroku data locally. +- `spec/` RSpec tests (Rails, GraphQL, feature/system). +- `bin/` developer scripts (`with-builder.sh`, `db-sync/*`, Rails binstubs). +- `lib/` supporting libraries, tasks, assets; `public/` static assets; `docker-compose.yml` + for local stack; `.circleci/` for CI; `.rubocop.yml` for style config. + +## Quickstart Commands +```bash +cp .env.example .env +docker-compose build +docker compose run --rm api rails db:setup +docker-compose up +# API available on http://localhost:3009 +``` + +## Development Workflow +- Prefer Docker compose; mounts project into `editor-api:builder` image with tmpfs for `tmp/`. +- Use `./bin/with-builder.sh ` for operations that modify Gemfile.lock (e.g. `bundle update`). +- Seeds (dev): `docker compose run --rm api rails projects:create_all` and + `docker compose run --rm api rails for_education:seed_a_school_with_lessons_and_students` + (others in README). +- DB sync (needs Heroku access): `./bin/db-sync/production-to-local.sh` or `staging-to-local.sh`. +- Background jobs use GoodJob; Procfile defines `worker: bundle exec good_job start --max-threads=8`. + +## Testing & CI +- Full suite: `docker-compose run api rspec` +- Single spec: `docker-compose run api rspec spec/path/to/spec.rb` +- CI (CircleCI): Ruby 3.2 images with Postgres 12 + Redis; steps include `bin/rails db:setup --trace`, + `ruby/rspec-test`, RuboCop via `ruby/rubocop-check`, coverage artifacts uploaded and posted via + `.circleci/record_coverage`. + +## Code Style & Conventions +- Ruby `~> 3.2.0`. +- RuboCop uses Raspberry Pi Foundation shared configs plus Rails/RSpec/GraphQL cops; many metrics and + line-length checks are relaxed. +- Variable numbers must be snake_case (allowed: `sha256`, `X-Hub-Signature-256`). +- Tests in RSpec (`spec/`), Jbuilder for JSON views, GraphQL types under `app/graphql/`. +- GoodJob for background processing; Puma configured via `config/puma.rb`; release hook runs migrations + then `rake projects:create_experience_cs_examples` (see Procfile). + +## Security & Safety Guardrails +- Never commit secrets: `.env`, `config/master.key`, AWS/Postmark tokens, Hydra/Profile secrets, + webhook secrets. Keep `.env.example` values as references only. +- DB sync scripts fetch production/staging data; run only if authorized and handle dumps securely. +- Generated/ignored paths: `log/`, `tmp/`, `storage/`, `coverage/`, `public/assets/`, `.bundle/`, + `docker-compose.override.yml` (gitignored); do not add noise from these. +- Webhooks and smee tunnel secrets live in env vars; avoid logging or sharing real values. + +## Common Tasks (add feature, add test, refactor, release/deploy if applicable) +- Run app locally: ensure `.env`, then `docker-compose up` (build + `rails db:setup` first). +- Lint: `docker-compose run --rm api bundle exec rubocop` (mirrors CI RuboCop check). +- Migrate: `docker compose run --rm api rails db:migrate` (release hook also migrates). +- Update gems: `./bin/with-builder.sh bundle update` (keeps builder image and lockfiles in sync). +- Seed dev data: commands in README (e.g. `rails projects:create_all`, + `rails for_education:seed_a_school_with_lessons_and_students`). +- Sync DB from Heroku: `./bin/db-sync/production-to-local.sh` or `staging-to-local.sh` (requires Heroku + CLI + app access). +- Release/deploy: Procfile release runs migrations then `rake projects:create_experience_cs_examples`; + confirm platform/trigger before running manually. > TODO: document official deploy pipeline and branch + triggers. + +## Further Reading (relative links) +- `README.md` +- `.circleci/config.yml` +- `.rubocop.yml` +- `.env.example` +- `Procfile` +- `bin/db-sync/load-local-db.sh` From 5e71aaca517ba70b8e53426df26b8d40e00005b6 Mon Sep 17 00:00:00 2001 From: Adrian Lansdown Date: Fri, 30 Jan 2026 15:55:22 +0000 Subject: [PATCH 2/4] Update AGENTS.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index f71534dea..787b9e514 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,7 +38,7 @@ docker-compose up `.circleci/record_coverage`. ## Code Style & Conventions -- Ruby `~> 3.2.0`. +- Ruby 3.2.3 (specified as `~> 3.2.0` in Gemfile). - RuboCop uses Raspberry Pi Foundation shared configs plus Rails/RSpec/GraphQL cops; many metrics and line-length checks are relaxed. - Variable numbers must be snake_case (allowed: `sha256`, `X-Hub-Signature-256`). From 5049f890f4aacf258be794f34d2caa63f8cd4832 Mon Sep 17 00:00:00 2001 From: Adrian Lansdown Date: Thu, 26 Feb 2026 11:10:54 +0000 Subject: [PATCH 3/4] Remove copilot instructions; update AGENTS.md Delete obsolete .github/copilot-instructions.md and rewrite AGENTS.md to consolidate architecture and onboarding information. --- .github/copilot-instructions.md | 42 ------------- AGENTS.md | 105 ++++++++++++-------------------- 2 files changed, 38 insertions(+), 109 deletions(-) delete mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 37aa223db..000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,42 +0,0 @@ -# Copilot instructions for editor-api - -This repo is a Rails 7.1 exposing REST and GraphQL APIs for the Raspberry Pi Foundation Code Editor and Code Editor for Education features. - -Architecture and boundaries -- HTTP surfaces: REST under `app/controllers/api/**` (responses via jbuilder in `app/views/api/**`) and GraphQL at `/graphql` (schema in `app/graphql/**`). -- Authentication: Browser/session via OmniAuth (OIDC to Hydra) in `config/initializers/omniauth.rb` and `AuthController`; API token via `Authorization: Bearer ` with lookup in `Identifiable#identify_user` → `User.from_token` → `HydraPublicApiClient`. -- Authorization: `cancancan` in `app/models/ability.rb`. Permissions differ for students/teachers/owners/admin. Use `load_and_authorize_resource` in controllers and `Types::ProjectType.authorized?` plus `GraphqlController` context `current_ability` for GraphQL. -- Domain: `Project` (+ `Component`) with attachments (`images/videos/audio` via Active Storage). Higher-level operations live under `lib/concepts/**` (e.g., `Project::Create`, `Project::Update`, `Project::CreateRemix`). Prefer calling these from controllers/mutations. -- Jobs: GoodJob (`config/initializers/good_job.rb`, Procfile `worker`). Admin UI is mounted at `/admin/good_job` and gated by `AuthenticationHelper#current_user.admin?`. -- Integrations: Profile API (`lib/profile_api_client.rb`) for schools/students and safeguarding flags; UserInfo API for user detail fan-out; GitHub GraphQL client in `lib/github_api.rb`; GitHub webhooks via `GithubWebhooksController` trigger `UploadJob` when `ref == ENV['GITHUB_WEBHOOK_REF']` and files under `*/code/` change. -- Storage/CORS: Active Storage uses S3 in non-dev; `config/initializers/cors.rb` and `lib/origin_parser.rb` parse `ALLOWED_ORIGINS`. `CorpMiddleware` sets CORP for Active Storage routes. - -Key conventions and patterns -- GraphQL context includes: `current_user`, `current_ability`, and `remix_origin` (see `GraphqlController`). Max depth/complexity guard rails in `EditorApiSchema`. -- GraphQL object IDs use GlobalID; locale fallback for projects via `ProjectLoader` in order `[requested, 'en', nil]`. -- Jbuilder responses: see `app/views/api/projects/show.json.jbuilder` for shape (components, media URLs via `rails_blob_url`, optional `parent`). -- Pagination for REST lists returns HTTP `Link` header (see `Api::ProjectsController#pagination_link_header`). -- Project rules: identifiers unique per locale; default component’s name/extension immutable on update; students cannot update `instructions` on school projects; creating a project within a school auto-builds a `SchoolProject`. -- Remix: `Project::CreateRemix` clones media/components, sets `remix_origin` from `request.origin` and clears `lesson_id`. -- Errors: domain ops return `OperationResponse` with `:error`; controllers return 4xx heads for common cases; GraphQL raises `GraphQL::ExecutionError`. Exceptions are reported to Sentry. - -Developer workflows (docker-first) -- Setup: copy `.env.example` → `.env`. Build with `docker-compose build`. Prepare DB: `docker compose run --rm api rails db:setup`. -- Run: `docker-compose up` (API on http://localhost:3009). GraphiQL available in non-production at `/graphql`. -- Tests: `docker-compose run api rspec` (or pass a spec path). Bullet, WebMock, and RSpec rails are configured in `spec/rails_helper.rb`. -- Seeds: run `projects:create_all` and `for_education:*` Rake tasks (examples in README). Experience CS examples auto-run on release (see Procfile `release`). -- DB sync: scripts in `bin/db-sync/*` pull Heroku backups into your local Docker DB and reset Active Storage to `local`. -- Gems: update inside the builder image with `./bin/with-builder.sh bundle update`. - -Important env vars (see `.env.example`) -- Postgres: `POSTGRES_HOST/DB/USER/PASSWORD`. Hydra/identity: `HYDRA_PUBLIC_URL`, `HYDRA_PUBLIC_API_URL`, `HYDRA_PUBLIC_TOKEN_URL`, `HYDRA_CLIENT_ID/SECRET`, `IDENTITY_URL`, `HOST_URL`. -- External APIs: `USERINFO_API_URL/KEY`, `PROFILE_API_KEY`. Webhooks: `GITHUB_WEBHOOK_SECRET`, `GITHUB_WEBHOOK_REF`, optional `GITHUB_AUTH_TOKEN` for GitHub GraphQL. -- CORS/storage: `ALLOWED_ORIGINS`, `AWS_*` (S3). Local auth/dev: `BYPASS_OAUTH=true` to stub identity; `SMEE_TUNNEL` for local webhook relay. - -Examples to follow -- REST: `GET /api/projects/:id` resolves by identifier+locale (uses `ProjectLoader`); `POST /api/projects/:project_id/images` attaches files; `POST /api/projects/:project_id/remix` creates a remix (requires auth). -- GraphQL query snippet: `projects(userId: "") { edges { node { identifier name components { nodes { name extension } } } } }` and `project(identifier: "abc123") { name images { nodes { filename } } }`. - -Where to look first -- Routes: `config/routes.rb`. Auth: `config/initializers/omniauth.rb`, `app/helpers/authentication_helper.rb`, `app/controllers/concerns/identifiable.rb`. -- Permissions: `app/models/ability.rb`. Domain ops: `lib/concepts/**`. Data models: `app/models/**`. API views: `app/views/api/**`. GraphQL types/mutations: `app/graphql/**`. \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 787b9e514..212e501ba 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,76 +1,47 @@ # Project Overview -- Rails 7.1 monolith for the Raspberry Pi Code Editor API (REST + GraphQL), served at - `editor-api.raspberrypi.org`. Provides auth, project storage, and education features. -- Primary runtime via Docker; API listens on port 3009 in containers. - -## Repository Structure -- `app/` Rails application code (REST, GraphQL, jobs, views, admin). -- `config/` environment, initializers, Puma, CORS, credentials. -- `db/` migrations, seeds, schema helpers; `bin/db-sync/` for pulling Heroku data locally. -- `spec/` RSpec tests (Rails, GraphQL, feature/system). -- `bin/` developer scripts (`with-builder.sh`, `db-sync/*`, Rails binstubs). -- `lib/` supporting libraries, tasks, assets; `public/` static assets; `docker-compose.yml` - for local stack; `.circleci/` for CI; `.rubocop.yml` for style config. - -## Quickstart Commands +- Rails 7.1 monolith for the Raspberry Pi Code Editor API (REST + GraphQL), served at `editor-api.raspberrypi.org`. +- Primary runtime via Docker; API listens on port 3009. + +## Architecture +- **REST** under `app/controllers/api/**` with Jbuilder views in `app/views/api/**`; **GraphQL** at `/graphql` (schema in `app/graphql/**`). +- **Auth**: Browser/session via OmniAuth (OIDC to Hydra); API token via `Authorization: Bearer` with `Identifiable#identify_user` → `User.from_token` → `HydraPublicApiClient`. +- **Authorization**: cancancan in `app/models/ability.rb`. Use `load_and_authorize_resource` in controllers; GraphQL uses `Types::ProjectType.authorized?` and `current_ability` in context. +- **Domain**: `Project` (+ `Component`) with Active Storage attachments. Domain operations in `lib/concepts/**` (e.g. `Project::Create`, `Project::CreateRemix`). Prefer calling these from controllers/mutations. +- **Jobs**: GoodJob (`bundle exec good_job start --max-threads=8`). Admin UI at `/admin/good_job`. +- **Integrations**: Profile API (`lib/profile_api_client.rb`), UserInfo API, GitHub GraphQL (`lib/github_api.rb`), GitHub webhooks via `GithubWebhooksController`. +- **Storage/CORS**: Active Storage uses S3 in non-dev. CORS via `config/initializers/cors.rb` and `lib/origin_parser.rb`. `CorpMiddleware` sets CORP for Active Storage routes. + +## Key Conventions +- GraphQL context: `current_user`, `current_ability`, `remix_origin`. Object IDs use GlobalID. Locale fallback via `ProjectLoader`: `[requested, 'en', nil]`. +- REST pagination returns HTTP `Link` header (see `Api::ProjectsController#pagination_link_header`). +- Project rules: identifiers unique per locale; default component name/extension immutable on update; students cannot update `instructions` on school projects; creating a project in a school auto-builds `SchoolProject`. +- Remix: `Project::CreateRemix` clones media/components, sets `remix_origin`, clears `lesson_id`. +- Errors: domain ops return `OperationResponse` with `:error`; controllers return 4xx heads; GraphQL raises `GraphQL::ExecutionError`. Exceptions reported to Sentry. +- snake_case for variable numbers (exceptions: `sha256`, `X-Hub-Signature-256`). + +## Quickstart ```bash cp .env.example .env -docker-compose build +docker compose build docker compose run --rm api rails db:setup -docker-compose up -# API available on http://localhost:3009 +docker compose up ``` -## Development Workflow -- Prefer Docker compose; mounts project into `editor-api:builder` image with tmpfs for `tmp/`. -- Use `./bin/with-builder.sh ` for operations that modify Gemfile.lock (e.g. `bundle update`). -- Seeds (dev): `docker compose run --rm api rails projects:create_all` and - `docker compose run --rm api rails for_education:seed_a_school_with_lessons_and_students` - (others in README). -- DB sync (needs Heroku access): `./bin/db-sync/production-to-local.sh` or `staging-to-local.sh`. -- Background jobs use GoodJob; Procfile defines `worker: bundle exec good_job start --max-threads=8`. - -## Testing & CI -- Full suite: `docker-compose run api rspec` -- Single spec: `docker-compose run api rspec spec/path/to/spec.rb` -- CI (CircleCI): Ruby 3.2 images with Postgres 12 + Redis; steps include `bin/rails db:setup --trace`, - `ruby/rspec-test`, RuboCop via `ruby/rubocop-check`, coverage artifacts uploaded and posted via - `.circleci/record_coverage`. - -## Code Style & Conventions -- Ruby 3.2.3 (specified as `~> 3.2.0` in Gemfile). -- RuboCop uses Raspberry Pi Foundation shared configs plus Rails/RSpec/GraphQL cops; many metrics and - line-length checks are relaxed. -- Variable numbers must be snake_case (allowed: `sha256`, `X-Hub-Signature-256`). -- Tests in RSpec (`spec/`), Jbuilder for JSON views, GraphQL types under `app/graphql/`. -- GoodJob for background processing; Puma configured via `config/puma.rb`; release hook runs migrations - then `rake projects:create_experience_cs_examples` (see Procfile). +## Development +- Use `docker compose` for all commands; project mounts into `editor-api:builder` with tmpfs for `tmp/`. +- Seeds: `docker compose run --rm api rails projects:create_all` (see README for others). +- DB sync (needs Heroku CLI): `./bin/db-sync/production-to-local.sh` or `staging-to-local.sh`. -## Security & Safety Guardrails -- Never commit secrets: `.env`, `config/master.key`, AWS/Postmark tokens, Hydra/Profile secrets, - webhook secrets. Keep `.env.example` values as references only. -- DB sync scripts fetch production/staging data; run only if authorized and handle dumps securely. -- Generated/ignored paths: `log/`, `tmp/`, `storage/`, `coverage/`, `public/assets/`, `.bundle/`, - `docker-compose.override.yml` (gitignored); do not add noise from these. -- Webhooks and smee tunnel secrets live in env vars; avoid logging or sharing real values. +## Testing +- Full suite: `docker compose run --rm api rspec` +- Single spec: `docker compose run --rm api rspec spec/path/to/spec.rb` +- Lint: `docker compose run --rm api bundle exec rubocop` +- CI: CircleCI with Ruby 3.2, Postgres 12, Redis. -## Common Tasks (add feature, add test, refactor, release/deploy if applicable) -- Run app locally: ensure `.env`, then `docker-compose up` (build + `rails db:setup` first). -- Lint: `docker-compose run --rm api bundle exec rubocop` (mirrors CI RuboCop check). -- Migrate: `docker compose run --rm api rails db:migrate` (release hook also migrates). -- Update gems: `./bin/with-builder.sh bundle update` (keeps builder image and lockfiles in sync). -- Seed dev data: commands in README (e.g. `rails projects:create_all`, - `rails for_education:seed_a_school_with_lessons_and_students`). -- Sync DB from Heroku: `./bin/db-sync/production-to-local.sh` or `staging-to-local.sh` (requires Heroku - CLI + app access). -- Release/deploy: Procfile release runs migrations then `rake projects:create_experience_cs_examples`; - confirm platform/trigger before running manually. > TODO: document official deploy pipeline and branch - triggers. +## Where to Look First +- Routes: `config/routes.rb`. Auth: `config/initializers/omniauth.rb`, `app/helpers/authentication_helper.rb`, `app/controllers/concerns/identifiable.rb`. +- Permissions: `app/models/ability.rb`. Domain ops: `lib/concepts/**`. Models: `app/models/**`. GraphQL: `app/graphql/**`. -## Further Reading (relative links) -- `README.md` -- `.circleci/config.yml` -- `.rubocop.yml` -- `.env.example` -- `Procfile` -- `bin/db-sync/load-local-db.sh` +## Security +- Never commit secrets (`.env`, `config/master.key`, API tokens, webhook secrets). +- `.env.example` contains placeholder values only. From a43f26d0f877ad3e580e0121ee17444a23e81e28 Mon Sep 17 00:00:00 2001 From: Adrian Lansdown Date: Thu, 26 Feb 2026 11:26:24 +0000 Subject: [PATCH 4/4] Remove instructions on seeding and pulling from Heroku --- AGENTS.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 212e501ba..7b47a2cff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,8 +29,6 @@ docker compose up ## Development - Use `docker compose` for all commands; project mounts into `editor-api:builder` with tmpfs for `tmp/`. -- Seeds: `docker compose run --rm api rails projects:create_all` (see README for others). -- DB sync (needs Heroku CLI): `./bin/db-sync/production-to-local.sh` or `staging-to-local.sh`. ## Testing - Full suite: `docker compose run --rm api rspec`