Skip to content

Commit 0f5374b

Browse files
etewiahclaude
andcommitted
Add rake task to sync public/fonts to CDN (R2)
The Material Symbols font was missing on production CDN because public/fonts/ was never synced to R2 - only public/assets/ was. Changes: - Add assets:sync_fonts_to_r2 task to upload public/fonts/ to R2 - Update assets:cdn_deploy to include fonts sync - Refactor sync logic into reusable sync_directory_to_r2 helper - Add .otf to content type mapping To fix production immediately: rake assets:sync_fonts_to_r2 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 45b3c0d commit 0f5374b

File tree

1 file changed

+53
-24
lines changed

1 file changed

+53
-24
lines changed

lib/tasks/assets_cdn.rake

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,9 @@ namespace :assets do
5454
)
5555
end
5656

57-
desc "Sync compiled assets to Cloudflare R2 for CDN delivery"
58-
task sync_to_r2: :environment do
59-
bucket = assets_bucket
60-
client = assets_r2_client
61-
assets_path = Rails.root.join("public", "assets")
62-
63-
unless assets_path.exist?
64-
puts "No assets directory found. Run 'rails assets:precompile' first."
65-
exit 1
66-
end
67-
68-
puts "Syncing assets to R2 bucket: #{bucket}"
69-
70-
# Content type mapping
71-
content_types = {
57+
# Content type mapping for uploads
58+
def content_type_for(ext)
59+
{
7260
".js" => "application/javascript",
7361
".css" => "text/css",
7462
".png" => "image/png",
@@ -80,21 +68,25 @@ namespace :assets do
8068
".woff2" => "font/woff2",
8169
".ttf" => "font/ttf",
8270
".eot" => "application/vnd.ms-fontobject",
71+
".otf" => "font/otf",
8372
".ico" => "image/x-icon",
8473
".map" => "application/json",
8574
".json" => "application/json",
8675
".webp" => "image/webp"
87-
}
76+
}[ext.downcase] || "application/octet-stream"
77+
end
8878

79+
# Helper to sync a directory to R2
80+
def sync_directory_to_r2(client, bucket, local_path, r2_prefix, cache_control: "public, max-age=31536000, immutable")
8981
uploaded = 0
9082
skipped = 0
9183

92-
Dir.glob("#{assets_path}/**/*").each do |file_path|
84+
Dir.glob("#{local_path}/**/*").each do |file_path|
9385
next if File.directory?(file_path)
9486

95-
key = "assets/#{file_path.sub("#{assets_path}/", "")}"
96-
ext = File.extname(file_path).downcase
97-
content_type = content_types[ext] || "application/octet-stream"
87+
key = "#{r2_prefix}/#{file_path.sub("#{local_path}/", "")}"
88+
ext = File.extname(file_path)
89+
content_type = content_type_for(ext)
9890

9991
# Check if file already exists with same size
10092
begin
@@ -107,28 +99,65 @@ namespace :assets do
10799
# File doesn't exist, will upload
108100
end
109101

110-
# Upload with cache headers (assets are digest-stamped, cache forever)
102+
# Upload with cache headers
111103
File.open(file_path, "rb") do |file|
112104
client.put_object(
113105
bucket: bucket,
114106
key: key,
115107
body: file,
116108
content_type: content_type,
117-
cache_control: "public, max-age=31536000, immutable"
109+
cache_control: cache_control
118110
)
119111
end
120112

121113
uploaded += 1
122114
puts " Uploaded: #{key}" if ENV["VERBOSE"]
123115
end
124116

125-
puts "Done! Uploaded: #{uploaded}, Skipped (unchanged): #{skipped}"
117+
{ uploaded: uploaded, skipped: skipped }
118+
end
119+
120+
desc "Sync compiled assets to Cloudflare R2 for CDN delivery"
121+
task sync_to_r2: :environment do
122+
bucket = assets_bucket
123+
client = assets_r2_client
124+
assets_path = Rails.root.join("public", "assets")
125+
126+
unless assets_path.exist?
127+
puts "No assets directory found. Run 'rails assets:precompile' first."
128+
exit 1
129+
end
130+
131+
puts "Syncing assets to R2 bucket: #{bucket}"
132+
133+
result = sync_directory_to_r2(client, bucket, assets_path, "assets")
134+
135+
puts "Done! Uploaded: #{result[:uploaded]}, Skipped (unchanged): #{result[:skipped]}"
136+
end
137+
138+
desc "Sync public/fonts to Cloudflare R2 for CDN delivery"
139+
task sync_fonts_to_r2: :environment do
140+
bucket = assets_bucket
141+
client = assets_r2_client
142+
fonts_path = Rails.root.join("public", "fonts")
143+
144+
unless fonts_path.exist?
145+
puts "No fonts directory found at public/fonts."
146+
exit 1
147+
end
148+
149+
puts "Syncing fonts to R2 bucket: #{bucket}"
150+
151+
result = sync_directory_to_r2(client, bucket, fonts_path, "fonts")
152+
153+
puts "Done! Uploaded: #{result[:uploaded]}, Skipped (unchanged): #{result[:skipped]}"
126154
end
127155

128-
desc "Precompile assets and sync to R2"
156+
desc "Precompile assets and sync to R2 (includes fonts)"
129157
task cdn_deploy: :environment do
130158
Rake::Task["assets:precompile"].invoke
131159
Rake::Task["assets:sync_to_r2"].invoke
160+
Rake::Task["assets:sync_fonts_to_r2"].invoke
132161
end
133162

134163
desc "Configure CORS on R2 bucket for CDN delivery"

0 commit comments

Comments
 (0)