From aa9f9969d62c48c7310634e798afaee9a99504bf Mon Sep 17 00:00:00 2001 From: Jasper Mayone Date: Mon, 28 Sep 2026 15:59:38 -0400 Subject: [PATCH 1/6] chore: update route annotations --- config/routes.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index 675282f..1d18777 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,6 +42,7 @@ # POST /login(.:format) auth#login # send_magic_link POST /auth/magic_link(.:format) auth#send_magic_link # magic_link_login GET /auth/magic_link/:token(.:format) auth#magic_link_login +# confirm_magic_link POST /auth/magic_link/:token(.:format) auth#confirm_magic_link # check_password_login POST /auth/check_password_login(.:format) auth#check_password_login # logout DELETE /logout(.:format) auth#logout # me GET /auth/me(.:format) auth#me @@ -54,6 +55,11 @@ # email_confirmation GET /email_confirmation(.:format) email_confirmations#show # confirm_email GET /confirm_email/:token(.:format) email_confirmations#confirm # resend_email_confirmation POST /email_confirmation/resend(.:format) email_confirmations#resend +# make_primary_email_address PATCH /profile/emails/:id/make_primary(.:format) email_addresses#make_primary +# resend_confirmation_email_address POST /profile/emails/:id/resend_confirmation(.:format) email_addresses#resend_confirmation +# email_addresses POST /profile/emails(.:format) email_addresses#create +# email_address DELETE /profile/emails/:id(.:format) email_addresses#destroy +# confirm_email_address GET /emails/confirm/:token(.:format) email_addresses#confirm # user_profile_photo GET /user/:p_id/pfp(.:format) profile_photos#show # user_avatar GET /user/:p_id/avatar/:variant(.:format) profile_photos#avatar # user_avatar_square GET /user/:p_id/avatar/:variant/square(.:format) profile_photos#avatar_square From 5c4e492988eea0d25c72c5a9a9cbc02f8e05a852 Mon Sep 17 00:00:00 2001 From: Jasper Mayone Date: Mon, 28 Sep 2026 16:04:12 -0400 Subject: [PATCH 2/6] ci: autofix annotations and lint on pull requests --- .github/workflows/autofix.yml | 94 +++++++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 25 ++++++++++ 2 files changed, 119 insertions(+) create mode 100644 .github/workflows/autofix.yml diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml new file mode 100644 index 0000000..051ef1f --- /dev/null +++ b/.github/workflows/autofix.yml @@ -0,0 +1,94 @@ +name: Autofix + +# Fixes what can be fixed without a person on pull requests: model and route +# annotations (annotaterb) and safe RuboCop corrections. When something changes, +# it commits the fix to the PR branch and starts CI again for the new commit. +# +# A push made with GITHUB_TOKEN does not trigger other workflows, so this job +# cannot loop, and it starts CI itself through workflow_dispatch. +# +# Pull requests from forks are skipped: GITHUB_TOKEN can't push to them. + +on: + pull_request: + branches: + - main + types: [opened, synchronize, reopened] + +concurrency: + group: autofix-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + contents: write + actions: write + +jobs: + autofix: + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:17 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 5s + --health-timeout 5s + --health-retries 10 + + env: + RAILS_ENV: test + PGUSER: postgres + PGPASSWORD: postgres + + steps: + - name: Checkout PR branch + uses: actions/checkout@v6 + with: + ref: ${{ github.event.pull_request.head.ref }} + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: .ruby-version + bundler-cache: true + + - name: Load database schema + run: bin/rails db:create db:schema:load + + - name: Update annotations + run: | + bundle exec annotaterb models + bundle exec annotaterb routes + + - name: Apply safe RuboCop corrections + # Offenses that can't be corrected safely are left for CI's lint job. + run: bin/rubocop -a || true + + - name: Commit and push fixes + id: commit + run: | + if git diff --quiet; then + echo "Nothing to fix." + echo "pushed=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + git diff --stat + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git commit -am "chore: autofix annotations and lint" + git push origin HEAD:${{ github.event.pull_request.head.ref }} + echo "pushed=true" >> "$GITHUB_OUTPUT" + + - name: Start CI for the fixed commit + if: steps.commit.outputs.pushed == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: gh workflow run ci.yml --repo "${{ github.repository }}" --ref "${{ github.event.pull_request.head.ref }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed2f152..8e43152 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,28 @@ jobs: annotate_check: runs-on: ubuntu-latest + + # annotaterb reads the columns from a real database. Without one it skips + # every model and reports them as unchanged. + services: + postgres: + image: postgres:17 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 5s + --health-timeout 5s + --health-retries 10 + + env: + RAILS_ENV: test + PGUSER: postgres + PGPASSWORD: postgres + steps: - name: Checkout code uses: actions/checkout@v6 @@ -68,6 +90,9 @@ jobs: ruby-version: .ruby-version bundler-cache: true + - name: Load database schema + run: bin/rails db:create db:schema:load + - name: Check if annotations are up to date run: | bundle exec annotaterb models --frozen --show-migration From 0c7331300e6988d1f492a285139e0c7bb900677a Mon Sep 17 00:00:00 2001 From: Jasper Mayone Date: Mon, 28 Sep 2026 16:04:12 -0400 Subject: [PATCH 3/6] test: remove a route annotation to check autofix --- config/routes.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 1d18777..99cadff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,7 +42,6 @@ # POST /login(.:format) auth#login # send_magic_link POST /auth/magic_link(.:format) auth#send_magic_link # magic_link_login GET /auth/magic_link/:token(.:format) auth#magic_link_login -# confirm_magic_link POST /auth/magic_link/:token(.:format) auth#confirm_magic_link # check_password_login POST /auth/check_password_login(.:format) auth#check_password_login # logout DELETE /logout(.:format) auth#logout # me GET /auth/me(.:format) auth#me From 57b30186e958ff6d0554ffc264bc7c5ef08ab55a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 20:05:04 +0000 Subject: [PATCH 4/6] chore: autofix annotations and lint --- config/routes.rb | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 99cadff..763bd07 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,6 +42,7 @@ # POST /login(.:format) auth#login # send_magic_link POST /auth/magic_link(.:format) auth#send_magic_link # magic_link_login GET /auth/magic_link/:token(.:format) auth#magic_link_login +# confirm_magic_link POST /auth/magic_link/:token(.:format) auth#confirm_magic_link # check_password_login POST /auth/check_password_login(.:format) auth#check_password_login # logout DELETE /logout(.:format) auth#logout # me GET /auth/me(.:format) auth#me @@ -128,7 +129,6 @@ # admin_blazer /admin/blazer Blazer::Engine # admin GET /admin(.:format) redirect(301, /login) # GET /admin/*path(.:format) redirect(301, /login) -# letter_opener_web /letter_opener LetterOpenerWeb::Engine # api_v1_health GET /api/v1/health(.:format) api/v1/health#index # webhooks_slack_events POST /webhooks/slack/events(.:format) webhooks/slack#events # webhooks_slack_interactions POST /webhooks/slack/interactions(.:format) webhooks/slack#interactions @@ -158,7 +158,6 @@ # rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show # update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update # rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create -# actual_db_schema /rails ActualDbSchema::Engine # # Routes for OkComputer::Engine: # Prefix Verb URI Pattern Controller#Action @@ -235,29 +234,6 @@ # PUT /dashboards/:id(.:format) blazer/dashboards#update # DELETE /dashboards/:id(.:format) blazer/dashboards#destroy # root GET / blazer/queries#home -# -# Routes for LetterOpenerWeb::Engine: -# Prefix Verb URI Pattern Controller#Action -# letters GET / letter_opener_web/letters#index -# clear_letters POST /clear(.:format) letter_opener_web/letters#clear -# letter GET /:id(/:style)(.:format) letter_opener_web/letters#show -# delete_letter POST /:id/delete(.:format) letter_opener_web/letters#destroy -# GET /:id/attachments/:file(.:format) letter_opener_web/letters#attachment {file: /[^\/]+/} -# -# Routes for ActualDbSchema::Engine: -# Prefix Verb URI Pattern Controller#Action -# rollback_migration POST /migrations/:id/rollback(.:format) actual_db_schema/migrations#rollback -# migrate_migration POST /migrations/:id/migrate(.:format) actual_db_schema/migrations#migrate -# migrations GET /migrations(.:format) actual_db_schema/migrations#index -# migration GET /migrations/:id(.:format) actual_db_schema/migrations#show -# rollback_phantom_migration POST /phantom_migrations/:id/rollback(.:format) actual_db_schema/phantom_migrations#rollback -# rollback_all_phantom_migrations POST /phantom_migrations/rollback_all(.:format) actual_db_schema/phantom_migrations#rollback_all -# phantom_migrations GET /phantom_migrations(.:format) actual_db_schema/phantom_migrations#index -# phantom_migration GET /phantom_migrations/:id(.:format) actual_db_schema/phantom_migrations#show -# delete_broken_version POST /broken_versions/:id/delete(.:format) actual_db_schema/broken_versions#delete -# delete_all_broken_versions POST /broken_versions/delete_all(.:format) actual_db_schema/broken_versions#delete_all -# broken_versions GET /broken_versions(.:format) actual_db_schema/broken_versions#index -# schema GET /schema(.:format) actual_db_schema/schema#index require_relative "../lib/admin_constraint" From 5c2ccb48bc2ea3d6ae305f2837956e85dd106cf9 Mon Sep 17 00:00:00 2001 From: Jasper Mayone Date: Mon, 28 Sep 2026 16:06:12 -0400 Subject: [PATCH 5/6] ci: annotate routes in development so dev-only routes stay --- .github/workflows/autofix.yml | 13 +++++++++---- .github/workflows/ci.yml | 13 +++++++++---- config/routes.rb | 26 +++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 051ef1f..cab2eb1 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -62,10 +62,15 @@ jobs: - name: Load database schema run: bin/rails db:create db:schema:load - - name: Update annotations - run: | - bundle exec annotaterb models - bundle exec annotaterb routes + - name: Update model annotations + run: bundle exec annotaterb models + + # Some routes (letter_opener, actual_db_schema) exist only in development, + # and the committed annotations include them. + - name: Update route annotations + env: + RAILS_ENV: development + run: bundle exec annotaterb routes - name: Apply safe RuboCop corrections # Offenses that can't be corrected safely are left for CI's lint job. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e43152..baceab8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,7 +93,12 @@ jobs: - name: Load database schema run: bin/rails db:create db:schema:load - - name: Check if annotations are up to date - run: | - bundle exec annotaterb models --frozen --show-migration - bundle exec annotaterb routes --frozen + - name: Check if model annotations are up to date + run: bundle exec annotaterb models --frozen --show-migration + + # Some routes (letter_opener, actual_db_schema) exist only in development, + # and the committed annotations include them. + - name: Check if route annotations are up to date + env: + RAILS_ENV: development + run: bundle exec annotaterb routes --frozen diff --git a/config/routes.rb b/config/routes.rb index 763bd07..99cadff 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,7 +42,6 @@ # POST /login(.:format) auth#login # send_magic_link POST /auth/magic_link(.:format) auth#send_magic_link # magic_link_login GET /auth/magic_link/:token(.:format) auth#magic_link_login -# confirm_magic_link POST /auth/magic_link/:token(.:format) auth#confirm_magic_link # check_password_login POST /auth/check_password_login(.:format) auth#check_password_login # logout DELETE /logout(.:format) auth#logout # me GET /auth/me(.:format) auth#me @@ -129,6 +128,7 @@ # admin_blazer /admin/blazer Blazer::Engine # admin GET /admin(.:format) redirect(301, /login) # GET /admin/*path(.:format) redirect(301, /login) +# letter_opener_web /letter_opener LetterOpenerWeb::Engine # api_v1_health GET /api/v1/health(.:format) api/v1/health#index # webhooks_slack_events POST /webhooks/slack/events(.:format) webhooks/slack#events # webhooks_slack_interactions POST /webhooks/slack/interactions(.:format) webhooks/slack#interactions @@ -158,6 +158,7 @@ # rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show # update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update # rails_direct_uploads POST /rails/active_storage/direct_uploads(.:format) active_storage/direct_uploads#create +# actual_db_schema /rails ActualDbSchema::Engine # # Routes for OkComputer::Engine: # Prefix Verb URI Pattern Controller#Action @@ -234,6 +235,29 @@ # PUT /dashboards/:id(.:format) blazer/dashboards#update # DELETE /dashboards/:id(.:format) blazer/dashboards#destroy # root GET / blazer/queries#home +# +# Routes for LetterOpenerWeb::Engine: +# Prefix Verb URI Pattern Controller#Action +# letters GET / letter_opener_web/letters#index +# clear_letters POST /clear(.:format) letter_opener_web/letters#clear +# letter GET /:id(/:style)(.:format) letter_opener_web/letters#show +# delete_letter POST /:id/delete(.:format) letter_opener_web/letters#destroy +# GET /:id/attachments/:file(.:format) letter_opener_web/letters#attachment {file: /[^\/]+/} +# +# Routes for ActualDbSchema::Engine: +# Prefix Verb URI Pattern Controller#Action +# rollback_migration POST /migrations/:id/rollback(.:format) actual_db_schema/migrations#rollback +# migrate_migration POST /migrations/:id/migrate(.:format) actual_db_schema/migrations#migrate +# migrations GET /migrations(.:format) actual_db_schema/migrations#index +# migration GET /migrations/:id(.:format) actual_db_schema/migrations#show +# rollback_phantom_migration POST /phantom_migrations/:id/rollback(.:format) actual_db_schema/phantom_migrations#rollback +# rollback_all_phantom_migrations POST /phantom_migrations/rollback_all(.:format) actual_db_schema/phantom_migrations#rollback_all +# phantom_migrations GET /phantom_migrations(.:format) actual_db_schema/phantom_migrations#index +# phantom_migration GET /phantom_migrations/:id(.:format) actual_db_schema/phantom_migrations#show +# delete_broken_version POST /broken_versions/:id/delete(.:format) actual_db_schema/broken_versions#delete +# delete_all_broken_versions POST /broken_versions/delete_all(.:format) actual_db_schema/broken_versions#delete_all +# broken_versions GET /broken_versions(.:format) actual_db_schema/broken_versions#index +# schema GET /schema(.:format) actual_db_schema/schema#index require_relative "../lib/admin_constraint" From b41d8577b7cda1997e3411fd094a20eba4a538c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 20:07:08 +0000 Subject: [PATCH 6/6] chore: autofix annotations and lint --- config/routes.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/config/routes.rb b/config/routes.rb index 99cadff..1d18777 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -42,6 +42,7 @@ # POST /login(.:format) auth#login # send_magic_link POST /auth/magic_link(.:format) auth#send_magic_link # magic_link_login GET /auth/magic_link/:token(.:format) auth#magic_link_login +# confirm_magic_link POST /auth/magic_link/:token(.:format) auth#confirm_magic_link # check_password_login POST /auth/check_password_login(.:format) auth#check_password_login # logout DELETE /logout(.:format) auth#logout # me GET /auth/me(.:format) auth#me