mirror of
https://github.com/discourse/discourse.git
synced 2026-08-07 13:19:19 +08:00
This commit adds several pieces of functionality to help keep admins in the loop about upcoming changes. First of all, there is a new initializer on boot that will notify admins about newly available upcoming changes, as well as log removed changes and status movement of existing changes. * When there is a new upcoming change, we only notify admins about it when the status is the `promote_upcoming_changes_on_status` - 1, e.g. if `promote_upcoming_changes_on_status` is `beta` then we only tell admin about the change once it has reached `alpha`. This means we may log the `added` event in one deploy, but only actually notify admins in a subsequent deploy. * We log removed upcoming changes so we can automatically delete old site setting data in a future job as needed. We also now notify admins when upcoming changes are automatically promoted to enabled based on the site's `promote_upcoming_changes_on_status`: <img width="378" height="600" alt="image" src="https://github.com/user-attachments/assets/4200fbee-9990-4bbc-a378-85946e631e77" /> In addition, we now show an indicator in the admin sidebar if there are new upcoming changes that have been added since they last visited the upcoming change config page. This data is stored in a user custom field, because Redis is ephemeral, and storing in the User table is overkill because 99% of users are not staff: <img width="248" height="112" alt="image" src="https://github.com/user-attachments/assets/4c3d3cf7-ac39-45f8-a2c8-a049cb85b8e9" /> Finally, this commit moves both the Track and Promote initializer logic behind a `DistributedMutex`, we don't want multiple processes running the same logic here, it needs to be only once. --------- Co-authored-by: Loïc Guitaut <loic@discourse.org> Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
158 lines
4.9 KiB
Ruby
Vendored
158 lines
4.9 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
RSpec.describe UpcomingChanges::Promote do
|
|
describe UpcomingChanges::Promote::Contract, type: :model do
|
|
subject(:contract) { described_class.new }
|
|
|
|
it { is_expected.to validate_presence_of(:setting_name) }
|
|
it { is_expected.to validate_presence_of(:promotion_status_threshold) }
|
|
it do
|
|
is_expected.to validate_inclusion_of(:promotion_status_threshold).in_array(
|
|
UpcomingChanges.statuses.keys,
|
|
)
|
|
end
|
|
end
|
|
|
|
describe ".call" do
|
|
subject(:result) { described_class.call(params:, **dependencies) }
|
|
|
|
fab!(:admin)
|
|
let(:current_change_status) { :beta }
|
|
let(:dependencies) { { guardian: } }
|
|
let(:guardian) { admin.guardian }
|
|
let(:params) { { setting_name: :enable_upload_debug_mode, promotion_status_threshold: } }
|
|
|
|
before do
|
|
mock_upcoming_change_metadata(
|
|
{
|
|
enable_upload_debug_mode: {
|
|
impact: "other,developers",
|
|
status: current_change_status,
|
|
impact_type: "other",
|
|
impact_role: "developers",
|
|
},
|
|
},
|
|
)
|
|
end
|
|
|
|
context "when contract is invalid" do
|
|
let(:params) { {} }
|
|
|
|
it { is_expected.to fail_a_contract }
|
|
end
|
|
|
|
context "when the upcoming change does not meet the status promotion criteria" do
|
|
let(:promotion_status_threshold) { :stable }
|
|
|
|
it { is_expected.to fail_a_policy(:meets_promotion_criteria) }
|
|
end
|
|
|
|
context "when the underlying setting for the upcoming change already exists in the DB (admin has modified it)" do
|
|
let(:promotion_status_threshold) { :beta }
|
|
|
|
before do
|
|
SiteSetting.enable_upload_debug_mode = false
|
|
SiteSetting.create!(
|
|
name: "enable_upload_debug_mode",
|
|
value: false,
|
|
data_type: SiteSetting.types[:bool],
|
|
)
|
|
end
|
|
|
|
after { SiteSetting.find_by(name: "enable_upload_debug_mode").destroy! }
|
|
|
|
context "when the current_change_status is not permanent" do
|
|
let(:current_change_status) { :beta }
|
|
|
|
it { is_expected.to fail_a_policy(:setting_not_modified) }
|
|
end
|
|
|
|
context "when the current_change_status is permanent" do
|
|
let(:current_change_status) { :permanent }
|
|
|
|
it "enables the upcoming change setting" do
|
|
expect { result }.to change { SiteSetting.enable_upload_debug_mode }.to be_truthy
|
|
end
|
|
|
|
it "logs the change context in the staff action log" do
|
|
expect { result }.to change {
|
|
UserHistory.where(
|
|
action: UserHistory.actions[:upcoming_change_toggled],
|
|
subject: "enable_upload_debug_mode",
|
|
).count
|
|
}.by(1)
|
|
|
|
expect(UserHistory.last.context).to eq(
|
|
I18n.t(
|
|
"staff_action_logs.upcoming_changes.log_promoted",
|
|
change_status: UpcomingChanges.change_status(:enable_upload_debug_mode).to_s.titleize,
|
|
base_path: Discourse.base_path,
|
|
),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when the upcoming change is already enabled" do
|
|
let(:promotion_status_threshold) { :beta }
|
|
|
|
before { SiteSetting.enable_upload_debug_mode = true }
|
|
|
|
it { is_expected.to fail_a_policy(:setting_not_already_enabled) }
|
|
end
|
|
|
|
context "when everything is ok" do
|
|
fab!(:admin_2, :admin)
|
|
|
|
let(:promotion_status_threshold) { :beta }
|
|
|
|
it { is_expected.to run_successfully }
|
|
|
|
it "enables the upcoming change setting" do
|
|
expect { result }.to change { SiteSetting.enable_upload_debug_mode }.to be_truthy
|
|
end
|
|
|
|
it "logs the change context in the staff action log" do
|
|
expect { result }.to change {
|
|
UserHistory.where(
|
|
action: UserHistory.actions[:upcoming_change_toggled],
|
|
subject: "enable_upload_debug_mode",
|
|
).count
|
|
}.by(1)
|
|
|
|
expect(UserHistory.last.context).to eq(
|
|
I18n.t(
|
|
"staff_action_logs.upcoming_changes.log_promoted",
|
|
change_status: UpcomingChanges.change_status(:enable_upload_debug_mode).to_s.titleize,
|
|
base_path: Discourse.base_path,
|
|
),
|
|
)
|
|
end
|
|
|
|
it "notifies admins about the upcoming change" do
|
|
expect { result }.to change {
|
|
Notification.where(
|
|
notification_type: Notification.types[:upcoming_change_automatically_promoted],
|
|
user_id: [admin.id, admin_2.id],
|
|
).count
|
|
}.by(2)
|
|
|
|
expect(Notification.last.data).to eq(
|
|
{
|
|
upcoming_change_name: :enable_upload_debug_mode,
|
|
upcoming_change_humanized_name: "Enable upload debug mode",
|
|
}.to_json,
|
|
)
|
|
end
|
|
|
|
it "creates an admins_notified_automatic_promotion event" do
|
|
expect { result }.to change {
|
|
UpcomingChangeEvent.where(
|
|
event_type: :admins_notified_automatic_promotion,
|
|
upcoming_change_name: :enable_upload_debug_mode,
|
|
).count
|
|
}.by(1)
|
|
end
|
|
end
|
|
end
|
|
end
|