From 877d8c903ea8df9ea60a50d561192dc72e7596f1 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Fri, 10 Apr 2026 10:43:56 +0100 Subject: [PATCH 01/57] build: remove unused pysch dependency --- Gemfile | 4 ---- Gemfile.lock | 2 -- config/initializers/psych.rb | 22 ---------------------- 3 files changed, 28 deletions(-) delete mode 100644 config/initializers/psych.rb diff --git a/Gemfile b/Gemfile index e8fd7fba0e..9af49f31c4 100644 --- a/Gemfile +++ b/Gemfile @@ -21,10 +21,6 @@ group :default do gem 'faraday-multipart' gem 'rest-client' # Deprecated, but still used in some places, replace with Faraday where possible - # Fix incompatibility with between Ruby 3.1 and Psych 4 (used for yaml) - # see https://stackoverflow.com/a/71192990 - gem 'psych', '< 4' - # State machine gem 'aasm' gem 'after_commit_everywhere', '~> 1.0' # Required by AASM diff --git a/Gemfile.lock b/Gemfile.lock index 9e483c2e0d..fcd49354c8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -378,7 +378,6 @@ GEM pry (>= 0.13, < 0.17) pry-rails (0.3.11) pry (>= 0.13.0) - psych (3.3.4) public_suffix (7.0.5) puma (7.2.0) nio4r (~> 2.0) @@ -714,7 +713,6 @@ DEPENDENCIES prettier_print pry-byebug pry-rails - psych (< 4) puma rack-acceptable rack-cors diff --git a/config/initializers/psych.rb b/config/initializers/psych.rb deleted file mode 100644 index 472b3025db..0000000000 --- a/config/initializers/psych.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -Rails.application.configure do - # Fix for Psych::DisallowedClass: Tried to load unspecified class - config.active_record.yaml_column_permitted_classes = - Array(config.active_record.yaml_column_permitted_classes) + - %w[ - Symbol - ActiveSupport::HashWithIndifferentAccess - ActiveSupport::TimeWithZone - ActiveSupport::TimeZone - HashWithIndifferentAccess - RequestType::Validator::ArrayWithDefault - RequestType::Validator::LibraryTypeValidator - RequestType::Validator::FlowcellTypeValidator - ActionController::Parameters - Set - Range - FieldInfo - Time - ] -end From 960f766eccbaaf6c950343054ad8e0d1ab2a19d2 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Fri, 10 Apr 2026 10:49:33 +0100 Subject: [PATCH 02/57] revert: restore deleted file - turns out it is used --- config/initializers/psych.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 config/initializers/psych.rb diff --git a/config/initializers/psych.rb b/config/initializers/psych.rb new file mode 100644 index 0000000000..472b3025db --- /dev/null +++ b/config/initializers/psych.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +Rails.application.configure do + # Fix for Psych::DisallowedClass: Tried to load unspecified class + config.active_record.yaml_column_permitted_classes = + Array(config.active_record.yaml_column_permitted_classes) + + %w[ + Symbol + ActiveSupport::HashWithIndifferentAccess + ActiveSupport::TimeWithZone + ActiveSupport::TimeZone + HashWithIndifferentAccess + RequestType::Validator::ArrayWithDefault + RequestType::Validator::LibraryTypeValidator + RequestType::Validator::FlowcellTypeValidator + ActionController::Parameters + Set + Range + FieldInfo + Time + ] +end From 604d5d6ffd21e11dd961664f3af904770f54109e Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Mon, 27 Apr 2026 13:20:08 +0100 Subject: [PATCH 03/57] fix: update usages of unsafe loading for controlled files --- app/sequencescape_excel/sequencescape_excel/helpers.rb | 2 +- config/initializers/process_locale_files_with_erb.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/sequencescape_excel/sequencescape_excel/helpers.rb b/app/sequencescape_excel/sequencescape_excel/helpers.rb index 07c60d4513..a591556ea0 100644 --- a/app/sequencescape_excel/sequencescape_excel/helpers.rb +++ b/app/sequencescape_excel/sequencescape_excel/helpers.rb @@ -5,7 +5,7 @@ module SequencescapeExcel # Helpers module Helpers def load_file(folder, filename) - YAML.load_file(Rails.root.join(folder, "#{filename}.yml")).with_indifferent_access + YAML.load_file(Rails.root.join(folder, "#{filename}.yml"), aliases: true).with_indifferent_access end end end diff --git a/config/initializers/process_locale_files_with_erb.rb b/config/initializers/process_locale_files_with_erb.rb index c4eb19e95d..78cc131d13 100644 --- a/config/initializers/process_locale_files_with_erb.rb +++ b/config/initializers/process_locale_files_with_erb.rb @@ -4,7 +4,7 @@ module I18n module Backend module Base def load_yml(filename) - YAML.load(ERB.new(File.read(filename)).result) + YAML.unsafe_load(ERB.new(File.read(filename)).result) end end end From 98923647deec486e79c89e92986177a678c6bb6b Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 29 Apr 2026 14:08:18 +0100 Subject: [PATCH 04/57] test: add descriptive context to bulk submission specs --- .rubocop_todo.yml | 1 - spec/models/bulk_submission_spec.rb | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 313220ee53..e24bc77a2e 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -649,7 +649,6 @@ RSpec/ContextWording: - 'spec/models/barcode_spec.rb' - 'spec/models/broadcast_event/lab_event_spec.rb' - 'spec/models/broadcast_event/qc_assay_spec.rb' - - 'spec/models/bulk_submission_spec.rb' - 'spec/models/comment_spec.rb' - 'spec/models/illumina_htp/initial_stock_tube_purpose_spec.rb' - 'spec/models/location_report_form_spec.rb' diff --git a/spec/models/bulk_submission_spec.rb b/spec/models/bulk_submission_spec.rb index 68422d4018..0fc0ad2818 100644 --- a/spec/models/bulk_submission_spec.rb +++ b/spec/models/bulk_submission_spec.rb @@ -57,7 +57,7 @@ create(:project, name: 'Test project') end - context 'a simple submission' do + context 'when creating a simple submission' do let(:submission_template_hash) do { name: 'Illumina-A - Cherrypick for pulldown - Pulldown WGS - HiSeq Paired end sequencing', @@ -93,7 +93,7 @@ end end - context 'an asset driven submission' do + context 'when creating an asset driven submission' do let(:spreadsheet_filename) { 'template_for_bulk_submission.csv' } let!(:asset) { create(:plate, barcode: 'SQPD-1', well_count: 1, well_factory: :untagged_well) } let(:submission_template_hash) do @@ -126,7 +126,7 @@ end end - context 'a submission with PCR cycles' do + context 'when creating a submission with PCR cycles' do let(:spreadsheet_filename) { 'pcr_cycles.csv' } let!(:submission_template) do @@ -157,7 +157,7 @@ end end - context 'a submission with primer_panels' do + context 'when creating a submission with primer_panels' do let(:spreadsheet_filename) { 'primer_panels.csv' } let!(:primer_panel) { create(:primer_panel, name: 'Test panel') } @@ -190,7 +190,7 @@ end end - context 'a submission with bait libraries' do + context 'when creating a submission with bait libraries' do let(:spreadsheet_filename) { '2_valid_sc_submissions.csv' } let!(:bait_library) { create(:bait_library, name: 'Bait library 1') } let!(:bait_library_2) { create(:bait_library, name: 'Bait library 2') } @@ -222,7 +222,7 @@ end end - context 'a submission with a lowercase library type' do + context 'when creating a submission with a lowercase library type' do let(:spreadsheet_filename) { 'with_lowercase_library_type.csv' } let!(:submission_template) do @@ -253,7 +253,7 @@ end end - context 'a submission with an unrecognised library type' do + context 'when creating a submission with an unrecognised library type' do let(:spreadsheet_filename) { 'with_unknown_library_type.csv' } let!(:submission_template) do @@ -274,7 +274,7 @@ end end - context 'a submission with additional template name validations' do + context 'when creating a submission with additional template name validations' do context 'when valid for scRNA template' do let(:submission_template_hash) do { @@ -434,7 +434,7 @@ end end - context 'a submission with a NovaSeqX sequencing request type' do + context 'when creating a submission with a NovaSeqX sequencing request type' do let(:spreadsheet_filename) { 'novaseqx_bulk_submission.csv' } let!(:request_types) { [create(:nova_seq_x_sequencing_request_type)] } let(:study) { create(:study, name: 'UAT Study') } @@ -480,7 +480,7 @@ end end - context 'a submission with a NovaSeq 6000 PE sequencing request type' do + context 'when creating a submission with a NovaSeq 6000 PE sequencing request type' do let(:spreadsheet_filename) { 'nova_seq_6000_pe_bulk_submission.csv' } let!(:request_types) { [create(:nova_seq_6000_p_e_sequencing_request_type)] } let(:study) { create(:study, name: 'UAT Study') } From 2890168404f53459e065c7ad2303c098d75fa8d3 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 29 Apr 2026 14:59:52 +0100 Subject: [PATCH 05/57] fix: update YAML file loading functions --- app/models/submission/request_options_behaviour.rb | 2 +- app/sequencescape_excel/sequencescape_excel/helpers.rb | 2 +- config/initializers/accession.rb | 2 +- config/initializers/failure_reasons.rb | 2 +- config/initializers/flipper.rb | 2 +- config/initializers/phi_x.rb | 2 +- db/seeds/0001_snp_plate_purposes.rb | 2 +- lib/accession.rb | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index f140271a35..b215396ad3 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -7,7 +7,7 @@ class HashWrapper def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? - YAML.load(hash_yaml) + YAML.unsafe_load(hash_yaml) end def self.dump(hash) diff --git a/app/sequencescape_excel/sequencescape_excel/helpers.rb b/app/sequencescape_excel/sequencescape_excel/helpers.rb index a591556ea0..423b89b999 100644 --- a/app/sequencescape_excel/sequencescape_excel/helpers.rb +++ b/app/sequencescape_excel/sequencescape_excel/helpers.rb @@ -5,7 +5,7 @@ module SequencescapeExcel # Helpers module Helpers def load_file(folder, filename) - YAML.load_file(Rails.root.join(folder, "#{filename}.yml"), aliases: true).with_indifferent_access + YAML.unsafe_load_file(Rails.root.join(folder, "#{filename}.yml")).with_indifferent_access end end end diff --git a/config/initializers/accession.rb b/config/initializers/accession.rb index 5602bbdd39..c00c57dbe7 100644 --- a/config/initializers/accession.rb +++ b/config/initializers/accession.rb @@ -9,5 +9,5 @@ end # add ena requirement fields here -ena_requirement_fields = YAML.load_file('config/ena_requirement_fields.yml') +ena_requirement_fields = YAML.unsafe_load_file('config/ena_requirement_fields.yml') Rails.application.config.ena_requirement_fields = ena_requirement_fields.with_indifferent_access diff --git a/config/initializers/failure_reasons.rb b/config/initializers/failure_reasons.rb index 87c12b48f6..7f3b12f975 100644 --- a/config/initializers/failure_reasons.rb +++ b/config/initializers/failure_reasons.rb @@ -1,2 +1,2 @@ # frozen_string_literal: true -FAILURE_REASONS = YAML.load(File.open("#{Rails.root}/config/failure_reasons.yml")) +FAILURE_REASONS = YAML.unsafe_load_file("#{Rails.root}/config/failure_reasons.yml") diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb index c64a5c5c81..5d9d87a3b9 100644 --- a/config/initializers/flipper.rb +++ b/config/initializers/flipper.rb @@ -2,7 +2,7 @@ require 'yaml' require 'flipper/adapters/active_record' -FLIPPER_FEATURES = YAML.load_file('./config/feature_flags.yml') +FLIPPER_FEATURES = YAML.safe_load_file('./config/feature_flags.yml') Rails.application.configure do ## Memoization ensures that only one adapter call is made per feature per request. diff --git a/config/initializers/phi_x.rb b/config/initializers/phi_x.rb index d9c687a077..b4c8cb406e 100644 --- a/config/initializers/phi_x.rb +++ b/config/initializers/phi_x.rb @@ -1,4 +1,4 @@ # frozen_string_literal: true -phi_x_config = YAML.load_file('config/phi_x.yml') +phi_x_config = YAML.safe_load_file('config/phi_x.yml') Rails.application.config.phi_x = phi_x_config.with_indifferent_access diff --git a/db/seeds/0001_snp_plate_purposes.rb b/db/seeds/0001_snp_plate_purposes.rb index 2693e3e51a..c2c5b73765 100644 --- a/db/seeds/0001_snp_plate_purposes.rb +++ b/db/seeds/0001_snp_plate_purposes.rb @@ -51,7 +51,7 @@ EOS YAML - .load(plate_purposes) + .unsafe_load(plate_purposes) .each do |plate_purpose| attributes = plate_purpose.reverse_merge( diff --git a/lib/accession.rb b/lib/accession.rb index 8c27017783..91f0859962 100644 --- a/lib/accession.rb +++ b/lib/accession.rb @@ -24,7 +24,7 @@ module Accession # @see ftp://ftp.sra.ebi.ac.uk/meta/xsd/ Schema definitions module Helpers def load_file(folder, filename) - YAML.load_file(Rails.root.join(folder, "#{filename}.yml")).with_indifferent_access + YAML.safe_load_file(Rails.root.join(folder, "#{filename}.yml")).with_indifferent_access end end From 6f880c8d8495efa87800ef172592764383918303 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 29 Apr 2026 15:00:48 +0100 Subject: [PATCH 06/57] fix: allow the use of unsafe loading in Psych 4, same behaviour as in 3 --- config/initializers/psych.rb | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/config/initializers/psych.rb b/config/initializers/psych.rb index 472b3025db..ee5458f939 100644 --- a/config/initializers/psych.rb +++ b/config/initializers/psych.rb @@ -1,22 +1,20 @@ # frozen_string_literal: true Rails.application.configure do - # Fix for Psych::DisallowedClass: Tried to load unspecified class - config.active_record.yaml_column_permitted_classes = - Array(config.active_record.yaml_column_permitted_classes) + - %w[ - Symbol - ActiveSupport::HashWithIndifferentAccess - ActiveSupport::TimeWithZone - ActiveSupport::TimeZone - HashWithIndifferentAccess - RequestType::Validator::ArrayWithDefault - RequestType::Validator::LibraryTypeValidator - RequestType::Validator::FlowcellTypeValidator - ActionController::Parameters - Set - Range - FieldInfo - Time - ] + # Allow legacy YAML deserialization for ActiveRecord serialized columns + # Ideally replace with `permitted_classes`, such as those used in the list below: + # Symbol + # ActiveSupport::HashWithIndifferentAccess + # ActiveSupport::TimeWithZone + # ActiveSupport::TimeZone + # HashWithIndifferentAccess + # RequestType::Validator::ArrayWithDefault + # RequestType::Validator::LibraryTypeValidator + # RequestType::Validator::FlowcellTypeValidator + # ActionController::Parameters + # Set + # Range + # FieldInfo + # Time + config.active_record.use_yaml_unsafe_load = true end From f99987c7959633f080706ab14735522da558f98c Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 29 Apr 2026 15:04:58 +0100 Subject: [PATCH 07/57] stlye: lint --- spec/models/bulk_submission_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/bulk_submission_spec.rb b/spec/models/bulk_submission_spec.rb index 0fc0ad2818..6cbde9a8ce 100644 --- a/spec/models/bulk_submission_spec.rb +++ b/spec/models/bulk_submission_spec.rb @@ -420,13 +420,13 @@ end context 'when an scRNA Bulk Submission given with invalid number of samples per pool' do - context 'number of samples per pool < 5' do + context 'when number of samples per pool < 5' do let(:spreadsheet_filename) { 'scRNA_bulk_submission_tube_invalid.csv' } it_behaves_like 'an invalid scRNA Bulk Submission', 'scRNA_bulk_submission_tube_invalid', 4 end - context 'number of samples per pool > 25' do + context 'when number of samples per pool > 25' do let(:spreadsheet_filename) { 'scRNA_bulk_submission_tube_invalid_greater.csv' } it_behaves_like 'an invalid scRNA Bulk Submission', 'scRNA_bulk_submission_tube_invalid_greater', 32 From aa5f96caad42a5132bfcc8388d1eecac7af0e748 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 29 Apr 2026 15:13:18 +0100 Subject: [PATCH 08/57] fix: allow symbol loading for accessioning tags --- lib/accession.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/accession.rb b/lib/accession.rb index 91f0859962..64d98f2365 100644 --- a/lib/accession.rb +++ b/lib/accession.rb @@ -24,7 +24,8 @@ module Accession # @see ftp://ftp.sra.ebi.ac.uk/meta/xsd/ Schema definitions module Helpers def load_file(folder, filename) - YAML.safe_load_file(Rails.root.join(folder, "#{filename}.yml")).with_indifferent_access + file_path = Rails.root.join(folder, "#{filename}.yml") + YAML.safe_load_file(file_path, permitted_classes: [Symbol]).with_indifferent_access end end From 8e6f07e9c527f2e2ee1125dfa22278b181f13b3b Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 14:44:46 +0100 Subject: [PATCH 09/57] fix: preview against latest record_loader beta version --- Gemfile | 2 +- Gemfile.lock | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index bbdd96401b..1157904c8f 100644 --- a/Gemfile +++ b/Gemfile @@ -30,7 +30,7 @@ group :default do # Provides bulk insert capabilities gem 'activerecord-import' - gem 'record_loader', git: 'https://github.com/sanger/record_loader' + gem 'record_loader', git: 'https://github.com/sanger/record_loader', branch: 'sh51/psych-5' gem 'mysql2', platforms: :mri gem 'will_paginate' diff --git a/Gemfile.lock b/Gemfile.lock index 39a36346fd..7b9899460c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,9 +16,11 @@ GIT GIT remote: https://github.com/sanger/record_loader - revision: 9e7481f4d2342f042ab13465962e5d6689863198 + revision: cbd59574d1090a0a81a770478a6b4042fa882f6a + branch: sh51/psych-5 specs: - record_loader (0.3.0) + record_loader (1.0.0) + psych (~> 5.0) GIT remote: https://github.com/sanger/sanger_barcode_format.git @@ -377,6 +379,9 @@ GEM pry (>= 0.13, < 0.17) pry-rails (0.3.11) pry (>= 0.13.0) + psych (5.3.1) + date + stringio public_suffix (7.0.5) puma (7.2.0) nio4r (~> 2.0) @@ -585,6 +590,7 @@ GEM rbtree set (~> 1.0) ssrf_filter (1.2.0) + stringio (3.2.0) syntax_tree (6.3.0) prettier_print (>= 1.2.0) syntax_tree-haml (4.0.3) From b8ebbfe4daf40d18dde6dababb4aede4f159607a Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 15:09:19 +0100 Subject: [PATCH 10/57] fix: remove unrequired commas from asset_shapes --- config/default_records/asset_shapes/default_records.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/default_records/asset_shapes/default_records.yml b/config/default_records/asset_shapes/default_records.yml index 593842c86c..6e5b3778b5 100644 --- a/config/default_records/asset_shapes/default_records.yml +++ b/config/default_records/asset_shapes/default_records.yml @@ -62,8 +62,8 @@ Fluidigm192: sizes: [192] StripTubeColumn: - horizontal_ratio: 1, - vertical_ratio: 8, + horizontal_ratio: 1 + vertical_ratio: 8 description_strategy: Sequential sizes: [8] From a28b17379ad4f1746691314871db2c757843ee79 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 15:16:26 +0100 Subject: [PATCH 11/57] security: be stricter in what files are unsafe_loaded --- .../submission/request_options_behaviour.rb | 2 +- .../sequencescape_excel/helpers.rb | 2 +- config/initializers/accession.rb | 2 +- config/initializers/failure_reasons.rb | 2 +- config/initializers/psych.rb | 20 ------------------- 5 files changed, 4 insertions(+), 24 deletions(-) delete mode 100644 config/initializers/psych.rb diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index b215396ad3..7d431e0810 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -7,7 +7,7 @@ class HashWrapper def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? - YAML.unsafe_load(hash_yaml) + YAML.safe_load(hash_yaml) end def self.dump(hash) diff --git a/app/sequencescape_excel/sequencescape_excel/helpers.rb b/app/sequencescape_excel/sequencescape_excel/helpers.rb index 423b89b999..9462318a00 100644 --- a/app/sequencescape_excel/sequencescape_excel/helpers.rb +++ b/app/sequencescape_excel/sequencescape_excel/helpers.rb @@ -5,7 +5,7 @@ module SequencescapeExcel # Helpers module Helpers def load_file(folder, filename) - YAML.unsafe_load_file(Rails.root.join(folder, "#{filename}.yml")).with_indifferent_access + YAML.safe_load_file(Rails.root.join(folder, "#{filename}.yml")).with_indifferent_access end end end diff --git a/config/initializers/accession.rb b/config/initializers/accession.rb index c00c57dbe7..cd061dc4f8 100644 --- a/config/initializers/accession.rb +++ b/config/initializers/accession.rb @@ -9,5 +9,5 @@ end # add ena requirement fields here -ena_requirement_fields = YAML.unsafe_load_file('config/ena_requirement_fields.yml') +ena_requirement_fields = YAML.safe_load_file('config/ena_requirement_fields.yml') Rails.application.config.ena_requirement_fields = ena_requirement_fields.with_indifferent_access diff --git a/config/initializers/failure_reasons.rb b/config/initializers/failure_reasons.rb index 7f3b12f975..4efde706db 100644 --- a/config/initializers/failure_reasons.rb +++ b/config/initializers/failure_reasons.rb @@ -1,2 +1,2 @@ # frozen_string_literal: true -FAILURE_REASONS = YAML.unsafe_load_file("#{Rails.root}/config/failure_reasons.yml") +FAILURE_REASONS = YAML.safe_load_file("#{Rails.root}/config/failure_reasons.yml") diff --git a/config/initializers/psych.rb b/config/initializers/psych.rb deleted file mode 100644 index ee5458f939..0000000000 --- a/config/initializers/psych.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -Rails.application.configure do - # Allow legacy YAML deserialization for ActiveRecord serialized columns - # Ideally replace with `permitted_classes`, such as those used in the list below: - # Symbol - # ActiveSupport::HashWithIndifferentAccess - # ActiveSupport::TimeWithZone - # ActiveSupport::TimeZone - # HashWithIndifferentAccess - # RequestType::Validator::ArrayWithDefault - # RequestType::Validator::LibraryTypeValidator - # RequestType::Validator::FlowcellTypeValidator - # ActionController::Parameters - # Set - # Range - # FieldInfo - # Time - config.active_record.use_yaml_unsafe_load = true -end From dcc7da5cfb02045d0c97c4e7cb9b33f75e219286 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 15:29:16 +0100 Subject: [PATCH 12/57] fix: allow symbol loading for excel helpers --- app/sequencescape_excel/sequencescape_excel/helpers.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/sequencescape_excel/sequencescape_excel/helpers.rb b/app/sequencescape_excel/sequencescape_excel/helpers.rb index 9462318a00..5d018d146b 100644 --- a/app/sequencescape_excel/sequencescape_excel/helpers.rb +++ b/app/sequencescape_excel/sequencescape_excel/helpers.rb @@ -5,7 +5,8 @@ module SequencescapeExcel # Helpers module Helpers def load_file(folder, filename) - YAML.safe_load_file(Rails.root.join(folder, "#{filename}.yml")).with_indifferent_access + file_path = Rails.root.join(folder, "#{filename}.yml") + YAML.safe_load_file(file_path, permitted_classes: [Symbol]).with_indifferent_access end end end From 5f4d038c5cde0a82ad34f4b162a726b8e0aa7dc4 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 15:51:38 +0100 Subject: [PATCH 13/57] fix: allow aliases for excel helper --- app/sequencescape_excel/sequencescape_excel/helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/sequencescape_excel/sequencescape_excel/helpers.rb b/app/sequencescape_excel/sequencescape_excel/helpers.rb index 5d018d146b..0317705694 100644 --- a/app/sequencescape_excel/sequencescape_excel/helpers.rb +++ b/app/sequencescape_excel/sequencescape_excel/helpers.rb @@ -6,7 +6,7 @@ module SequencescapeExcel module Helpers def load_file(folder, filename) file_path = Rails.root.join(folder, "#{filename}.yml") - YAML.safe_load_file(file_path, permitted_classes: [Symbol]).with_indifferent_access + YAML.safe_load_file(file_path, permitted_classes: [Symbol], aliases: true).with_indifferent_access end end end From 08fff5b6ab54635fa0f428b8951ba52864bf9c45 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 15:59:36 +0100 Subject: [PATCH 14/57] fix: allow HashWithIndifferentAccess loading for request options loading --- app/models/submission/request_options_behaviour.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index 7d431e0810..0528997793 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -7,7 +7,7 @@ class HashWrapper def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? - YAML.safe_load(hash_yaml) + YAML.safe_load(hash_yaml, permitted_classes: [ActiveSupport::HashWithIndifferentAccess]) end def self.dump(hash) From a6b82fc06321b77e4fcae69f5b60ec124049f3d2 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 16:15:18 +0100 Subject: [PATCH 15/57] fix: allow request type serialization --- app/models/request_type/validator.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/request_type/validator.rb b/app/models/request_type/validator.rb index d863ac909f..6fea8a85ce 100644 --- a/app/models/request_type/validator.rb +++ b/app/models/request_type/validator.rb @@ -104,7 +104,7 @@ def valid_options belongs_to :request_type, optional: false validates :request_option, :valid_options, presence: true - serialize :valid_options, coder: YAML + serialize :valid_options, coder: YAML, yaml: { permitted_classes: [Range, RequestType::Validator::ArrayWithDefault] } delegate :include?, to: :valid_options From ba929443dbee767963e0befe9bac5e9b9f51f5b8 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 16:43:13 +0100 Subject: [PATCH 16/57] fix: allow additional request type serialization --- app/models/request_type/validator.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/models/request_type/validator.rb b/app/models/request_type/validator.rb index 6fea8a85ce..db32ee1cba 100644 --- a/app/models/request_type/validator.rb +++ b/app/models/request_type/validator.rb @@ -104,7 +104,14 @@ def valid_options belongs_to :request_type, optional: false validates :request_option, :valid_options, presence: true - serialize :valid_options, coder: YAML, yaml: { permitted_classes: [Range, RequestType::Validator::ArrayWithDefault] } + serialize :valid_options, coder: YAML, yaml: { + permitted_classes: [ + Range, + RequestType::Validator::ArrayWithDefault, + RequestType::Validator::FlowcellTypeValidator, + RequestType::Validator::LibraryTypeValidator + ] + } delegate :include?, to: :valid_options From 6846d27daec80b00ee42815592487b524580ea3b Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 16:57:43 +0100 Subject: [PATCH 17/57] fix: allow primer programs serialization --- app/models/primer_panel.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/primer_panel.rb b/app/models/primer_panel.rb index 4b194e01b7..8fd4926bc1 100644 --- a/app/models/primer_panel.rb +++ b/app/models/primer_panel.rb @@ -11,7 +11,7 @@ class PrimerPanel < ApplicationRecord include ActiveModel::Validations include SharedBehaviour::Named - serialize :programs, coder: YAML + serialize :programs, coder: YAML, yaml: { permitted_classes: [ActiveSupport::HashWithIndifferentAccess] } # The name: Used to identify the assay set. validates :name, presence: true From 544c06c3e58e2e4a68b80b467640558e31b3d49e Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 17:01:15 +0100 Subject: [PATCH 18/57] fix: allow broadcase_event serialization --- app/models/broadcast_event.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/broadcast_event.rb b/app/models/broadcast_event.rb index 7493d841e6..e0e9a39796 100644 --- a/app/models/broadcast_event.rb +++ b/app/models/broadcast_event.rb @@ -19,7 +19,7 @@ class BroadcastEvent < ApplicationRecord # https://api.rubyonrails.org/classes/ActiveRecord/Inheritance/ClassMethods.html validates :sti_type, presence: true - serialize :properties, coder: YAML + serialize :properties, coder: YAML, yaml: { permitted_classes: [ActionController::Parameters, ActiveSupport::HashWithIndifferentAccess] } self.inheritance_column = 'sti_type' broadcast_with_warren From c40fd04a725302c9478a04d38870ff27d2a1bf9a Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 30 Apr 2026 17:03:19 +0100 Subject: [PATCH 19/57] fix: alllow loading of request options behaviour --- app/models/submission/request_options_behaviour.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index 0528997793..a47407c45b 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -7,7 +7,7 @@ class HashWrapper def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? - YAML.safe_load(hash_yaml, permitted_classes: [ActiveSupport::HashWithIndifferentAccess]) + YAML.safe_load(hash_yaml, permitted_classes: [ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone]) end def self.dump(hash) From 7bb8f070a9bcc36d0f189657cd0c96da58c8fa76 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Fri, 1 May 2026 08:40:25 +0100 Subject: [PATCH 20/57] fix: allow tag_layout serialization --- app/models/tag_layout.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/tag_layout.rb b/app/models/tag_layout.rb index e85e0a368e..2be3ed563f 100644 --- a/app/models/tag_layout.rb +++ b/app/models/tag_layout.rb @@ -48,7 +48,7 @@ def walking_by self.inheritance_column = 'sti_type' - serialize :substitutions, type: Hash, coder: YAML + serialize :substitutions, type: Hash, coder: YAML, yaml: { permitted_classes: [ActiveSupport::HashWithIndifferentAccess] } # The user performing the layout belongs_to :user, optional: false From a6969d1110f36479260819ed078a72854873fb12 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Fri, 1 May 2026 09:02:42 +0100 Subject: [PATCH 21/57] fix: allow Time for request options behaviour --- app/models/submission/request_options_behaviour.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index a47407c45b..a2725edacf 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -7,7 +7,8 @@ class HashWrapper def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? - YAML.safe_load(hash_yaml, permitted_classes: [ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone]) + YAML.safe_load(hash_yaml, + permitted_classes: [ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone, Time]) end def self.dump(hash) From aba4e203ea90cb62837dbad1402b926de5d3a0f8 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Tue, 5 May 2026 12:43:19 +0100 Subject: [PATCH 22/57] security: allow HashWithIndifferentAccess to be loaded by default --- app/models/broadcast_event.rb | 2 +- app/models/primer_panel.rb | 2 +- app/models/submission/request_options_behaviour.rb | 2 +- config/initializers/psych.rb | 6 ++++++ 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 config/initializers/psych.rb diff --git a/app/models/broadcast_event.rb b/app/models/broadcast_event.rb index e0e9a39796..146c5571ea 100644 --- a/app/models/broadcast_event.rb +++ b/app/models/broadcast_event.rb @@ -19,7 +19,7 @@ class BroadcastEvent < ApplicationRecord # https://api.rubyonrails.org/classes/ActiveRecord/Inheritance/ClassMethods.html validates :sti_type, presence: true - serialize :properties, coder: YAML, yaml: { permitted_classes: [ActionController::Parameters, ActiveSupport::HashWithIndifferentAccess] } + serialize :properties, coder: YAML, yaml: { permitted_classes: [ActionController::Parameters] } self.inheritance_column = 'sti_type' broadcast_with_warren diff --git a/app/models/primer_panel.rb b/app/models/primer_panel.rb index 8fd4926bc1..4b194e01b7 100644 --- a/app/models/primer_panel.rb +++ b/app/models/primer_panel.rb @@ -11,7 +11,7 @@ class PrimerPanel < ApplicationRecord include ActiveModel::Validations include SharedBehaviour::Named - serialize :programs, coder: YAML, yaml: { permitted_classes: [ActiveSupport::HashWithIndifferentAccess] } + serialize :programs, coder: YAML # The name: Used to identify the assay set. validates :name, presence: true diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index a2725edacf..5adc582f7a 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -8,7 +8,7 @@ def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? YAML.safe_load(hash_yaml, - permitted_classes: [ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone, Time]) + permitted_classes: [ActiveSupport::TimeWithZone, Time]) end def self.dump(hash) diff --git a/config/initializers/psych.rb b/config/initializers/psych.rb new file mode 100644 index 0000000000..d4c7384d6e --- /dev/null +++ b/config/initializers/psych.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +Rails.application.configure do + # Allow YAML columns to contain HashWithIndifferentAccess objects by default + ActiveRecord.yaml_column_permitted_classes += [ActiveSupport::HashWithIndifferentAccess] +end From 26c9a5b851320607b819bd7555b7edee28d1a853 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Tue, 5 May 2026 15:00:03 +0100 Subject: [PATCH 23/57] revert: add HashWithIndifferentAccess back to non-ActiveRecord loading --- app/models/submission/request_options_behaviour.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index 5adc582f7a..a2725edacf 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -8,7 +8,7 @@ def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? YAML.safe_load(hash_yaml, - permitted_classes: [ActiveSupport::TimeWithZone, Time]) + permitted_classes: [ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone, Time]) end def self.dump(hash) From 33481a04f3e76afd7afd52b2993d8e9bf4d4e182 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Tue, 5 May 2026 15:09:37 +0100 Subject: [PATCH 24/57] fix: remove already-permitted HashWithIndifferentAccess --- app/models/tag_layout.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/tag_layout.rb b/app/models/tag_layout.rb index 2be3ed563f..e85e0a368e 100644 --- a/app/models/tag_layout.rb +++ b/app/models/tag_layout.rb @@ -48,7 +48,7 @@ def walking_by self.inheritance_column = 'sti_type' - serialize :substitutions, type: Hash, coder: YAML, yaml: { permitted_classes: [ActiveSupport::HashWithIndifferentAccess] } + serialize :substitutions, type: Hash, coder: YAML # The user performing the layout belongs_to :user, optional: false From f61d3227cd9a445fe81bc73cd81906cde620a43d Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Tue, 5 May 2026 15:10:25 +0100 Subject: [PATCH 25/57] fix: allow request options behaviour to load ActiveSupport::TimeZone --- app/models/submission/request_options_behaviour.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index a2725edacf..aeeac42f25 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -7,8 +7,12 @@ class HashWrapper def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? - YAML.safe_load(hash_yaml, - permitted_classes: [ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone, Time]) + YAML.safe_load(hash_yaml, permitted_classes: [ + ActiveSupport::HashWithIndifferentAccess, + ActiveSupport::TimeWithZone, + ActiveSupport::TimeZone, + Time + ]) end def self.dump(hash) From 49b76887d70563d690f405587ddf4125523e540d Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Tue, 5 May 2026 15:14:18 +0100 Subject: [PATCH 26/57] fix: allow request options behaviour to load aliases --- app/models/submission/request_options_behaviour.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index aeeac42f25..b207c98708 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -7,7 +7,7 @@ class HashWrapper def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? - YAML.safe_load(hash_yaml, permitted_classes: [ + YAML.safe_load(hash_yaml, aliases: true, permitted_classes: [ ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone, ActiveSupport::TimeZone, From d4620706999d1c83dd622890d7c21c5378e6c68d Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Tue, 5 May 2026 15:31:23 +0100 Subject: [PATCH 27/57] style: lint --- .../submission/request_options_behaviour.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/app/models/submission/request_options_behaviour.rb b/app/models/submission/request_options_behaviour.rb index b207c98708..24489254f1 100644 --- a/app/models/submission/request_options_behaviour.rb +++ b/app/models/submission/request_options_behaviour.rb @@ -7,7 +7,9 @@ class HashWrapper def self.load(hash_yaml) return hash_yaml if hash_yaml.nil? - YAML.safe_load(hash_yaml, aliases: true, permitted_classes: [ + YAML.safe_load(hash_yaml, + aliases: true, + permitted_classes: [ ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone, ActiveSupport::TimeZone, @@ -33,22 +35,20 @@ def request_options=(options) super end + private + def check_request_options check_multipliers_are_valid end - private :check_request_options - # rubocop:todo Metrics/PerceivedComplexity, Metrics/AbcSize - def check_multipliers_are_valid # rubocop:todo Metrics/CyclomaticComplexity + def check_multipliers_are_valid # rubocop:disable Metrics/CyclomaticComplexity multipliers = request_options.try(:[], :multiplier) return if multipliers.blank? # We're ok with nothing being specified! # TODO[xxx]: should probably error if they've specified a request type that isn't being used - errors.add(:request_options, 'negative multiplier supplied') if multipliers.values.map(&:to_i).any?(&:negative?) - errors.add(:request_options, 'zero multiplier supplied') if multipliers.values.map(&:to_i).any?(&:zero?) + multiplier_values = multipliers.values.map(&:to_i) + errors.add(:request_options, 'negative multiplier supplied') if multiplier_values.any?(&:negative?) + errors.add(:request_options, 'zero multiplier supplied') if multiplier_values.any?(&:zero?) false unless errors.empty? end - - # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity - private :check_multipliers_are_valid end From 582847edda6960f8be977d1920bf003c20fa7933 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Tue, 5 May 2026 15:38:29 +0100 Subject: [PATCH 28/57] style: update rubocop todo --- .rubocop_todo.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index e24bc77a2e..4c111a75c9 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config --no-exclude-limit` -# on 2026-04-22 14:37:36 UTC using RuboCop version 1.86.1. +# on 2026-05-05 14:36:42 UTC using RuboCop version 1.86.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -301,12 +301,11 @@ Lint/UselessOr: - 'app/models/qcable/statemachine.rb' - 'app/models/ui_helper/summary.rb' -# Offense count: 11 +# Offense count: 7 # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max. Metrics/AbcSize: Exclude: - 'app/controllers/api/v2/transfers_controller.rb' - - 'app/controllers/studies/information_controller.rb' - 'app/jobs/export_pool_xp_to_traction_job.rb' - 'app/models/accession_service/base_service.rb' - 'app/sample_manifest_excel/sample_manifest_excel/manifest_type_list.rb' @@ -334,7 +333,7 @@ Metrics/CyclomaticComplexity: - 'app/models/accession_service/base_service.rb' - 'lib/limber/helper.rb' -# Offense count: 18 +# Offense count: 17 # Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns. Metrics/MethodLength: Exclude: @@ -617,7 +616,7 @@ RSpec/BeforeAfterAll: - 'spec/sample_manifest_excel/worksheet_spec.rb' - 'spec/sequencescape_excel/worksheet_spec.rb' -# Offense count: 342 +# Offense count: 330 # Configuration parameters: Prefixes, AllowedPatterns. # Prefixes: when, with, without RSpec/ContextWording: @@ -1196,7 +1195,7 @@ RSpec/MultipleExpectations: - 'spec/views/labware/show_chromium_chip_spec.rb' - 'spec/views/samples/index_html_erb_spec.rb' -# Offense count: 243 +# Offense count: 249 # Configuration parameters: EnforcedStyle, IgnoreSharedExamples. # SupportedStyles: always, named_only RSpec/NamedSubject: From ec6d69c2a26da1ec046e946602ed3b76b820c157 Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Wed, 6 May 2026 15:28:48 +0100 Subject: [PATCH 29/57] wip(qc_report): adds plate barcodes to qc report model --- app/models/qc_report.rb | 1 + .../20260506135637_add_plate_barcodes_to_qc_reports.rb | 6 ++++++ db/schema.rb | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20260506135637_add_plate_barcodes_to_qc_reports.rb diff --git a/app/models/qc_report.rb b/app/models/qc_report.rb index 771890b6f1..363e03677e 100644 --- a/app/models/qc_report.rb +++ b/app/models/qc_report.rb @@ -128,6 +128,7 @@ def generate_report # rubocop:todo Metrics/AbcSize has_many :qc_metrics serialize :plate_purposes, type: Array, coder: YAML + serialize :plate_barcodes, type: Array, coder: YAML before_validation :generate_report_identifier, if: :identifier_required? diff --git a/db/migrate/20260506135637_add_plate_barcodes_to_qc_reports.rb b/db/migrate/20260506135637_add_plate_barcodes_to_qc_reports.rb new file mode 100644 index 0000000000..ca681b46be --- /dev/null +++ b/db/migrate/20260506135637_add_plate_barcodes_to_qc_reports.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +class AddPlateBarcodesToQcReports < ActiveRecord::Migration[8.0] + def change + add_column :qc_reports, :plate_barcodes, :text, size: :medium + end +end diff --git a/db/schema.rb b/db/schema.rb index 43fff10f6a..099a46426b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2026_04_24_000000) do +ActiveRecord::Schema[8.0].define(version: 2026_05_06_135637) do create_table "accession_sample_statuses", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| t.integer "sample_id", null: false t.string "status", null: false @@ -1053,6 +1053,7 @@ t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false t.text "plate_purposes", size: :medium + t.text "plate_barcodes", size: :medium t.index ["product_criteria_id"], name: "fk_qc_reports_to_product_criteria" t.index ["report_identifier"], name: "index_qc_reports_on_report_identifier", unique: true t.index ["study_id"], name: "fk_qc_reports_to_studies" From 02f3dd35acb7addb8b3c043f9f15b3d70db00080 Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Thu, 7 May 2026 15:12:25 +0100 Subject: [PATCH 30/57] feat(qc_report): adds plate_barcodes to qc_report --- app/controllers/qc_reports_controller.rb | 11 +++++++++- app/models/qc_report.rb | 9 +++++++++ app/views/qc_reports/_qc_report_form.html.erb | 8 ++++++++ spec/models/qc_report_spec.rb | 20 +++++++++++++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/app/controllers/qc_reports_controller.rb b/app/controllers/qc_reports_controller.rb index 28da03c102..15e458aef1 100644 --- a/app/controllers/qc_reports_controller.rb +++ b/app/controllers/qc_reports_controller.rb @@ -45,12 +45,14 @@ def show # rubocop:todo Metrics/AbcSize def create # rubocop:todo Metrics/AbcSize, Metrics/MethodLength study = Study.find_by(id: params[:qc_report][:study_id]) exclude_existing = params[:qc_report][:exclude_existing] == '1' + plate_barcodes = format_plate_barcodes(params[:qc_report][:plate_barcodes_text]) qc_report = QcReport.new( study: study, product_criteria: @product.stock_criteria, exclude_existing: exclude_existing, - plate_purposes: params[:qc_report][:plate_purposes].try(:reject, &:blank?) + plate_purposes: params[:qc_report][:plate_purposes].try(:reject, &:blank?), + plate_barcodes: plate_barcodes ) if qc_report.save @@ -64,6 +66,13 @@ def create # rubocop:todo Metrics/AbcSize, Metrics/MethodLength end end + def format_plate_barcodes(plate_barcodes) + return nil if plate_barcodes.nil? + + # Split the input string into an array of barcodes + plate_barcodes.split(/\s+/) + end + # On form submit of a qc_file. Strictly speaking this should be an update action # on the qc_report itself. However we don't want to force the user to extract # the report identifier from the file. diff --git a/app/models/qc_report.rb b/app/models/qc_report.rb index 363e03677e..c78a57f3f9 100644 --- a/app/models/qc_report.rb +++ b/app/models/qc_report.rb @@ -140,6 +140,15 @@ def generate_report # rubocop:todo Metrics/AbcSize validates :exclude_existing, inclusion: { in: [true, false], message: 'should be true or false.' } + validate :check_valid_plate_barcodes, if: -> { plate_barcodes.present? } + + def check_valid_plate_barcodes + invalid_barcodes = plate_barcodes.reject { |barcode| Plate.find_by_barcode(barcode) } + return unless invalid_barcodes.any? + + errors.add(:plate_barcodes, "contain invalid barcodes: #{invalid_barcodes.join(', ')}") + end + # Reports are handled asynchronously def schedule_report Delayed::Job.enqueue QcReportJob.new(id) diff --git a/app/views/qc_reports/_qc_report_form.html.erb b/app/views/qc_reports/_qc_report_form.html.erb index 31ef040db3..c7745ddd0e 100644 --- a/app/views/qc_reports/_qc_report_form.html.erb +++ b/app/views/qc_reports/_qc_report_form.html.erb @@ -11,6 +11,14 @@ <%= f.label "plate_purposes", 'Plate purpose' %> <%= f.select :plate_purposes, plate_purposes, { prompt: 'Select plate purpose...', disabled: [''], selected: 'Stock Plate'}, { autocomplete: 'off', class: 'form-control select2', multiple: 'multiple' } %> +
+ <%= f.label "plate_barcodes_text", 'Plate barcodes' %> + <%= text_area_tag 'qc_report[plate_barcodes_text]', + params.dig(:qc_report, :plate_barcodes_text), + cols: 40, + rows: 4, + class: 'form-control' %> +
<%= f.label "exclude_existing", 'Exclude samples with existing reports' %> <%= f.check_box :exclude_existing %> diff --git a/spec/models/qc_report_spec.rb b/spec/models/qc_report_spec.rb index fdfd3e9803..8b28c6f3b0 100644 --- a/spec/models/qc_report_spec.rb +++ b/spec/models/qc_report_spec.rb @@ -11,6 +11,26 @@ expect(build(:qc_report, product_criteria: nil)).not_to be_valid end + context 'plate_barcodes' do + it 'is not valid if it is present and contains invalid barcodes' do + plate = create(:plate) + report = build(:qc_report, plate_barcodes: [plate.human_barcode.to_s, 'INVALID1', 'INVALID2']) + expect(report).not_to be_valid + expect(report.errors[:plate_barcodes]).to include('contain invalid barcodes: INVALID1, INVALID2') + end + + it 'is valid if all barcodes are valid' do + plates = create_list(:plate, 2) + report = build(:qc_report, plate_barcodes: [plates[0].human_barcode.to_s, plates[1].human_barcode.to_s]) + expect(report).to be_valid + end + + it 'is valid if it is nil' do + report = build(:qc_report, plate_barcodes: nil) + expect(report).to be_valid + end + end + context 'include existing' do attr_reader :study, :other_study, :stock_plate, :qc_report, :qc_metric_count From 65a4992dbfc5abac91ac884c34ff88ca2c84f21e Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Thu, 7 May 2026 15:30:02 +0100 Subject: [PATCH 31/57] feat(qc_report): remove requirement for study if plate_barcodes are present --- app/models/qc_report.rb | 5 ++- app/views/qc_reports/_qc_report_form.html.erb | 2 +- ...135637_add_plate_barcodes_to_qc_reports.rb | 3 ++ db/schema.rb | 2 +- spec/models/qc_report_spec.rb | 44 +++++++++++++++++-- 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/app/models/qc_report.rb b/app/models/qc_report.rb index c78a57f3f9..88ebd0b3e1 100644 --- a/app/models/qc_report.rb +++ b/app/models/qc_report.rb @@ -136,12 +136,15 @@ def generate_report # rubocop:todo Metrics/AbcSize scope :for_report_page, ->(conditions) { order(id: :desc).where(conditions).joins(:product_criteria) } - validates :product_criteria, :study, :state, presence: true + validates :product_criteria, :state, presence: true validates :exclude_existing, inclusion: { in: [true, false], message: 'should be true or false.' } validate :check_valid_plate_barcodes, if: -> { plate_barcodes.present? } + # We allow null values for study_id to allow qc_reports to be create without a study (just plate_barcodes) + validates :study_id, presence: true, unless: -> { plate_barcodes.present? } + def check_valid_plate_barcodes invalid_barcodes = plate_barcodes.reject { |barcode| Plate.find_by_barcode(barcode) } return unless invalid_barcodes.any? diff --git a/app/views/qc_reports/_qc_report_form.html.erb b/app/views/qc_reports/_qc_report_form.html.erb index c7745ddd0e..6e11169636 100644 --- a/app/views/qc_reports/_qc_report_form.html.erb +++ b/app/views/qc_reports/_qc_report_form.html.erb @@ -1,7 +1,7 @@ <%= form_for(qc_report) do |f| %>
<%= f.label "study_id", 'Study' %> - <%= f.select :study_id, studies, {prompt: 'Select study...', disabled: ['']}, { autocomplete: 'off', required: 'required', class: 'form-control select2' } %> + <%= f.select :study_id, studies, {prompt: 'Select study...', disabled: ['']}, { autocomplete: 'off', class: 'form-control select2' } %>
<%= f.label "product_id", 'Product' %> diff --git a/db/migrate/20260506135637_add_plate_barcodes_to_qc_reports.rb b/db/migrate/20260506135637_add_plate_barcodes_to_qc_reports.rb index ca681b46be..af45c4b4c2 100644 --- a/db/migrate/20260506135637_add_plate_barcodes_to_qc_reports.rb +++ b/db/migrate/20260506135637_add_plate_barcodes_to_qc_reports.rb @@ -2,5 +2,8 @@ class AddPlateBarcodesToQcReports < ActiveRecord::Migration[8.0] def change add_column :qc_reports, :plate_barcodes, :text, size: :medium + + # Allow null values for study_id to allow qc_reports to be create without a study (just plate_barcodes) + change_column_null :qc_reports, :study_id, true end end diff --git a/db/schema.rb b/db/schema.rb index 099a46426b..24e210ae0e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1046,7 +1046,7 @@ create_table "qc_reports", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", options: "ENGINE=InnoDB ROW_FORMAT=DYNAMIC", force: :cascade do |t| t.string "report_identifier", null: false - t.integer "study_id", null: false + t.integer "study_id" t.integer "product_criteria_id", null: false t.boolean "exclude_existing", null: false t.string "state" diff --git a/spec/models/qc_report_spec.rb b/spec/models/qc_report_spec.rb index 8b28c6f3b0..9be846e2e0 100644 --- a/spec/models/qc_report_spec.rb +++ b/spec/models/qc_report_spec.rb @@ -3,8 +3,16 @@ require 'rails_helper' RSpec.describe QcReport do - it 'is not valid without a study' do - expect(build(:qc_report, study: nil)).not_to be_valid + context 'study' do + it 'is not valid without a study if plate_barcodes are not present' do + expect(build(:qc_report, study: nil, plate_barcodes: nil)).not_to be_valid + end + + it 'is valid without a study if plate_barcodes are present' do + plate = create(:plate) + report = build(:qc_report, study: nil, plate_barcodes: [plate.human_barcode.to_s]) + expect(report).to be_valid + end end it 'is not valid without a product criteria' do @@ -25,8 +33,8 @@ expect(report).to be_valid end - it 'is valid if it is nil' do - report = build(:qc_report, plate_barcodes: nil) + it 'is valid if it is nil and a study is present' do + report = build(:qc_report, plate_barcodes: nil, study: create(:study)) expect(report).to be_valid end end @@ -219,4 +227,32 @@ expect(qc_report.qc_metrics.count).to eq(3) end end + + context 'limit by plate barcodes' do + attr_reader :qc_report + + let!(:study) { create(:study) } + let(:plates) { create_list(:plate, 4, plate_purpose: PlatePurpose.find_or_create_by(name: 'Stock plate')) } + + before do + create(:well_for_qc_report, study: study, plate: plates[0]) + create(:well_for_qc_report, study: study, plate: plates[1]) + create(:well_for_qc_report, study: study, plate: plates[2]) + + @qc_report = + create( + :qc_report, + study: study, + exclude_existing: false, + product_criteria: create(:product_criteria), + plate_purposes: [], + plate_barcodes: plates.map(&:human_barcode) + ) + qc_report.generate! + end + + it 'generates qc_metrics per sample which needs them' do + expect(qc_report.qc_metrics.count).to eq(3) + end + end end From 0888738d6aebce8c5ad15f21988a12c6c89f270c Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Fri, 8 May 2026 13:33:50 +0100 Subject: [PATCH 32/57] feat(qc_report): moves well calculations to Well model from Study, adds scope to filter plate_barcodes --- app/models/qc_report.rb | 8 +- app/models/study.rb | 14 ---- app/models/well.rb | 26 +++++- spec/models/study_spec.rb | 34 -------- spec/models/well_spec.rb | 168 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+), 52 deletions(-) diff --git a/app/models/qc_report.rb b/app/models/qc_report.rb index 88ebd0b3e1..31ebe24c9b 100644 --- a/app/models/qc_report.rb +++ b/app/models/qc_report.rb @@ -82,10 +82,12 @@ module ReportBehaviour # You can trigger a synchronous report manually by calling #generate! # rubocop:todo Metrics/MethodLength def generate_report # rubocop:todo Metrics/AbcSize - study.each_well_for_qc_report_in_batches( + Well.qc_report_in_batches( + study, exclude_existing, product_criteria, - (plate_purposes.empty? ? nil : plate_purposes) + (plate_purposes.empty? ? nil : plate_purposes), + (plate_barcodes.empty? ? nil : plate_barcodes) ) do |assets| # If there are some wells of interest, we get them in a list connected_wells = Well.hash_stock_with_targets(assets, product_criteria.target_plate_purposes) @@ -142,7 +144,7 @@ def generate_report # rubocop:todo Metrics/AbcSize validate :check_valid_plate_barcodes, if: -> { plate_barcodes.present? } - # We allow null values for study_id to allow qc_reports to be create without a study (just plate_barcodes) + # We allow null values for study_id to allow qc_reports to be created without a study (just plate_barcodes) validates :study_id, presence: true, unless: -> { plate_barcodes.present? } def check_valid_plate_barcodes diff --git a/app/models/study.rb b/app/models/study.rb index 0d2f9127fc..c752cb02a2 100644 --- a/app/models/study.rb +++ b/app/models/study.rb @@ -446,20 +446,6 @@ def validate_ethically_approved false end - def each_well_for_qc_report_in_batches(exclude_existing, product_criteria, plate_purposes = nil) - # @note We include aliquots here, despite the fact they are only needed if we have to set a poor-quality flag - # as in some cases failures are not as rare as you may imagine, and it can cause major performance issues. - base_scope = - Well - .on_plate_purpose_included(PlatePurpose.where(name: plate_purposes || STOCK_PLATE_PURPOSES)) - .for_study_through_aliquot(self) - .without_blank_samples - .includes(:well_attribute, :aliquots, :map, samples: :sample_metadata) - .readonly(true) - scope = exclude_existing ? base_scope.without_report(product_criteria) : base_scope - scope.find_in_batches { |wells| yield wells } - end - def warnings # These studies are now invalid, but the warning should remain until existing studies are fixed. if study_metadata.managed? && study_metadata.data_access_group.blank? diff --git a/app/models/well.rb b/app/models/well.rb index 3cc7b94c57..8e033486c9 100644 --- a/app/models/well.rb +++ b/app/models/well.rb @@ -91,6 +91,14 @@ def poly_metadata scope :on_plate_purpose, ->(purposes) { joins(:labware).where(labware: { plate_purpose_id: purposes }) } + scope :on_plate_barcode, + lambda { |*barcodes| + if barcodes.flatten.any? + db_barcodes = Barcode.extract_barcodes(barcodes) + joins(labware: :barcodes).where(barcodes: { barcode: db_barcodes }).distinct + end + } + # added version of scope with includes to avoid multiple calls to LabWhere in qc report when getting storage location # for wells in the same plate scope :on_plate_purpose_included, @@ -98,7 +106,9 @@ def poly_metadata includes(labware: :barcodes).references(:labware).where(labware: { plate_purpose_id: purposes }) end - scope :for_study_through_aliquot, ->(study) { joins(:aliquots).where(aliquots: { study_id: study }) } + scope :for_study_through_aliquot, ->(study) { + joins(:aliquots).where(aliquots: { study_id: study }) if study.present? + } scope :with_report, ->(product_criteria) do @@ -369,4 +379,18 @@ def library_name def empty? aliquots.blank? end + + def self.qc_report_in_batches(study, exclude_existing, product_criteria, plate_purposes, plate_barcodes, &) + # @note We include aliquots here, despite the fact they are only needed if we have to set a poor-quality flag + # as in some cases failures are not as rare as you may imagine, and it can cause major performance issues. + base_scope = + for_study_through_aliquot(study) + .on_plate_barcode(plate_barcodes) + .on_plate_purpose_included(PlatePurpose.where(name: plate_purposes || Study::STOCK_PLATE_PURPOSES)) + .without_blank_samples + .includes(:well_attribute, :aliquots, :map, samples: :sample_metadata) + .readonly(true) + scope = exclude_existing ? base_scope.without_report(product_criteria) : base_scope + scope.find_in_batches(&) + end end diff --git a/spec/models/study_spec.rb b/spec/models/study_spec.rb index 7057d7237f..5c875d158e 100644 --- a/spec/models/study_spec.rb +++ b/spec/models/study_spec.rb @@ -346,40 +346,6 @@ expect(study.name).to eq('Squish double spaces and flanking whitespace but not double letters') end end - - describe '#each_well_for_qc_report_in_batches' do - let!(:study) { create(:study) } - let(:purpose_1) { PlatePurpose.stock_plate_purpose } - let(:purpose_2) { create(:plate_purpose) } - let(:purpose_3) { create(:plate_purpose) } - let(:purpose_4) { create(:plate_purpose) } - let!(:well_1) { create(:well_for_qc_report, study: study, plate: create(:plate, plate_purpose: purpose_1)) } - let!(:well_2) { create(:well_for_qc_report, study: study, plate: create(:plate, plate_purpose: purpose_2)) } - let!(:well_3) { create(:well_for_qc_report, study: study, plate: create(:plate, plate_purpose: purpose_3)) } - let!(:well_4) { create(:well_for_qc_report, study: study, plate: create(:plate, plate_purpose: purpose_4)) } - - it 'will limit by stock plate purposes if there are no plate purposes' do - wells_count = 0 - study.each_well_for_qc_report_in_batches(false, 'Bespoke RNA') { |wells| wells_count += wells.length } - expect(wells_count).to eq(1) - end - - it 'will limit by passed plates purposes' do - wells_count = 0 - study.each_well_for_qc_report_in_batches( - false, - 'Bespoke RNA', - [purpose_2.name, purpose_3.name, purpose_4.name] - ) { |wells| wells_count += wells.length } - expect(wells_count).to eq(3) - - wells_count = 0 - study.each_well_for_qc_report_in_batches(false, 'Bespoke RNA', [purpose_2.name, purpose_3.name]) do |wells| - wells_count += wells.length - end - expect(wells_count).to eq(2) - end - end end describe '#mailing_list_of_managers' do diff --git a/spec/models/well_spec.rb b/spec/models/well_spec.rb index 31e2601abe..5ad7138988 100644 --- a/spec/models/well_spec.rb +++ b/spec/models/well_spec.rb @@ -867,4 +867,172 @@ end.to change(Warren.handler.messages, :count).from(0) end end + + describe '.on_plate_purpose_included' do + let(:purpose_1) { create(:plate_purpose) } + let(:purpose_2) { create(:plate_purpose) } + let(:well_1) { create(:well, plate: create(:plate, plate_purpose: purpose_1)) } + let(:well_2) { create(:well, plate: create(:plate, plate_purpose: purpose_2)) } + + it 'returns wells with the included plate purposes' do + expect(described_class.on_plate_purpose_included([purpose_1])).to eq([well_1]) + expect(described_class.on_plate_purpose_included([purpose_2])).to eq([well_2]) + expect(described_class.on_plate_purpose_included([purpose_1, + purpose_2])).to contain_exactly(well_1, well_2) + end + + it 'returns an empty array if no wells have the included plate purposes' do + expect(described_class.on_plate_purpose_included(['Non-existent Purpose'])).to eq([]) + end + end + + describe '.on_plate_barcode' do + let(:plate_1) { create(:plate) } + let(:plate_2) { create(:plate) } + let(:well_1) { create(:well, plate: plate_1) } + let(:well_2) { create(:well, plate: plate_2) } + + it 'returns wells with the included plate barcodes' do + expect(described_class.on_plate_barcode([plate_1.human_barcode])).to eq([well_1]) + expect(described_class.on_plate_barcode([plate_2.human_barcode])).to eq([well_2]) + expect(described_class.on_plate_barcode([plate_1.human_barcode, + plate_2.human_barcode])).to contain_exactly(well_1, well_2) + end + + it 'returns an empty array if no wells have the included plate barcodes' do + expect(described_class.on_plate_barcode(['Non-existent Barcode'])).to eq([]) + end + end + + describe '.for_study_through_aliquot' do + let(:study) { create(:study) } + let(:aliquot_1) { create(:aliquot, study:) } + let(:aliquot_2) { create(:aliquot, study:) } + let(:well_1) { create(:well, aliquots: [aliquot_1]) } + let(:well_2) { create(:well, aliquots: [aliquot_2]) } + + it 'returns wells for the given study through their aliquots' do + expect(described_class.for_study_through_aliquot(study)).to contain_exactly(well_1, well_2) + end + + it 'returns an empty array if no wells are associated with the given study through their aliquots' do + other_study = create(:study) + expect(described_class.for_study_through_aliquot(other_study)).to eq([]) + end + end + + describe '.without_blank_samples' do + let(:study) { create(:study) } + let(:aliquot_1) { create(:aliquot, study: study, sample: create(:sample, empty_supplier_sample_name: false)) } + let(:aliquot_2) { create(:aliquot, study: study, sample: create(:sample, empty_supplier_sample_name: true)) } + let(:well_1) { create(:well, aliquots: [aliquot_1]) } + let(:well_2) { create(:well, aliquots: [aliquot_2]) } + + it 'returns wells with samples without empty_supplier_sample_names' do + expect(described_class.without_blank_samples).to contain_exactly(well_1) + end + + it 'returns an empty array if no wells contain samples without empty_supplier_sample_names' do + aliquot_2.sample.update(empty_supplier_sample_name: true) + expect(described_class.without_blank_samples).to eq([]) + end + end + + describe '#qc_report_in_batches' do + let!(:study) { create(:study) } + let(:purpose_1) { PlatePurpose.stock_plate_purpose } + let(:purpose_2) { create(:plate_purpose) } + let(:purpose_3) { create(:plate_purpose) } + let(:purpose_4) { create(:plate_purpose) } + + before do + create(:well_for_qc_report, study: study, plate: create(:plate, plate_purpose: purpose_1)) + create(:well_for_qc_report, study: study, plate: create(:plate, plate_purpose: purpose_2)) + create(:well_for_qc_report, study: study, plate: create(:plate, plate_purpose: purpose_3)) + create(:well_for_qc_report, study: study, plate: create(:plate, plate_purpose: purpose_4)) + end + + it 'limits by stock plate purposes if there are no plate purposes' do + wells_count = 0 + described_class.qc_report_in_batches(study, false, 'Bespoke RNA', nil, nil) do |wells| + wells_count += wells.length + end + expect(wells_count).to eq(1) + end + + it 'limits by passed plates purposes' do + wells_count = 0 + described_class.qc_report_in_batches( + study, + false, + 'Bespoke RNA', + [purpose_2.name, purpose_3.name, purpose_4.name], + nil + ) { |wells| wells_count += wells.length } + expect(wells_count).to eq(3) + + wells_count = 0 + described_class.qc_report_in_batches(study, false, 'Bespoke RNA', [purpose_2.name, purpose_3.name], + nil) do |wells| + wells_count += wells.length + end + expect(wells_count).to eq(2) + end + + it 'limits by plate barcodes' do + plate_1 = create(:plate, plate_purpose: purpose_1) + plate_2 = create(:plate, plate_purpose: purpose_1) + create(:well_for_qc_report, study: study, plate: plate_1) + create(:well_for_qc_report, study: study, plate: plate_2) + + wells_count = 0 + described_class.qc_report_in_batches(nil, false, 'Bespoke RNA', [purpose_1.name], + [plate_1.human_barcode]) do |wells| + wells_count += wells.length + end + expect(wells_count).to eq(1) + end + + it 'limits by study' do + study_2 = create(:study) + create(:well_for_qc_report, study: study_2, plate: create(:plate, plate_purpose: purpose_1)) + + wells_count = 0 + described_class.qc_report_in_batches(study, false, 'Bespoke RNA', [purpose_1.name], nil) do |wells| + wells_count += wells.length + end + expect(wells_count).to eq(1) + end + + context 'combination of filters' do + it 'limits by study and plate purposes' do + study_2 = create(:study) + create(:well_for_qc_report, study: study_2, plate: create(:plate, plate_purpose: purpose_2)) + + wells_count = 0 + described_class.qc_report_in_batches(study, false, 'Bespoke RNA', [purpose_2.name], nil) do |wells| + wells_count += wells.length + end + expect(wells_count).to eq(1) + end + + it 'limits by study, plate purposes and plate barcodes' do + study_2 = create(:study) + plate_1 = create(:plate, plate_purpose: purpose_2) + plate_2 = create(:plate, plate_purpose: purpose_2) + create(:well_for_qc_report, study: study_2, plate: plate_1) + create(:well_for_qc_report, study: study_2, plate: plate_2) + + wells_count = 0 + described_class.qc_report_in_batches( + study_2, + false, + 'Bespoke RNA', + [purpose_2.name], + [plate_1.human_barcode] + ) { |wells| wells_count += wells.length } + expect(wells_count).to eq(1) + end + end + end end From 4b1b70ddec548ce4b7ffd0e454ffdeea778ae549 Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Fri, 8 May 2026 14:10:30 +0100 Subject: [PATCH 33/57] fix(qc_report): validate study instead of study_id --- app/models/qc_report.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/qc_report.rb b/app/models/qc_report.rb index 31ebe24c9b..8c8558cb53 100644 --- a/app/models/qc_report.rb +++ b/app/models/qc_report.rb @@ -145,7 +145,7 @@ def generate_report # rubocop:todo Metrics/AbcSize validate :check_valid_plate_barcodes, if: -> { plate_barcodes.present? } # We allow null values for study_id to allow qc_reports to be created without a study (just plate_barcodes) - validates :study_id, presence: true, unless: -> { plate_barcodes.present? } + validates :study, presence: true, unless: -> { plate_barcodes.present? } def check_valid_plate_barcodes invalid_barcodes = plate_barcodes.reject { |barcode| Plate.find_by_barcode(barcode) } From c03712a261d82ed9daf75608da8ee8ba3c5e95ae Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Mon, 11 May 2026 10:45:12 +0100 Subject: [PATCH 34/57] feat(qc_report): removes default plate_purposes if plate_barcodes are present --- app/models/well.rb | 9 +++++++-- spec/models/well_spec.rb | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/models/well.rb b/app/models/well.rb index 8e033486c9..73eedae295 100644 --- a/app/models/well.rb +++ b/app/models/well.rb @@ -103,7 +103,9 @@ def poly_metadata # for wells in the same plate scope :on_plate_purpose_included, ->(purposes) do - includes(labware: :barcodes).references(:labware).where(labware: { plate_purpose_id: purposes }) + if purposes.any? + includes(labware: :barcodes).references(:labware).where(labware: { plate_purpose_id: purposes }) + end end scope :for_study_through_aliquot, ->(study) { @@ -383,10 +385,13 @@ def empty? def self.qc_report_in_batches(study, exclude_existing, product_criteria, plate_purposes, plate_barcodes, &) # @note We include aliquots here, despite the fact they are only needed if we have to set a poor-quality flag # as in some cases failures are not as rare as you may imagine, and it can cause major performance issues. + # Plate purposes don't need to be specified if plate_barcodes are provided. + # If they are not, then we default to the stock plate purposes if not plate_purposes are provided. + default_plate_purposes = plate_barcodes.present? ? nil : Study::STOCK_PLATE_PURPOSES base_scope = for_study_through_aliquot(study) .on_plate_barcode(plate_barcodes) - .on_plate_purpose_included(PlatePurpose.where(name: plate_purposes || Study::STOCK_PLATE_PURPOSES)) + .on_plate_purpose_included(PlatePurpose.where(name: plate_purposes || default_plate_purposes)) .without_blank_samples .includes(:well_attribute, :aliquots, :map, samples: :sample_metadata) .readonly(true) diff --git a/spec/models/well_spec.rb b/spec/models/well_spec.rb index 5ad7138988..3cdeaad2d9 100644 --- a/spec/models/well_spec.rb +++ b/spec/models/well_spec.rb @@ -884,6 +884,10 @@ it 'returns an empty array if no wells have the included plate purposes' do expect(described_class.on_plate_purpose_included(['Non-existent Purpose'])).to eq([]) end + + it 'returns an empty array if no purposes are included' do + expect(described_class.on_plate_purpose_included([])).to eq([]) + end end describe '.on_plate_barcode' do @@ -952,7 +956,7 @@ create(:well_for_qc_report, study: study, plate: create(:plate, plate_purpose: purpose_4)) end - it 'limits by stock plate purposes if there are no plate purposes' do + it 'limits by stock plate purposes if there are no plate purposes and no plate barcodes' do wells_count = 0 described_class.qc_report_in_batches(study, false, 'Bespoke RNA', nil, nil) do |wells| wells_count += wells.length @@ -960,6 +964,17 @@ expect(wells_count).to eq(1) end + it 'does not limit by stock plate purposes if there are no plate purposes but there are plate barcodes' do + plate_1 = create(:plate, plate_purpose: purpose_4) + create(:well_for_qc_report, study: study, plate: plate_1) + + wells_count = 0 + described_class.qc_report_in_batches(study, false, 'Bespoke RNA', nil, [plate_1.human_barcode]) do |wells| + wells_count += wells.length + end + expect(wells_count).to eq(1) + end + it 'limits by passed plates purposes' do wells_count = 0 described_class.qc_report_in_batches( From 851d4e4ccd271a2b07c0970c745b934afd3972ab Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Mon, 11 May 2026 11:07:36 +0100 Subject: [PATCH 35/57] fix(qc_report): remove further dependency on study --- app/models/presenters/qc_report_presenter.rb | 2 +- app/models/qc_report.rb | 4 ++-- app/views/qc_reports/_qc_reports.html.erb | 2 +- app/views/qc_reports/show.html.erb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/models/presenters/qc_report_presenter.rb b/app/models/presenters/qc_report_presenter.rb index d9e83a0d44..f549ece1d1 100644 --- a/app/models/presenters/qc_report_presenter.rb +++ b/app/models/presenters/qc_report_presenter.rb @@ -31,7 +31,7 @@ def product_name end def study_name - qc_report.study.name + qc_report.study&.name || 'N/A' end def study_abbreviation diff --git a/app/models/qc_report.rb b/app/models/qc_report.rb index 8c8558cb53..317912a24f 100644 --- a/app/models/qc_report.rb +++ b/app/models/qc_report.rb @@ -181,9 +181,9 @@ def identifier_required? # same product / study abbreviation combo within one second # of each other. def generate_report_identifier - return true if study.nil? || product_criteria.nil? + return true if product_criteria.nil? - rid = [study.abbreviation, product_criteria.product.name, DateTime.now.to_fs(:number)].compact + rid = [study&.abbreviation, product_criteria.product.name, DateTime.now.to_fs(:number)].compact .join('_') .downcase .gsub(/[^\w]/, '_') diff --git a/app/views/qc_reports/_qc_reports.html.erb b/app/views/qc_reports/_qc_reports.html.erb index cd454acf1b..689824e6c5 100644 --- a/app/views/qc_reports/_qc_reports.html.erb +++ b/app/views/qc_reports/_qc_reports.html.erb @@ -12,7 +12,7 @@ <% qc_reports.each do |report| %> - <%= report.study.name %> + <%= report.study&.name || 'N/A'%> <%= report.product.name %> <%= report.state.humanize %> <%= report.created_at.to_formatted_s(:sortable) %> diff --git a/app/views/qc_reports/show.html.erb b/app/views/qc_reports/show.html.erb index 614c372819..85c2b40211 100644 --- a/app/views/qc_reports/show.html.erb +++ b/app/views/qc_reports/show.html.erb @@ -1,4 +1,4 @@ -

<%= @report_presenter.product_name %> report for <%= link_to(@report_presenter.study_name,@report_presenter.study) %>

+

<%= @report_presenter.product_name %> report for <%= @report_presenter.study.present? ? link_to(@report_presenter.study_name,@report_presenter.study) : 'N/A' %>

<% @report_presenter.each_header do |attribute,value| %> From 06e84a2bca93eda76282fc32a94a9081ae709ee6 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Mon, 11 May 2026 15:51:51 +0100 Subject: [PATCH 36/57] build: upgrade to record_loader 1.0.0 --- Gemfile | 6 +----- Gemfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index 2c7017c5a1..a06153f792 100644 --- a/Gemfile +++ b/Gemfile @@ -21,10 +21,6 @@ group :default do gem 'faraday-multipart' gem 'rest-client' # Deprecated, but still used in some places, replace with Faraday where possible - # Fix incompatibility with between Ruby 3.1 and Psych 4 (used for yaml) - # see https://stackoverflow.com/a/71192990 - gem 'psych', '< 4' - # State machine gem 'aasm' gem 'after_commit_everywhere', '~> 1.0' # Required by AASM @@ -34,7 +30,7 @@ group :default do # Provides bulk insert capabilities gem 'activerecord-import' - gem 'record_loader', git: 'https://github.com/sanger/record_loader', tag: 'v0.3.0' + gem 'record_loader', git: 'https://github.com/sanger/record_loader', tag: 'v1.0.0' gem 'mysql2', platforms: :mri gem 'will_paginate' diff --git a/Gemfile.lock b/Gemfile.lock index 1844fa9471..b22d7e57ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,10 +16,11 @@ GIT GIT remote: https://github.com/sanger/record_loader - revision: 238db7fa24fffee5ad413bd9cd4c6b857d1626c9 - tag: v0.3.0 + revision: c6e73543bf610fdde2633c362430ab27e5508f5a + tag: v1.0.0 specs: - record_loader (0.3.0) + record_loader (1.0.0) + psych (~> 3.0) GIT remote: https://github.com/sanger/sanger_barcode_format.git @@ -716,7 +717,6 @@ DEPENDENCIES prettier_print pry-byebug pry-rails - psych (< 4) puma rack-acceptable rack-cors From fb407d970b1f726d736eb20f9fa9d694de5a8d1c Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Mon, 11 May 2026 17:03:15 +0100 Subject: [PATCH 37/57] build: upgrade to record_loader 1.1.0 --- Gemfile | 2 +- Gemfile.lock | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Gemfile b/Gemfile index a06153f792..e8e4f3a7cb 100644 --- a/Gemfile +++ b/Gemfile @@ -30,7 +30,7 @@ group :default do # Provides bulk insert capabilities gem 'activerecord-import' - gem 'record_loader', git: 'https://github.com/sanger/record_loader', tag: 'v1.0.0' + gem 'record_loader', git: 'https://github.com/sanger/record_loader', tag: 'v1.1.0' gem 'mysql2', platforms: :mri gem 'will_paginate' diff --git a/Gemfile.lock b/Gemfile.lock index b22d7e57ee..6e221cab5b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,11 +16,11 @@ GIT GIT remote: https://github.com/sanger/record_loader - revision: c6e73543bf610fdde2633c362430ab27e5508f5a - tag: v1.0.0 + revision: 9e7f1b67c3eeee1895f1befc6a54682f11c02135 + tag: v1.1.0 specs: - record_loader (1.0.0) - psych (~> 3.0) + record_loader (1.1.0) + psych (~> 5.0) GIT remote: https://github.com/sanger/sanger_barcode_format.git @@ -379,7 +379,9 @@ GEM pry (>= 0.13, < 0.17) pry-rails (0.3.11) pry (>= 0.13.0) - psych (3.3.4) + psych (5.3.1) + date + stringio public_suffix (7.0.5) puma (7.2.0) nio4r (~> 2.0) @@ -590,6 +592,7 @@ GEM rbtree set (~> 1.0) ssrf_filter (1.2.0) + stringio (3.2.0) syntax_tree (6.3.0) prettier_print (>= 1.2.0) syntax_tree-haml (4.0.3) From 4eef83b45fc5ee6fa13eb1ba635a0f06c8d42c88 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 19:05:19 +0000 Subject: [PATCH 38/57] Update knapsack_pro to version 10.0.0 --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1844fa9471..2cbcf86f1c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -287,9 +287,9 @@ GEM railties (>= 4.1) jsonapi-resources-matchers (1.0.0) jsonapi-resources (>= 0.9.0) - knapsack_pro (9.2.3) - logger + knapsack_pro (10.0.0) rake + thor (~> 1.4) language_server-protocol (3.17.0.5) launchy (3.1.1) addressable (~> 2.8) From db94bcf26c0106648f91e515654c7c7f7de99fed Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 19:40:16 +0000 Subject: [PATCH 39/57] Update postcss to version 8.5.14 --- package.json | 2 +- yarn.lock | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f3127c1f9d..e2f0f32fe1 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "jsbarcode": "^3.12.3", "jszip": "^3.10.1", "popper.js": "^1.16.1", - "postcss": "^8.5.9", + "postcss": "^8.5.14", "postcss-flexbugs-fixes": "^5.0.2", "postcss-import": "^14.1.0", "postcss-preset-env": "^7.8.3", diff --git a/yarn.lock b/yarn.lock index 444336d89b..974fba0ef3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3109,7 +3109,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.33, postcss@^8.5.10, postcss@^8.5.9: +postcss@^8.4.33, postcss@^8.5.10: version "8.5.12" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.12.tgz#cd0c0f667f7cb0521e2313234ea6e707a9ec1ddb" integrity sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA== @@ -3118,6 +3118,15 @@ postcss@^8.4.33, postcss@^8.5.10, postcss@^8.5.9: picocolors "^1.1.1" source-map-js "^1.2.1" +postcss@^8.5.14: + version "8.5.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.14.tgz#a66c2d7808fadf69ebb5b84a03f8bafd76c4919c" + integrity sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" From ebbc9e187f7c6b6fb7a7461981021498a160576e Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 19:00:23 +0000 Subject: [PATCH 40/57] Update vite_rails to version 3.11.0 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6a055aa724..713a4c8e39 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -622,7 +622,7 @@ GEM uri (1.1.1) useragent (0.16.11) uuidtools (3.0.0) - vite_rails (3.10.0) + vite_rails (3.11.0) railties (>= 5.1, < 9) vite_ruby (~> 3.0, >= 3.2.2) vite_ruby (3.10.2) From b6e01dd48c18455b6e380f4bfd46050b201f9890 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 19:45:13 +0000 Subject: [PATCH 41/57] Update vite-plugin-ruby to version 5.2.2 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f3127c1f9d..e57b8e0d26 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "jsdom": "^26.0", "prettier": "^3.3", "vite": "^8.0", - "vite-plugin-ruby": "^5.2.0", + "vite-plugin-ruby": "^5.2.2", "vitest": "^4.1.5" }, "_comment": "Required to address https://github.com/npm/cli/issues/4828", diff --git a/yarn.lock b/yarn.lock index 444336d89b..a0a36dd347 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3501,10 +3501,10 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -vite-plugin-ruby@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/vite-plugin-ruby/-/vite-plugin-ruby-5.2.1.tgz#8422e2a69cd923b5d63f510744f0503a622bf4c6" - integrity sha512-wI3F/Yr4e4mEwiMff/cvNwGu8nZok5wrwUjHxO8we+h3y9+qCluO3Y5dzvz6vHJDBya9fKXkltoMwoJhaB2SRg== +vite-plugin-ruby@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/vite-plugin-ruby/-/vite-plugin-ruby-5.2.2.tgz#3a92ba5d67ebb32f655eee4fb5c79b6995b1bb91" + integrity sha512-q4UY9Bu0pngcbSmrsJBWbzz35Wzx4vAGMPaOIwZLo6TQ6I2n5/x0BL64GXmOxZ4SeDz+t+nDANKTBpp7CFF9AA== dependencies: obug "^2.0" tinyglobby "^0.2.12" From e6d130b505d009366764a2a7cdb6b6cc1c56e23e Mon Sep 17 00:00:00 2001 From: Ben Topping Date: Thu, 14 May 2026 15:15:13 +0100 Subject: [PATCH 42/57] misc: adds placeholder to qc_report plate_barcodes_text, removes redundant let! from qc_report tests --- app/views/qc_reports/_qc_report_form.html.erb | 1 + spec/models/qc_report_spec.rb | 2 +- spec/models/well_spec.rb | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/views/qc_reports/_qc_report_form.html.erb b/app/views/qc_reports/_qc_report_form.html.erb index 6e11169636..a62a97c8c0 100644 --- a/app/views/qc_reports/_qc_report_form.html.erb +++ b/app/views/qc_reports/_qc_report_form.html.erb @@ -15,6 +15,7 @@ <%= f.label "plate_barcodes_text", 'Plate barcodes' %> <%= text_area_tag 'qc_report[plate_barcodes_text]', params.dig(:qc_report, :plate_barcodes_text), + placeholder: 'SQPP-1234 SQPP-5678 ... ', cols: 40, rows: 4, class: 'form-control' %> diff --git a/spec/models/qc_report_spec.rb b/spec/models/qc_report_spec.rb index 9be846e2e0..17e45b8bca 100644 --- a/spec/models/qc_report_spec.rb +++ b/spec/models/qc_report_spec.rb @@ -231,7 +231,7 @@ context 'limit by plate barcodes' do attr_reader :qc_report - let!(:study) { create(:study) } + let(:study) { create(:study) } let(:plates) { create_list(:plate, 4, plate_purpose: PlatePurpose.find_or_create_by(name: 'Stock plate')) } before do diff --git a/spec/models/well_spec.rb b/spec/models/well_spec.rb index 3cdeaad2d9..aed736920f 100644 --- a/spec/models/well_spec.rb +++ b/spec/models/well_spec.rb @@ -943,7 +943,7 @@ end describe '#qc_report_in_batches' do - let!(:study) { create(:study) } + let(:study) { create(:study) } let(:purpose_1) { PlatePurpose.stock_plate_purpose } let(:purpose_2) { create(:plate_purpose) } let(:purpose_3) { create(:plate_purpose) } From 1248fc83b8000eb243d333d238d23c9682e1762d Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Thu, 14 May 2026 16:04:21 +0100 Subject: [PATCH 43/57] fix: make seed data as explicitly safe No errors were raised --- db/seeds/0001_snp_plate_purposes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/seeds/0001_snp_plate_purposes.rb b/db/seeds/0001_snp_plate_purposes.rb index c2c5b73765..9096a9a031 100644 --- a/db/seeds/0001_snp_plate_purposes.rb +++ b/db/seeds/0001_snp_plate_purposes.rb @@ -51,7 +51,7 @@ EOS YAML - .unsafe_load(plate_purposes) + .safe_load(plate_purposes) .each do |plate_purpose| attributes = plate_purpose.reverse_merge( From b29f9b5d2ffa8d0a758904a409a8441357137c3a Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 16:45:22 +0000 Subject: [PATCH 44/57] Update faraday to version 2.14.2 --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 713a4c8e39..fadce3a5d7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -233,7 +233,7 @@ GEM factory_bot_rails (6.5.1) factory_bot (~> 6.5) railties (>= 6.1.0) - faraday (2.14.1) + faraday (2.14.2) faraday-net_http (>= 2.0, < 3.5) json logger @@ -280,7 +280,7 @@ GEM prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) - json (2.19.4) + json (2.19.5) jsonapi-resources (0.9.0) activerecord (>= 4.1) concurrent-ruby From 5f1bdb02cd8cc62bd103d099e98e43438326c63e Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Tue, 19 May 2026 19:40:16 +0000 Subject: [PATCH 45/57] Update vitest to version 4.1.6 --- package.json | 2 +- yarn.lock | 92 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index e57b8e0d26..10566657b1 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "prettier": "^3.3", "vite": "^8.0", "vite-plugin-ruby": "^5.2.2", - "vitest": "^4.1.5" + "vitest": "^4.1.6" }, "_comment": "Required to address https://github.com/npm/cli/issues/4828", "optionalDependencies": { diff --git a/yarn.lock b/yarn.lock index a0a36dd347..88b81a7b79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1508,24 +1508,24 @@ std-env "^4.0.0-rc.1" tinyrainbow "^3.1.0" -"@vitest/expect@4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.1.5.tgz#5caab19535cfb04fbc37087c5608d46e74dc9292" - integrity sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw== +"@vitest/expect@4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.1.6.tgz#b50c9390aae6957ab4d9e20722cebb17d5bf169a" + integrity sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg== dependencies: "@standard-schema/spec" "^1.1.0" "@types/chai" "^5.2.2" - "@vitest/spy" "4.1.5" - "@vitest/utils" "4.1.5" + "@vitest/spy" "4.1.6" + "@vitest/utils" "4.1.6" chai "^6.2.2" tinyrainbow "^3.1.0" -"@vitest/mocker@4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-4.1.5.tgz#9d5791733e4866cfb8af2d48ca371b127e7d2e93" - integrity sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw== +"@vitest/mocker@4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-4.1.6.tgz#6b624045745236b02aca879a02aef68b72d9d4cd" + integrity sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ== dependencies: - "@vitest/spy" "4.1.5" + "@vitest/spy" "4.1.6" estree-walker "^3.0.3" magic-string "^0.30.21" @@ -1536,28 +1536,35 @@ dependencies: tinyrainbow "^3.1.0" -"@vitest/runner@4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.1.5.tgz#a14dd2d2f48603f906dd52304a10c7fc623bb1de" - integrity sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ== +"@vitest/pretty-format@4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.1.6.tgz#24a1c03a6b68a8775f8ddfec51d3636315edc3f5" + integrity sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw== dependencies: - "@vitest/utils" "4.1.5" + tinyrainbow "^3.1.0" + +"@vitest/runner@4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.1.6.tgz#b6d189e68bd9927c4f111ad089ff96e4757591b1" + integrity sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA== + dependencies: + "@vitest/utils" "4.1.6" pathe "^2.0.3" -"@vitest/snapshot@4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-4.1.5.tgz#d07970d1448190ee5a258db6ab79c65b8018c13b" - integrity sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ== +"@vitest/snapshot@4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-4.1.6.tgz#14fdfc8baf6b4b3e4e35763431dbea3aaa8aa0eb" + integrity sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw== dependencies: - "@vitest/pretty-format" "4.1.5" - "@vitest/utils" "4.1.5" + "@vitest/pretty-format" "4.1.6" + "@vitest/utils" "4.1.6" magic-string "^0.30.21" pathe "^2.0.3" -"@vitest/spy@4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.1.5.tgz#fa7858ffab746fa9ac29496e626f5a0caf9a5a7f" - integrity sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ== +"@vitest/spy@4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.1.6.tgz#0a316893630f47fa545e33026cfc91575070d165" + integrity sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg== "@vitest/utils@4.1.5": version "4.1.5" @@ -1568,6 +1575,15 @@ convert-source-map "^2.0.0" tinyrainbow "^3.1.0" +"@vitest/utils@4.1.6": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.1.6.tgz#3f4acf1f60e135ec1ce896f10baa4cd6466d0d38" + integrity sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ== + dependencies: + "@vitest/pretty-format" "4.1.6" + convert-source-map "^2.0.0" + tinyrainbow "^3.1.0" + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -3522,18 +3538,18 @@ vite-plugin-ruby@^5.2.2: optionalDependencies: fsevents "~2.3.3" -vitest@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-4.1.5.tgz#cda189c0cd9dd1c920be477c0f371b64ec14782a" - integrity sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg== - dependencies: - "@vitest/expect" "4.1.5" - "@vitest/mocker" "4.1.5" - "@vitest/pretty-format" "4.1.5" - "@vitest/runner" "4.1.5" - "@vitest/snapshot" "4.1.5" - "@vitest/spy" "4.1.5" - "@vitest/utils" "4.1.5" +vitest@^4.1.6: + version "4.1.6" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-4.1.6.tgz#754875c9a09c5a3e8ca7d07d440659d92c19787f" + integrity sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ== + dependencies: + "@vitest/expect" "4.1.6" + "@vitest/mocker" "4.1.6" + "@vitest/pretty-format" "4.1.6" + "@vitest/runner" "4.1.6" + "@vitest/snapshot" "4.1.6" + "@vitest/spy" "4.1.6" + "@vitest/utils" "4.1.6" es-module-lexer "^2.0.0" expect-type "^1.3.0" magic-string "^0.30.21" From f9c390410d97636772f43c2a86db28566d584a02 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 11:25:01 +0000 Subject: [PATCH 46/57] Update @vitest/coverage-v8 to version 4.1.6 --- yarn.lock | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/yarn.lock b/yarn.lock index 88b81a7b79..269558a81c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1493,12 +1493,12 @@ systemjs "^6.15.1" "@vitest/coverage-v8@^4.0": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-4.1.5.tgz#26bbdbebecd66be77fa1b63a9ed985dd86a3ba85" - integrity sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A== + version "4.1.7" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-4.1.7.tgz#77059bf103673f44602ddcba1074df58993c3cca" + integrity sha512-qsYPeXc5Q9dFLd1i8Ap+Bx8sQgcp+rFVQo4R0dDsWNBzl26ldVF1qOO+RL24K7FDrR6pA+50XedRLSoSG24bVQ== dependencies: "@bcoe/v8-coverage" "^1.0.2" - "@vitest/utils" "4.1.5" + "@vitest/utils" "4.1.7" ast-v8-to-istanbul "^1.0.0" istanbul-lib-coverage "^3.2.2" istanbul-lib-report "^3.0.1" @@ -1529,13 +1529,6 @@ estree-walker "^3.0.3" magic-string "^0.30.21" -"@vitest/pretty-format@4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.1.5.tgz#4c13d77a77e2931e44db95522ed5700bcf0570d4" - integrity sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g== - dependencies: - tinyrainbow "^3.1.0" - "@vitest/pretty-format@4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.1.6.tgz#24a1c03a6b68a8775f8ddfec51d3636315edc3f5" @@ -1543,6 +1536,13 @@ dependencies: tinyrainbow "^3.1.0" +"@vitest/pretty-format@4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.1.7.tgz#86b37ea96d4c5bd1357be66982e60a89a343c1bb" + integrity sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw== + dependencies: + tinyrainbow "^3.1.0" + "@vitest/runner@4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.1.6.tgz#b6d189e68bd9927c4f111ad089ff96e4757591b1" @@ -1566,15 +1566,6 @@ resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.1.6.tgz#0a316893630f47fa545e33026cfc91575070d165" integrity sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg== -"@vitest/utils@4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.1.5.tgz#20d6a6ae651a0dd33f945548921698d49701fa43" - integrity sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug== - dependencies: - "@vitest/pretty-format" "4.1.5" - convert-source-map "^2.0.0" - tinyrainbow "^3.1.0" - "@vitest/utils@4.1.6": version "4.1.6" resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.1.6.tgz#3f4acf1f60e135ec1ce896f10baa4cd6466d0d38" @@ -1584,6 +1575,15 @@ convert-source-map "^2.0.0" tinyrainbow "^3.1.0" +"@vitest/utils@4.1.7": + version "4.1.7" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.1.7.tgz#8d2350588f7054246f24d0dbadffbef5d3a5caff" + integrity sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw== + dependencies: + "@vitest/pretty-format" "4.1.7" + convert-source-map "^2.0.0" + tinyrainbow "^3.1.0" + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" From b8c3bfee9b7a1e4a20527b9c57a5b1f8d5a07056 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 11:28:19 +0000 Subject: [PATCH 47/57] Update @vitejs/plugin-legacy to version 8.0.2 --- yarn.lock | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/yarn.lock b/yarn.lock index 88b81a7b79..adfac0cc30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,11 +22,16 @@ js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.28.6", "@babel/compat-data@^7.29.0": +"@babel/compat-data@^7.28.6": version "7.29.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== +"@babel/compat-data@^7.29.3": + version "7.29.3" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.3.tgz#e3f5347f0589596c91d227ccb6a541d37fb1307b" + integrity sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg== + "@babel/core@^7.29.0": version "7.29.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" @@ -239,6 +244,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" +"@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@^7.29.3": + version "7.29.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz#2e14f9335803d892ccb67ef487e23cf9726156fe" + integrity sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg== + dependencies: + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz#e134a5479eb2ba9c02714e8c1ebf1ec9076124fd" @@ -479,10 +492,10 @@ "@babel/helper-module-transforms" "^7.28.6" "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-modules-systemjs@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz#e458a95a17807c415924106a3ff188a3b8dee964" - integrity sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ== +"@babel/plugin-transform-modules-systemjs@^7.29.4": + version "7.29.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz#f621105da99919c15cf4bde6fcc7346ef95e7b20" + integrity sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w== dependencies: "@babel/helper-module-transforms" "^7.28.6" "@babel/helper-plugin-utils" "^7.28.6" @@ -680,18 +693,19 @@ "@babel/helper-create-regexp-features-plugin" "^7.28.5" "@babel/helper-plugin-utils" "^7.28.6" -"@babel/preset-env@^7.29.2": - version "7.29.2" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.29.2.tgz#5a173f22c7d8df362af1c9fe31facd320de4a86c" - integrity sha512-DYD23veRYGvBFhcTY1iUvJnDNpuqNd/BzBwCvzOTKUnJjKg5kpUBh3/u9585Agdkgj+QuygG7jLfOPWMa2KVNw== +"@babel/preset-env@^7.29.5": + version "7.29.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.29.5.tgz#c48b7ed94582c8b685e21b8b42de8633ec289268" + integrity sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA== dependencies: - "@babel/compat-data" "^7.29.0" + "@babel/compat-data" "^7.29.3" "@babel/helper-compilation-targets" "^7.28.6" "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-validator-option" "^7.27.1" "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.28.5" "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.27.1" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.27.1" + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array" "^7.29.3" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.27.1" "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.28.6" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" @@ -723,7 +737,7 @@ "@babel/plugin-transform-member-expression-literals" "^7.27.1" "@babel/plugin-transform-modules-amd" "^7.27.1" "@babel/plugin-transform-modules-commonjs" "^7.28.6" - "@babel/plugin-transform-modules-systemjs" "^7.29.0" + "@babel/plugin-transform-modules-systemjs" "^7.29.4" "@babel/plugin-transform-modules-umd" "^7.27.1" "@babel/plugin-transform-named-capturing-groups-regex" "^7.29.0" "@babel/plugin-transform-new-target" "^7.27.1" @@ -1475,17 +1489,17 @@ integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@vitejs/plugin-legacy@^8.0": - version "8.0.1" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-legacy/-/plugin-legacy-8.0.1.tgz#3925b78ba9a016654a52552d438e63c112a6ac32" - integrity sha512-8zeDeuNPqXd49rIVgFgluQYB8vQICHR7l+W2I3CxYK4gTjTorajVr0wLvSjALIwEwLRxBn68EgNVyGP4j6hP7w== + version "8.0.2" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-legacy/-/plugin-legacy-8.0.2.tgz#9ed05d941dddb46279d9b95987b1deb112aef34f" + integrity sha512-+n6znc/hTsuD2v/qWX3nfR6UFWFKwpL+XS+SPyiPuEwAvR24iRvLkJQDh18u0XrHPEwuwxPmw0VopvlmLSg66Q== dependencies: "@babel/core" "^7.29.0" "@babel/plugin-transform-dynamic-import" "^7.27.1" - "@babel/plugin-transform-modules-systemjs" "^7.29.0" - "@babel/preset-env" "^7.29.2" + "@babel/plugin-transform-modules-systemjs" "^7.29.4" + "@babel/preset-env" "^7.29.5" babel-plugin-polyfill-corejs3 "^0.14.2" babel-plugin-polyfill-regenerator "^0.6.8" - browserslist "^4.28.1" + browserslist "^4.28.2" browserslist-to-esbuild "^2.1.1" core-js "^3.49.0" magic-string "^0.30.21" @@ -1708,7 +1722,7 @@ browserslist-to-esbuild@^2.1.1: dependencies: meow "^13.0.0" -browserslist@^4.21.4, browserslist@^4.24.0, browserslist@^4.24.4, browserslist@^4.28.1: +browserslist@^4.21.4, browserslist@^4.24.0, browserslist@^4.24.4, browserslist@^4.28.1, browserslist@^4.28.2: version "4.28.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.2.tgz#f50b65362ef48974ca9f50b3680566d786b811d2" integrity sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg== From 23e4c6bbe36c677a3d14739f1c1587b0826e24e3 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 11:35:37 +0000 Subject: [PATCH 48/57] Update rubocop to version 1.86.2 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9a72d6abf7..1d07afba4f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -503,7 +503,7 @@ GEM rspec-support (>= 3.13.0, < 5.0.0) rspec-support (3.13.7) ruboclean (0.7.1) - rubocop (1.86.1) + rubocop (1.86.2) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) From 0b2bf7bcc303e344f57fdb7c6aebe8325642bc7e Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 11:35:45 +0000 Subject: [PATCH 49/57] Update ruboclean to version 0.8.0 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9a72d6abf7..340bc676c6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -502,7 +502,7 @@ GEM rspec-mocks (>= 3.13.0, < 5.0.0) rspec-support (>= 3.13.0, < 5.0.0) rspec-support (3.13.7) - ruboclean (0.7.1) + ruboclean (0.8.0) rubocop (1.86.1) json (~> 2.3) language_server-protocol (~> 3.17.0.2) From 55f4b43c63e5fabfca2d5b1b0db93c72e88f4441 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 20 May 2026 14:09:00 +0100 Subject: [PATCH 50/57] style: disable Style/RedundantSelf --- .rubocop.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 5709cbcfd1..8bf76fb35a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -284,6 +284,10 @@ Style/QuotedSymbols: Exclude: - bin/* +# Keep consistency across attribute writers and readers +Style/RedundantSelf: + Enabled: false + # Files generated by Rails Style/StringLiterals: Exclude: From ae29c470a5a80545d626ce21f7904daaf55e3339 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Wed, 20 May 2026 13:43:09 +0000 Subject: [PATCH 51/57] Update concurrent-ruby to version 1.3.6 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index e8e4f3a7cb..3d0170a9ce 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' group :default do gem 'bootsnap' - gem 'concurrent-ruby', '1.3.5' + gem 'concurrent-ruby', '1.3.6' gem 'configatron' gem 'formtastic' gem 'rails', '~> 8.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index cac0ddc235..1c02d826f9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -171,7 +171,7 @@ GEM choice (0.2.0) chronic (0.10.2) coderay (1.1.3) - concurrent-ruby (1.3.5) + concurrent-ruby (1.3.6) configatron (4.5.1) connection_pool (2.5.5) crack (1.0.1) @@ -684,7 +684,7 @@ DEPENDENCIES capybara carrierwave caxlsx - concurrent-ruby (= 1.3.5) + concurrent-ruby (= 1.3.6) configatron csv (~> 3.3) cucumber-rails From f9fc9cd0c2ae500c73f4203806aaf08c90e29142 Mon Sep 17 00:00:00 2001 From: Stephen Hulme Date: Wed, 20 May 2026 14:45:41 +0100 Subject: [PATCH 52/57] build: remove constraint on concurrent-ruby Should be fine now that we are on Rails 8 --- Gemfile | 2 +- Gemfile.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 3d0170a9ce..bc295f5e31 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' group :default do gem 'bootsnap' - gem 'concurrent-ruby', '1.3.6' + gem 'concurrent-ruby' gem 'configatron' gem 'formtastic' gem 'rails', '~> 8.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 1c02d826f9..965766eae6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -684,7 +684,7 @@ DEPENDENCIES capybara carrierwave caxlsx - concurrent-ruby (= 1.3.6) + concurrent-ruby configatron csv (~> 3.3) cucumber-rails From 75d94eea2616406a8d8c1a5f36814b8aa14ecc39 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 09:45:46 +0000 Subject: [PATCH 53/57] Update flipper to version 1.4.2 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 965766eae6..00442d8267 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -245,7 +245,7 @@ GEM ffi (1.17.3-arm64-darwin) ffi (1.17.3-x86_64-darwin) ffi (1.17.3-x86_64-linux-gnu) - flipper (1.4.1) + flipper (1.4.2) concurrent-ruby (< 2) flipper-active_record (1.4.1) activerecord (>= 4.2, < 9) From 9662ea127b345f73be999a53b9188d936ad980b4 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 09:47:43 +0000 Subject: [PATCH 54/57] Update flipper-active_record to version 1.4.2 --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 00442d8267..e5d147043f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -247,9 +247,9 @@ GEM ffi (1.17.3-x86_64-linux-gnu) flipper (1.4.2) concurrent-ruby (< 2) - flipper-active_record (1.4.1) + flipper-active_record (1.4.2) activerecord (>= 4.2, < 9) - flipper (~> 1.4.1) + flipper (~> 1.4.2) flipper-ui (1.4.1) erubi (>= 1.0.0, < 2.0.0) flipper (~> 1.4.1) From d324c3374d6fa1ad0a62821197da02c62b0c3de1 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 11:42:14 +0000 Subject: [PATCH 55/57] Update flipper-ui to version 1.4.2 --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e5d147043f..cf2e7339da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -250,9 +250,9 @@ GEM flipper-active_record (1.4.2) activerecord (>= 4.2, < 9) flipper (~> 1.4.2) - flipper-ui (1.4.1) + flipper-ui (1.4.2) erubi (>= 1.0.0, < 2.0.0) - flipper (~> 1.4.1) + flipper (~> 1.4.2) rack (>= 1.4, < 4) rack-protection (>= 1.5.3, < 5.0.0) rack-session (>= 1.0.2, < 3.0.0) From 315fb535816cdae101294f1ddf6276e39307c6ec Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 19:00:25 +0000 Subject: [PATCH 56/57] Update selenium-webdriver to version 4.44.0 --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index cf2e7339da..93b9cd7fa2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -555,7 +555,7 @@ GEM ffi (~> 1.12) logger ruby2_keywords (0.0.5) - rubyzip (3.2.2) + rubyzip (3.3.0) sanger-jsonapi-resources (0.1.2) activerecord (>= 4.1) concurrent-ruby @@ -565,7 +565,7 @@ GEM crass (~> 1.0.2) nokogiri (>= 1.16.8) securerandom (0.4.1) - selenium-webdriver (4.43.0) + selenium-webdriver (4.44.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) From 55b1fceaf901971f514100785d51c0968ceb0935 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 19:20:13 +0000 Subject: [PATCH 57/57] Update terser to version 5.47.1 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 912d30538e..d6d3ad2c75 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "sass": "^1.99.0", "select2": "^4.1.0-rc.0", "sortablejs": "^1.15.7", - "terser": "^5.46.2" + "terser": "^5.47.1" }, "devDependencies": { "@vitejs/plugin-legacy": "^8.0", diff --git a/yarn.lock b/yarn.lock index 149a2dad6b..8d9bbbab53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3419,10 +3419,10 @@ systemjs@^6.15.1: resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-6.15.1.tgz#74175b6810e27a79e1177d21db5f0e3057118cea" integrity sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA== -terser@^5.46.2: - version "5.46.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.2.tgz#b9529672d5b0024c7959571c83b82f65077b2a4f" - integrity sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw== +terser@^5.47.1: + version "5.47.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.47.1.tgz#99b298e51bc41214304847de1429ec92fd1f7648" + integrity sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.15.0"