Finding unused Rails view partials with DataDog and Github Actions
As a Rails application matures, views gets moved around and sometimes rewritten. This opens up the possibility of dead view partials lying around: partials that are no longer rendered by any views. These unused view partials clutter up our workspace and impose a small maintenance burden. At BiggerPockets, we use DataDog and Github Actions to identify these unused partials so that they can be manually cleaned up.
The script
This script lives in bin/list-unused-partials
. When provided with the correct arguments, it connects to DataDog and
gets a list of all of the view partials that have been used in the last 90 days. It then compares that list of all of
the view partials in the Rails project. Finally, it returns a list of view partials that don’t show up in DataDog:
#!/usr/bin/env ruby
# frozen_string_literal: true
require "net/http"
require "uri"
require "json"
# Get a list of unused view partials
class UnusedPartials
attr_reader :options
def initialize(**options)
@options = options
end
# @return [Array<String>]
def all
get_all_partials - get_used_partials
end
private
class HTTPError < StandardError; end
def get_all_partials
entries = Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), "../app/views/**/_*.erb")))
entries.map { |entry| entry.gsub(/.+\/app\/views/, "app/views") }
end
def get_used_partials
uri = URI.parse("https://app.datadoghq.com/metric/flat_tags_for_metric?metric=trace.rails.render_partial.hits&filter%5B%5D=service%3A#{options[:datadog_service]}&filter%5B%5D=env%3Aproduction&window=#{2_592_000 * 3}&limit=10000")
header = {
"DD-API-KEY": options[:datadog_api_key],
"DD-APPLICATION-KEY": options[:datadog_app_key],
"Content-type": "application/json",
}
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri, header)
response = http.request(request)
raise HTTPError unless response.code == "200"
JSON.parse(response.body)["tags"].select { |tag| tag =~ /^resource_name:/ }.map { |tag| tag.gsub(/^resource_name:/, "app/views/") }
rescue HTTPError => error
$stderr.puts("Error fetching list of view partials from DataDog: #{response.body}")
raise error
end
end
def main
require "optparse"
options = {}
parser = OptionParser.new do |parser|
parser.banner = <<~EOS
List view partials not used in the last 90 days
Usage: list-unused-partials [options]
EOS
parser.on("-a", "--datadog-api-key [STRING]", String, "Set the DataDog API key") do |val|
options[:datadog_api_key] = val
end
parser.on("-p", "--datadog-app-key [STRING]", String, "Set the DataDog Application key") do |val|
options[:datadog_app_key] = val
end
parser.on("-s", "--datadog-service [STRING]", String, "Set the DataDog Service name") do |val|
options[:datadog_service] = val
end
end
if ARGV.empty?
puts parser
exit 1
end
parser.parse!
partials = UnusedPartials.new(**options).all
puts partials
exit partials.count > 0 ? 1 : 0
end
main if __FILE__ == $PROGRAM_NAME
Next up is a custom Github Action that runs once a week (or manually) that executes the above script. If there are any unused view partials, the script exits with a non-zero code and the action fails.
Note that this script requires two secrets to be stored in Github:
- DATADOG_API_KEY
- DATADOG_APP_KEY
name: Unused view partials
on:
workflow_dispatch:
schedule:
- cron: "0 0 1 * *"
jobs:
list_unused_partials:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Detect unused partials
run: bin/list-unused-partials -a "$" -p "$" -s "biggerpockets"