Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: RuboCop

on: [push, pull_request]

permissions:
contents: read

jobs:
rubocop:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Ruby
uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
with:
bundler-cache: true
ruby-version: "3.4"
- name: Run RuboCop
run: bundle exec rubocop
15 changes: 0 additions & 15 deletions .github/workflows/standardrb.yaml

This file was deleted.

33 changes: 24 additions & 9 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
inherit_mode:
merge:
- Exclude
- Include

inherit_gem:
rubocop-gusto:
- config/default.yml
- config/sidekiq.yml

plugins:
- rubocop-gusto
- rubocop-rspec
- rubocop-performance
- rubocop-rake

AllCops:
TargetRubyVersion: 3.3
Exclude:
- 'vendor/**/*'

require:
- standard
- standard-custom
- standard-performance

inherit_gem:
standard: config/base.yml
standard-custom: config/base.yml
standard-performance: config/base.yml
# singed does not use Sorbet: there is no sorbet/ config, no sorbet runtime dependency, and no
# typecheck step. The Sorbet cops would only add `# typed:` sigils that nothing reads, and
# `# typed: strict` would claim a level of typing this gem does not have. rubocop-gusto's own
# .rubocop.yml disables the department for the same reason.
Sorbet:
Enabled: false
2 changes: 0 additions & 2 deletions .standard.yml

This file was deleted.

2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ gemspec
gem "activejob"
gem "rake", "~> 13.4"
gem "rspec"
gem "rubocop-gusto", require: false
gem "rubyzip"
gem "sidekiq"
gem "standard"
73 changes: 59 additions & 14 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ GEM
ast (2.4.3)
base64 (0.3.0)
bigdecimal (4.1.2)
code_teams (1.3.1)
sorbet-runtime
concurrent-ruby (1.3.8)
connection_pool (3.0.2)
diff-lcs (1.6.2)
drb (2.2.3)
erubi (1.13.1)
globalid (1.4.0)
activesupport (>= 6.1)
i18n (1.15.2)
Expand All @@ -50,9 +53,17 @@ GEM
rack (3.2.7)
rainbow (3.1.1)
rake (13.4.2)
rbi (0.4.3)
prism (~> 1.0)
rbs (>= 4.0.1)
rbs (4.2.0)
logger
prism (>= 1.6.0)
tsort
redis-client (0.30.1)
connection_pool
regexp_parser (2.12.0)
rexml (3.4.4)
rspec (3.13.2)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
Expand Down Expand Up @@ -80,10 +91,32 @@ GEM
rubocop-ast (1.50.0)
parser (>= 3.3.7.2)
prism (~> 1.7)
rubocop-gusto (11.9.0)
code_teams
lint_roller
rubocop (>= 1.76)
rubocop-performance
rubocop-rake
rubocop-rspec
rubocop-sorbet (>= 0.13.0)
smart_todo
thor
rubocop-performance (1.26.1)
lint_roller (~> 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.47.1, < 2.0)
rubocop-rake (0.7.1)
lint_roller (~> 1.1)
rubocop (>= 1.72.1)
rubocop-rspec (3.10.2)
lint_roller (~> 1.1)
regexp_parser (>= 2.0)
rubocop (~> 1.86, >= 1.86.2)
rubocop-sorbet (0.16.0)
lint_roller
rbi (~> 0.4)
rubocop (>= 1.75.2)
spoom (~> 1.8)
ruby-progressbar (1.13.0)
rubyzip (3.5.0)
securerandom (0.4.1)
Expand All @@ -93,19 +126,28 @@ GEM
logger (>= 1.7.0)
rack (>= 3.2.0)
redis-client (>= 0.29.0)
smart_todo (1.11.0)
prism (~> 1.0)
sorbet (0.6.13508)
sorbet-static (= 0.6.13508)
sorbet-runtime (0.6.13508)
sorbet-static (0.6.13508-aarch64-linux)
sorbet-static (0.6.13508-universal-darwin)
sorbet-static (0.6.13508-x86_64-linux)
sorbet-static-and-runtime (0.6.13508)
sorbet (= 0.6.13508)
sorbet-runtime (= 0.6.13508)
spoom (1.8.9)
erubi (>= 1.10.0)
prism (>= 0.28.0)
rbi (>= 0.4.2)
rbs (>= 4.0.0.dev.5)
rexml (>= 3.2.6)
sorbet-static-and-runtime (>= 0.5.10187)
thor (>= 0.19.2)
stackprof (0.2.28)
standard (1.56.0)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.0)
rubocop (~> 1.88.0)
standard-custom (~> 1.0.0)
standard-performance (~> 1.8)
standard-custom (1.0.2)
lint_roller (~> 1.0)
rubocop (~> 1.50)
standard-performance (1.9.0)
lint_roller (~> 1.1)
rubocop-performance (~> 1.26.0)
thor (1.5.0)
tsort (0.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (3.2.0)
Expand All @@ -114,16 +156,19 @@ GEM
uri (1.1.1)

PLATFORMS
ruby
aarch64-linux
arm64-darwin
x86_64-darwin
x86_64-linux

DEPENDENCIES
activejob
rake (~> 13.4)
rspec
rubocop-gusto
rubyzip
sidekiq
singed!
standard

BUNDLED WITH
4.0.15
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,4 @@ end
Rake::Task[:build].enhance ["speedscope:vendor"]
Rake::Task[:clobber].enhance ["speedscope:clobber"]

task default: %i[]
task default: %i()
6 changes: 4 additions & 2 deletions bin/rspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
abort(
"Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again."
)
end
end

Expand Down
1 change: 1 addition & 0 deletions exe/singed
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require "singed/cli"
if Singed::CLI.chdir_rails_root
Expand Down
7 changes: 5 additions & 2 deletions lib/singed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
require "stackprof"

module Singed
extend self
# Methods defined with plain `def` below are both module methods (Singed.start) and public
# instance methods of Singed, which is how the gem has shipped since its first release.
# `class << self` would remove those instance methods for anyone who includes or extends Singed.
extend self # rubocop:disable Style/ModuleFunction

# Where should flamegraphs be saved?
def output_directory=(directory)
Expand Down Expand Up @@ -49,7 +52,7 @@ def start(label = nil, ignore_gc: false, interval: 1000)
return unless enabled?
return if profiling?

@current_flamegraph = Flamegraph.new(label: label, ignore_gc: ignore_gc, interval: interval)
@current_flamegraph = Flamegraph.new(label:, ignore_gc:, interval:)
@current_flamegraph.tap(&:start)
end

Expand Down
2 changes: 2 additions & 0 deletions lib/singed/backtrace_cleaner_ext.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module ActiveSupport
class BacktraceCleaner
def filter_line(line)
Expand Down
14 changes: 8 additions & 6 deletions lib/singed/cli.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require "shellwords"
require "tmpdir"
require "optionparser"
Expand Down Expand Up @@ -75,14 +77,14 @@ def run
format: "speedscope",
file: filename.to_s,
rate: @rate,
silent: nil
silent: nil,
}

rbspy_args = [
"record",
*options.map { |k, v| ["--#{k}", v].compact }.flatten,
"--",
*argv
*argv,
]

loop do
Expand All @@ -92,9 +94,9 @@ def run
prompt_password
end

rbspy = lambda do
rbspy = -> do
# don't run things with spring, because it forks and rbspy won't see it
sudo ["rbspy", *rbspy_args], reason: "Singed needs to run as root, but will drop permissions back to your user.", env: {"DISABLE_SPRING" => "1"}
sudo ["rbspy", *rbspy_args], reason: "Singed needs to run as root, but will drop permissions back to your user.", env: { "DISABLE_SPRING" => "1" }
end

if defined?(Bundler)
Expand Down Expand Up @@ -122,7 +124,7 @@ def run
end
filename.write(JSON.dump(json))

flamegraph = Singed::Flamegraph.new(filename: filename)
flamegraph = Singed::Flamegraph.new(filename:)
flamegraph.open
end

Expand Down Expand Up @@ -153,7 +155,7 @@ def sudo(system_args, reason:, env: {})
sudo_args = [
"sudo",
"--preserve-env",
*system_args.map(&:to_s)
*system_args.map(&:to_s),
]

puts "$ #{Shellwords.join(sudo_args)}"
Expand Down
10 changes: 6 additions & 4 deletions lib/singed/controller_ext.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# frozen_string_literal: true

require "active_support/concern"

module Singed
module ControllerExt
def self.included(base)
base.extend(ClassMethods)
end
extend ActiveSupport::Concern

module ClassMethods
# Define an around_action to generate flamegraph for a controller action.
def flamegraph(target_action, ignore_gc: false, interval: 1000)
around_action(only: target_action) do |controller, action|
controller.flamegraph(ignore_gc: ignore_gc, interval: interval, &action)
controller.flamegraph(ignore_gc:, interval:, &action)
end
end
end
Expand Down
10 changes: 6 additions & 4 deletions lib/singed/flamegraph.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module Singed
class Flamegraph
attr_accessor :profile, :filename
Expand All @@ -17,8 +19,8 @@ def initialize(label: nil, ignore_gc: false, interval: 1000, filename: nil)
else
@ignore_gc = ignore_gc
@interval = interval
@time = Time.now # rubocop:disable Rails/TimeZone
@filename = self.class.generate_filename(label: label, time: @time)
@time = Time.now
@filename = self.class.generate_filename(label:, time: @time)
end
end

Expand Down Expand Up @@ -69,11 +71,11 @@ def open_command
Singed::Speedscope.open_command(@filename)
end

def self.generate_filename(label: nil, time: Time.now) # rubocop:disable Rails/TimeZone
def self.generate_filename(label: nil, time: Time.now)
formatted_time = time.strftime("%Y%m%d%H%M%S-%6N")
basename_parts = ["speedscope", label, formatted_time].compact

file = Singed.output_directory.join("#{basename_parts.join("-")}.json")
file = Singed.output_directory.join("#{basename_parts.join('-')}.json")
# convert to relative directory if it's an absolute path and within the current
pwd = Pathname.pwd
file = file.relative_path_from(pwd) if file.absolute? && file.to_s.start_with?(pwd.to_s)
Expand Down
4 changes: 3 additions & 1 deletion lib/singed/kernel_ext.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

module Kernel
def flamegraph(label = nil, open: true, ignore_gc: false, interval: 1000, io: $stdout, &block)
fg = Singed::Flamegraph.new(label: label, ignore_gc: ignore_gc, interval: interval)
fg = Singed::Flamegraph.new(label:, ignore_gc:, interval:)
result = fg.record(&block)
fg.save

Expand Down
2 changes: 2 additions & 0 deletions lib/singed/rack_middleware.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

# Rack Middleware

require "rack"
Expand Down
2 changes: 2 additions & 0 deletions lib/singed/railtie.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require "singed/backtrace_cleaner_ext"
require "singed/controller_ext"

Expand Down
Loading
Loading