Skip to content

Commit

Permalink
Merge pull request #1604 from Shopify/uk-add-gitattributes
Browse files Browse the repository at this point in the history
Start generating `.gitattributes` file for generated and vendored RBI files
  • Loading branch information
paracycle authored Sep 8, 2023
2 parents f747c7b + 6f2f432 commit 4201c89
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 5 deletions.
12 changes: 7 additions & 5 deletions lib/tapioca/commands/annotations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def initialize(
typed_overrides: {}
)
super()
@outpath = T.let(Pathname.new(DEFAULT_ANNOTATIONS_DIR), Pathname)
@central_repo_root_uris = central_repo_root_uris
@auth = auth
@netrc_file = netrc_file
Expand All @@ -38,8 +39,11 @@ def initialize(
def execute
@indexes = fetch_indexes
project_gems = list_gemfile_gems

remove_expired_annotations(project_gems)
fetch_annotations(project_gems)
ensure
GitAttributes.create_vendored_attribute_file(@outpath)
end

sig { returns(T::Array[String]) }
Expand All @@ -56,7 +60,7 @@ def list_gemfile_gems
def remove_expired_annotations(project_gems)
say("Removing annotations for gems that have been removed... ", [:blue, :bold])

annotations = Pathname.glob("#{DEFAULT_ANNOTATIONS_DIR}/*.rbi").map { |f| f.basename(".*").to_s }
annotations = Pathname.glob(@outpath.join("*.rbi")).map { |f| f.basename(".*").to_s }
expired = annotations - project_gems

if expired.empty?
Expand All @@ -67,7 +71,7 @@ def remove_expired_annotations(project_gems)
say("\n")
expired.each do |gem_name|
say("\n")
path = "#{DEFAULT_ANNOTATIONS_DIR}/#{gem_name}.rbi"
path = @outpath.join("#{gem_name}.rbi")
remove_file(path)
end
say("\nDone\n\n", :green)
Expand Down Expand Up @@ -140,10 +144,8 @@ def fetch_annotation(repo_uris, gem_name)
content = apply_typed_override(gem_name, content)
content = add_header(gem_name, content)

dir = DEFAULT_ANNOTATIONS_DIR
FileUtils.mkdir_p(dir)
say("\n Fetched #{set_color(gem_name, :yellow, :bold)}", :green)
create_file("#{dir}/#{gem_name}.rbi", content)
create_file(@outpath.join("#{gem_name}.rbi"), content)
end

sig { params(repo_uri: String, path: String).returns(T.nilable(String)) }
Expand Down
2 changes: 2 additions & 0 deletions lib/tapioca/commands/dsl_generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def execute

say("All operations performed in working directory.", [:green, :bold])
say("Please review changes and commit them.", [:green, :bold])
ensure
GitAttributes.create_generated_attribute_file(@outpath)
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/tapioca/commands/gem_generate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ def execute
else
say("No operations performed, all RBIs are up-to-date.", [:green, :bold])
end
ensure
GitAttributes.create_generated_attribute_file(@outpath)
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/tapioca/commands/gem_sync.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ def execute
end

puts
ensure
GitAttributes.create_generated_attribute_file(@outpath)
end
end
end
Expand Down
34 changes: 34 additions & 0 deletions lib/tapioca/helpers/git_attributes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# typed: strict
# frozen_string_literal: true

class GitAttributes
class << self
extend T::Sig

sig { params(path: Pathname).void }
def create_generated_attribute_file(path)
create_gitattributes_file(path, <<~CONTENT)
**/*.rbi linguist-generated=true
CONTENT
end

sig { params(path: Pathname).void }
def create_vendored_attribute_file(path)
create_gitattributes_file(path, <<~CONTENT)
**/*.rbi linguist-vendored=true
CONTENT
end

private

sig { params(path: Pathname, content: String).void }
def create_gitattributes_file(path, content)
# We don't want to start creating folders, just to write
# the `.gitattributes` file. So, if the folder doesn't
# exist, we just return.
return unless path.exist?

File.write(path.join(".gitattributes"), content)
end
end
end
1 change: 1 addition & 0 deletions lib/tapioca/internal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
require "tapioca/runtime/dynamic_mixin_compiler"
require "tapioca/helpers/gem_helper"

require "tapioca/helpers/git_attributes"
require "tapioca/helpers/sorbet_helper"
require "tapioca/helpers/rbi_helper"
require "tapioca/sorbet_ext/backcompat_patches"
Expand Down
4 changes: 4 additions & 0 deletions spec/tapioca/cli/annotations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class AnnotationsTest < SpecWithProject
OUT

assert_success_status(result)
refute(File.directory?(@project.absolute_path("sorbet/rbi/annotations")))

repo.destroy!
end
Expand Down Expand Up @@ -94,6 +95,9 @@ class AnnotationForRBI; end
class AnnotationForSpoom; end
RBI

assert_project_file_equal("sorbet/rbi/annotations/.gitattributes", <<~CONTENT)
**/*.rbi linguist-vendored=true
CONTENT
refute_project_file_exist("sorbet/rbi/annotations/foo.rbi")
assert_success_status(result)

Expand Down
34 changes: 34 additions & 0 deletions spec/tapioca/cli/dsl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,40 @@ def self.application
@project.remove!("sorbet/rbi/dsl")
end

it "must generate a .gitattributes file in the output folder" do
@project.write("lib/post.rb", <<~RB)
require "smart_properties"
class Post
include SmartProperties
property :title, accepts: String
end
RB

result = @project.tapioca("dsl Post --outdir output")

assert_empty_stderr(result)
assert_success_status(result)

assert_project_file_equal("output/.gitattributes", <<~CONTENT)
**/*.rbi linguist-generated=true
CONTENT
ensure
@project.remove("output")
end

it "must not generate a .gitattributes file if the output folder is not created" do
result = @project.tapioca("dsl --outdir output")

assert_equal(<<~ERR, result.err)
No classes/modules can be matched for RBI generation.
Please check that the requested classes/modules include processable DSL methods.
ERR
refute_project_file_exist("output/.gitattributes")
ensure
@project.remove("output")
end

it "respects the Gemfile and Gemfile.lock" do
gem = mock_gem("foo", "1.0.0") do
write!("lib/foo.rb", <<~RB)
Expand Down
60 changes: 60 additions & 0 deletions spec/tapioca/cli/gem_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,52 @@ class << self
project.remove!("config/application.rb")
end

it "must generate a .gitattributes file in the output folder" do
foo = mock_gem("foo", "0.0.1") do
write("lib/foo.rb", FOO_RB)
end

@project.require_mock_gem(foo)
@project.bundle_install
result = @project.tapioca("gem foo --outdir output")

assert_stdout_includes(result, <<~OUT)
Compiled foo
OUT

assert_empty_stderr(result)
assert_success_status(result)

assert_project_file_equal("output/.gitattributes", <<~CONTENT)
**/*.rbi linguist-generated=true
CONTENT
ensure
@project.remove("output")
end

it "must not generate a .gitattributes file if the output folder is not created" do
foo = mock_gem("foo", "0.0.1") do
write("lib/foo.rb", FOO_RB)
end

@project.require_mock_gem(foo)
@project.bundle_install

# Generate for `foo` but exclude it as well, so that we don't create the output folder
result = @project.tapioca("gem foo --outdir output --exclude foo")

assert_stdout_includes(result, <<~OUT)
Nothing to do.
OUT

assert_empty_stderr(result)
assert_success_status(result)

refute_project_file_exist("output/.gitattributes")
ensure
@project.remove("output")
end

it "must generate a single gem RBI" do
foo = mock_gem("foo", "0.0.1") do
write!("lib/foo.rb", FOO_RB)
Expand Down Expand Up @@ -1638,6 +1684,20 @@ def baz; end
@project.remove!("../gems")
end

it "must generate a .gitattributes file in the output folder" do
result = @project.tapioca("gem --outdir output")

assert_stdout_includes(result, "create output/[email protected]")
assert_empty_stderr(result)
assert_success_status(result)

assert_project_file_equal("output/.gitattributes", <<~CONTENT)
**/*.rbi linguist-generated=true
CONTENT
ensure
@project.remove("output")
end

it "must perform no operations if everything is up-to-date" do
@project.write!("sorbet/rbi/gems/[email protected]")
@project.write!("sorbet/rbi/gems/[email protected]")
Expand Down

0 comments on commit 4201c89

Please sign in to comment.