From 7db25aa1597dd62d580c86aceff23913ee85d842 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 09:47:42 +0100 Subject: [PATCH 01/30] Bump Rails to 8 and update gem dependencies ENG-1743 --- Gemfile | 10 +- Gemfile.lock | 256 +++++++++++++++++++++++++-------------------------- 2 files changed, 132 insertions(+), 134 deletions(-) diff --git a/Gemfile b/Gemfile index e68bc19756..2a42ca1728 100644 --- a/Gemfile +++ b/Gemfile @@ -4,10 +4,9 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby "3.3.11" gem "activestorage-validator" -gem "administrate", "~> 0.20.0" +gem "administrate" gem "administrate-field-active_storage" gem "administrate-field-jsonb" -gem "administrate-field-nested_has_many" gem "attr_encrypted" gem "audited" gem "aws-sdk-s3", require: false @@ -43,7 +42,10 @@ gem "prawn" gem "puma" gem "rack-attack" gem "rack-cors", require: "rack/cors" -gem "rails", "~> 7.1.0" +gem "ostruct" +gem "rails", "~> 8.0" +gem "sprockets-rails" +gem "sassc-rails" gem "rails-healthcheck" gem "rest-client" gem "scout_apm" @@ -96,7 +98,7 @@ group :development do gem "spring" gem "spring-watcher-listen" gem "web-console", ">= 3.3.0" - gem "bullet", "~> 7.1.0" + gem "bullet" gem "standard" gem "standard-rails" end diff --git a/Gemfile.lock b/Gemfile.lock index 3b0b5c6361..7aee0b5ba1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,104 +1,95 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.1.5.2) - actionpack (= 7.1.5.2) - activesupport (= 7.1.5.2) + action_text-trix (2.1.19) + railties + actioncable (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.5.2) - actionpack (= 7.1.5.2) - activejob (= 7.1.5.2) - activerecord (= 7.1.5.2) - activestorage (= 7.1.5.2) - activesupport (= 7.1.5.2) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.5.2) - actionpack (= 7.1.5.2) - actionview (= 7.1.5.2) - activejob (= 7.1.5.2) - activesupport (= 7.1.5.2) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp + actionmailbox (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) + mail (>= 2.8.0) + actionmailer (8.1.3) + actionpack (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activesupport (= 8.1.3) + mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.1.5.2) - actionview (= 7.1.5.2) - activesupport (= 7.1.5.2) + actionpack (8.1.3) + actionview (= 8.1.3) + activesupport (= 8.1.3) nokogiri (>= 1.8.5) - racc rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.5.2) - actionpack (= 7.1.5.2) - activerecord (= 7.1.5.2) - activestorage (= 7.1.5.2) - activesupport (= 7.1.5.2) + useragent (~> 0.16) + actiontext (8.1.3) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.5.2) - activesupport (= 7.1.5.2) + actionview (8.1.3) + activesupport (= 8.1.3) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.1.5.2) - activesupport (= 7.1.5.2) + activejob (8.1.3) + activesupport (= 8.1.3) globalid (>= 0.3.6) - activemodel (7.1.5.2) - activesupport (= 7.1.5.2) - activerecord (7.1.5.2) - activemodel (= 7.1.5.2) - activesupport (= 7.1.5.2) + activemodel (8.1.3) + activesupport (= 8.1.3) + activerecord (8.1.3) + activemodel (= 8.1.3) + activesupport (= 8.1.3) timeout (>= 0.4.0) - activestorage (7.1.5.2) - actionpack (= 7.1.5.2) - activejob (= 7.1.5.2) - activerecord (= 7.1.5.2) - activesupport (= 7.1.5.2) + activestorage (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activesupport (= 8.1.3) marcel (~> 1.0) activestorage-validator (0.4.0) rails (>= 6.1.0) - activesupport (7.1.5.2) + activesupport (8.1.3) base64 - benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + json logger (>= 1.4.2) minitest (>= 5.1) - mutex_m securerandom (>= 0.3) - tzinfo (~> 2.0) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) - administrate (0.20.1) - actionpack (>= 6.0, < 8.0) - actionview (>= 6.0, < 8.0) - activerecord (>= 6.0, < 8.0) - jquery-rails (~> 4.6.0) + administrate (1.0.0) + actionpack (>= 6.0, < 9.0) + actionview (>= 6.0, < 9.0) + activerecord (>= 6.0, < 9.0) kaminari (~> 1.2.2) - sassc-rails (~> 2.1) - selectize-rails (~> 0.6) administrate-field-active_storage (1.0.6) administrate (>= 0.2.2) rails (>= 7.0) - administrate-field-jsonb (0.4.6) - administrate (< 1.0.0) + administrate-field-jsonb (0.4.8) + administrate (< 2.0) rails (>= 4.2) - administrate-field-nested_has_many (2.1.0) - administrate (>= 0.19, < 1) - cocoon (~> 1.2, >= 1.2.11) ast (2.4.3) attr_encrypted (4.2.0) encryptor (~> 3.0.0) @@ -146,7 +137,7 @@ GEM erubi (~> 1.4) parser (>= 2.4) smart_properties - bigdecimal (3.2.2) + bigdecimal (3.3.1) bindex (0.8.1) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) @@ -154,7 +145,7 @@ GEM msgpack (~> 1.2) brakeman (6.0.1) builder (3.3.0) - bullet (7.1.6) + bullet (8.1.3) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) byebug (12.0.0) @@ -169,20 +160,19 @@ GEM xpath (~> 3.2) choice (0.2.0) climate_control (1.2.0) - cloudflare-rails (6.2.0) - actionpack (>= 7.1.0, < 8.1.0) - activesupport (>= 7.1.0, < 8.1.0) - railties (>= 7.1.0, < 8.1.0) + cloudflare-rails (7.0.0) + actionpack (>= 7.2.0, < 8.2.0) + activesupport (>= 7.2.0, < 8.2.0) + railties (>= 7.2.0, < 8.2.0) zeitwerk (>= 2.5.0) - cocoon (1.2.15) coderay (1.1.3) coercible (1.0.0) descendants_tracker (~> 0.0.1) combine_pdf (1.0.23) matrix ruby-rc4 (>= 0.1.5) - concurrent-ruby (1.3.5) - connection_pool (2.5.4) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) crack (0.4.5) rexml crass (1.0.6) @@ -237,7 +227,7 @@ GEM enumerize (2.7.0) activesupport (>= 3.2) equalizer (0.0.11) - erb (5.0.2) + erb (6.0.4) erb_lint (0.5.0) activesupport better_html (>= 2.0.1) @@ -262,7 +252,7 @@ GEM logger faraday-net_http (3.4.0) net-http (>= 0.5.0) - ffi (1.17.2) + ffi (1.17.4) fiber-storage (1.0.1) fog-aws (3.30.0) base64 (~> 0.2.0) @@ -287,7 +277,7 @@ GEM geocoder (1.8.5) base64 (>= 0.1.0) csv (>= 3.0.0) - globalid (1.2.1) + globalid (1.3.0) activesupport (>= 6.1) graphlient (0.8.0) faraday (~> 2.0) @@ -320,22 +310,19 @@ GEM http-cookie (1.0.5) domain_name (~> 0.5) humanize (2.5.1) - i18n (1.14.7) + i18n (1.14.8) concurrent-ruby (~> 1.0) ice_nine (0.11.2) interception (0.5) - io-console (0.8.1) - irb (1.15.2) + io-console (0.8.2) + irb (1.18.0) pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jaro_winkler (1.5.6) jmespath (1.6.2) - jquery-rails (4.6.0) - rails-dom-testing (>= 1, < 3) - railties (>= 4.2.0) - thor (>= 0.14, < 2.0) - json (2.7.1) + json (2.19.8) jwt (2.7.1) kaminari (1.2.2) activesupport (>= 4.1.0) @@ -364,16 +351,17 @@ GEM activesupport (>= 4) railties (>= 4) request_store (~> 1.0) - loofah (2.24.1) + loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) lumberjack (1.2.9) - mail (2.8.1) + mail (2.9.0) + logger mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.0.4) + marcel (1.2.1) matrix (0.4.3) memcachier (0.0.2) method_source (1.1.0) @@ -386,15 +374,16 @@ GEM rake mini_mime (1.1.5) mini_portile2 (2.8.9) - minitest (5.25.5) + minitest (6.0.6) + drb (~> 2.0) + prism (~> 1.5) msgpack (1.8.0) multi_json (1.15.0) multi_xml (0.6.0) - mutex_m (0.3.0) nenv (0.3.0) net-http (0.6.0) uri - net-imap (0.5.14) + net-imap (0.6.4.1) date net-protocol net-pop (0.1.2) @@ -404,7 +393,7 @@ GEM net-smtp (0.5.1) net-protocol netrc (0.11.0) - nio4r (2.7.4) + nio4r (2.7.5) nokogiri (1.19.3) mini_portile2 (~> 2.8.2) racc (~> 1.4) @@ -431,6 +420,7 @@ GEM omniauth-rails_csrf_protection (1.0.1) actionpack (>= 4.2) omniauth (~> 2.0) + ostruct (0.6.3) pagy (6.4.3) parallel (1.24.0) parser (3.3.11.1) @@ -441,12 +431,13 @@ GEM pg_search (2.3.6) activerecord (>= 5.2) activesupport (>= 5.2) - pp (0.6.2) + pp (0.6.3) prettyprint prawn (2.4.0) pdf-core (~> 0.9.0) ttfunk (~> 1.7) prettyprint (0.2.0) + prism (1.9.0) pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) @@ -458,7 +449,7 @@ GEM pry-rescue (1.6.0) interception (>= 0.5) pry (>= 0.12.0) - psych (5.2.6) + psych (5.4.0) date stringio public_suffix (7.0.5) @@ -483,22 +474,22 @@ GEM rack (>= 3.0.0) rack-test (2.2.0) rack (>= 1.3) - rackup (2.2.1) + rackup (2.3.1) rack (>= 3) - rails (7.1.5.2) - actioncable (= 7.1.5.2) - actionmailbox (= 7.1.5.2) - actionmailer (= 7.1.5.2) - actionpack (= 7.1.5.2) - actiontext (= 7.1.5.2) - actionview (= 7.1.5.2) - activejob (= 7.1.5.2) - activemodel (= 7.1.5.2) - activerecord (= 7.1.5.2) - activestorage (= 7.1.5.2) - activesupport (= 7.1.5.2) + rails (8.1.3) + actioncable (= 8.1.3) + actionmailbox (= 8.1.3) + actionmailer (= 8.1.3) + actionpack (= 8.1.3) + actiontext (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activemodel (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) bundler (>= 1.15.0) - railties (= 7.1.5.2) + railties (= 8.1.3) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -515,26 +506,28 @@ GEM rails-healthcheck (1.4.0) actionpack railties - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.1.5.2) - actionpack (= 7.1.5.2) - activesupport (= 7.1.5.2) - irb + railties (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) + irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.0) + rake (13.4.2) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) rbs (2.8.4) - rdoc (6.14.2) + rdoc (7.2.0) erb psych (>= 4.0.0) + tsort redis-client (0.24.0) connection_pool reek (6.3.0) @@ -543,7 +536,7 @@ GEM rainbow (>= 2.0, < 4.0) rexml (~> 3.1) regexp_parser (2.11.2) - reline (0.6.2) + reline (0.6.3) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) @@ -618,7 +611,6 @@ GEM scout_apm (6.2.0) parser securerandom (0.4.1) - selectize-rails (0.12.6) selenium-webdriver (4.35.0) base64 (~> 0.2) logger (~> 1.4) @@ -703,14 +695,15 @@ GEM lint_roller (~> 1.0) rubocop-rails (~> 2.20.2) statesman (13.1.0) - stringio (3.1.7) + stringio (3.2.0) terser (1.2.5) execjs (>= 0.3.0, < 3) - thor (1.4.0) + thor (1.5.0) thread_safe (0.3.6) - tilt (2.6.1) + tilt (2.7.0) timecop (0.9.10) timeout (0.6.1) + tsort (0.2.0) ttfunk (1.7.0) turbolinks (5.2.1) turbolinks-source (~> 5.2) @@ -723,13 +716,14 @@ GEM unf_ext unf_ext (0.0.8.2) unicode-display_width (2.5.0) - uniform_notifier (1.16.0) - uri (1.0.4) + uniform_notifier (1.18.0) + uri (1.1.1) + useragent (0.16.11) version_gem (1.1.3) - view_component (3.11.0) - activesupport (>= 5.2.0, < 8.0) - concurrent-ruby (~> 1.0) - method_source (~> 1.0) + view_component (4.12.0) + actionview (>= 7.1.0) + activesupport (>= 7.1.0) + concurrent-ruby (~> 1) virtus (1.0.5) axiom-types (~> 0.1) coercible (~> 1.0) @@ -750,7 +744,7 @@ GEM railties (>= 5.2) semantic_range (>= 2.3.0) websocket (1.2.11) - websocket-driver (0.8.0) + websocket-driver (0.8.1) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -759,17 +753,16 @@ GEM xpath (3.2.0) nokogiri (~> 1.8) yard (0.9.36) - zeitwerk (2.7.3) + zeitwerk (2.8.2) PLATFORMS ruby DEPENDENCIES activestorage-validator - administrate (~> 0.20.0) + administrate administrate-field-active_storage administrate-field-jsonb - administrate-field-nested_has_many attr_encrypted audited awesome_print @@ -779,7 +772,7 @@ DEPENDENCIES binding_of_caller bootsnap (>= 1.5.1) brakeman - bullet (~> 7.1.0) + bullet byebug capybara climate_control @@ -817,6 +810,7 @@ DEPENDENCIES omniauth-auth0 omniauth-oauth2 omniauth-rails_csrf_protection + ostruct pagy pg pg_search @@ -829,7 +823,7 @@ DEPENDENCIES rack-attack rack-cors rack-mini-profiler - rails (~> 7.1.0) + rails (~> 8.0) rails-controller-testing rails-erd rails-healthcheck @@ -840,6 +834,7 @@ DEPENDENCIES rspec-rails rspec_junit_formatter ruby-debug-ide + sassc-rails scout_apm selenium-webdriver (~> 4.35.0) sentry-rails @@ -853,6 +848,7 @@ DEPENDENCIES solargraph spring spring-watcher-listen + sprockets-rails stackprof standard standard-rails From c208364c260a8b3a1e8b38ebe5c4a5cba1726c04 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 09:48:07 +0100 Subject: [PATCH 02/30] Remove administrate-field-nested_has_many asset manifest entries ENG-1743 --- app/assets/config/manifest.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index 6893239885..1c21ceb666 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -1,7 +1,5 @@ //= link administrate/application.css //= link administrate/application.js -//= link administrate-field-nested_has_many/application.css -//= link administrate-field-nested_has_many/application.js //= link administrate-field-active_storage/application.css //= link administrate-field-jsonb/application.css //= link administrate-field-jsonb/application.js From 270078612db557a5756f75da851edc423206d137 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 09:48:27 +0100 Subject: [PATCH 03/30] Fix Rails 8 enum syntax in Badge ENG-1743 --- app/models/badge.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/badge.rb b/app/models/badge.rb index 3ba5528e6e..0149b9bf82 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -4,7 +4,7 @@ class Badge < ApplicationRecord validates :academic_year, presence: true, format: /\A\d{4}-\d{2}\z/ validates :trigger_type, presence: true - enum trigger_type: [:cpd, :completion] + enum :trigger_type, [:cpd, :completion] scope :active, -> { where(active: true) } end From 65abfb75f9a107ab5ae0f5687758b63450be5b9b Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 09:50:39 +0100 Subject: [PATCH 04/30] Fix routes for Rails 8 and add ViewComponent preview routes Rails 8 rejects non-standard action names in :only. Remove :perform_sync and :perform_reset from users resources and add them as standalone named routes. Add explicit ViewComponent preview routes before the CMS wildcard. ENG-1743 --- config/routes.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 66e4946158..2fe7e5e0d4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -32,7 +32,7 @@ end resources :sent_emails, only: %i[index show] resources :support_audits, only: %i[index show update edit] - resources :users, only: %i[index create show edit perform_sync perform_reset update] do + resources :users, only: %i[index create show edit update] do get "/perform_sync/:user_id", to: "users#perform_sync", as: :perform_sync get "/perform_reset/:user_id", to: "users#perform_reset_tests", as: :perform_reset get "/generate_assessment_attempt", to: "users#generate_assessment_attempt", as: :generate_assessment_attempt unless Rails.env.production? @@ -247,5 +247,10 @@ get "/blog", to: "cms#blog", as: :cms_posts get "/blog/articles", to: redirect(path: "/blog") get "/blog/:page_slug", to: "cms#blog_resource", as: :cms_post + if Rails.env.development? || Rails.env.staging? + get "/rails/components", to: "view_components#index" + get "/rails/components/*path", to: "view_components#previews" + end + get "/*page_slug", to: "cms#web_page_resource", as: :cms_page end From 9abbee26ca7d1a1faafa5f91d0af5307feee8a75 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 09:52:53 +0100 Subject: [PATCH 05/30] Fix ViewComponent 4 breaking changes Initialize no longer accepts arguments, replace super with super() in components that define their own initialize Add initializer to delegate image_pack_tag to helpers, which ViewComponent 4 no longer does automatically. ENG-1743 --- app/components/modal_component.rb | 2 +- app/components/related_links_component.rb | 2 +- config/initializers/view_component_helpers.rb | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 config/initializers/view_component_helpers.rb diff --git a/app/components/modal_component.rb b/app/components/modal_component.rb index 07e0b584ef..0d921c4d8a 100644 --- a/app/components/modal_component.rb +++ b/app/components/modal_component.rb @@ -8,7 +8,7 @@ class ModalComponent < ViewComponent::Base attr_reader :expanded, :title, :reopen_button_text, :show_corner_decoration, :class_name, :reopen_button_class, :modal_id def initialize(title:, expanded: false, reopen_button_text: nil, show_corner_decoration: true, class_name: nil, reopen_button_class: nil, z_index: 1000, modal_id: SecureRandom.hex(6), data: {}) - super + super() @expanded = expanded @title = title @reopen_button_text = reopen_button_text diff --git a/app/components/related_links_component.rb b/app/components/related_links_component.rb index 930fc18720..1780ebfe6a 100644 --- a/app/components/related_links_component.rb +++ b/app/components/related_links_component.rb @@ -4,7 +4,7 @@ class RelatedLinksComponent < ViewComponent::Base include ViewComponent::Translatable def initialize(links: [], class_name: nil, image_url: nil) - super + super() @links = links @class_name = class_name @image_url = image_url diff --git a/config/initializers/view_component_helpers.rb b/config/initializers/view_component_helpers.rb new file mode 100644 index 0000000000..f500418f77 --- /dev/null +++ b/config/initializers/view_component_helpers.rb @@ -0,0 +1,6 @@ +# image_pack_tag is a Webpacker helper. ViewComponent 4.x no longer auto-delegates +# helper methods, so we delegate explicitly here. Remove in Phase 3 when webpacker +# is replaced by importmap-rails and image_pack_tag becomes image_tag. +Rails.application.config.to_prepare do + ViewComponent::Base.delegate :image_pack_tag, to: :helpers +end From 9327a64caf51760eaa9d1a953629a8c04f926d96 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 09:54:02 +0100 Subject: [PATCH 06/30] Update environment configs for Rails 8 and ViewComponent 4 Remove config.sass, update mailer preview_path to preview_paths and update ViewComponent preview config to the 4 API ENG-1743 --- config/environments/development.rb | 6 +++--- config/environments/production.rb | 2 -- config/environments/staging.rb | 8 +++----- config/environments/test.rb | 2 -- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index 5b7a9539ce..dca48c501a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -81,7 +81,7 @@ # Suppress logger output for asset requests. config.assets.quiet = true - config.action_mailer.preview_path = "#{Rails.root}/previews/mailers" + config.action_mailer.preview_paths = ["#{Rails.root}/previews/mailers"] config.action_mailer.asset_host = "http://localhost:3000" config.action_mailer.default_url_options = {host: "http://localhost:3000"} @@ -106,8 +106,8 @@ config.hosts << "teachcomputing.test" config.autoload_paths << "lib" - config.view_component.preview_paths << "#{Rails.root}/previews/components" - config.view_component.preview_route = "/rails/components" + config.view_component.previews.paths << "#{Rails.root}/previews/components" + config.view_component.previews.route = "/rails/components" config.view_component.generate.sidecar = true config.action_mailer.default_url_options = {host: "teachcomputing.rpfdev.com"} diff --git a/config/environments/production.rb b/config/environments/production.rb index bc0578a038..abf371b470 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -27,8 +27,6 @@ # Compress JavaScripts and CSS. config.assets.js_compressor = :terser config.assets.css_compressor = nil - config.sass.style = :compressed - config.sass.line_comments = false # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 387645d4eb..555054601e 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -25,8 +25,6 @@ # Compress JavaScripts and CSS. config.assets.js_compressor = :terser config.assets.css_compressor = nil - config.sass.style = :compressed - config.sass.line_comments = false # Do not fallback to assets pipeline if a precompiled asset is missed. config.assets.compile = false @@ -80,7 +78,7 @@ config.action_mailer.show_previews = true config.action_mailer.asset_host = "https://staging.teachcomputing.org" - config.action_mailer.preview_path = "#{Rails.root}/previews/mailers" + config.action_mailer.preview_paths = ["#{Rails.root}/previews/mailers"] config.action_mailer.default_url_options = {host: "https://staging.teachcomputing.org"} config.action_mailer.smtp_settings = { address: "smtp.mandrillapp.com", @@ -126,8 +124,8 @@ config.middleware.use Rack::Attack - config.view_component.preview_paths << "#{Rails.root}/previews/components" - config.view_component.preview_route = "/rails/components" + config.view_component.previews.paths << "#{Rails.root}/previews/components" + config.view_component.previews.route = "/rails/components" config.view_component.show_previews = true # Enable secure cookies (will only work on https) diff --git a/config/environments/test.rb b/config/environments/test.rb index 97ef7138e3..28f6baf6c4 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -64,8 +64,6 @@ config.i18n.raise_on_missing_translations = true config.assets.css_compressor = nil - config.sass.style = :compressed - config.sass.line_comments = false config.middleware.use Rack::Attack From 3e2ffcf5d90e6b16712ac01a853b815cd4dd5356 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 09:55:07 +0100 Subject: [PATCH 07/30] Fix Time integer arithmetic for Rails 8.1 Rails 8.1 no longer allows adding a plain integer to a TimeWithZone. Add .seconds to convert before adding ENG-1743 --- app/views/certificates/cs_accelerator/_csa-test.html.erb | 2 +- app/views/certificates/cs_accelerator/_exam.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/certificates/cs_accelerator/_csa-test.html.erb b/app/views/certificates/cs_accelerator/_csa-test.html.erb index 1353a49748..0b8d97b9eb 100644 --- a/app/views/certificates/cs_accelerator/_csa-test.html.erb +++ b/app/views/certificates/cs_accelerator/_csa-test.html.erb @@ -16,7 +16,7 @@

<% elsif user_programme_assessment.can_take_test_at != 0 %>

- Your <%= to_word_ordinal(user_programme_assessment.failed_num_attempts + 1) %> attempt at the test can be done after<%= "#{(Time.zone.now + user_programme_assessment.can_take_test_at).in_time_zone('London').strftime('%l%P on %A')}" %>. + Your <%= to_word_ordinal(user_programme_assessment.failed_num_attempts + 1) %> attempt at the test can be done after <%= "#{(Time.zone.now + user_programme_assessment.can_take_test_at.seconds).in_time_zone('London').strftime('%-l%P on %A')}" %>.

You may want to consider completing additional courses before your next attempt. You can find full details of areas for improvement on your results email, and course recommendations in the <%= link_to 'CSA Handbook', 'https://ncce.io/csa-handbook', class: 'ncce-link', data: tracking_data('CSA Handbook') %>. diff --git a/app/views/certificates/cs_accelerator/_exam.html.erb b/app/views/certificates/cs_accelerator/_exam.html.erb index e742a2706d..1774e60626 100644 --- a/app/views/certificates/cs_accelerator/_exam.html.erb +++ b/app/views/certificates/cs_accelerator/_exam.html.erb @@ -18,7 +18,7 @@ <% if @user_programme_assessment.currently_taking_test? %> You are currently taking the test. If you have recently failed, please come back in 2 hours. <% elsif @user_programme_assessment.can_take_test_at != 0 %> - Your <%= to_word_ordinal(@user_programme_assessment.num_attempts + 1) %> attempt at the test can be done after <%= "#{(Time.zone.now + @user_programme_assessment.can_take_test_at).in_time_zone('London').strftime('%l%P on %A')}" %>, please come back then! + Your <%= to_word_ordinal(@user_programme_assessment.num_attempts + 1) %> attempt at the test can be done after <%= "#{(Time.zone.now + @user_programme_assessment.can_take_test_at.seconds).in_time_zone('London').strftime('%-l%P on %A')}" %>, please come back then! <% else %> <%= form_for(current_user, method: :post, url: assessment_attempts_path(assessment_attempt: { assessment_id: @programme.assessment.id, user_id: current_user }), html: { From b0bafa7fe3daf1eb5ccd83184ed6909198d0c9ab Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 09:55:35 +0100 Subject: [PATCH 08/30] Fix specs for Rails 8 and ViewComponent 4 changes ENG-1743 --- spec/components/no_courses_component_spec.rb | 4 ++-- spec/views/certificates/cs_accelerator/_csa-test_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/components/no_courses_component_spec.rb b/spec/components/no_courses_component_spec.rb index 87162ebe36..85c45896cc 100644 --- a/spec/components/no_courses_component_spec.rb +++ b/spec/components/no_courses_component_spec.rb @@ -2,12 +2,12 @@ RSpec.describe NoCoursesComponent, type: :component do it "shows link to all courses" do - render_inline(described_class.new(hub: nil)) + render_inline(described_class.new) expect(page).to have_link("Show all courses", href: "/courses#results-top") end it "shows correct text" do - render_inline(described_class.new(hub: nil)) + render_inline(described_class.new) expect(page).to have_text("Sorry, we couldn't find any courses") end end diff --git a/spec/views/certificates/cs_accelerator/_csa-test_spec.rb b/spec/views/certificates/cs_accelerator/_csa-test_spec.rb index c72c2a0870..794606279d 100644 --- a/spec/views/certificates/cs_accelerator/_csa-test_spec.rb +++ b/spec/views/certificates/cs_accelerator/_csa-test_spec.rb @@ -70,12 +70,12 @@ user_programme_assessment = instance_double(UserProgrammeAssessment) allow(user_programme_assessment).to receive(:currently_taking_test?).and_return(false) - allow(user_programme_assessment).to receive(:can_take_test_at).and_return(time + 1.day) + allow(user_programme_assessment).to receive(:can_take_test_at).and_return(1.day.to_i) allow(user_programme_assessment).to receive(:failed_num_attempts).and_return(1) render partial: "csa-test", locals: {user_programme_assessment:} - expect(rendered).to have_content("Your second attempt at the test can be done after 1am on Thursday.") + expect(rendered).to have_content("Your second attempt at the test can be done after 12pm on Thursday.") end end end From fc44e4f047310be22576f917a40f9b95111003a2 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 10:35:17 +0100 Subject: [PATCH 09/30] rspec-rails bumped to 8.0.4 Webpacker compile block now captures the result properly ENG-1743 --- Gemfile.lock | 26 +++++++++++++------------- spec/rails_helper.rb | 5 ++++- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7aee0b5ba1..2d72b68af8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -185,7 +185,7 @@ GEM debug_inspector (1.2.0) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - diff-lcs (1.5.1) + diff-lcs (1.6.2) docile (1.4.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) @@ -553,24 +553,24 @@ GEM rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) - rspec-core (3.13.2) + rspec-core (3.13.6) rspec-support (~> 3.13.0) - rspec-expectations (3.13.3) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-json_expectations (2.2.0) - rspec-mocks (3.13.2) + rspec-mocks (3.13.8) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (7.1.0) - actionpack (>= 7.0) - activesupport (>= 7.0) - railties (>= 7.0) - rspec-core (~> 3.13) - rspec-expectations (~> 3.13) - rspec-mocks (~> 3.13) - rspec-support (~> 3.13) - rspec-support (3.13.2) + rspec-rails (8.0.4) + actionpack (>= 7.2) + activesupport (>= 7.2) + railties (>= 7.2) + rspec-core (>= 3.13.0, < 5.0.0) + rspec-expectations (>= 3.13.0, < 5.0.0) + rspec-mocks (>= 3.13.0, < 5.0.0) + rspec-support (>= 3.13.0, < 5.0.0) + rspec-support (3.13.7) rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) rubocop (1.59.0) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 323b7e797a..fe58bb9fbb 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -86,7 +86,10 @@ def page end config.before(:suite) do - Webpacker.compile if Webpacker.instance.compiler.stale? + if Webpacker.instance.compiler.stale? + success = Webpacker.compile + warn "Webpacker compilation failed" unless success + end end config.before(:each) { stub_cloudflare_ip_lookup } From 229b66385899e73f4a9fd45d2bc88597cc964610 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 11:01:26 +0100 Subject: [PATCH 10/30] Improve Webpacker compilation error handling in RSpec configuration ENG-1743 --- spec/rails_helper.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index fe58bb9fbb..9742f6409a 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -86,10 +86,9 @@ def page end config.before(:suite) do - if Webpacker.instance.compiler.stale? - success = Webpacker.compile - warn "Webpacker compilation failed" unless success - end + Webpacker.compile if Webpacker.instance.compiler.stale? + rescue => e + warn "Webpacker setup error (non-fatal): #{e.message}" end config.before(:each) { stub_cloudflare_ip_lookup } From b10560ae2034256280c4af8b1803e34cfd011752 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 11:15:48 +0100 Subject: [PATCH 11/30] Add diagnostic hook for SimpleCov to handle spurious exit codes ENG-1743 --- spec/rails_helper.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 9742f6409a..81f2d768d0 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,6 +21,16 @@ end end +# Diagnostic hook: runs just before SimpleCov's at_exit (LIFO order). +# Remove once the root cause of the spurious exit code 1 is identified. +at_exit do + next if $!.nil? + next if $!.is_a?(SystemExit) && $!.status == 0 + + warn "[SimpleCov diagnostic] $! = #{$!.class}: #{$!.message}" + warn $!.backtrace.first(10).join("\n") if $!.respond_to?(:backtrace) && $!.backtrace +end + ENV["RAILS_ENV"] ||= "test" Dotenv.overload(".env.test") # Ensure .env.test is used in dev environments From 14ede4768ce89a4d33fa26fbf9fa5f6a80bb4417 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 12:30:32 +0100 Subject: [PATCH 12/30] Refactor SimpleCov at_exit hook to ensure report generation despite spurious exit codes ENG-1743 --- spec/rails_helper.rb | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 81f2d768d0..67a8b6cf1d 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,14 +21,12 @@ end end -# Diagnostic hook: runs just before SimpleCov's at_exit (LIFO order). -# Remove once the root cause of the spurious exit code 1 is identified. -at_exit do - next if $!.nil? - next if $!.is_a?(SystemExit) && $!.status == 0 - - warn "[SimpleCov diagnostic] $! = #{$!.class}: #{$!.message}" - warn $!.backtrace.first(10).join("\n") if $!.respond_to?(:backtrace) && $!.backtrace +# Capybara's Selenium driver calls exit() in its at_exit hook to quit Chrome, which +# sets $! to SystemExit before SimpleCov's at_exit runs. SimpleCov's default handler +# treats any non-nil $! as a "previous error" and skips report generation, causing a +# spurious exit code 1. Override it to always generate the report. +SimpleCov.at_exit do + SimpleCov.result.format! end ENV["RAILS_ENV"] ||= "test" From f82e427651cf61dfcfbbbf041bca585b7bde9fae Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 13:21:47 +0100 Subject: [PATCH 13/30] Set THOR_SILENCE_DEPRECATION environment variable and remove SimpleCov at_exit override ENG-1743 --- .circleci/config.yml | 1 + spec/rails_helper.rb | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index da9f0dd523..c15f200018 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -39,6 +39,7 @@ jobs: PGHOST: 127.0.0.1 PGUSER: ncce_test RAILS_ENV: test + THOR_SILENCE_DEPRECATION: "1" - image: cimg/postgres:14.7 environment: POSTGRES_USER: postgres diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 67a8b6cf1d..f8a371285e 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,13 +21,6 @@ end end -# Capybara's Selenium driver calls exit() in its at_exit hook to quit Chrome, which -# sets $! to SystemExit before SimpleCov's at_exit runs. SimpleCov's default handler -# treats any non-nil $! as a "previous error" and skips report generation, causing a -# spurious exit code 1. Override it to always generate the report. -SimpleCov.at_exit do - SimpleCov.result.format! -end ENV["RAILS_ENV"] ||= "test" Dotenv.overload(".env.test") # Ensure .env.test is used in dev environments From 56fad595318d9c1a68f319e593c648d3958e5813 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 13:25:00 +0100 Subject: [PATCH 14/30] Standardrb fix ENG-1743 --- spec/rails_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index f8a371285e..9742f6409a 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,7 +21,6 @@ end end - ENV["RAILS_ENV"] ||= "test" Dotenv.overload(".env.test") # Ensure .env.test is used in dev environments From bcd6e30bc6754d24e902228b5db9243d2f2a21f9 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 13:35:45 +0100 Subject: [PATCH 15/30] Diagnosing ci pipleine test discrepancies and additional logging ENG-1743 --- .circleci/config.yml | 10 ++++++++++ spec/rails_helper.rb | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c15f200018..6fa04f7e36 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -83,11 +83,21 @@ jobs: - browser-tools/install-browser-tools: chrome-version: latest replace-existing-chrome: true + - run: + name: Spec file count (dry run) + command: | + echo "=== Spec files on disk ===" + find spec -name "*_spec.rb" | wc -l + echo "=== Examples RSpec would load ===" + bundle exec rspec --dry-run --format progress 2>&1 | tail -3 - run: name: Run rspec command: | mkdir ~/rspec bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml + EXIT=$? + echo "=== RSpec exit code: $EXIT ===" + exit $EXIT - store_test_results: path: ~/rspec - store_artifacts: diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 9742f6409a..022bd6b47c 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,6 +21,16 @@ end end +# Diagnostic: runs just before SimpleCov's at_exit (LIFO). Remove once root cause is identified. +at_exit do + if $! + warn "[diagnostic] at_exit $! = #{$!.class}: #{$!.message} (status=#{$!.status if $!.respond_to?(:status)})" + warn "[diagnostic] backtrace:\n#{$!.backtrace&.first(15)&.join("\n")}" + else + warn "[diagnostic] at_exit $! is nil — clean exit" + end +end + ENV["RAILS_ENV"] ||= "test" Dotenv.overload(".env.test") # Ensure .env.test is used in dev environments From 0d3bf2d89d24ab26cd3c7391298e06132ece171a Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 15:01:53 +0100 Subject: [PATCH 16/30] Refactor RSpec commands for improved logging and remove diagnostic at_exit hook ENG-1743 --- .circleci/config.yml | 10 +++++----- spec/rails_helper.rb | 10 ---------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6fa04f7e36..2890c71e1e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -84,19 +84,19 @@ jobs: chrome-version: latest replace-existing-chrome: true - run: - name: Spec file count (dry run) + name: Spec file count command: | echo "=== Spec files on disk ===" find spec -name "*_spec.rb" | wc -l - echo "=== Examples RSpec would load ===" - bundle exec rspec --dry-run --format progress 2>&1 | tail -3 - run: name: Run rspec command: | mkdir ~/rspec - bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml - EXIT=$? + bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml 2>&1 | tee /tmp/rspec.log + EXIT=${PIPESTATUS[0]} echo "=== RSpec exit code: $EXIT ===" + echo "=== Spec load errors ===" + grep -A 3 "An error occurred while loading" /tmp/rspec.log || echo "None" exit $EXIT - store_test_results: path: ~/rspec diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 022bd6b47c..9742f6409a 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -21,16 +21,6 @@ end end -# Diagnostic: runs just before SimpleCov's at_exit (LIFO). Remove once root cause is identified. -at_exit do - if $! - warn "[diagnostic] at_exit $! = #{$!.class}: #{$!.message} (status=#{$!.status if $!.respond_to?(:status)})" - warn "[diagnostic] backtrace:\n#{$!.backtrace&.first(15)&.join("\n")}" - else - warn "[diagnostic] at_exit $! is nil — clean exit" - end -end - ENV["RAILS_ENV"] ||= "test" Dotenv.overload(".env.test") # Ensure .env.test is used in dev environments From 2a7dce2536bbf4ffe35b4a0b900a1618cebc07eb Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 11 Jun 2026 15:49:00 +0100 Subject: [PATCH 17/30] CircleCI test debugging --- .circleci/config.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2890c71e1e..c5f8997d86 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -92,11 +92,18 @@ jobs: name: Run rspec command: | mkdir ~/rspec + set +e bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml 2>&1 | tee /tmp/rspec.log EXIT=${PIPESTATUS[0]} echo "=== RSpec exit code: $EXIT ===" - echo "=== Spec load errors ===" - grep -A 3 "An error occurred while loading" /tmp/rspec.log || echo "None" + echo "=== Load error count ===" + grep -c "An error occurred while loading" /tmp/rspec.log || echo "0" + echo "=== Load error details ===" + grep -A 5 "An error occurred while loading" /tmp/rspec.log || echo "None" + echo "=== Errors outside examples ===" + grep "errors occurred outside of examples" /tmp/rspec.log || echo "None" + echo "=== RSpec summary ===" + grep -E "^[0-9]+ examples?" /tmp/rspec.log || echo "Not found" exit $EXIT - store_test_results: path: ~/rspec From 22734810d31cf104e07e7ff1557dc6c684b191b2 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Fri, 12 Jun 2026 08:19:44 +0100 Subject: [PATCH 18/30] Enhance SimpleCov debugging output for better error tracking ENG-1743 --- spec/rails_helper.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 9742f6409a..8ea45bf1b2 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -3,6 +3,20 @@ require "simplecov" SimpleCov.minimum_coverage ENV["SIMPLECOV_MIN_COVERAGE"].to_i + +SimpleCov.singleton_class.prepend(Module.new do + def exit_status_from_exception + err = $ERROR_INFO + if err + $stderr.puts "[SC Debug] $! = #{err.class}: #{err.message}" + $stderr.puts "[SC Debug] backtrace: #{err.backtrace&.first(3)&.join(" | ")}" + else + $stderr.puts "[SC Debug] $! is nil (clean exit)" + end + super + end +end) + SimpleCov.start "rails" do require_relative "support/simplecov_warnings_patch" # To remove excess warnings from line below enable_coverage_for_eval From 00ab3b5baf75232fb0caa4299be2862bf07b8446 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Fri, 12 Jun 2026 08:50:39 +0100 Subject: [PATCH 19/30] Enhance SimpleCov error handling and coverage processing in RSpec ENG-1743 --- .circleci/config.yml | 6 +++++- spec/rails_helper.rb | 22 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c5f8997d86..25880158a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -101,9 +101,13 @@ jobs: echo "=== Load error details ===" grep -A 5 "An error occurred while loading" /tmp/rspec.log || echo "None" echo "=== Errors outside examples ===" - grep "errors occurred outside of examples" /tmp/rspec.log || echo "None" + grep "error occurred outside of examples" /tmp/rspec.log || echo "None" echo "=== RSpec summary ===" grep -E "^[0-9]+ examples?" /tmp/rspec.log || echo "Not found" + echo "=== Pending examples ===" + grep -E "^[0-9]+ pending" /tmp/rspec.log || echo "None" + echo "=== JUnit formatter exit ===" + grep -i "junit\|xml\|format" /tmp/rspec.log | tail -5 || echo "None" exit $EXIT - store_test_results: path: ~/rspec diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 8ea45bf1b2..5039f1bbfd 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -8,13 +8,33 @@ def exit_status_from_exception err = $ERROR_INFO if err - $stderr.puts "[SC Debug] $! = #{err.class}: #{err.message}" + status = err.is_a?(SystemExit) ? err.status : "N/A" + $stderr.puts "[SC Debug] $! = #{err.class} (status=#{status}): #{err.message}" $stderr.puts "[SC Debug] backtrace: #{err.backtrace&.first(3)&.join(" | ")}" else $stderr.puts "[SC Debug] $! is nil (clean exit)" end super end + + # Capybara's at_exit calls `exit @exit_status` to preserve the test suite exit code, + # which SimpleCov sees as a "previous error" and short-circuits without processing + # coverage. This override always runs coverage and exits with the max of both statuses. + def run_exit_tasks! + error_exit_status = exit_status_from_exception + at_exit.call + + coverage_exit_status = 0 + if ready_to_process_results? + coverage_exit_status = process_result(result) + if coverage_exit_status.positive? && print_error_status + warn("SimpleCov failed with exit #{coverage_exit_status} due to a coverage related error") + end + end + + final_exit_status = [error_exit_status.to_i, coverage_exit_status].max + Kernel.exit(final_exit_status) if final_exit_status.positive? + end end) SimpleCov.start "rails" do From 9dbdc50cd9690a97835f6a3c3328462d28a7a882 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Fri, 12 Jun 2026 09:37:40 +0100 Subject: [PATCH 20/30] Enhance RSpec debugging output for improved error tracking and clarity ENG-1743 --- .circleci/config.yml | 4 ++++ spec/rails_helper.rb | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 25880158a6..c46a02ddf7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -96,10 +96,14 @@ jobs: bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml 2>&1 | tee /tmp/rspec.log EXIT=${PIPESTATUS[0]} echo "=== RSpec exit code: $EXIT ===" + echo "=== Runner debug ===" + grep "\[Runner Debug\]" /tmp/rspec.log || echo "None" echo "=== Load error count ===" grep -c "An error occurred while loading" /tmp/rspec.log || echo "0" echo "=== Load error details ===" grep -A 5 "An error occurred while loading" /tmp/rspec.log || echo "None" + echo "=== Suite/context hook errors ===" + grep "An error occurred in" /tmp/rspec.log || echo "None" echo "=== Errors outside examples ===" grep "error occurred outside of examples" /tmp/rspec.log || echo "None" echo "=== RSpec summary ===" diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 5039f1bbfd..838192093b 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -9,10 +9,10 @@ def exit_status_from_exception err = $ERROR_INFO if err status = err.is_a?(SystemExit) ? err.status : "N/A" - $stderr.puts "[SC Debug] $! = #{err.class} (status=#{status}): #{err.message}" - $stderr.puts "[SC Debug] backtrace: #{err.backtrace&.first(3)&.join(" | ")}" + warn "[SC Debug] $! = #{err.class} (status=#{status}): #{err.message}" + warn "[SC Debug] backtrace: #{err.backtrace&.first(3)&.join(" | ")}" else - $stderr.puts "[SC Debug] $! is nil (clean exit)" + warn "[SC Debug] $! is nil (clean exit)" end super end @@ -62,6 +62,15 @@ def run_exit_tasks! abort("The Rails environment is running in production mode!") if Rails.env.production? require "rspec/rails" + +RSpec::Core::Runner.prepend(Module.new do + def exit_code(examples_passed = false) + result = super + warn "[Runner Debug] exit_code: examples_passed=#{examples_passed}, non_example_failure=#{@world.non_example_failure}, result=#{result}" + result + end +end) + require "webmock/rspec" require "rspec/json_expectations" require "capybara/rspec" From b62749adfe842dffc59d3d592340d064b36d49d5 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Fri, 12 Jun 2026 10:05:50 +0100 Subject: [PATCH 21/30] Enhance RSpec debugging output with additional reporter and exit trace information ENG-1743 --- .circleci/config.yml | 2 +- spec/rails_helper.rb | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c46a02ddf7..f1febf447b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -97,7 +97,7 @@ jobs: EXIT=${PIPESTATUS[0]} echo "=== RSpec exit code: $EXIT ===" echo "=== Runner debug ===" - grep "\[Runner Debug\]" /tmp/rspec.log || echo "None" + grep "\[Runner Debug\]\|\[Reporter Debug\]\|\[Exit Trace\]" /tmp/rspec.log || echo "None" echo "=== Load error count ===" grep -c "An error occurred while loading" /tmp/rspec.log || echo "0" echo "=== Load error details ===" diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 838192093b..f9a33f03ab 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -71,6 +71,26 @@ def exit_code(examples_passed = false) end end) +RSpec::Core::Reporter.prepend(Module.new do + def finish + warn "[Reporter Debug] finish() starting, scope=#{RSpec.current_scope}" + super + warn "[Reporter Debug] finish() completed" + rescue => e + warn "[Reporter Debug] finish() raised #{e.class}: #{e.message}" + raise + end +end) + +TracePoint.new(:raise) do |tp| + ex = tp.raised_exception + next unless ex.is_a?(SystemExit) && ex.status != 0 + + scope = RSpec.respond_to?(:current_scope) ? RSpec.current_scope : :unknown + warn "[Exit Trace] SystemExit(#{ex.status}) at #{tp.path}:#{tp.lineno}, scope=#{scope}" + warn "[Exit Trace] caller: #{caller.first(8).join(" | ")}" +end.enable + require "webmock/rspec" require "rspec/json_expectations" require "capybara/rspec" From e6e733f59ec2af95d3072821d70152c0683134c4 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Fri, 12 Jun 2026 13:34:34 +0100 Subject: [PATCH 22/30] Fix generator invocation for view components and improve error handling ENG-1743 --- lib/generators/strapi/component_generator.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/generators/strapi/component_generator.rb b/lib/generators/strapi/component_generator.rb index 0302985789..eca948011b 100644 --- a/lib/generators/strapi/component_generator.rb +++ b/lib/generators/strapi/component_generator.rb @@ -48,17 +48,19 @@ def create_data_text def run_view_component_generator Rails::Generators.invoke( - "component", + "view_component:component", ["Cms::#{@component_name_class}", *@rails_param_names, "--test-framework=rspec", "--sidecar", "--preview"].compact, - behaviour: :invoke, + behavior: :invoke, destination_root: ) - rescue + # Rails 8.1's Generators.invoke calls `exit 1` when the generator isn't + # found, so SystemExit must be caught here or it kills the process + rescue SystemExit, StandardError puts <<~HEREDOC #{"*" * 80} Unable to create component, please run this command seperatly - rails generate component Cms::#{@component_name_class} #{@rails_param_names.join(" ")} --test-framework=rspec + rails generate view_component:component Cms::#{@component_name_class} #{@rails_param_names.join(" ")} --test-framework=rspec #{"*" * 80} HEREDOC From dca183baff281a43e2bb4116a88bd16e38634080 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Fri, 12 Jun 2026 13:59:11 +0100 Subject: [PATCH 23/30] Remove CircleCI and rails_helper logging and diagnostics ENG-1743 --- .circleci/config.yml | 27 +------------------ spec/rails_helper.rb | 63 -------------------------------------------- 2 files changed, 1 insertion(+), 89 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f1febf447b..c15f200018 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -83,36 +83,11 @@ jobs: - browser-tools/install-browser-tools: chrome-version: latest replace-existing-chrome: true - - run: - name: Spec file count - command: | - echo "=== Spec files on disk ===" - find spec -name "*_spec.rb" | wc -l - run: name: Run rspec command: | mkdir ~/rspec - set +e - bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml 2>&1 | tee /tmp/rspec.log - EXIT=${PIPESTATUS[0]} - echo "=== RSpec exit code: $EXIT ===" - echo "=== Runner debug ===" - grep "\[Runner Debug\]\|\[Reporter Debug\]\|\[Exit Trace\]" /tmp/rspec.log || echo "None" - echo "=== Load error count ===" - grep -c "An error occurred while loading" /tmp/rspec.log || echo "0" - echo "=== Load error details ===" - grep -A 5 "An error occurred while loading" /tmp/rspec.log || echo "None" - echo "=== Suite/context hook errors ===" - grep "An error occurred in" /tmp/rspec.log || echo "None" - echo "=== Errors outside examples ===" - grep "error occurred outside of examples" /tmp/rspec.log || echo "None" - echo "=== RSpec summary ===" - grep -E "^[0-9]+ examples?" /tmp/rspec.log || echo "Not found" - echo "=== Pending examples ===" - grep -E "^[0-9]+ pending" /tmp/rspec.log || echo "None" - echo "=== JUnit formatter exit ===" - grep -i "junit\|xml\|format" /tmp/rspec.log | tail -5 || echo "None" - exit $EXIT + bundle exec rspec --format progress --format RspecJunitFormatter -o ~/rspec/rspec.xml - store_test_results: path: ~/rspec - store_artifacts: diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index f9a33f03ab..9742f6409a 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -3,40 +3,6 @@ require "simplecov" SimpleCov.minimum_coverage ENV["SIMPLECOV_MIN_COVERAGE"].to_i - -SimpleCov.singleton_class.prepend(Module.new do - def exit_status_from_exception - err = $ERROR_INFO - if err - status = err.is_a?(SystemExit) ? err.status : "N/A" - warn "[SC Debug] $! = #{err.class} (status=#{status}): #{err.message}" - warn "[SC Debug] backtrace: #{err.backtrace&.first(3)&.join(" | ")}" - else - warn "[SC Debug] $! is nil (clean exit)" - end - super - end - - # Capybara's at_exit calls `exit @exit_status` to preserve the test suite exit code, - # which SimpleCov sees as a "previous error" and short-circuits without processing - # coverage. This override always runs coverage and exits with the max of both statuses. - def run_exit_tasks! - error_exit_status = exit_status_from_exception - at_exit.call - - coverage_exit_status = 0 - if ready_to_process_results? - coverage_exit_status = process_result(result) - if coverage_exit_status.positive? && print_error_status - warn("SimpleCov failed with exit #{coverage_exit_status} due to a coverage related error") - end - end - - final_exit_status = [error_exit_status.to_i, coverage_exit_status].max - Kernel.exit(final_exit_status) if final_exit_status.positive? - end -end) - SimpleCov.start "rails" do require_relative "support/simplecov_warnings_patch" # To remove excess warnings from line below enable_coverage_for_eval @@ -62,35 +28,6 @@ def run_exit_tasks! abort("The Rails environment is running in production mode!") if Rails.env.production? require "rspec/rails" - -RSpec::Core::Runner.prepend(Module.new do - def exit_code(examples_passed = false) - result = super - warn "[Runner Debug] exit_code: examples_passed=#{examples_passed}, non_example_failure=#{@world.non_example_failure}, result=#{result}" - result - end -end) - -RSpec::Core::Reporter.prepend(Module.new do - def finish - warn "[Reporter Debug] finish() starting, scope=#{RSpec.current_scope}" - super - warn "[Reporter Debug] finish() completed" - rescue => e - warn "[Reporter Debug] finish() raised #{e.class}: #{e.message}" - raise - end -end) - -TracePoint.new(:raise) do |tp| - ex = tp.raised_exception - next unless ex.is_a?(SystemExit) && ex.status != 0 - - scope = RSpec.respond_to?(:current_scope) ? RSpec.current_scope : :unknown - warn "[Exit Trace] SystemExit(#{ex.status}) at #{tp.path}:#{tp.lineno}, scope=#{scope}" - warn "[Exit Trace] caller: #{caller.first(8).join(" | ")}" -end.enable - require "webmock/rspec" require "rspec/json_expectations" require "capybara/rspec" From 1c148baf570e8d7a7d01d5f86ccaa37ec9ddef1b Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Fri, 12 Jun 2026 15:13:16 +0100 Subject: [PATCH 24/30] Fix ViewComponent previews by using the renamed previews.enabled setting ENG-1743 --- config/environments/staging.rb | 2 +- config/routes.rb | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 555054601e..c779ac8b73 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -126,7 +126,7 @@ config.view_component.previews.paths << "#{Rails.root}/previews/components" config.view_component.previews.route = "/rails/components" - config.view_component.show_previews = true + config.view_component.previews.enabled = true # Enable secure cookies (will only work on https) config.session_store :cookie_store, diff --git a/config/routes.rb b/config/routes.rb index 2fe7e5e0d4..847e44ba18 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -247,10 +247,5 @@ get "/blog", to: "cms#blog", as: :cms_posts get "/blog/articles", to: redirect(path: "/blog") get "/blog/:page_slug", to: "cms#blog_resource", as: :cms_post - if Rails.env.development? || Rails.env.staging? - get "/rails/components", to: "view_components#index" - get "/rails/components/*path", to: "view_components#previews" - end - get "/*page_slug", to: "cms#web_page_resource", as: :cms_page end From facb63f984ebe5bbfb68380df3be2661c8dab813 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Wed, 17 Jun 2026 09:18:41 +0100 Subject: [PATCH 25/30] Remove error handling for generator invocation in ComponentGenerator The rescue isn't needed anymore. The SystemExit was only being raised because "component" wasn't found - now that the namespace is correctly "view_component:component", the generator will be found and exit 1 won't be called ENG-1743 --- lib/generators/strapi/component_generator.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/generators/strapi/component_generator.rb b/lib/generators/strapi/component_generator.rb index eca948011b..f555d51928 100644 --- a/lib/generators/strapi/component_generator.rb +++ b/lib/generators/strapi/component_generator.rb @@ -53,17 +53,6 @@ def run_view_component_generator behavior: :invoke, destination_root: ) - # Rails 8.1's Generators.invoke calls `exit 1` when the generator isn't - # found, so SystemExit must be caught here or it kills the process - rescue SystemExit, StandardError - puts <<~HEREDOC - #{"*" * 80} - Unable to create component, please run this command seperatly - - rails generate view_component:component Cms::#{@component_name_class} #{@rails_param_names.join(" ")} --test-framework=rspec - #{"*" * 80} - - HEREDOC end def print_method_defintions From d6a20adb7061b20cdda17b381a62f2df1234b393 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Wed, 17 Jun 2026 14:39:48 +0100 Subject: [PATCH 26/30] Refactor user routes and controller actions to use member routes and update parameter handling ENG-1743 --- app/controllers/admin/users_controller.rb | 12 ++++++------ app/views/admin/users/_actions.html.erb | 8 ++++---- .../users/generate_assessment_attempt.html.erb | 2 +- config/routes.rb | 10 ++++++---- spec/requests/admin/users_spec.rb | 14 +++++++------- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 2e37a66aae..2b8d4d245b 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,7 +1,7 @@ module Admin class UsersController < Admin::ApplicationController def perform_sync - user_id = params[:user_id] + user_id = params[:id] Support::UserUtilities.sync(user_id) redirect_back( @@ -10,13 +10,13 @@ def perform_sync ) end - def perform_reset_tests + def perform_reset admin_user = User.find_by_email(ENV.fetch("DEFAULT_ADMIN_EMAIL")) - result = Support::UserUtilities.reset_tests(params[:user_id]) + result = Support::UserUtilities.reset_tests(params[:id]) if result.empty? redirect_back( - fallback_location: admin_users_path(params[:user_id]), + fallback_location: admin_users_path(params[:id]), flash: {notice: I18n.t("admin.users.actions.reset.empty")} ) else @@ -26,11 +26,11 @@ def perform_reset_tests end def generate_assessment_attempt - @user = User.find(params[:user_id]) + @user = User.find(params[:id]) end def process_assessment_attempt - @user = User.find(params[:user_id]) + @user = User.find(params[:id]) assessment = Assessment.find(params[:assessment_id]) if assessment.activity achievement = assessment.activity.achievements.find_or_initialize_by(user_id: @user.id) diff --git a/app/views/admin/users/_actions.html.erb b/app/views/admin/users/_actions.html.erb index 2f9cf757a8..1738b3ddfb 100644 --- a/app/views/admin/users/_actions.html.erb +++ b/app/views/admin/users/_actions.html.erb @@ -3,19 +3,19 @@

<%= link_to( t(".sync.label"), - admin_user_perform_sync_path(user_id: resource.id), + perform_sync_admin_user_path(resource), class: "button" ) if accessible_action?(resource, :perform_sync) %>

<%= link_to( t(".reset.label"), - admin_user_perform_reset_path(user_id: resource.id), + perform_reset_admin_user_path(resource), class: "button" - ) if accessible_action?(resource, :perform_reset_tests) %> + ) if accessible_action?(resource, :perform_reset) %>

<%= link_to( 'Generate Assessment Attempt', - admin_user_generate_assessment_attempt_path(user_id: resource.id), + generate_assessment_attempt_admin_user_path(resource), class: "button" ) unless Rails.env.production? %> diff --git a/app/views/admin/users/generate_assessment_attempt.html.erb b/app/views/admin/users/generate_assessment_attempt.html.erb index ee5cab096e..e2f5415658 100644 --- a/app/views/admin/users/generate_assessment_attempt.html.erb +++ b/app/views/admin/users/generate_assessment_attempt.html.erb @@ -7,7 +7,7 @@
- <%= form_tag(admin_user_process_assessment_attempt_path(user_id: @user.id), method: :post) do %> + <%= form_tag(process_assessment_attempt_admin_user_path(@user), method: :post) do %>
diff --git a/config/routes.rb b/config/routes.rb index 847e44ba18..a5f02a0742 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -33,10 +33,12 @@ resources :sent_emails, only: %i[index show] resources :support_audits, only: %i[index show update edit] resources :users, only: %i[index create show edit update] do - get "/perform_sync/:user_id", to: "users#perform_sync", as: :perform_sync - get "/perform_reset/:user_id", to: "users#perform_reset_tests", as: :perform_reset - get "/generate_assessment_attempt", to: "users#generate_assessment_attempt", as: :generate_assessment_attempt unless Rails.env.production? - post "/process_assessment_attempt", to: "users#process_assessment_attempt", as: :process_assessment_attempt unless Rails.env.production? + member do + get :perform_sync + get :perform_reset + get :generate_assessment_attempt unless Rails.env.production? + post :process_assessment_attempt unless Rails.env.production? + end end resources :user_programme_enrolments, only: %i[index show edit update] do member do diff --git a/spec/requests/admin/users_spec.rb b/spec/requests/admin/users_spec.rb index 97908b33a7..efa186a82e 100644 --- a/spec/requests/admin/users_spec.rb +++ b/spec/requests/admin/users_spec.rb @@ -15,14 +15,14 @@ end it "calls the sync service and redirects back with a notice" do - get admin_user_perform_sync_path(user_id: user.id) + get perform_sync_admin_user_path(user) expect(response).to redirect_to(admin_users_path(user_id: user.id)) expect(flash[:notice]).to eq("Sync complete") end end - describe "GET #perform_reset_tests" do + describe "GET #perform_reset" do before do allow(User).to receive(:find_by_email).and_return(admin_user) allow(Support::UserUtilities).to receive(:reset_tests).and_return([]) @@ -30,7 +30,7 @@ context "when reset tests result is empty" do it "calls the reset service and redirects back with a notice" do - get admin_user_perform_reset_path(user_id: user.id) + get perform_reset_admin_user_path(user) expect(response).to redirect_to(admin_users_path(user.id)) expect(flash[:notice]).to eq("Nothing to do!") @@ -46,7 +46,7 @@ end it "calls the reset service and redirects to edit the last support audit" do - get admin_user_perform_reset_path(user_id: user.id) + get perform_reset_admin_user_path(user) expect(response).to redirect_to(edit_admin_support_audit_path(id: support_audit.id)) end @@ -55,7 +55,7 @@ context "GET #generate_assessment_attempt" do before do - get admin_user_generate_assessment_attempt_path(user_id: user.id) + get generate_assessment_attempt_admin_user_path(user) end it "should render correct template" do @@ -66,7 +66,7 @@ context "POST #process_assessment_attempt" do context "with passing score" do before do - post admin_user_process_assessment_attempt_path(user_id: user.id), params: { + post process_assessment_attempt_admin_user_path(user), params: { assessment_id: assessment.id, score: 85 } @@ -84,7 +84,7 @@ context "with failing score" do before do - post admin_user_process_assessment_attempt_path(user_id: user.id), params: { + post process_assessment_attempt_admin_user_path(user), params: { assessment_id: assessment.id, score: 25 } From cc99694635cab1015db92f69395ea7ea99013a3f Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Wed, 17 Jun 2026 14:48:26 +0100 Subject: [PATCH 27/30] Update omniauth-rails_csrf_protection to 8.1 compatible version ENG-1743 --- Gemfile | 2 +- Gemfile.lock | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index 2a42ca1728..7a4dca9be6 100644 --- a/Gemfile +++ b/Gemfile @@ -34,7 +34,7 @@ gem "oauth2" gem "omniauth" gem "omniauth-oauth2" gem "omniauth-auth0" -gem "omniauth-rails_csrf_protection" +gem "omniauth-rails_csrf_protection", "~> 2.0" gem "pagy" gem "pg" gem "pg_search" diff --git a/Gemfile.lock b/Gemfile.lock index 2d72b68af8..1676405ab3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -171,7 +171,7 @@ GEM combine_pdf (1.0.23) matrix ruby-rc4 (>= 0.1.5) - concurrent-ruby (1.3.6) + concurrent-ruby (1.3.7) connection_pool (3.0.2) crack (0.4.5) rexml @@ -304,13 +304,14 @@ GEM guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) hashdiff (1.0.1) - hashie (5.0.0) + hashie (5.1.0) + logger htmlentities (4.3.4) http-accept (1.7.0) http-cookie (1.0.5) domain_name (~> 0.5) humanize (2.5.1) - i18n (1.14.8) + i18n (1.15.0) concurrent-ruby (~> 1.0) ice_nine (0.11.2) interception (0.5) @@ -322,7 +323,7 @@ GEM reline (>= 0.4.2) jaro_winkler (1.5.6) jmespath (1.6.2) - json (2.19.8) + json (2.19.9) jwt (2.7.1) kaminari (1.2.2) activesupport (>= 4.1.0) @@ -407,8 +408,9 @@ GEM rack (>= 1.2, < 4) snaky_hash (~> 2.0) version_gem (~> 1.1) - omniauth (2.1.2) + omniauth (2.1.4) hashie (>= 3.4.6) + logger rack (>= 2.2.3) rack-protection omniauth-auth0 (3.1.1) @@ -417,7 +419,7 @@ GEM omniauth-oauth2 (1.8.0) oauth2 (>= 1.4, < 3) omniauth (~> 2.0) - omniauth-rails_csrf_protection (1.0.1) + omniauth-rails_csrf_protection (2.0.1) actionpack (>= 4.2) omniauth (~> 2.0) ostruct (0.6.3) @@ -463,7 +465,7 @@ GEM rack (>= 2.0.0) rack-mini-profiler (3.1.1) rack (>= 1.2.0) - rack-protection (4.1.1) + rack-protection (4.2.1) base64 (>= 0.1.0) logger (>= 1.6.0) rack (>= 3.0.0, < 4) @@ -809,7 +811,7 @@ DEPENDENCIES omniauth omniauth-auth0 omniauth-oauth2 - omniauth-rails_csrf_protection + omniauth-rails_csrf_protection (~> 2.0) ostruct pagy pg From d863797b5f1e0fb23f0bfcf8474dc0b158ffde38 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Wed, 17 Jun 2026 15:02:16 +0100 Subject: [PATCH 28/30] Refactor routes to address sonarcloud warnings ENG-1743 --- config/routes.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index a5f02a0742..2eef56ff84 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,6 @@ Rails.application.routes.draw do Healthcheck.routes(self) - root to: "cms#home", action: :home + root to: "cms#home" resources :achievements, only: %i[create destroy update] do collection do @@ -34,10 +34,12 @@ resources :support_audits, only: %i[index show update edit] resources :users, only: %i[index create show edit update] do member do - get :perform_sync - get :perform_reset - get :generate_assessment_attempt unless Rails.env.production? - post :process_assessment_attempt unless Rails.env.production? + get "perform_sync", to: "users#perform_sync" + get "perform_reset", to: "users#perform_reset" + unless Rails.env.production? + get "generate_assessment_attempt", to: "users#generate_assessment_attempt" + post "process_assessment_attempt", to: "users#process_assessment_attempt" + end end end resources :user_programme_enrolments, only: %i[index show edit update] do From 1740155be9b2c137005f1745bab6dee156d04720 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Thu, 18 Jun 2026 10:08:51 +0100 Subject: [PATCH 29/30] Rename perform_reset action to perform_reset_tests and update related routes and views ENG-1743 --- app/controllers/admin/users_controller.rb | 2 +- app/views/admin/users/_actions.html.erb | 4 ++-- config/routes.rb | 2 +- spec/requests/admin/users_spec.rb | 6 +++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 2b8d4d245b..f4a0821637 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -10,7 +10,7 @@ def perform_sync ) end - def perform_reset + def perform_reset_tests admin_user = User.find_by_email(ENV.fetch("DEFAULT_ADMIN_EMAIL")) result = Support::UserUtilities.reset_tests(params[:id]) diff --git a/app/views/admin/users/_actions.html.erb b/app/views/admin/users/_actions.html.erb index 1738b3ddfb..efe5ae4171 100644 --- a/app/views/admin/users/_actions.html.erb +++ b/app/views/admin/users/_actions.html.erb @@ -9,9 +9,9 @@

<%= link_to( t(".reset.label"), - perform_reset_admin_user_path(resource), + perform_reset_tests_admin_user_path(resource), class: "button" - ) if accessible_action?(resource, :perform_reset) %> + ) if accessible_action?(resource, :perform_reset_tests) %>

<%= link_to( 'Generate Assessment Attempt', diff --git a/config/routes.rb b/config/routes.rb index 2eef56ff84..6d56cfe3bb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -35,7 +35,7 @@ resources :users, only: %i[index create show edit update] do member do get "perform_sync", to: "users#perform_sync" - get "perform_reset", to: "users#perform_reset" + get "perform_reset_tests", to: "users#perform_reset_tests" unless Rails.env.production? get "generate_assessment_attempt", to: "users#generate_assessment_attempt" post "process_assessment_attempt", to: "users#process_assessment_attempt" diff --git a/spec/requests/admin/users_spec.rb b/spec/requests/admin/users_spec.rb index efa186a82e..723d1d1592 100644 --- a/spec/requests/admin/users_spec.rb +++ b/spec/requests/admin/users_spec.rb @@ -22,7 +22,7 @@ end end - describe "GET #perform_reset" do + describe "GET #perform_reset_tests" do before do allow(User).to receive(:find_by_email).and_return(admin_user) allow(Support::UserUtilities).to receive(:reset_tests).and_return([]) @@ -30,7 +30,7 @@ context "when reset tests result is empty" do it "calls the reset service and redirects back with a notice" do - get perform_reset_admin_user_path(user) + get perform_reset_tests_admin_user_path(user) expect(response).to redirect_to(admin_users_path(user.id)) expect(flash[:notice]).to eq("Nothing to do!") @@ -46,7 +46,7 @@ end it "calls the reset service and redirects to edit the last support audit" do - get perform_reset_admin_user_path(user) + get perform_reset_tests_admin_user_path(user) expect(response).to redirect_to(edit_admin_support_audit_path(id: support_audit.id)) end From 89a9cfb3a50d421a03a95595fe152baf7bf43be7 Mon Sep 17 00:00:00 2001 From: Adam Wheatley Date: Fri, 19 Jun 2026 11:01:55 +0100 Subject: [PATCH 30/30] Pin connection_pool gem version to ~> 2.4 Sidekiq 7.3.9 calls ConnectionPool::TimedStack#pop with a positional argument. connection_pool 3.0 changed this to keyword-only, breaking the Sidekiq scheduler thread on startup ENG-1743 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 7a4dca9be6..8bc7b3e695 100644 --- a/Gemfile +++ b/Gemfile @@ -13,7 +13,7 @@ gem "aws-sdk-s3", require: false gem "bootsnap", ">= 1.5.1", require: false gem "cloudflare-rails" gem "combine_pdf", ">= 1.0.18" -gem "connection_pool" +gem "connection_pool", "~> 2.4" gem "dalli" gem "enumerize" gem "faraday" diff --git a/Gemfile.lock b/Gemfile.lock index 1676405ab3..12c893ad0d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -172,7 +172,7 @@ GEM matrix ruby-rc4 (>= 0.1.5) concurrent-ruby (1.3.7) - connection_pool (3.0.2) + connection_pool (2.5.5) crack (0.4.5) rexml crass (1.0.6) @@ -780,7 +780,7 @@ DEPENDENCIES climate_control cloudflare-rails combine_pdf (>= 1.0.18) - connection_pool + connection_pool (~> 2.4) dalli debase dotenv-rails