Skip to content

Commit ca040aa

Browse files
Merge pull request #3417 from ClearlyClaire/glitch-soc/merge-4.3
Merge upstream changes up to 9437cdd into stable-4.3
2 parents a6ba52a + 7cff310 commit ca040aa

File tree

7 files changed

+62
-8
lines changed

7 files changed

+62
-8
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [4.3.20] - 2026-02-24
6+
7+
### Added
8+
9+
- Add `--suspended-only` option to `tootctl emoji purge` (#37828 and #37861 by @ClearlyClaire and @mjankowski)
10+
11+
### Fixed
12+
13+
- Fix processing of object updates with duplicate hashtags (#37756 by @ClearlyClaire)
14+
515
## [4.3.19] - 2026-02-03
616

717
### Security

app/services/activitypub/process_status_update_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def update_tags!
194194
Tag.find_or_create_by_names([tag]).filter(&:valid?)
195195
rescue ActiveRecord::RecordInvalid
196196
[]
197-
end
197+
end.uniq
198198

199199
return unless @status.distributable?
200200

docker-compose.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ services:
5959
web:
6060
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
6161
# build: .
62-
image: ghcr.io/glitch-soc/mastodon:v4.3.19
62+
image: ghcr.io/glitch-soc/mastodon:v4.3.20
6363
restart: always
6464
env_file: .env.production
6565
command: bundle exec puma -C config/puma.rb
@@ -83,7 +83,7 @@ services:
8383
# build:
8484
# dockerfile: ./streaming/Dockerfile
8585
# context: .
86-
image: ghcr.io/glitch-soc/mastodon-streaming:v4.3.19
86+
image: ghcr.io/glitch-soc/mastodon-streaming:v4.3.20
8787
restart: always
8888
env_file: .env.production
8989
command: node ./streaming/index.js
@@ -102,7 +102,7 @@ services:
102102
sidekiq:
103103
# You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes
104104
# build: .
105-
image: ghcr.io/glitch-soc/mastodon:v4.3.19
105+
image: ghcr.io/glitch-soc/mastodon:v4.3.20
106106
restart: always
107107
env_file: .env.production
108108
command: bundle exec sidekiq

lib/mastodon/cli/emoji.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,27 @@ def export(path)
107107
end
108108

109109
option :remote_only, type: :boolean
110+
option :suspended_only, type: :boolean
110111
desc 'purge', 'Remove all custom emoji'
111112
long_desc <<-LONG_DESC
112113
Removes all custom emoji.
113114
114115
With the --remote-only option, only remote emoji will be deleted.
116+
117+
With the --suspended-only option, only emoji from suspended servers will be deleted.
115118
LONG_DESC
116119
def purge
117-
scope = options[:remote_only] ? CustomEmoji.remote : CustomEmoji
118-
scope.in_batches.destroy_all
120+
if options[:suspended_only]
121+
DomainBlock.where(severity: :suspend).find_each do |domain_block|
122+
CustomEmoji.by_domain_and_subdomains(domain_block.domain).find_in_batches do |custom_emojis|
123+
AttachmentBatch.new(CustomEmoji, custom_emojis).delete
124+
end
125+
end
126+
else
127+
scope = options[:remote_only] ? CustomEmoji.remote : CustomEmoji
128+
scope.in_batches.destroy_all
129+
end
130+
119131
say('OK', :green)
120132
end
121133

lib/mastodon/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def minor
1313
end
1414

1515
def patch
16-
19
16+
20
1717
end
1818

1919
def default_prerelease

spec/lib/mastodon/cli/emoji_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,36 @@
2323
.to output_results('OK')
2424
end
2525
end
26+
27+
context 'with --suspended-only and existing custom emoji on blocked servers' do
28+
let(:blocked_domain) { 'evil.com' }
29+
let(:blocked_subdomain) { 'subdomain.evil.org' }
30+
let(:blocked_domain_without_emoji) { 'blocked.com' }
31+
let(:silenced_domain) { 'silenced.com' }
32+
33+
let(:options) { { suspended_only: true } }
34+
35+
before do
36+
Fabricate(:custom_emoji)
37+
Fabricate(:custom_emoji, domain: blocked_domain)
38+
Fabricate(:custom_emoji, domain: blocked_subdomain)
39+
Fabricate(:custom_emoji, domain: silenced_domain)
40+
41+
Fabricate(:domain_block, severity: :suspend, domain: blocked_domain)
42+
Fabricate(:domain_block, severity: :suspend, domain: 'evil.org')
43+
Fabricate(:domain_block, severity: :suspend, domain: blocked_domain_without_emoji)
44+
Fabricate(:domain_block, severity: :silence, domain: silenced_domain)
45+
end
46+
47+
it 'reports a successful purge' do
48+
expect { subject }
49+
.to output_results('OK')
50+
.and change { CustomEmoji.by_domain_and_subdomains(blocked_domain).count }.to(0)
51+
.and change { CustomEmoji.by_domain_and_subdomains('evil.org').count }.to(0)
52+
.and not_change { CustomEmoji.by_domain_and_subdomains(silenced_domain).count }
53+
.and(not_change { CustomEmoji.local.count })
54+
end
55+
end
2656
end
2757

2858
describe '#import' do

spec/services/activitypub/process_status_update_service_spec.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,8 @@
259259
{ type: 'Hashtag', name: 'foo' },
260260
{ type: 'Hashtag', name: 'bar' },
261261
{ type: 'Hashtag', name: '#2024' },
262+
{ type: 'Hashtag', name: 'Foo Bar' },
263+
{ type: 'Hashtag', name: 'FooBar' },
262264
],
263265
}
264266
end
@@ -270,7 +272,7 @@
270272

271273
it 'updates tags and featured tags' do
272274
expect { subject.call(status, json, json) }
273-
.to change { status.tags.reload.pluck(:name) }.from(%w(test foo)).to(%w(foo bar))
275+
.to change { status.tags.reload.pluck(:name) }.from(contain_exactly('test', 'foo')).to(contain_exactly('foo', 'bar', 'foobar'))
274276
.and change { status.account.featured_tags.find_by(name: 'test').statuses_count }.by(-1)
275277
.and change { status.account.featured_tags.find_by(name: 'bar').statuses_count }.by(1)
276278
.and change { status.account.featured_tags.find_by(name: 'bar').last_status_at }.from(nil).to(be_present)

0 commit comments

Comments
 (0)