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
25 changes: 18 additions & 7 deletions app/models/report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,29 @@
class Report < ApplicationRecord
belongs_to :server
validates :server_id, :presence => true
validates :revision, :numericality => { :only_integer => true }, allow_nil: true
# SVN revision number or full 40-hex git commit SHA
validates :revision, :format => { :with => /\A(?:\d+|\h{40})\z/ }, allow_nil: true
validates :datetime, :uniqueness => { :scope => [:server_id, :branch] }
validates :branch, :presence => true
validates :summary, :presence => true

def self.extract_full_sha(ltsv)
return nil unless ltsv
ltsv[%r<"https\\x3A//github.com/ruby/ruby":(\h{40})>, 1] ||
ltsv[%r<https://github.com/ruby/ruby:(\h{40})>, 1]
end

def sha1
!ltsv ? nil : \
ltsv[%r<"https\\x3A//github.com/ruby/ruby":([^\t]+)>, 1] ||
ltsv[%r<https://github.com/ruby/ruby:([^\t]+)>, 1]
if revision&.match?(/\A\h{40}\z/)
revision
elsif ltsv
ltsv[%r<"https\\x3A//github.com/ruby/ruby":([^\t]+)>, 1] ||
ltsv[%r<https://github.com/ruby/ruby:([^\t]+)>, 1]
end
end

def revisionuri
if revision
if revision&.match?(/\A\d+\z/)
"https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=#{revision}"
elsif sha1
"https://github.com/ruby/ruby/commit/#{sha1}"
Expand Down Expand Up @@ -156,8 +166,9 @@ def self.scan_recent_ltsv(server, depsuffixed_name, body)
end
ary.reverse_each do |line, h, dt, datetime|
puts "reporting #{server.name} #{depsuffixed_name} #{dt} ..."
# subversion revision of ruby is less than 99999
revision = h["ruby_rev"] && h["ruby_rev"].size <= 6 ? h["ruby_rev"][1, 5].to_i : nil
# git commit SHA if present, otherwise subversion revision (less than 99999)
revision = extract_full_sha(line) ||
(h["ruby_rev"] && h["ruby_rev"].size <= 6 ? h["ruby_rev"][1, 5] : nil)
summary = h["title"]
summary << ' success' if h["result"] == 'success'
diff = h["different_sections"]
Expand Down
4 changes: 2 additions & 2 deletions app/views/reports/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

diffuri = report.diffuri

if report.revision
if report.revision&.match?(/\A\d+\z/)
revision = "r#{report.revision}"
elsif report.sha1
revision = report.sha1[0, 11]
Expand Down Expand Up @@ -108,7 +108,7 @@
<td class="datetime"><%= link_to report.sjstdt, report.loguri, title: report.jstdt %></td>
<td class="branch"><%= link_to report.branch, report.recenturi %></td>
<td class="option"><%= report.option %></td>
<td class="revision"><%= report.patchlevel + " " if report.patchlevel %><%= report.revision %></td>
<td class="revision"><%= report.patchlevel + " " if report.patchlevel %><%= report.revision&.match?(/\A\d+\z/) ? report.revision : report.revision.to_s[0, 11] %></td>
<% if report.build %>
<td class="build" colspan="3"><%= report.build %></td>
<% else %>
Expand Down
27 changes: 27 additions & 0 deletions db/migrate/20260727051843_change_reports_revision_to_string.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class ChangeReportsRevisionToString < ActiveRecord::Migration[8.1]
def up
if postgresql?
change_column :reports, :revision, :string, using: 'revision::text'
# varchar_pattern_ops is required for LIKE 'abc1234%' prefix searches
add_index :reports, :revision, opclass: :varchar_pattern_ops
else
change_column :reports, :revision, :string
add_index :reports, :revision
end
end

def down
remove_index :reports, :revision
if postgresql?
change_column :reports, :revision, :integer, using: 'revision::integer'
else
change_column :reports, :revision, :integer
end
end

private

def postgresql?
connection.adapter_name.match?(/postgresql/i)
end
end
5 changes: 3 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.1].define(version: 2024_11_21_042607) do
ActiveRecord::Schema[8.1].define(version: 2026_07_27_051843) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"

Expand Down Expand Up @@ -67,12 +67,13 @@
t.datetime "datetime", precision: nil
t.text "ltsv"
t.string "option"
t.integer "revision"
t.string "revision"
t.integer "server_id"
t.text "summary"
t.datetime "updated_at", precision: nil
t.index ["branch"], name: "index_reports_on_branch"
t.index ["datetime"], name: "index_reports_on_datetime"
t.index ["revision"], name: "index_reports_on_revision", opclass: :varchar_pattern_ops
t.index ["server_id", "branch", "option"], name: "index_reports_on_server_id_and_branch_and_option"
end

Expand Down
20 changes: 20 additions & 0 deletions lib/tasks/backfill_revision.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace :reports do
desc "Backfill reports.revision with full commit SHA extracted from ltsv"
task :backfill_revision => :environment do
scope = Report.where(revision: nil).where.not(ltsv: nil)
total = scope.count
puts "#{total} reports to scan"
scanned = 0
updated = 0
scope.find_each do |report|
scanned += 1
sha = Report.extract_full_sha(report.ltsv)
if sha
report.update_column(:revision, sha)
updated += 1
end
puts "#{scanned}/#{total} scanned, #{updated} updated" if scanned % 1000 == 0
end
puts "done: #{scanned} scanned, #{updated} updated"
end
end
98 changes: 98 additions & 0 deletions test/models/report_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
require "test_helper"

class ReportTest < ActiveSupport::TestCase
SHA = "0123456789abcdef0123456789abcdef01234567"

@@ordinal = 0

def create_server(name)
Server.create!(name: name, uri: "https://rubyci.s3.amazonaws.com/#{name}", ordinal: (@@ordinal += 1))
end

def build_report(attrs = {})
server = attrs.delete(:server) || create_server("server-#{attrs.object_id}")
Report.new({
server: server,
branch: "master",
datetime: Time.now.utc,
summary: "ruby 3.5.0dev success",
}.merge(attrs))
end

test "revision accepts nil, digits and 40-hex SHA" do
assert_predicate build_report(revision: nil), :valid?
assert_predicate build_report(revision: "12345"), :valid?
assert_predicate build_report(revision: SHA), :valid?
end

test "revision rejects other strings" do
assert_not build_report(revision: "r12345").valid?
assert_not build_report(revision: SHA[0, 11]).valid?
assert_not build_report(revision: "#{SHA} ").valid?
end

test "revisionuri for svn revision" do
report = build_report(revision: "12345")
assert_equal "https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=12345", report.revisionuri
end

test "revisionuri for git commit SHA" do
report = build_report(revision: SHA)
assert_equal "https://github.com/ruby/ruby/commit/#{SHA}", report.revisionuri
end

test "revisionuri falls back to SHA from ltsv" do
report = build_report(revision: nil, ltsv: "depsuffixed_name:ruby-master\thttps://github.com/ruby/ruby:#{SHA}")
assert_equal "https://github.com/ruby/ruby/commit/#{SHA}", report.revisionuri
end

test "revisionuri without revision and ltsv" do
assert_nil build_report(revision: nil, ltsv: nil).revisionuri
end

test "sha1 prefers revision column" do
report = build_report(revision: SHA, ltsv: nil)
assert_equal SHA, report.sha1
end

test "sha1 falls back to ltsv" do
report = build_report(revision: nil, ltsv: "https://github.com/ruby/ruby:#{SHA}")
assert_equal SHA, report.sha1

report = build_report(revision: nil, ltsv: %["https\\x3A//github.com/ruby/ruby":#{SHA}])
assert_equal SHA, report.sha1
end

test "extract_full_sha" do
assert_equal SHA, Report.extract_full_sha("https://github.com/ruby/ruby:#{SHA}")
assert_equal SHA, Report.extract_full_sha(%["https\\x3A//github.com/ruby/ruby":#{SHA}])
assert_nil Report.extract_full_sha("https://github.com/ruby/ruby:#{SHA[0, 11]}")
assert_nil Report.extract_full_sha(nil)
end

test "scan_recent_ltsv stores full SHA from ltsv" do
server = create_server("scan-sha")
line = [
"start_time:20260727T051800Z",
"https://github.com/ruby/ruby:#{SHA}",
"title:ruby 3.5.0dev (2026-07-27) [x86_64-linux]",
"result:success",
].join("\t")
Report.scan_recent_ltsv(server, "ruby-master", line + "\n")
report = Report.where(server_id: server.id).last
assert_equal SHA, report.revision
end

test "scan_recent_ltsv stores svn revision as digits" do
server = create_server("scan-svn")
line = [
"start_time:20260727T051800Z",
"ruby_rev:r54321",
"title:ruby 2.7.0dev (2026-07-27) [x86_64-linux]",
"result:success",
].join("\t")
Report.scan_recent_ltsv(server, "ruby-2.7", line + "\n")
report = Report.where(server_id: server.id).last
assert_equal "54321", report.revision
end
end
Loading