From c1a49fac1e6837a88a2a3bb920f07faa7d89e6cb Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Fri, 29 May 2026 13:19:32 +0100
Subject: [PATCH 01/20] Bump rails, rack, sidekiq. Audit gems
---
.github/workflows/test.yml | 2 +
Gemfile | 6 +-
Gemfile.lock | 181 ++++++++++++++++++++-----------------
3 files changed, 104 insertions(+), 85 deletions(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 578947491..686e3573a 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -47,6 +47,8 @@ jobs:
uses: ruby/setup-ruby@v1
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
+ - name: Audit gems
+ run: bundle audit check --update
- name: Configure and initialize database
run: |
cp test/config/test_tess.yml config/tess.yml
diff --git a/Gemfile b/Gemfile
index 98b174175..f3dcaa43b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,7 +2,7 @@
source 'https://rubygems.org'
-gem 'rails', '7.2.3'
+gem 'rails', '7.2.3.1'
gem 'active_model_serializers'
gem 'activerecord-session_store'
@@ -51,6 +51,7 @@ gem 'redis'
gem 'rest-client'
gem 'reverse_markdown'
gem 'rss'
+gem 'ruby-openai'
gem 'sassc-rails'
gem 'sass-rails'
gem 'sentry-rails'
@@ -85,6 +86,7 @@ end
group :development do
gem 'better_errors'
gem 'binding_of_caller'
+ gem 'bundle-audit'
gem 'listen'
gem 'puma'
gem 'web-console'
@@ -100,5 +102,3 @@ group :test do
gem 'vcr'
gem 'webmock'
end
-
-gem 'ruby-openai'
diff --git a/Gemfile.lock b/Gemfile.lock
index 0f8b3d923..5aebc10ef 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -9,29 +9,29 @@ GIT
GEM
remote: https://rubygems.org/
specs:
- actioncable (7.2.3)
- actionpack (= 7.2.3)
- activesupport (= 7.2.3)
+ actioncable (7.2.3.1)
+ actionpack (= 7.2.3.1)
+ activesupport (= 7.2.3.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
- actionmailbox (7.2.3)
- actionpack (= 7.2.3)
- activejob (= 7.2.3)
- activerecord (= 7.2.3)
- activestorage (= 7.2.3)
- activesupport (= 7.2.3)
+ actionmailbox (7.2.3.1)
+ actionpack (= 7.2.3.1)
+ activejob (= 7.2.3.1)
+ activerecord (= 7.2.3.1)
+ activestorage (= 7.2.3.1)
+ activesupport (= 7.2.3.1)
mail (>= 2.8.0)
- actionmailer (7.2.3)
- actionpack (= 7.2.3)
- actionview (= 7.2.3)
- activejob (= 7.2.3)
- activesupport (= 7.2.3)
+ actionmailer (7.2.3.1)
+ actionpack (= 7.2.3.1)
+ actionview (= 7.2.3.1)
+ activejob (= 7.2.3.1)
+ activesupport (= 7.2.3.1)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
- actionpack (7.2.3)
- actionview (= 7.2.3)
- activesupport (= 7.2.3)
+ actionpack (7.2.3.1)
+ actionview (= 7.2.3.1)
+ activesupport (= 7.2.3.1)
cgi
nokogiri (>= 1.8.5)
racc
@@ -41,15 +41,15 @@ GEM
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
- actiontext (7.2.3)
- actionpack (= 7.2.3)
- activerecord (= 7.2.3)
- activestorage (= 7.2.3)
- activesupport (= 7.2.3)
+ actiontext (7.2.3.1)
+ actionpack (= 7.2.3.1)
+ activerecord (= 7.2.3.1)
+ activestorage (= 7.2.3.1)
+ activesupport (= 7.2.3.1)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (7.2.3)
- activesupport (= 7.2.3)
+ actionview (7.2.3.1)
+ activesupport (= 7.2.3.1)
builder (~> 3.1)
cgi
erubi (~> 1.11)
@@ -60,32 +60,32 @@ GEM
activemodel (>= 4.1)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
- activejob (7.2.3)
- activesupport (= 7.2.3)
+ activejob (7.2.3.1)
+ activesupport (= 7.2.3.1)
globalid (>= 0.3.6)
- activemodel (7.2.3)
- activesupport (= 7.2.3)
+ activemodel (7.2.3.1)
+ activesupport (= 7.2.3.1)
activemodel-serializers-xml (1.0.3)
activemodel (>= 5.0.0.a)
activesupport (>= 5.0.0.a)
builder (~> 3.1)
- activerecord (7.2.3)
- activemodel (= 7.2.3)
- activesupport (= 7.2.3)
+ activerecord (7.2.3.1)
+ activemodel (= 7.2.3.1)
+ activesupport (= 7.2.3.1)
timeout (>= 0.4.0)
- activerecord-session_store (2.0.0)
- actionpack (>= 5.2.4.1)
- activerecord (>= 5.2.4.1)
- multi_json (~> 1.11, >= 1.11.2)
- rack (>= 2.0.8, < 3)
- railties (>= 5.2.4.1)
- activestorage (7.2.3)
- actionpack (= 7.2.3)
- activejob (= 7.2.3)
- activerecord (= 7.2.3)
- activesupport (= 7.2.3)
+ activerecord-session_store (2.2.0)
+ actionpack (>= 7.0)
+ activerecord (>= 7.0)
+ cgi (>= 0.3.6)
+ rack (>= 2.0.8, < 4)
+ railties (>= 7.0)
+ activestorage (7.2.3.1)
+ actionpack (= 7.2.3.1)
+ activejob (= 7.2.3.1)
+ activerecord (= 7.2.3.1)
+ activesupport (= 7.2.3.1)
marcel (~> 1.0)
- activesupport (7.2.3)
+ activesupport (7.2.3.1)
base64
benchmark (>= 0.3)
bigdecimal
@@ -94,7 +94,7 @@ GEM
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
- minitest (>= 5.1)
+ minitest (>= 5.1, < 6)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
addressable (2.9.0)
@@ -134,6 +134,11 @@ GEM
bootstrap-tab-history-rails (0.1.0)
railties (>= 3.1)
builder (3.3.0)
+ bundle-audit (0.2.0)
+ bundler-audit
+ bundler-audit (0.9.3)
+ bundler (>= 1.2.0)
+ thor (~> 1.0)
byebug (11.1.3)
capybara (3.40.0)
addressable
@@ -407,8 +412,9 @@ GEM
faraday (< 3)
faraday-follow_redirects (>= 0.3.0, < 2)
rexml
- omniauth (2.1.1)
+ omniauth (2.1.4)
hashie (>= 3.4.6)
+ logger
rack (>= 2.2.3)
rack-protection
omniauth-rails_csrf_protection (1.0.1)
@@ -464,7 +470,7 @@ GEM
pundit (2.3.1)
activesupport (>= 3.0.0)
racc (1.8.1)
- rack (2.2.23)
+ rack (3.2.6)
rack-cors (2.0.2)
rack (>= 2.0.0)
rack-oauth2 (2.2.0)
@@ -474,29 +480,31 @@ GEM
faraday-follow_redirects
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
- rack-protection (3.1.0)
- rack (~> 2.2, >= 2.2.4)
- rack-session (1.0.2)
- rack (< 3)
+ rack-protection (4.2.1)
+ base64 (>= 0.1.0)
+ logger (>= 1.6.0)
+ rack (>= 3.0.0, < 4)
+ rack-session (2.1.2)
+ base64 (>= 0.1.0)
+ rack (>= 3.0.0)
rack-test (2.2.0)
rack (>= 1.3)
- rackup (1.0.1)
- rack (< 3)
- webrick
- rails (7.2.3)
- actioncable (= 7.2.3)
- actionmailbox (= 7.2.3)
- actionmailer (= 7.2.3)
- actionpack (= 7.2.3)
- actiontext (= 7.2.3)
- actionview (= 7.2.3)
- activejob (= 7.2.3)
- activemodel (= 7.2.3)
- activerecord (= 7.2.3)
- activestorage (= 7.2.3)
- activesupport (= 7.2.3)
+ rackup (2.3.1)
+ rack (>= 3)
+ rails (7.2.3.1)
+ actioncable (= 7.2.3.1)
+ actionmailbox (= 7.2.3.1)
+ actionmailer (= 7.2.3.1)
+ actionpack (= 7.2.3.1)
+ actiontext (= 7.2.3.1)
+ actionview (= 7.2.3.1)
+ activejob (= 7.2.3.1)
+ activemodel (= 7.2.3.1)
+ activerecord (= 7.2.3.1)
+ activestorage (= 7.2.3.1)
+ activesupport (= 7.2.3.1)
bundler (>= 1.15.0)
- railties (= 7.2.3)
+ railties (= 7.2.3.1)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
@@ -518,9 +526,9 @@ GEM
nested_form (~> 0.3)
rails (>= 6.0, < 9)
turbo-rails (>= 1.0, < 3)
- railties (7.2.3)
- actionpack (= 7.2.3)
- activesupport (= 7.2.3)
+ railties (7.2.3.1)
+ actionpack (= 7.2.3.1)
+ activesupport (= 7.2.3.1)
cgi
irb (~> 1.13)
rackup (>= 1.0.0)
@@ -611,7 +619,7 @@ GEM
redcarpet (3.6.0)
redis (5.0.7)
redis-client (>= 0.9.0)
- redis-client (0.16.0)
+ redis-client (0.29.0)
connection_pool
regexp_parser (2.8.1)
reline (0.6.3)
@@ -676,14 +684,20 @@ GEM
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 4.0)
websocket (~> 1.0)
+ sentry-rails (6.5.0)
+ railties (>= 5.2.0)
+ sentry-ruby (~> 6.5.0)
+ sentry-ruby (6.5.0)
+ bigdecimal
sentry-rails (5.11.0)
railties (>= 5.0)
sentry-ruby (~> 5.11.0)
sentry-ruby (5.11.0)
concurrent-ruby (~> 1.0, >= 1.0.2)
- sentry-sidekiq (5.11.0)
- sentry-ruby (~> 5.11.0)
- sidekiq (>= 3.0)
+ logger
+ sentry-sidekiq (6.5.0)
+ sentry-ruby (~> 6.5.0)
+ sidekiq (>= 5.0)
shacl (0.4.1)
json-ld (~> 3.3)
rdf (~> 3.3)
@@ -698,14 +712,17 @@ GEM
rdf-xsd (~> 3.3)
sparql (~> 3.3)
sxp (~> 1.3)
- sidekiq (7.1.6)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sidekiq-status (3.0.3)
+ sidekiq (8.1.6)
+ connection_pool (>= 3.0.0)
+ json (>= 2.16.0)
+ logger (>= 1.7.0)
+ rack (>= 3.2.0)
+ redis-client (>= 0.29.0)
+ sidekiq-status (4.0.0)
+ base64
chronic_duration
- sidekiq (>= 6.0, < 8)
+ logger
+ sidekiq (>= 7, < 9)
simple_calendar (2.4.3)
rails (>= 3.0)
simple_form (5.2.0)
@@ -817,7 +834,6 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
- webrick (1.9.2)
websocket (1.2.11)
websocket-driver (0.8.0)
base64
@@ -850,6 +866,7 @@ DEPENDENCIES
bootstrap-datepicker-rails
bootstrap-sass
bootstrap-tab-history-rails
+ bundle-audit
byebug
capybara
committee
@@ -888,7 +905,7 @@ DEPENDENCIES
puma
pundit
rack-cors
- rails (= 7.2.3)
+ rails (= 7.2.3.1)
rails-controller-testing
rails-i18n
rails_admin
From 860b8b02766d4e426a187a1da3e06e55be558ada Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Fri, 29 May 2026 13:21:00 +0100
Subject: [PATCH 02/20] Fix rails/rack status code conversion
---
app/controllers/application_controller.rb | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 13fc15d1f..505184359 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -37,6 +37,7 @@ def pundit_user
def handle_error(status_code = 500, message = nil)
status_code = (params[:status_code] || status_code) # params[:status_code] comes from routes for 500, 503, 422 and 404 errors
if status_code.is_a?(Symbol) # Convert :forbidden, :not_found, etc. to 403, 404 etc.
+ status_code = :unprocessable_content if status_code == :unprocessable_entity
status_code = Rack::Utils::SYMBOL_TO_STATUS_CODE[status_code] || status_code
end
From 271d58b993484a196ab8197cf6e323a9c7beae6a Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Fri, 29 May 2026 13:21:23 +0100
Subject: [PATCH 03/20] Headers are lowercase now (because of rack change?)
---
test/controllers/workflows_controller_test.rb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/controllers/workflows_controller_test.rb b/test/controllers/workflows_controller_test.rb
index b9ef36777..297ccbf33 100644
--- a/test/controllers/workflows_controller_test.rb
+++ b/test/controllers/workflows_controller_test.rb
@@ -81,7 +81,7 @@ class WorkflowsControllerTest < ActionController::TestCase
test "should show workflow" do
get :show, params: { id: @workflow }
assert_response :success
- assert_includes response.headers.keys, 'X-Frame-Options', 'X-Frame-Options header should be present in all actions except `embed`'
+ assert_includes response.headers.keys.map(&:downcase), 'x-frame-options', 'X-Frame-Options header should be present in all actions except `embed`'
assert assigns(:workflow)
end
@@ -98,7 +98,7 @@ class WorkflowsControllerTest < ActionController::TestCase
get :embed, params: { id: @workflow }
assert_response :success
assert_select '.embedded-container', count: 1
- assert_not_includes response.headers.keys, 'X-Frame-Options', 'X-Frame-Options header should be removed to allow embedding in iframes'
+ assert_not_includes response.headers.keys.map(&:downcase), 'x-frame-options', 'X-Frame-Options header should be removed to allow embedding in iframes'
end
test "should not show embedded private workflow" do
From e18f77ea6ce1cfb6b6df7daa0605d8225515cc39 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Fri, 29 May 2026 13:21:58 +0100
Subject: [PATCH 04/20] Fix `sidekiq/testing` deprecation
---
test/models/event_test.rb | 1 -
test/models/profile_test.rb | 1 -
test/test_helper.rb | 2 ++
test/workers/edit_suggestion_worker_test.rb | 1 -
test/workers/geocoding_worker_test.rb | 1 -
test/workers/person_link_worker_test.rb | 1 -
test/workers/source_test_worker_test.rb | 1 -
7 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/test/models/event_test.rb b/test/models/event_test.rb
index 283874e02..fd25b901a 100644
--- a/test/models/event_test.rb
+++ b/test/models/event_test.rb
@@ -1,5 +1,4 @@
require 'test_helper'
-require 'sidekiq/testing'
class EventTest < ActiveSupport::TestCase
setup do
diff --git a/test/models/profile_test.rb b/test/models/profile_test.rb
index c834fee04..901f07b4b 100644
--- a/test/models/profile_test.rb
+++ b/test/models/profile_test.rb
@@ -1,5 +1,4 @@
require 'test_helper'
-require 'sidekiq/testing'
class ProfileTest < ActiveSupport::TestCase
setup do
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 8d6583b3f..ed091a0a8 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -39,6 +39,8 @@
# config.allow_http_connections_when_no_cassette = true
end
+Sidekiq.testing!(:fake)
+
class ActiveSupport::TestCase
include SchemaHelper
diff --git a/test/workers/edit_suggestion_worker_test.rb b/test/workers/edit_suggestion_worker_test.rb
index 8f4f8eaa5..bb238503a 100644
--- a/test/workers/edit_suggestion_worker_test.rb
+++ b/test/workers/edit_suggestion_worker_test.rb
@@ -1,5 +1,4 @@
require 'test_helper'
-require 'sidekiq/testing'
class EditSuggestionWorkerTest < ActiveSupport::TestCase
diff --git a/test/workers/geocoding_worker_test.rb b/test/workers/geocoding_worker_test.rb
index c21d4e789..c11948254 100644
--- a/test/workers/geocoding_worker_test.rb
+++ b/test/workers/geocoding_worker_test.rb
@@ -1,5 +1,4 @@
require 'test_helper'
-require 'sidekiq/testing'
class GeocodingWorkerTest < ActiveSupport::TestCase
test 'get coordinates for an event' do
diff --git a/test/workers/person_link_worker_test.rb b/test/workers/person_link_worker_test.rb
index bc1ca0464..b0bac2419 100644
--- a/test/workers/person_link_worker_test.rb
+++ b/test/workers/person_link_worker_test.rb
@@ -1,5 +1,4 @@
require 'test_helper'
-require 'sidekiq/testing'
class PersonLinkWorkerTest < ActiveSupport::TestCase
setup do
diff --git a/test/workers/source_test_worker_test.rb b/test/workers/source_test_worker_test.rb
index bba337594..078c3c3f4 100644
--- a/test/workers/source_test_worker_test.rb
+++ b/test/workers/source_test_worker_test.rb
@@ -1,5 +1,4 @@
require 'test_helper'
-require 'sidekiq/testing'
class SourceTestWorkerTest < ActiveSupport::TestCase
test 'test source' do
From 7c196bf243091a42cd0cc2f80ea34e187ffdf138 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Fri, 29 May 2026 16:04:28 +0100
Subject: [PATCH 05/20] Try and fix `bundle audit` in CI
---
.github/workflows/test.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 686e3573a..54773dd29 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -48,7 +48,7 @@ jobs:
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Audit gems
- run: bundle audit check --update
+ run: bundle exec bundle audit check --update
- name: Configure and initialize database
run: |
cp test/config/test_tess.yml config/tess.yml
From fcac430ee743c7d688b0d21fa1031b4e41b9e161 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 09:55:12 +0100
Subject: [PATCH 06/20] Update bundler. Add `cooldown` to Gemfile.
https://blog.rubygems.org/2026/06/03/cooldown-let-new-gems-be-vetted.html
---
Gemfile | 2 +-
Gemfile.lock | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Gemfile b/Gemfile
index f3dcaa43b..be07a9914 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-source 'https://rubygems.org'
+source 'https://rubygems.org', cooldown: 14
gem 'rails', '7.2.3.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index 5aebc10ef..fcc5ebb7b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -947,4 +947,4 @@ DEPENDENCIES
will_paginate
BUNDLED WITH
- 2.5.22
+ 4.0.13
From c2a97546fb4e939d42f465d2606a15d6196ae7f8 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 09:56:45 +0100
Subject: [PATCH 07/20] Update puma
---
Gemfile.lock | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Gemfile.lock b/Gemfile.lock
index fcc5ebb7b..68e8c14c9 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -465,7 +465,7 @@ GEM
i18n (>= 0.5.0)
railties (>= 6.1.0)
public_suffix (7.0.5)
- puma (6.5.0)
+ puma (8.0.2)
nio4r (~> 2.0)
pundit (2.3.1)
activesupport (>= 3.0.0)
@@ -947,4 +947,4 @@ DEPENDENCIES
will_paginate
BUNDLED WITH
- 4.0.13
+ 4.0.13
From 6f1c86f92a2665ac85e14d2090d9077f745dea4c Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 10:45:12 +0100
Subject: [PATCH 08/20] Bump actions/checkout to stop deprecation warning
---
.github/workflows/codeql-analysis.yml | 2 +-
.github/workflows/deployment-checks.yml | 2 +-
.github/workflows/docker-prod-test.yml | 2 +-
.github/workflows/docker-test.yml | 2 +-
.github/workflows/migration-test.yml | 2 +-
.github/workflows/test.yml | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 90f48cb53..7891e85fa 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -38,7 +38,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
diff --git a/.github/workflows/deployment-checks.yml b/.github/workflows/deployment-checks.yml
index e360af9f4..840f53e88 100644
--- a/.github/workflows/deployment-checks.yml
+++ b/.github/workflows/deployment-checks.yml
@@ -36,7 +36,7 @@ jobs:
ports:
- 6379:6379
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
name: Set up Ruby
- uses: ruby/setup-ruby@v1
with:
diff --git a/.github/workflows/docker-prod-test.yml b/.github/workflows/docker-prod-test.yml
index d60e16a42..7470afb86 100644
--- a/.github/workflows/docker-prod-test.yml
+++ b/.github/workflows/docker-prod-test.yml
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup
run: |
cp env.sample .env
diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml
index e19ec6921..dbdf61479 100644
--- a/.github/workflows/docker-test.yml
+++ b/.github/workflows/docker-test.yml
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Setup
run: |
cp env.sample .env
diff --git a/.github/workflows/migration-test.yml b/.github/workflows/migration-test.yml
index b16fec19a..6dab87154 100644
--- a/.github/workflows/migration-test.yml
+++ b/.github/workflows/migration-test.yml
@@ -49,7 +49,7 @@ jobs:
sudo apt-get update
sudo apt-get install imagemagick
- name: Check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Ruby & gems
uses: ruby/setup-ruby@v1
with:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 54773dd29..7d6528a3f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -42,7 +42,7 @@ jobs:
sudo apt-get update
sudo apt-get install imagemagick
- name: Check out code
- uses: actions/checkout@v4
+ uses: actions/checkout@v6
- name: Install Ruby & gems
uses: ruby/setup-ruby@v1
with:
From 45afbef206ae313ade9e863a519e5ef7bed73766 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 13:48:56 +0100
Subject: [PATCH 09/20] `bundle install`
---
Gemfile.lock | 6 ------
1 file changed, 6 deletions(-)
diff --git a/Gemfile.lock b/Gemfile.lock
index 68e8c14c9..f3b159538 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -689,12 +689,6 @@ GEM
sentry-ruby (~> 6.5.0)
sentry-ruby (6.5.0)
bigdecimal
- sentry-rails (5.11.0)
- railties (>= 5.0)
- sentry-ruby (~> 5.11.0)
- sentry-ruby (5.11.0)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- logger
sentry-sidekiq (6.5.0)
sentry-ruby (~> 6.5.0)
sidekiq (>= 5.0)
From 2b178191b1f7b915fdcb97409e27f725d8b94491 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 13:55:57 +0100
Subject: [PATCH 10/20] `bundle install` again
---
Gemfile.lock | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Gemfile.lock b/Gemfile.lock
index f3b159538..017b0e44a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -689,6 +689,8 @@ GEM
sentry-ruby (~> 6.5.0)
sentry-ruby (6.5.0)
bigdecimal
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ logger
sentry-sidekiq (6.5.0)
sentry-ruby (~> 6.5.0)
sidekiq (>= 5.0)
From 002c8b292858b874ba4991eae53b65c7ad9bdc85 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 14:42:33 +0100
Subject: [PATCH 11/20] Bump Ruby version to 3.4.9. Fix lots of `warning:
literal string will be frozen in the future`
---
.ruby-version | 2 +-
Dockerfile | 2 +-
Gemfile.lock | 16 ++++++++--------
app/helpers/application_helper.rb | 6 +++---
app/helpers/materials_helper.rb | 14 +++++++-------
lib/ingestors/bioschemas_ingestor.rb | 10 +++++-----
lib/ingestors/oai_pmh_ingestor.rb | 2 +-
lib/scraper.rb | 20 ++++++++++----------
8 files changed, 36 insertions(+), 36 deletions(-)
diff --git a/.ruby-version b/.ruby-version
index f0f3ba8b6..61a52c914 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-ruby-3.3.10
\ No newline at end of file
+ruby-3.4.9
diff --git a/Dockerfile b/Dockerfile
index 7124e862e..e41954a87 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-ARG RUBY_VERSION=3.3.10
+ARG RUBY_VERSION=3.4.9
#use ruby base image
FROM ruby:$RUBY_VERSION-slim AS base
diff --git a/Gemfile.lock b/Gemfile.lock
index 017b0e44a..f8cec9b55 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -304,12 +304,12 @@ GEM
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
- kt-paperclip (7.2.0)
+ kt-paperclip (7.3.0)
activemodel (>= 4.2.0)
activesupport (>= 4.2.0)
marcel (~> 1.0.1)
mime-types
- terrapin (~> 0.6.0)
+ terrapin (>= 0.6.0, < 2.0)
language_server-protocol (3.17.0.3)
ld-patch (3.3.0)
ebnf (~> 2.4)
@@ -632,7 +632,7 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
- reverse_markdown (2.1.1)
+ reverse_markdown (3.0.2)
nokogiri
rexml (3.4.4)
rouge (4.1.3)
@@ -739,7 +739,7 @@ GEM
sitemap-parser (0.5.6)
nokogiri (~> 1.6)
typhoeus (>= 0.6, < 2.0)
- sitemap_generator (6.3.0)
+ sitemap_generator (7.0.1)
builder (~> 3.0)
slim (5.1.1)
temple (~> 0.10.0)
@@ -780,8 +780,8 @@ GEM
matrix (~> 0.4)
rdf (~> 3.3)
temple (0.10.4)
- terrapin (0.6.0)
- climate_control (>= 0.0.3, < 1.0)
+ terrapin (1.1.1)
+ climate_control
terser (1.1.17)
execjs (>= 0.3.0, < 3)
thor (1.5.0)
@@ -826,7 +826,7 @@ GEM
activesupport
faraday (~> 2.0)
faraday-follow_redirects
- webmock (3.18.1)
+ webmock (3.26.2)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -837,7 +837,7 @@ GEM
websocket-extensions (0.1.5)
whenever (1.0.0)
chronic (>= 0.6.3)
- will_paginate (4.0.0)
+ will_paginate (4.0.1)
xpath (3.2.0)
nokogiri (~> 1.8)
yaml-ld (0.0.3)
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 8c1ef2d02..bd7e0cb38 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -251,8 +251,8 @@ def twitter_link(username)
end
def info_button(title, opts = {}, &block)
- classes = 'btn btn-default has-popover'
- classes << " #{opts[:class]}" if opts[:class]
+ classes = %w(btn btn-default has-popover)
+ classes << opts[:class] if opts[:class]
title_text = opts[:hide_text] ? '' : title
content_tag(:a, tabindex: 0, class: classes,
data: { toggle: 'popover', placement: 'bottom',
@@ -300,7 +300,7 @@ def tab(text, icon, href, disabled: { check: false }, active: false, count: nil,
options['data-tab-history-update-url'] = true
end
- text << " (#{count})" if count
+ text += " (#{count})" if count
link_to("##{href}", options) do
content_tag(:i, nil, class: icon, 'aria-hidden': 'true') + ' ' + text
diff --git a/app/helpers/materials_helper.rb b/app/helpers/materials_helper.rb
index 9ddb7feb1..3b9ef9a2d 100644
--- a/app/helpers/materials_helper.rb
+++ b/app/helpers/materials_helper.rb
@@ -86,21 +86,21 @@ def display_attribute(resource, attribute, show_label: true, title: nil, markdow
end
string = ""
unless value.blank? || value.try(:strip) == 'License Not Specified'
- string << " #{title || resource.class.human_attribute_name(attribute)}: " if show_label
+ string += " #{title || resource.class.human_attribute_name(attribute)}: " if show_label
if list
- string << '
'
+ string += ''
value.each do |v|
- string << "- #{v}
"
+ string += "- #{v}
"
end
- string << '
'
+ string += '
'
elsif expandable
height_limit = expandable.is_a?(Numeric) ? expandable : nil
- string << "" + value.to_s + '
'
+ string += "" + value.to_s + '
'
else
- string << value.to_s
+ string += value.to_s
end
end
- string << '
'
+ string += ''
string.html_safe
end
diff --git a/lib/ingestors/bioschemas_ingestor.rb b/lib/ingestors/bioschemas_ingestor.rb
index 8be6fb050..8e37f129a 100644
--- a/lib/ingestors/bioschemas_ingestor.rb
+++ b/lib/ingestors/bioschemas_ingestor.rb
@@ -33,14 +33,14 @@ def read(source_url)
output[:totals].each do |key, value|
totals[key] += value
end
- no_bioschema_urls << "\n - #{url}" if !source.nil? && output[:totals].values.sum.zero?
+ no_bioschema_urls += "\n - #{url}" if !source.nil? && output[:totals].values.sum.zero?
end
end
if totals.keys.any?
bioschemas_summary = "Bioschemas summary:\n"
totals.each do |type, count|
- bioschemas_summary << "\n - #{type}: #{count}"
+ bioschemas_summary += "\n - #{type}: #{count}"
end
@messages << bioschemas_summary
end
@@ -115,11 +115,11 @@ def read_content(content, url: nil)
end
message = "#{error} occurred while reading"
if url.present? && url != 'https://example.com'
- message << ": #{url} "
+ message += ": #{url} "
else
- message << " the source"
+ message += " the source"
end
- message << ". #{comment}" if comment
+ message += ". #{comment}" if comment
@messages << message
end
diff --git a/lib/ingestors/oai_pmh_ingestor.rb b/lib/ingestors/oai_pmh_ingestor.rb
index 9ce325e67..603ec8fd8 100644
--- a/lib/ingestors/oai_pmh_ingestor.rb
+++ b/lib/ingestors/oai_pmh_ingestor.rb
@@ -96,7 +96,7 @@ def read_oai_rdf(client)
if totals.keys.any?
bioschemas_summary = "Bioschemas summary:\n"
totals.each do |type, count|
- bioschemas_summary << "\n - #{type}: #{count}"
+ bioschemas_summary += "\n - #{type}: #{count}"
end
@messages << bioschemas_summary
end
diff --git a/lib/scraper.rb b/lib/scraper.rb
index 3cd864884..07717746a 100644
--- a/lib/scraper.rb
+++ b/lib/scraper.rb
@@ -114,9 +114,9 @@ def scrape(source, user = get_user, index: 0)
end
if validate_source(source)
log t('scraper.messages.valid_source'), 2
- output.concat "**Provider:** #{source.content_provider.title}\n\n"
- output.concat "**URL:** #{source.url}\n\n"
- output.concat "**Method:** #{source.ingestor_title}\n\n"
+ output += "**Provider:** #{source.content_provider.title}\n\n"
+ output += "**URL:** #{source.url}\n\n"
+ output += "**Method:** #{source.ingestor_title}\n\n"
# get ingestor
ingestor = Ingestors::IngestorFactory.get_ingestor(source.method)
@@ -127,16 +127,16 @@ def scrape(source, user = get_user, index: 0)
# read records
ingestor.read(source.url)
unless ingestor.messages.blank?
- output.concat "\n## Reading\n\n"
- ingestor.messages.each { |m| output.concat "#{m}\n" }
+ output += "\n## Reading\n\n"
+ ingestor.messages.each { |m| output += "#{m}\n" }
ingestor.messages.clear
end
# write resources
ingestor.write(user, source.content_provider, source: source)
unless ingestor.messages.blank?
- output.concat "\n## Writing\n\n"
- ingestor.messages.each { |m| output.concat "#{m}\n" }
+ output += "\n## Writing\n\n"
+ ingestor.messages.each { |m| output += "#{m}\n" }
ingestor.messages.clear
end
@@ -152,13 +152,13 @@ def scrape(source, user = get_user, index: 0)
", rejected[#{source.resources_rejected}]", 2
end
rescue StandardError => e
- output.concat "\n**Failed:** #{e.message}\n\n"
+ output += "\n**Failed:** #{e.message}\n\n"
log "Ingestor: #{ingestor.class} failed with: #{e.message}\t#{e.backtrace[0]}", 2
ensure
source.finished_at = Time.now
run_time = source.finished_at - source_start
- output.concat "\n**Finished at:** #{source.finished_at.strftime '%H:%M on %A, %d %B %Y (UTC)'}\n"
- output.concat "\n**Run time:** #{run_time.round(2)}s\n"
+ output += "\n**Finished at:** #{source.finished_at.strftime '%H:%M on %A, %d %B %Y (UTC)'}\n"
+ output += "\n**Run time:** #{run_time.round(2)}s\n"
source.log = output
begin
# only update enabled sources
From 09ab338fa3fbb3bee9002598b1f1a8ede1450c61 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 15:24:13 +0100
Subject: [PATCH 12/20] Fix backtrace test for Ruby 3.4 format
...Also log exception class in scraper log
---
lib/scraper.rb | 2 +-
test/unit/ingestors/scraper_test.rb | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/lib/scraper.rb b/lib/scraper.rb
index 07717746a..fd5eca1b0 100644
--- a/lib/scraper.rb
+++ b/lib/scraper.rb
@@ -87,7 +87,7 @@ def run
# --- finished
log t('scraper.messages.status', status: 'finish'), 0
rescue StandardError => e
- log " Run Scraper failed with: #{e.message}", 0
+ log " Run Scraper failed with: #{e.message} (#{e.class.name})", 0
e.backtrace.each do |line|
log " #{line}", 0
end
diff --git a/test/unit/ingestors/scraper_test.rb b/test/unit/ingestors/scraper_test.rb
index ad37c3fb1..5a45e23e0 100644
--- a/test/unit/ingestors/scraper_test.rb
+++ b/test/unit/ingestors/scraper_test.rb
@@ -225,10 +225,10 @@ def run
scraper.run
end
- assert logfile_contains(logfile, 'Run Scraper failed with')
- assert logfile_contains(logfile, 'in `block in run')
- assert logfile_contains(logfile, 'in `map')
- assert logfile_contains(logfile, 'in `run')
+ assert logfile_contains(logfile, 'Run Scraper failed with: unknown attribute')
+ assert logfile_contains(logfile, "in 'block in Scraper#run'")
+ assert logfile_contains(logfile, "in 'Array#map'")
+ assert logfile_contains(logfile, "in 'Scraper#run'")
end
test 'does not scrape disabled or unapproved sources' do
From fe695dd6b5c83530da323325221abb9a1d36b609 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 15:30:51 +0100
Subject: [PATCH 13/20] Bump actions/setup-node to stop deprecation warning
---
.github/workflows/deployment-checks.yml | 2 +-
.github/workflows/migration-test.yml | 2 +-
.github/workflows/test.yml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/deployment-checks.yml b/.github/workflows/deployment-checks.yml
index 840f53e88..966cf6744 100644
--- a/.github/workflows/deployment-checks.yml
+++ b/.github/workflows/deployment-checks.yml
@@ -49,7 +49,7 @@ jobs:
cp config/ingestion.example.yml config/ingestion.yml
bundle exec rake db:setup
- name: Set up Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
node-version: '20.x'
cache: 'yarn'
diff --git a/.github/workflows/migration-test.yml b/.github/workflows/migration-test.yml
index 6dab87154..8eac72cd1 100644
--- a/.github/workflows/migration-test.yml
+++ b/.github/workflows/migration-test.yml
@@ -76,7 +76,7 @@ jobs:
rm -rf db/migrate
mv db/migrate.bkp db/migrate
- name: Set up Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
node-version: '20.x'
cache: 'yarn'
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7d6528a3f..88f865203 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -56,7 +56,7 @@ jobs:
cp config/ingestion.example.yml config/ingestion.yml
bundle exec rake db:test:prepare
- name: Set up Node
- uses: actions/setup-node@v4
+ uses: actions/setup-node@v6
with:
node-version: '20.x'
cache: 'yarn'
From d1cf8678aca049f9ecb524306f126aabb95be43a Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 16:09:59 +0100
Subject: [PATCH 14/20] Fix? (even though it worked OK) audit step to stop
copilot moaning
---
.github/workflows/test.yml | 2 +-
Gemfile | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 88f865203..501914cc0 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -48,7 +48,7 @@ jobs:
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- name: Audit gems
- run: bundle exec bundle audit check --update
+ run: bundle exec bundle-audit check --update
- name: Configure and initialize database
run: |
cp test/config/test_tess.yml config/tess.yml
diff --git a/Gemfile b/Gemfile
index be07a9914..e7b20b5bc 100644
--- a/Gemfile
+++ b/Gemfile
@@ -76,6 +76,7 @@ gem 'whenever', require: false # Work around https://github.com/javan/whenever/i
gem 'will_paginate'
group :development, :test do
+ gem 'bundle-audit'
gem 'byebug'
gem 'pry-byebug'
gem 'rubocop'
@@ -86,7 +87,6 @@ end
group :development do
gem 'better_errors'
gem 'binding_of_caller'
- gem 'bundle-audit'
gem 'listen'
gem 'puma'
gem 'web-console'
From b5d194635e8866b310c5ec37f66a9f3499d08098 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 21:42:02 +0100
Subject: [PATCH 15/20] Switch to arrays and `content_tag`. Improve scraper log
formatting
---
app/helpers/materials_helper.rb | 27 ++++++++++-------------
lib/ingestors/bioschemas_ingestor.rb | 20 ++++++++---------
lib/ingestors/concerns/sitemap_helpers.rb | 2 +-
lib/ingestors/oai_pmh_ingestor.rb | 6 ++---
lib/scraper.rb | 24 ++++++++++----------
5 files changed, 38 insertions(+), 41 deletions(-)
diff --git a/app/helpers/materials_helper.rb b/app/helpers/materials_helper.rb
index 3b9ef9a2d..92d7e39eb 100644
--- a/app/helpers/materials_helper.rb
+++ b/app/helpers/materials_helper.rb
@@ -84,24 +84,21 @@ def display_attribute(resource, attribute, show_label: true, title: nil, markdow
html_escape(block_given? ? yield(value) : value)
end
end
- string = ""
- unless value.blank? || value.try(:strip) == 'License Not Specified'
- string += " #{title || resource.class.human_attribute_name(attribute)}: " if show_label
- if list
- string += '
'
- value.each do |v|
- string += "- #{v}
"
+ classes = [attribute]
+ classes << 'no-spacing' if show_label
+ content_tag(:p, class: classes) do
+ unless value.blank? || value.try(:strip) == 'License Not Specified'
+ concat content_tag(:strong, "#{title || resource.class.human_attribute_name(attribute)}: ", class: 'text-primary') if show_label
+ if list
+ concat content_tag(:ul, value.map { |v| content_tag(:li, v) })
+ elsif expandable
+ height_limit = expandable.is_a?(Numeric) ? expandable : nil
+ concat content_tag(:div, value.to_s, class: 'tess-expandable', 'data-height-limit': height_limit ? height_limit : nil)
+ else
+ concat value.to_s
end
- string += '
'
- elsif expandable
- height_limit = expandable.is_a?(Numeric) ? expandable : nil
- string += "" + value.to_s + '
'
- else
- string += value.to_s
end
end
- string += ''
- string.html_safe
end
def display_people(resource, attribute)
diff --git a/lib/ingestors/bioschemas_ingestor.rb b/lib/ingestors/bioschemas_ingestor.rb
index 8e37f129a..6d931fe31 100644
--- a/lib/ingestors/bioschemas_ingestor.rb
+++ b/lib/ingestors/bioschemas_ingestor.rb
@@ -23,7 +23,7 @@ def read(source_url)
provider_events = []
provider_materials = []
totals = Hash.new(0)
- no_bioschema_urls = "Bioschemas not found in:\n"
+ no_bioschema_urls = []
sources.each do |url|
source = open_url(url)
output = read_content(source, url: url)
@@ -33,19 +33,19 @@ def read(source_url)
output[:totals].each do |key, value|
totals[key] += value
end
- no_bioschema_urls += "\n - #{url}" if !source.nil? && output[:totals].values.sum.zero?
+ no_bioschema_urls << url if !source.nil? && output[:totals].values.sum.zero?
end
end
if totals.keys.any?
- bioschemas_summary = "Bioschemas summary:\n"
+ bioschemas_summary = ["Bioschemas summary:\n"]
totals.each do |type, count|
- bioschemas_summary += "\n - #{type}: #{count}"
+ bioschemas_summary << " - #{type}: #{count}"
end
- @messages << bioschemas_summary
+ @messages << bioschemas_summary.join("\n")
end
- @messages << no_bioschema_urls
+ @messages << "Bioschemas not found in:\n\n#{no_bioschema_urls.map { |u| " - #{u}" }.join("\n")}" if no_bioschema_urls.any?
deduplicate(provider_events).each do |event_params|
add_event(event_params)
@@ -113,13 +113,13 @@ def read_content(content, url: nil)
error = 'A parsing error'
comment = 'Please check your page contains valid JSON-LD or HTML.'
end
- message = "#{error} occurred while reading"
+ message = String.new("#{error} occurred while reading")
if url.present? && url != 'https://example.com'
- message += ": #{url} "
+ message << ": #{url} "
else
- message += " the source"
+ message << " the source"
end
- message += ". #{comment}" if comment
+ message << ". #{comment}" if comment
@messages << message
end
diff --git a/lib/ingestors/concerns/sitemap_helpers.rb b/lib/ingestors/concerns/sitemap_helpers.rb
index e9c6846c6..960f20e43 100644
--- a/lib/ingestors/concerns/sitemap_helpers.rb
+++ b/lib/ingestors/concerns/sitemap_helpers.rb
@@ -41,7 +41,7 @@ def parse_txt_sitemap(url)
end
def log_sitemap(ext, url, count)
- @messages << "Parsing .#{ext} sitemap: #{url}\n - #{count} URLs found"
+ @messages << "Parsing .#{ext} sitemap: #{url}\n\n - #{count} URLs found"
end
end
end
diff --git a/lib/ingestors/oai_pmh_ingestor.rb b/lib/ingestors/oai_pmh_ingestor.rb
index 603ec8fd8..fff2426e1 100644
--- a/lib/ingestors/oai_pmh_ingestor.rb
+++ b/lib/ingestors/oai_pmh_ingestor.rb
@@ -94,11 +94,11 @@ def read_oai_rdf(client)
end
if totals.keys.any?
- bioschemas_summary = "Bioschemas summary:\n"
+ bioschemas_summary = ["Bioschemas summary:\n"]
totals.each do |type, count|
- bioschemas_summary += "\n - #{type}: #{count}"
+ bioschemas_summary << " - #{type}: #{count}"
end
- @messages << bioschemas_summary
+ @messages << bioschemas_summary.join("\n")
end
@bioschemas_manager.deduplicate(provider_events).each do |event_params|
diff --git a/lib/scraper.rb b/lib/scraper.rb
index fd5eca1b0..b72594334 100644
--- a/lib/scraper.rb
+++ b/lib/scraper.rb
@@ -104,7 +104,7 @@ def run
def scrape(source, user = get_user, index: 0)
source_start = Time.now
- output = ''
+ output = []
log '', 1
if source.enabled
log t('scraper.messages.processing', source: source.content_provider&.title, num: index.to_s), 1
@@ -114,9 +114,9 @@ def scrape(source, user = get_user, index: 0)
end
if validate_source(source)
log t('scraper.messages.valid_source'), 2
- output += "**Provider:** #{source.content_provider.title}\n\n"
- output += "**URL:** #{source.url}\n\n"
- output += "**Method:** #{source.ingestor_title}\n\n"
+ output << "**Provider:** #{source.content_provider.title}"
+ output << "**URL:** #{source.url}"
+ output << "**Method:** #{source.ingestor_title}"
# get ingestor
ingestor = Ingestors::IngestorFactory.get_ingestor(source.method)
@@ -127,16 +127,16 @@ def scrape(source, user = get_user, index: 0)
# read records
ingestor.read(source.url)
unless ingestor.messages.blank?
- output += "\n## Reading\n\n"
- ingestor.messages.each { |m| output += "#{m}\n" }
+ output << "\n## Reading"
+ output += ingestor.messages
ingestor.messages.clear
end
# write resources
ingestor.write(user, source.content_provider, source: source)
unless ingestor.messages.blank?
- output += "\n## Writing\n\n"
- ingestor.messages.each { |m| output += "#{m}\n" }
+ output << "\n## Writing"
+ output += ingestor.messages
ingestor.messages.clear
end
@@ -152,14 +152,14 @@ def scrape(source, user = get_user, index: 0)
", rejected[#{source.resources_rejected}]", 2
end
rescue StandardError => e
- output += "\n**Failed:** #{e.message}\n\n"
+ output << "\n**Failed:** #{e.message}"
log "Ingestor: #{ingestor.class} failed with: #{e.message}\t#{e.backtrace[0]}", 2
ensure
source.finished_at = Time.now
run_time = source.finished_at - source_start
- output += "\n**Finished at:** #{source.finished_at.strftime '%H:%M on %A, %d %B %Y (UTC)'}\n"
- output += "\n**Run time:** #{run_time.round(2)}s\n"
- source.log = output
+ output << "\n**Finished at:** #{source.finished_at.strftime '%H:%M on %A, %d %B %Y (UTC)'}"
+ output << "**Run time:** #{run_time.round(2)}s"
+ source.log = output.join("\n\n")
begin
# only update enabled sources
source.save! if source.enabled && !source.is_a?(Scraper::ConfigSource)
From 7cd6c2bc483fcf4c2faf7b971f6dd7a3a827e2c5 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Tue, 9 Jun 2026 21:50:26 +0100
Subject: [PATCH 16/20] Fix list attribute display
---
app/helpers/materials_helper.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/helpers/materials_helper.rb b/app/helpers/materials_helper.rb
index 92d7e39eb..efcee3229 100644
--- a/app/helpers/materials_helper.rb
+++ b/app/helpers/materials_helper.rb
@@ -90,7 +90,7 @@ def display_attribute(resource, attribute, show_label: true, title: nil, markdow
unless value.blank? || value.try(:strip) == 'License Not Specified'
concat content_tag(:strong, "#{title || resource.class.human_attribute_name(attribute)}: ", class: 'text-primary') if show_label
if list
- concat content_tag(:ul, value.map { |v| content_tag(:li, v) })
+ concat content_tag(:ul) { value.each { |v| concat(content_tag(:li, v)) } }
elsif expandable
height_limit = expandable.is_a?(Numeric) ? expandable : nil
concat content_tag(:div, value.to_s, class: 'tess-expandable', 'data-height-limit': height_limit ? height_limit : nil)
From 3fa0457b951700422f31e1f14085206160ad93c2 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Wed, 10 Jun 2026 11:02:52 +0100
Subject: [PATCH 17/20] Update to Rails 8.1.3
---
Gemfile | 5 +-
Gemfile.lock | 149 +++++++++---------
config/initializers/omniauth.rb | 6 +-
...quest_forgery_protection_token_verifier.rb | 35 ----
4 files changed, 80 insertions(+), 115 deletions(-)
delete mode 100644 lib/request_forgery_protection_token_verifier.rb
diff --git a/Gemfile b/Gemfile
index e7b20b5bc..e84c1ef21 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,7 +2,7 @@
source 'https://rubygems.org', cooldown: 14
-gem 'rails', '7.2.3.1'
+gem 'rails', '8.1.3'
gem 'active_model_serializers'
gem 'activerecord-session_store'
@@ -37,7 +37,6 @@ gem 'maxmind-db'
gem 'money-rails'
gem 'oai'
gem 'omniauth_openid_connect'
-gem 'omniauth-rails_csrf_protection'
gem 'pg'
gem 'private_address_check'
gem 'public_activity'
@@ -61,7 +60,7 @@ gem 'sidekiq'
gem 'sidekiq-status'
gem 'simple_calendar', '~> 2.4'
gem 'simple_form'
-gem 'simple_token_authentication'
+gem 'simple_token_authentication', git: 'https://github.com/gonzalo-bulnes/simple_token_authentication.git', ref: 'f1cba4e' # Rails 8 support not yet released on Rubygems
gem 'sitemap_generator'
gem 'sitemap-parser'
gem 'slim'
diff --git a/Gemfile.lock b/Gemfile.lock
index f8cec9b55..581460f46 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -6,72 +6,82 @@ GIT
tess_rdf_extractors (1.2.0)
linkeddata (~> 3.2.0)
+GIT
+ remote: https://github.com/gonzalo-bulnes/simple_token_authentication.git
+ revision: f1cba4e3e614932ffcb6dfd2d8bf03acd64cb1ec
+ ref: f1cba4e
+ specs:
+ simple_token_authentication (1.18.1)
+ actionmailer (>= 3.2.6, < 9)
+ actionpack (>= 3.2.6, < 9)
+ devise (>= 3.2, < 6)
+
GEM
remote: https://rubygems.org/
specs:
- actioncable (7.2.3.1)
- actionpack (= 7.2.3.1)
- activesupport (= 7.2.3.1)
+ action_text-trix (2.1.19)
+ railties
+ actioncable (8.1.3)
+ actionpack (= 8.1.3)
+ activesupport (= 8.1.3)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
- actionmailbox (7.2.3.1)
- actionpack (= 7.2.3.1)
- activejob (= 7.2.3.1)
- activerecord (= 7.2.3.1)
- activestorage (= 7.2.3.1)
- activesupport (= 7.2.3.1)
+ actionmailbox (8.1.3)
+ actionpack (= 8.1.3)
+ activejob (= 8.1.3)
+ activerecord (= 8.1.3)
+ activestorage (= 8.1.3)
+ activesupport (= 8.1.3)
mail (>= 2.8.0)
- actionmailer (7.2.3.1)
- actionpack (= 7.2.3.1)
- actionview (= 7.2.3.1)
- activejob (= 7.2.3.1)
- activesupport (= 7.2.3.1)
+ actionmailer (8.1.3)
+ actionpack (= 8.1.3)
+ actionview (= 8.1.3)
+ activejob (= 8.1.3)
+ activesupport (= 8.1.3)
mail (>= 2.8.0)
rails-dom-testing (~> 2.2)
- actionpack (7.2.3.1)
- actionview (= 7.2.3.1)
- activesupport (= 7.2.3.1)
- cgi
+ actionpack (8.1.3)
+ actionview (= 8.1.3)
+ activesupport (= 8.1.3)
nokogiri (>= 1.8.5)
- racc
- rack (>= 2.2.4, < 3.3)
+ rack (>= 2.2.4)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
- actiontext (7.2.3.1)
- actionpack (= 7.2.3.1)
- activerecord (= 7.2.3.1)
- activestorage (= 7.2.3.1)
- activesupport (= 7.2.3.1)
+ actiontext (8.1.3)
+ action_text-trix (~> 2.1.15)
+ actionpack (= 8.1.3)
+ activerecord (= 8.1.3)
+ activestorage (= 8.1.3)
+ activesupport (= 8.1.3)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (7.2.3.1)
- activesupport (= 7.2.3.1)
+ actionview (8.1.3)
+ activesupport (= 8.1.3)
builder (~> 3.1)
- cgi
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
- active_model_serializers (0.10.14)
+ active_model_serializers (0.10.16)
actionpack (>= 4.1)
activemodel (>= 4.1)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
- activejob (7.2.3.1)
- activesupport (= 7.2.3.1)
+ activejob (8.1.3)
+ activesupport (= 8.1.3)
globalid (>= 0.3.6)
- activemodel (7.2.3.1)
- activesupport (= 7.2.3.1)
+ activemodel (8.1.3)
+ activesupport (= 8.1.3)
activemodel-serializers-xml (1.0.3)
activemodel (>= 5.0.0.a)
activesupport (>= 5.0.0.a)
builder (~> 3.1)
- activerecord (7.2.3.1)
- activemodel (= 7.2.3.1)
- activesupport (= 7.2.3.1)
+ activerecord (8.1.3)
+ activemodel (= 8.1.3)
+ activesupport (= 8.1.3)
timeout (>= 0.4.0)
activerecord-session_store (2.2.0)
actionpack (>= 7.0)
@@ -79,24 +89,25 @@ GEM
cgi (>= 0.3.6)
rack (>= 2.0.8, < 4)
railties (>= 7.0)
- activestorage (7.2.3.1)
- actionpack (= 7.2.3.1)
- activejob (= 7.2.3.1)
- activerecord (= 7.2.3.1)
- activesupport (= 7.2.3.1)
+ activestorage (8.1.3)
+ actionpack (= 8.1.3)
+ activejob (= 8.1.3)
+ activerecord (= 8.1.3)
+ activesupport (= 8.1.3)
marcel (~> 1.0)
- activesupport (7.2.3.1)
+ activesupport (8.1.3)
base64
- benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
+ json
logger (>= 1.4.2)
- minitest (>= 5.1, < 6)
+ minitest (>= 5.1)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
+ uri (>= 0.13.1)
addressable (2.9.0)
public_suffix (>= 2.0.2, < 8.0)
aes_key_wrap (1.1.0)
@@ -114,7 +125,6 @@ GEM
base64 (0.3.0)
bcp47_spec (0.2.1)
bcrypt (3.1.22)
- benchmark (0.5.0)
better_errors (2.10.1)
erubi (>= 1.0.0)
rack (>= 0.9.0)
@@ -417,9 +427,6 @@ GEM
logger
rack (>= 2.2.3)
rack-protection
- omniauth-rails_csrf_protection (1.0.1)
- actionpack (>= 4.2)
- omniauth (~> 2.0)
omniauth_openid_connect (0.7.1)
omniauth (>= 1.9, < 3)
openid_connect (~> 2.2)
@@ -491,20 +498,20 @@ GEM
rack (>= 1.3)
rackup (2.3.1)
rack (>= 3)
- rails (7.2.3.1)
- actioncable (= 7.2.3.1)
- actionmailbox (= 7.2.3.1)
- actionmailer (= 7.2.3.1)
- actionpack (= 7.2.3.1)
- actiontext (= 7.2.3.1)
- actionview (= 7.2.3.1)
- activejob (= 7.2.3.1)
- activemodel (= 7.2.3.1)
- activerecord (= 7.2.3.1)
- activestorage (= 7.2.3.1)
- activesupport (= 7.2.3.1)
+ rails (8.1.3)
+ actioncable (= 8.1.3)
+ actionmailbox (= 8.1.3)
+ actionmailer (= 8.1.3)
+ actionpack (= 8.1.3)
+ actiontext (= 8.1.3)
+ actionview (= 8.1.3)
+ activejob (= 8.1.3)
+ activemodel (= 8.1.3)
+ activerecord (= 8.1.3)
+ activestorage (= 8.1.3)
+ activesupport (= 8.1.3)
bundler (>= 1.15.0)
- railties (= 7.2.3.1)
+ railties (= 8.1.3)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
@@ -516,9 +523,9 @@ GEM
rails-html-sanitizer (1.7.0)
loofah (~> 2.25)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
- rails-i18n (7.0.8)
+ rails-i18n (8.1.0)
i18n (>= 0.7, < 2)
- railties (>= 6.0.0, < 8)
+ railties (>= 8.0.0, < 9)
rails_admin (3.3.0)
activemodel-serializers-xml (>= 1.0)
csv
@@ -526,10 +533,9 @@ GEM
nested_form (~> 0.3)
rails (>= 6.0, < 9)
turbo-rails (>= 1.0, < 3)
- railties (7.2.3.1)
- actionpack (= 7.2.3.1)
- activesupport (= 7.2.3.1)
- cgi
+ railties (8.1.3)
+ actionpack (= 8.1.3)
+ activesupport (= 8.1.3)
irb (~> 1.13)
rackup (>= 1.0.0)
rake (>= 12.2)
@@ -725,10 +731,6 @@ GEM
actionpack (>= 5.2)
activemodel (>= 5.2)
simple_po_parser (1.1.6)
- simple_token_authentication (1.18.1)
- actionmailer (>= 3.2.6, < 8)
- actionpack (>= 3.2.6, < 8)
- devise (>= 3.2, < 6)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
@@ -892,7 +894,6 @@ DEPENDENCIES
minitest-reporters
money-rails
oai
- omniauth-rails_csrf_protection
omniauth_openid_connect
pg
private_address_check
@@ -901,7 +902,7 @@ DEPENDENCIES
puma
pundit
rack-cors
- rails (= 7.2.3.1)
+ rails (= 8.1.3)
rails-controller-testing
rails-i18n
rails_admin
@@ -923,7 +924,7 @@ DEPENDENCIES
sidekiq-status
simple_calendar (~> 2.4)
simple_form
- simple_token_authentication
+ simple_token_authentication!
simplecov
simplecov-lcov
sitemap-parser
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index 798da2c0b..eaad99e40 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -1,6 +1,6 @@
-require 'request_forgery_protection_token_verifier'
-
Rails.application.config.middleware.use OmniAuth::Builder do
OmniAuth.config.allowed_request_methods = [:post]
- OmniAuth.config.before_request_phase = RequestForgeryProtectionTokenVerifier.new
+
+ OmniAuth.config.request_validation_phase = OmniAuth::AuthenticityTokenProtection.new(key: :_csrf_token)
end
+
diff --git a/lib/request_forgery_protection_token_verifier.rb b/lib/request_forgery_protection_token_verifier.rb
deleted file mode 100644
index ed85c1ebe..000000000
--- a/lib/request_forgery_protection_token_verifier.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# Provides a callable method that verifies Cross-Site Request Forgery protection
-# token. This class includes `ActionController::RequestForgeryProtection`
-# directly and utilizes `verified_request?` method to match the way Rails
-# performs token verification in Rails controllers.
-#
-# If you like to learn more about how Rails generate and verify authenticity
-# token, you can find the source code at
-# https://github.com/rails/rails/blob/v5.2.2/actionpack/lib/action_controller/metal/request_forgery_protection.rb#L217-L240.
-class RequestForgeryProtectionTokenVerifier
- include ActiveSupport::Configurable
- include ActionController::RequestForgeryProtection
-
- # `ActionController::RequestForgeryProtection` contains a few configurable
- # options. As we want to make sure that our configuration is the same as what
- # being set in `ActionController::Base`, we should make all out configuration
- # methods to delegate to `ActionController::Base`.
- config.each_key do |configuration_name|
- define_method configuration_name do
- ActionController::Base.config[configuration_name]
- end
- end
-
- def call(env)
- @request = ActionDispatch::Request.new(env)
-
- unless verified_request?
- raise ActionController::InvalidAuthenticityToken
- end
- end
-
- private
-
- attr_reader :request
- delegate :params, :session, to: :request
-end
From 8ce7adf4c5aba0d745efccedc0eca840b32a2281 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Wed, 10 Jun 2026 12:04:40 +0100
Subject: [PATCH 18/20] Bump `ruby-openai` to fix warning
---
Gemfile.lock | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Gemfile.lock b/Gemfile.lock
index 581460f46..d469a5335 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -660,7 +660,7 @@ GEM
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0)
parser (>= 3.2.1.0)
- ruby-openai (7.3.1)
+ ruby-openai (8.3.0)
event_stream_parser (>= 0.3.0, < 2.0.0)
faraday (>= 1)
faraday-multipart (>= 1)
From 2f4d08064dd42670adecb25ea66ea14d66273465 Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Wed, 10 Jun 2026 12:05:01 +0100
Subject: [PATCH 19/20] Skip omniauth CSRF protection in tests
---
config/initializers/omniauth.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index eaad99e40..3ddceac5c 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -1,6 +1,6 @@
Rails.application.config.middleware.use OmniAuth::Builder do
OmniAuth.config.allowed_request_methods = [:post]
- OmniAuth.config.request_validation_phase = OmniAuth::AuthenticityTokenProtection.new(key: :_csrf_token)
+ OmniAuth.config.request_validation_phase = Rails.env.test? ? nil : OmniAuth::AuthenticityTokenProtection.new(key: :_csrf_token)
end
From ce01caaedb349eba01d25d9930c8bed320d1354a Mon Sep 17 00:00:00 2001
From: Finn Bacall
Date: Thu, 11 Jun 2026 10:51:12 +0100
Subject: [PATCH 20/20] Take care with return value
---
app/helpers/materials_helper.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/helpers/materials_helper.rb b/app/helpers/materials_helper.rb
index efcee3229..c8a91f3cb 100644
--- a/app/helpers/materials_helper.rb
+++ b/app/helpers/materials_helper.rb
@@ -90,7 +90,7 @@ def display_attribute(resource, attribute, show_label: true, title: nil, markdow
unless value.blank? || value.try(:strip) == 'License Not Specified'
concat content_tag(:strong, "#{title || resource.class.human_attribute_name(attribute)}: ", class: 'text-primary') if show_label
if list
- concat content_tag(:ul) { value.each { |v| concat(content_tag(:li, v)) } }
+ concat content_tag(:ul) { safe_join(value.map { |v| content_tag(:li, v) }) }
elsif expandable
height_limit = expandable.is_a?(Numeric) ? expandable : nil
concat content_tag(:div, value.to_s, class: 'tess-expandable', 'data-height-limit': height_limit ? height_limit : nil)