From 70d3b5973be9d533edb461d9453e90bb178e4ad1 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 19 May 2025 14:35:51 +0300 Subject: [PATCH] feat: Scripts for publishing gems --- .gitignore | 4 + MAINTANERS.md | 9 ++ bin/codetracer-pure-ruby-recorder | 4 + codetracer_pure_ruby_recorder.gemspec | 16 ++++ scripts/publish_gems.rb | 34 ++++++++ src/trace.rb | 116 ++++++++++++-------------- 6 files changed, 122 insertions(+), 61 deletions(-) create mode 100755 bin/codetracer-pure-ruby-recorder create mode 100644 codetracer_pure_ruby_recorder.gemspec create mode 100755 scripts/publish_gems.rb diff --git a/.gitignore b/.gitignore index f10aa8e..b6da956 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ test/tmp/ .direnv/ pkg/ + +# Offline dependency sources +.codex/deps_src/ +.codex/internet_resources/ diff --git a/MAINTANERS.md b/MAINTANERS.md index 9fc2947..0505238 100644 --- a/MAINTANERS.md +++ b/MAINTANERS.md @@ -52,6 +52,12 @@ Two Ruby gems are published from this repository: * **codetracer_pure_ruby_recorder** – a pure Ruby fallback without the native extension. +A helper script is available to build and push all gems in one go: + +```bash +ruby scripts/publish_gems.rb +``` + ### Native extension gem 1. Install the development dependencies: @@ -89,3 +95,6 @@ gem push codetracer_pure_ruby_recorder-.gem Ensure the version matches the native extension gem so that both packages can be used interchangeably. + +All the above steps are automated by `scripts/publish_gems.rb` which +builds and publishes the pure Ruby gem and all native variants. diff --git a/bin/codetracer-pure-ruby-recorder b/bin/codetracer-pure-ruby-recorder new file mode 100755 index 0000000..8b56d83 --- /dev/null +++ b/bin/codetracer-pure-ruby-recorder @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require 'rbconfig' +script = File.expand_path('../src/trace.rb', __dir__) +exec RbConfig.ruby, script, *ARGV diff --git a/codetracer_pure_ruby_recorder.gemspec b/codetracer_pure_ruby_recorder.gemspec new file mode 100644 index 0000000..c1b0211 --- /dev/null +++ b/codetracer_pure_ruby_recorder.gemspec @@ -0,0 +1,16 @@ +Gem::Specification.new do |spec| + spec.name = 'codetracer_pure_ruby_recorder' + spec.version = '0.1.0' + spec.authors = ['Metacraft Labs'] + spec.email = ['info@metacraft-labs.com'] + + spec.summary = 'CodeTracer Ruby recorder implemented purely in Ruby' + spec.description = 'Ruby tracer that records execution steps using only Ruby code.' + spec.license = 'MIT' + spec.homepage = 'https://github.com/metacraft-labs/codetracer-ruby-recorder' + + spec.files = Dir['src/**/*', 'bin/*', 'README.md', 'LICENSE'] + spec.require_paths = ['src'] + spec.bindir = 'bin' + spec.executables = ['codetracer-pure-ruby-recorder'] +end diff --git a/scripts/publish_gems.rb b/scripts/publish_gems.rb new file mode 100755 index 0000000..6c48714 --- /dev/null +++ b/scripts/publish_gems.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'fileutils' + +TARGETS = [ + 'x86_64-unknown-linux-gnu', + 'aarch64-unknown-linux-gnu', + 'x86_64-apple-darwin', + 'aarch64-apple-darwin', + 'x86_64-pc-windows-msvc' +].freeze + + +def run(cmd, env = {}) + command = env.map { |k, v| "#{k}=#{v}" }.join(' ') + command = [command, cmd].reject(&:empty?).join(' ') + puts "$ #{command}" + system(env, *cmd.split(' ')) || abort("Command failed: #{command}") +end + +# Build and publish native extension gems +TARGETS.each do |target| + run('rake cross_native_gem', 'RB_SYS_CARGO_TARGET' => target) + gem_file = Dir['pkg/codetracer-ruby-recorder-*.gem'].max_by { |f| File.mtime(f) } + run("gem push #{gem_file}") + FileUtils.rm_f(gem_file) +end + +# Build and publish pure Ruby gem +run('gem build codetracer_pure_ruby_recorder.gemspec') +pure_gem = Dir['codetracer_pure_ruby_recorder-*.gem'].max_by { |f| File.mtime(f) } +run("gem push #{pure_gem}") +FileUtils.rm_f(pure_gem) diff --git a/src/trace.rb b/src/trace.rb index ecdc8a9..f8cdae9 100644 --- a/src/trace.rb +++ b/src/trace.rb @@ -5,13 +5,6 @@ require 'json' require_relative 'recorder' -if ARGV[0].nil? - $stderr.puts("ruby trace.rb []") - exit(1) -end - -program = ARGV[0] - # Warning: # probably related to our development env: # if we hit an `incompatible library version` error, like @@ -82,6 +75,7 @@ def initialize(record) @trace_stopped = false @record = record @ignore_list = [] + setup_tracepoints end def stop_tracing @@ -97,6 +91,32 @@ def ignore(path) @ignore_list << path end + def setup_tracepoints + @calls_tracepoint = TracePoint.new(:call) do |tp| + deactivate + record_call(tp) + activate + end + + @return_tracepoint = TracePoint.new(:return) do |tp| + deactivate + record_return(tp) + activate + end + + @line_tracepoint = TracePoint.new(:line) do |tp| + deactivate + record_step(tp) + activate + end + + @raise_tracepoint = TracePoint.new(:raise) do |tp| + deactivate + record_exception(tp) + activate + end + end + def prepare_args(tp) args_after_self = tp.parameters.map do |(kind, name)| value = if tp.binding.nil? || name.nil? @@ -229,64 +249,38 @@ def load_variables(binding) $tracer = Tracer.new($codetracer_record) -# also possible :c_call, :b_call: for now record ruby calls (:call) -# c_call: c lang -# b_call: block entry -# https://rubyapi.org/3.4/o/tracepoint -$tracer.calls_tracepoint = TracePoint.new(:call) do |tp| - $tracer.deactivate - $tracer.record_call(tp) - $tracer.activate -end +if __FILE__ == $PROGRAM_NAME + if ARGV[0].nil? + $stderr.puts('ruby trace.rb []') + exit(1) + end -$tracer.return_tracepoint = TracePoint.new(:return) do |tp| - $tracer.deactivate - $tracer.record_return(tp) - $tracer.activate -end + program = ARGV[0] -$tracer.line_tracepoint = TracePoint.new(:line) do |tp| - $tracer.deactivate - $tracer.record_step(tp) - $tracer.activate -end + $tracer.record.register_call('', 1, '', []) + $tracer.ignore('lib/ruby') + $tracer.ignore('trace.rb') + $tracer.ignore('recorder.rb') + $tracer.ignore(' e + old_puts '' + old_puts '==== trace.rb error while tracing program ===' + old_puts 'ERROR' + old_puts e + old_puts e.backtrace + old_puts '=====================' + old_puts '' + end + ARGV = trace_args -$tracer.record.register_call("", 1, "", []) -$tracer.ignore('lib/ruby') -$tracer.ignore('trace.rb') -$tracer.ignore('recorder.rb') -$tracer.ignore(' e - # important: rescue Exception, - # not just rescue as we originally did - # because a simple `rescue` doesn't catch some errors - # like SystemExit and others - # (when we call `exit` in the trace program and others) - # https://stackoverflow.com/questions/5118745/is-systemexit-a-special-kind-of-exception - old_puts "" - old_puts "==== trace.rb error while tracing program ===" - old_puts "ERROR" - old_puts e - old_puts e.backtrace - old_puts "=====================" - old_puts "" + $tracer.record.serialize(program) end -ARGV = trace_args - -$tracer.stop_tracing - -$tracer.record.serialize(program)