Skip to content

Commit a1fab76

Browse files
authored
Merge pull request #2915 from ruby/crate-setup
Fix crate setup
2 parents 26b3007 + 247ed3e commit a1fab76

File tree

9 files changed

+401
-10
lines changed

9 files changed

+401
-10
lines changed

.github/workflows/rust.yml

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ jobs:
2424
os: [ubuntu-latest, macos-latest, windows-latest]
2525
steps:
2626
- uses: actions/checkout@v6
27+
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
28+
- name: Set up Ruby
29+
uses: ruby/setup-ruby@v1
30+
with:
31+
ruby-version: ruby
32+
bundler: none
33+
- name: Update rubygems & bundler
34+
run: gem update --system
35+
- name: Install gems
36+
run: |
37+
bundle config set --local without libs:profilers
38+
bundle install --jobs 4 --retry 3
39+
- name: Set up vendored RBS source
40+
run: bundle exec rake rust:rbs:sync
2741
- name: Install Rust tools
2842
run: |
2943
rustup update --no-self-update stable
@@ -42,12 +56,30 @@ jobs:
4256
cd rust
4357
cargo test --verbose
4458
45-
publish-dry-run:
46-
name: cargo:publish-dry-run
59+
publish-dry-run-ruby-rbs-sys:
60+
name: rust:publish:ruby-rbs-sys
4761
runs-on: ubuntu-latest
4862
continue-on-error: true
4963
steps:
5064
- uses: actions/checkout@v6
65+
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
66+
- name: Set up git identity
67+
run: |
68+
git config user.name "GitHub Actions"
69+
git config user.email "actions@github.com"
70+
- name: Set up Ruby
71+
uses: ruby/setup-ruby@v1
72+
with:
73+
ruby-version: ruby
74+
bundler: none
75+
- name: Update rubygems & bundler
76+
run: gem update --system
77+
- name: Install gems
78+
run: |
79+
bundle config set --local without libs:profilers
80+
bundle install --jobs 4 --retry 3
81+
- name: Set up vendored RBS source
82+
run: bundle exec rake rust:rbs:sync
5183
- name: Install Rust tools
5284
run: |
5385
rustup update --no-self-update stable
@@ -61,16 +93,72 @@ jobs:
6193
key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }}
6294
restore-keys: |
6395
${{ runner.os }}-cargo-
64-
- name: Test publish crates
96+
- name: Test publish ruby-rbs-sys
97+
run: bundle exec rake rust:publish:ruby-rbs-sys
98+
env:
99+
RBS_RUST_PUBLISH_DRY_RUN: "1"
100+
101+
publish-dry-run-ruby-rbs:
102+
name: rust:publish:ruby-rbs
103+
runs-on: ubuntu-latest
104+
continue-on-error: true
105+
steps:
106+
- uses: actions/checkout@v6
107+
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
108+
- name: Set up git identity
65109
run: |
66-
cd rust
67-
cargo publish --dry-run
110+
git config user.name "GitHub Actions"
111+
git config user.email "actions@github.com"
112+
- name: Set up Ruby
113+
uses: ruby/setup-ruby@v1
114+
with:
115+
ruby-version: ruby
116+
bundler: none
117+
- name: Update rubygems & bundler
118+
run: gem update --system
119+
- name: Install gems
120+
run: |
121+
bundle config set --local without libs:profilers
122+
bundle install --jobs 4 --retry 3
123+
- name: Set up vendored RBS source
124+
run: bundle exec rake rust:rbs:sync
125+
- name: Install Rust tools
126+
run: |
127+
rustup update --no-self-update stable
128+
rustup default stable
129+
- uses: actions/cache@v5
130+
with:
131+
path: |
132+
~/.cargo/registry
133+
~/.cargo/git
134+
rust/target
135+
key: ${{ runner.os }}-cargo-${{ hashFiles('rust/Cargo.lock') }}
136+
restore-keys: |
137+
${{ runner.os }}-cargo-
138+
- name: Test publish ruby-rbs
139+
run: bundle exec rake rust:publish:ruby-rbs
140+
env:
141+
RBS_RUST_PUBLISH_DRY_RUN: "1"
68142

69143
lint:
70144
name: cargo:lint
71145
runs-on: ubuntu-latest
72146
steps:
73147
- uses: actions/checkout@v6
148+
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
149+
- name: Set up Ruby
150+
uses: ruby/setup-ruby@v1
151+
with:
152+
ruby-version: ruby
153+
bundler: none
154+
- name: Update rubygems & bundler
155+
run: gem update --system
156+
- name: Install gems
157+
run: |
158+
bundle config set --local without libs:profilers
159+
bundle install --jobs 4 --retry 3
160+
- name: Set up vendored RBS source
161+
run: bundle exec rake rust:rbs:sync
74162
- name: Install Rust tools
75163
run: |
76164
rustup update --no-self-update stable

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ doc/
2525
# For clangd's editor integration
2626
ext/rbs_extension/compile_commands.json
2727
ext/rbs_extension/.cache
28+
29+
# Rust crate vendored RBS source (managed by rake rust:rbs:sync or rust:rbs:symlink)
30+
rust/ruby-rbs-sys/vendor/rbs/
31+
rust/ruby-rbs/vendor/rbs/

Gemfile.lock

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,9 @@ GEM
116116
psych (4.0.6)
117117
stringio
118118
public_suffix (7.0.5)
119-
raap (1.3.0)
120-
rbs (~> 3.9.0)
119+
raap (2.0.0)
120+
logger
121+
rbs (~> 4.0)
121122
timeout (~> 0.4)
122123
racc (1.8.1)
123124
rainbow (3.1.1)

Rakefile

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,3 +548,207 @@ task :prepare_profiling do
548548
Rake::Task[:"templates"].invoke
549549
Rake::Task[:"compile"].invoke
550550
end
551+
552+
namespace :rust do
553+
namespace :rbs do
554+
RUST_DIR = File.expand_path("rust", __dir__)
555+
RBS_VERSION_FILE = File.join(RUST_DIR, "rbs_version")
556+
557+
VENDOR_TARGETS = {
558+
"ruby-rbs-sys" => %w[include src],
559+
"ruby-rbs" => %w[config.yml],
560+
}
561+
562+
desc "Sync vendored RBS source from the pinned version"
563+
task :sync do
564+
unless File.exist?(RBS_VERSION_FILE)
565+
raise "#{RBS_VERSION_FILE} not found. Run `rake rust:rbs:pin[VERSION]` first."
566+
end
567+
568+
version = File.read(RBS_VERSION_FILE).strip
569+
raise "#{RBS_VERSION_FILE} is empty" if version.empty?
570+
571+
puts "Syncing vendor/rbs/ from #{version}..."
572+
573+
VENDOR_TARGETS.each do |crate, entries|
574+
vendor_dir = File.join(RUST_DIR, crate, "vendor", "rbs")
575+
576+
puts " Copying files for #{crate}:"
577+
chmod_R "u+w", vendor_dir, verbose: false if File.exist?(vendor_dir)
578+
rm_rf vendor_dir, verbose: false
579+
mkdir_p vendor_dir, verbose: false
580+
581+
entries.each do |entry|
582+
target = File.join(vendor_dir, entry)
583+
584+
# Extract the entry from the pinned git tag using git archive
585+
IO.popen(["git", "archive", "--format=tar", version, "--", entry], "rb") do |tar|
586+
IO.popen(["tar", "xf", "-", "-C", vendor_dir], "wb") do |extract|
587+
IO.copy_stream(tar, extract)
588+
end
589+
end
590+
591+
raise "Failed to extract #{entry} from #{version}" unless File.exist?(target)
592+
puts " #{entry}"
593+
end
594+
595+
# Make files read-only to prevent accidental edits
596+
chmod_R "a-w", vendor_dir, verbose: false
597+
end
598+
599+
puts "📦 Synced vendor/rbs/ from #{version} (read-only)"
600+
end
601+
602+
desc "Pin a specific RBS version for Rust crates (e.g., rake rust:rbs:pin[v4.0.3])"
603+
task :pin, [:version] do |_t, args|
604+
version = args[:version] or raise "Usage: rake rust:rbs:pin[VERSION]"
605+
606+
# Verify the tag exists
607+
unless system("git", "rev-parse", "--verify", "#{version}^{commit}", out: File::NULL, err: File::NULL)
608+
raise "Tag #{version} not found"
609+
end
610+
611+
File.write(RBS_VERSION_FILE, "#{version}\n")
612+
puts "📌 Pinned RBS version to #{version}"
613+
614+
Rake::Task["rust:rbs:sync"].invoke
615+
end
616+
617+
desc "Create symlinks from vendor/rbs/ to the repository root (for development/CI)"
618+
task :symlink do
619+
VENDOR_TARGETS.each do |crate, entries|
620+
vendor_dir = File.join(RUST_DIR, crate, "vendor", "rbs")
621+
622+
puts "Setting up symlinks for #{crate}..."
623+
entries.each do |entry|
624+
puts " #{entry} -> repository root"
625+
end
626+
627+
chmod_R "u+w", vendor_dir, verbose: false if File.exist?(vendor_dir)
628+
rm_rf vendor_dir, verbose: false
629+
mkdir_p vendor_dir, verbose: false
630+
631+
entries.each do |entry|
632+
ln_s File.join("..", "..", "..", "..", entry), File.join(vendor_dir, entry), verbose: false
633+
end
634+
end
635+
636+
puts "🔗 Symlinked vendor/rbs/ to repository root"
637+
end
638+
end
639+
640+
namespace :publish do
641+
def self.prepare_publish_branch(crate_name)
642+
dry_run = ENV["RBS_RUST_PUBLISH_DRY_RUN"]
643+
644+
version_file = File.join(RUST_DIR, "rbs_version")
645+
646+
unless File.exist?(version_file)
647+
raise "#{version_file} not found. Run `rake rust:rbs:pin[VERSION]` first."
648+
end
649+
650+
rbs_version = File.read(version_file).strip
651+
raise "#{version_file} is empty" if rbs_version.empty?
652+
653+
crate_version = File.read(File.join(RUST_DIR, crate_name, "Cargo.toml"))[/^version\s*=\s*"(.+)"/, 1]
654+
release_branch = "rust/release-#{crate_name}-#{Time.now.strftime('%Y%m%d%H%M%S')}"
655+
656+
puts "=" * 60
657+
puts "Rust crate publish: #{crate_name}#{dry_run ? " (DRY RUN)" : ""}"
658+
puts "=" * 60
659+
puts " RBS source version: #{rbs_version}"
660+
puts " #{crate_name}: #{crate_version} (tag: #{crate_name}-v#{crate_version})"
661+
puts " Release branch: #{release_branch}"
662+
puts "=" * 60
663+
664+
# Check that vendor dirs contain real files, not symlinks
665+
entries = VENDOR_TARGETS.fetch(crate_name)
666+
entries.each do |entry|
667+
path = File.join(RUST_DIR, crate_name, "vendor", "rbs", entry)
668+
if File.symlink?(path)
669+
raise "#{path} is a symlink. Run `rake rust:rbs:sync` first."
670+
end
671+
unless File.exist?(path)
672+
raise "#{path} does not exist. Run `rake rust:rbs:sync` first."
673+
end
674+
end
675+
676+
# Ensure working tree is clean before publishing
677+
unless `git status --porcelain`.strip.empty?
678+
raise "💢 Working tree is dirty. Please commit or stash your changes before publishing."
679+
end
680+
681+
# Create a release branch with vendor files committed
682+
original_branch = `git rev-parse --abbrev-ref HEAD`.strip
683+
684+
sh "git", "checkout", "-b", release_branch, verbose: false
685+
vendor_path = File.join("rust", crate_name, "vendor", "rbs")
686+
sh "git", "add", "-f", vendor_path, verbose: false
687+
sh "git", "commit", "-m", "Publish #{crate_name} (RBS #{rbs_version})", verbose: false
688+
689+
[dry_run, crate_version, original_branch]
690+
end
691+
692+
desc "Publish ruby-rbs-sys crate to crates.io (set RBS_RUST_PUBLISH_DRY_RUN=1 for dry-run only)"
693+
task :"ruby-rbs-sys" do
694+
crate_name = "ruby-rbs-sys"
695+
dry_run, crate_version, original_branch = prepare_publish_branch(crate_name)
696+
697+
begin
698+
puts "🔰 Dry-run publishing..."
699+
700+
Dir.chdir(File.join(RUST_DIR, crate_name)) do
701+
sh "cargo", "publish", "--dry-run"
702+
end
703+
704+
puts "✅ Dry-run succeeded!"
705+
706+
unless dry_run
707+
puts "💪 Publishing #{crate_name} for real..."
708+
709+
Dir.chdir(File.join(RUST_DIR, crate_name)) do
710+
sh "cargo", "publish"
711+
end
712+
713+
sh "git", "tag", "#{crate_name}-v#{crate_version}"
714+
sh "git", "push", "origin", "#{crate_name}-v#{crate_version}"
715+
716+
puts "🎉 Published #{crate_name} successfully!"
717+
end
718+
ensure
719+
sh "git", "checkout", original_branch, verbose: false
720+
end
721+
end
722+
723+
desc "Publish ruby-rbs crate to crates.io (set RBS_RUST_PUBLISH_DRY_RUN=1 for dry-run only)"
724+
task :"ruby-rbs" do
725+
crate_name = "ruby-rbs"
726+
dry_run, crate_version, original_branch = prepare_publish_branch(crate_name)
727+
728+
begin
729+
puts "🔰 Dry-run publishing..."
730+
731+
Dir.chdir(File.join(RUST_DIR, crate_name)) do
732+
sh "cargo", "publish", "--dry-run", "--no-verify"
733+
end
734+
735+
puts "✅ Dry-run succeeded!"
736+
737+
unless dry_run
738+
puts "💪 Publishing #{crate_name} for real..."
739+
740+
Dir.chdir(File.join(RUST_DIR, crate_name)) do
741+
sh "cargo", "publish"
742+
end
743+
744+
sh "git", "tag", "#{crate_name}-v#{crate_version}"
745+
sh "git", "push", "origin", "#{crate_name}-v#{crate_version}"
746+
747+
puts "🎉 Published #{crate_name} successfully!"
748+
end
749+
ensure
750+
sh "git", "checkout", original_branch, verbose: false
751+
end
752+
end
753+
end
754+
end

0 commit comments

Comments
 (0)