Skip to content

Commit 7460a30

Browse files
committed
Fix AssetsPrecompile#symlink_file logic
When we changed this method in #513 we introduced a regression due to the difference between calling the shell's `ln -s` command and using Ruby's `File.symlink` command. Specifically, the former would not error if the symlink already existed, while the latter did throw an error. Because it is sometimes the case that the symlink will already exist, throwing in this case is not desirable. This should have not been a problem, however, as this scenario was supposed to have been properly handled, but that code was not correct. This commit fixes that code.
1 parent 1c4affb commit 7460a30

File tree

2 files changed

+61
-47
lines changed

2 files changed

+61
-47
lines changed

lib/react_on_rails/assets_precompile.rb

+40-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
module ReactOnRails
22
class AssetsPrecompile
3+
class SymlinkTargetDoesNotExistException < StandardError; end
4+
35
# Used by the rake task
46
def default_asset_path
57
dir = File.join(Rails.configuration.paths["public"].first,
@@ -20,30 +22,35 @@ def initialize(assets_path: nil,
2022
def symlink_file(target, symlink)
2123
target_path = @assets_path.join(target)
2224
symlink_path = @assets_path.join(symlink)
25+
2326
target_exists = File.exist?(target_path)
27+
raise SymlinkTargetDoesNotExistException, "Target Path was: #{target_path}" unless target_exists
2428

2529
# File.exist?(symlink_path) will check the file the sym is pointing to is existing
2630
# File.lstat(symlink_path).symlink? confirms that this is a symlink
27-
symlink_already_there_and_valid = File.exist?(symlink_path) &&
28-
File.lstat(symlink_path).symlink?
29-
if symlink_already_there_and_valid
30-
puts "React On Rails: Digested #{symlink} already exists indicating #{target} did not change."
31-
elsif target_exists
32-
if File.exist?(symlink_path) && File.lstat(symlink_path).symlink?
33-
puts "React On Rails: Removing invalid symlink #{symlink_path}"
34-
`cd #{@assets_path} && rm #{symlink}`
35-
end
36-
# Might be like:
37-
# "images/5cf5db49df178f9357603f945752a1ef.png":
38-
# "images/5cf5db49df178f9357603f945752a1ef-033650e1d6193b70d59bb60e773f47b6d9aefdd56abc7cc.png"
39-
# need to cd to directory and then symlink
40-
target_sub_path, _divider, target_filename = target.rpartition("/")
41-
_symlink_sub_path, _divider, symlink_filename = symlink.rpartition("/")
42-
puts "React On Rails: Symlinking \"#{target}\" to \"#{symlink}\""
43-
dest_path = File.join(@assets_path, target_sub_path)
44-
FileUtils.chdir(dest_path) do
45-
File.symlink(target_filename, symlink_filename)
46-
end
31+
valid_symlink_already_exists = File.exist?(symlink_path) && File.lstat(symlink_path).symlink?
32+
33+
if valid_symlink_already_exists
34+
puts "React On Rails: Digested version of #{symlink} already exists indicating #{target} did not change."
35+
return
36+
end
37+
38+
if file_or_symlink_exists_at_path(symlink_path)
39+
puts "React On Rails: Removing existing invalid symlink or file #{symlink_path}"
40+
`rm -f "#{symlink_path}"`
41+
end
42+
43+
# Might be like:
44+
# "images/5cf5db49df178f9357603f945752a1ef.png":
45+
# "images/5cf5db49df178f9357603f945752a1ef-033650e1d6193b70d59bb60e773f47b6d9aefdd56abc7cc.png"
46+
# need to cd to directory and then symlink
47+
target_sub_path, _divider, target_filename = target.rpartition("/")
48+
_symlink_sub_path, _divider, symlink_filename = symlink.rpartition("/")
49+
dest_path = File.join(@assets_path, target_sub_path)
50+
51+
puts "React On Rails: Symlinking \"#{target}\" to \"#{symlink}\""
52+
FileUtils.chdir(dest_path) do
53+
File.symlink(target_filename, symlink_filename)
4754
end
4855
end
4956

@@ -74,8 +81,10 @@ def symlink_non_digested_assets
7481
# already been symlinked by Webpack
7582
symlink_file(rails_digested_filename, original_filename)
7683

77-
# We want the gz ones as well
78-
symlink_file("#{rails_digested_filename}.gz", "#{original_filename}.gz")
84+
# We want the gz ones as well if they exist
85+
if File.exist?(@assets_path.join("#{rails_digested_filename}.gz"))
86+
symlink_file("#{rails_digested_filename}.gz", "#{original_filename}.gz")
87+
end
7988
end
8089
end
8190
end
@@ -108,5 +117,14 @@ def clobber
108117
puts "Could not find generated_assets_dir #{dir} defined in react_on_rails initializer: "
109118
end
110119
end
120+
121+
private
122+
123+
def file_or_symlink_exists_at_path(path)
124+
File.lstat(path)
125+
true
126+
rescue
127+
false
128+
end
111129
end
112130
end

spec/react_on_rails/assets_precompile_spec.rb

+21-25
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ module ReactOnRails
5454
expect(File.identical?(assets_path.join(filename),
5555
assets_path.join(digest_filename))).to be true
5656
end
57+
58+
context "when no file exists at the target path" do
59+
it "raises a ReactOnRails::AssetsPrecompile::SymlinkTargetDoesNotExistException" do
60+
expect do
61+
AssetsPrecompile.new(assets_path: assets_path).symlink_file("non_existent", "non_existent-digest")
62+
end.to raise_exception(AssetsPrecompile::SymlinkTargetDoesNotExistException)
63+
end
64+
end
5765
end
5866

5967
describe "symlink_non_digested_assets" do
@@ -69,35 +77,23 @@ module ReactOnRails
6977
symlink_non_digested_assets_regex: Regexp.new('.*\.js$'))
7078
end
7179

72-
context "correct nondigest filename" do
73-
it "creates valid symlink" do
74-
FileUtils.touch assets_path.join(digest_filename)
75-
checker.symlink_non_digested_assets
76-
77-
expect(assets_path.join(nondigest_filename).lstat.symlink?).to be true
78-
expect(File.identical?(assets_path.join(nondigest_filename),
79-
assets_path.join(digest_filename))).to be true
80-
end
81-
end
82-
83-
context "zipped nondigest filename" do
84-
it "creates valid symlink" do
85-
FileUtils.touch assets_path.join("#{digest_filename}.gz")
86-
checker.symlink_non_digested_assets
80+
it "creates a symlink with the original filename that points to the digested filename" do
81+
FileUtils.touch assets_path.join(digest_filename)
82+
checker.symlink_non_digested_assets
8783

88-
expect(assets_path.join("#{nondigest_filename}.gz").lstat.symlink?).to be true
89-
expect(File.identical?(assets_path.join("#{nondigest_filename}.gz"),
90-
assets_path.join("#{digest_filename}.gz"))).to be true
91-
end
84+
expect(assets_path.join(nondigest_filename).lstat.symlink?).to be true
85+
expect(File.identical?(assets_path.join(nondigest_filename),
86+
assets_path.join(digest_filename))).to be true
9287
end
9388

94-
context "wrong nondigest filename" do
95-
it "should not create symlink" do
96-
FileUtils.touch assets_path.join("alfa.12345.jsx")
97-
checker.symlink_non_digested_assets
89+
it "creates a symlink with the original filename plus .gz that points to the gzipped digested filename" do
90+
FileUtils.touch assets_path.join(digest_filename)
91+
FileUtils.touch assets_path.join("#{digest_filename}.gz")
92+
checker.symlink_non_digested_assets
9893

99-
expect(assets_path.join("alfa.jsx")).not_to exist
100-
end
94+
expect(assets_path.join("#{nondigest_filename}.gz").lstat.symlink?).to be true
95+
expect(File.identical?(assets_path.join("#{nondigest_filename}.gz"),
96+
assets_path.join("#{digest_filename}.gz"))).to be true
10197
end
10298
end
10399

0 commit comments

Comments
 (0)