diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index ff2935a46a6..2e2d1bd41fe 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -15,10 +15,14 @@ text-align: start; background: var(--color-bg-primary); - &.critical { + .critical { font-weight: 700; color: var(--color-text-warning); } + + .unsupported { + color: var(--color-text-error); + } } .align-end { diff --git a/app/models/software_update.rb b/app/models/software_update.rb index 58b8342742b..2af760885ad 100644 --- a/app/models/software_update.rb +++ b/app/models/software_update.rb @@ -4,13 +4,14 @@ # # Table name: software_updates # -# id :bigint(8) not null, primary key -# release_notes :string default(""), not null -# type :integer default("patch"), not null -# urgent :boolean default(FALSE), not null -# version :string not null -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint(8) not null, primary key +# end_of_support :date +# release_notes :string default(""), not null +# type :integer default("patch"), not null +# urgent :boolean default(FALSE), not null +# version :string not null +# created_at :datetime not null +# updated_at :datetime not null # class SoftwareUpdate < ApplicationRecord @@ -32,6 +33,10 @@ class SoftwareUpdate < ApplicationRecord gem_version > runtime_version end + def unsupported? + end_of_support&.past? + end + class << self def check_enabled? Rails.configuration.x.mastodon.software_update_url.present? diff --git a/app/services/software_update_check_service.rb b/app/services/software_update_check_service.rb index aa2eadd94c8..353a9a6c203 100644 --- a/app/services/software_update_check_service.rb +++ b/app/services/software_update_check_service.rb @@ -47,9 +47,11 @@ class SoftwareUpdateCheckService < BaseService new_update_notices = update_notices['updatesAvailable'].filter { |notice| known_versions.exclude?(notice['version']) } return if new_update_notices.blank? - new_updates = new_update_notices.map do |notice| - SoftwareUpdate.create!(version: notice['version'], urgent: notice['urgent'], type: notice['type'], release_notes: notice['releaseNotes']) - end + SoftwareUpdate.upsert_all(update_notices['updatesAvailable'].map do |notice| + { version: notice['version'], urgent: notice['urgent'], type: notice['type'], release_notes: notice['releaseNotes'], end_of_support: notice['endOfSupport']&.to_date } + end, unique_by: :version) + + new_updates = SoftwareUpdate.where(version: new_update_notices.pluck('version')).to_a notify_devops!(new_updates) end diff --git a/app/views/admin/software_updates/_software_update.html.haml b/app/views/admin/software_updates/_software_update.html.haml index 9eb270e4974..98578d9125a 100644 --- a/app/views/admin/software_updates/_software_update.html.haml +++ b/app/views/admin/software_updates/_software_update.html.haml @@ -1,9 +1,14 @@ %tr{ id: dom_id(software_update) } %td= software_update.version %td= t("admin.software_updates.types.#{software_update.type}") - %td{ class: class_names(critical: software_update.urgent?) } + %td - if software_update.urgent? - = t('admin.software_updates.critical_update') + .critical= t('admin.software_updates.critical_update') + - if software_update.end_of_support.present? + - if software_update.unsupported? + .unsupported= t('admin.software_updates.unsupported_since', date: software_update.end_of_support) + - else + %div= t('admin.software_updates.supported_until', date: software_update.end_of_support) %td.release-notes = table_link_to 'link', t('admin.software_updates.release_notes'), diff --git a/config/locales/en.yml b/config/locales/en.yml index 09ce9d210a7..410e54980a3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1014,12 +1014,14 @@ en: description: It is recommended to keep your Mastodon installation up to date to benefit from the latest fixes and features. Moreover, it is sometimes critical to update Mastodon in a timely manner to avoid security issues. For these reasons, Mastodon checks for updates every 30 minutes, and will notify you according to your email notification preferences. documentation_link: Learn more release_notes: Release notes + supported_until: Support ends on %{date} title: Available updates type: Type types: major: Major release minor: Minor release patch: Patch release — bugfixes and easy to apply changes + unsupported_since: Unsupported since %{date} version: Version statuses: account: Author diff --git a/db/migrate/20260706122112_add_end_of_support_to_software_updates.rb b/db/migrate/20260706122112_add_end_of_support_to_software_updates.rb new file mode 100644 index 00000000000..451e4f91fef --- /dev/null +++ b/db/migrate/20260706122112_add_end_of_support_to_software_updates.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddEndOfSupportToSoftwareUpdates < ActiveRecord::Migration[8.1] + def change + add_column :software_updates, :end_of_support, :date + end +end diff --git a/db/schema.rb b/db/schema.rb index 6a753c72f9f..1b24c81b3fe 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.1].define(version: 2026_07_02_144128) do +ActiveRecord::Schema[8.1].define(version: 2026_07_06_122112) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -1159,6 +1159,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_07_02_144128) do create_table "software_updates", force: :cascade do |t| t.datetime "created_at", null: false + t.date "end_of_support" t.string "release_notes", default: "", null: false t.integer "type", default: 0, null: false t.datetime "updated_at", null: false diff --git a/spec/services/software_update_check_service_spec.rb b/spec/services/software_update_check_service_spec.rb index 581e494ada5..ea72c52734a 100644 --- a/spec/services/software_update_check_service_spec.rb +++ b/spec/services/software_update_check_service_spec.rb @@ -68,18 +68,23 @@ RSpec.describe SoftwareUpdateCheckService do context 'when the server returns new versions' do let(:server_json) do { + currentVersion: { + endOfSupport: nil, + }, updatesAvailable: [ { version: '4.2.1', urgent: false, type: 'patch', releaseNotes: 'https://github.com/mastodon/mastodon/releases/v4.2.1', + endOfSupport: '2026-01-08', }, { version: '4.3.0', urgent: false, type: 'minor', releaseNotes: 'https://github.com/mastodon/mastodon/releases/v4.3.0', + endOfSupport: '2026-05-06', }, { version: '5.0.0', @@ -111,6 +116,9 @@ RSpec.describe SoftwareUpdateCheckService do context 'when an update is urgent' do let(:server_json) do { + currentVersion: { + endOfSupport: nil, + }, updatesAvailable: [ { version: '5.0.0',