Skip to content

Commit a2a5e79

Browse files
committed
Fix: transitive dependencies in postinstall script
Postpones the execution of the postinstall script, so it's run after all dependencies have been installed, to solve the availability of transitive dependencies. Also postpones the installation of executables, since they're likely to be built by the postinstall script. Links the project's `lib` folder as the shard's own `lib` folder, to solve the accessibility of transitive dependencies (for executable for the postinstall script). This solution was preferred over manipulating the CRYSTAL_PATH environment variable, since a wrapper script of the crystal executable could replace it (e.g. the `bin/script` of crystal's repository does just that).
1 parent 79b5802 commit a2a5e79

File tree

6 files changed

+81
-14
lines changed

6 files changed

+81
-14
lines changed

src/commands/install.cr

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,26 @@ module Shards
4444
end
4545

4646
private def install(packages : Set)
47-
packages.each { |package| install(package) }
47+
packages
48+
.compact_map { |package| install(package) }
49+
.each(&.postinstall)
50+
51+
# always install executables because the path resolver never installs
52+
# dependencies, but uses them as-is:
53+
packages.each(&.install_executables)
4854
end
4955

5056
private def install(package : Package, version = nil)
5157
version ||= package.version
5258

5359
if package.installed?(version)
5460
Shards.logger.info "Using #{package.name} (#{package.report_version})"
55-
else
56-
Shards.logger.info "Installing #{package.name} (#{package.report_version})"
57-
package.install(version)
58-
package.install_executables
61+
return
5962
end
63+
64+
Shards.logger.info "Installing #{package.name} (#{package.report_version})"
65+
package.install(version)
66+
package
6067
end
6168

6269
private def generate_lockfile?

src/commands/update.cr

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,34 @@ module Shards
77
def run(*args)
88
manager.resolve
99

10-
manager.packages.each do |package|
11-
if package.installed?
12-
Shards.logger.info "Using #{package.name} (#{package.report_version})"
13-
else
14-
Shards.logger.info "Installing #{package.name} (#{package.report_version})"
15-
package.install
16-
end
17-
package.install_executables
18-
end
10+
install(manager.packages)
1911

2012
if generate_lockfile?
2113
manager.to_lock(lockfile_path)
2214
end
2315
end
2416

17+
private def install(packages : Set)
18+
packages
19+
.compact_map { |package| install(package) }
20+
.each(&.postinstall)
21+
22+
# always install executables because the path resolver never installs
23+
# dependencies, but uses them as-is:
24+
packages.each(&.install_executables)
25+
end
26+
27+
private def install(package : Package)
28+
if package.installed?
29+
Shards.logger.info "Using #{package.name} (#{package.report_version})"
30+
return
31+
end
32+
33+
Shards.logger.info "Installing #{package.name} (#{package.report_version})"
34+
package.install
35+
package
36+
end
37+
2538
private def generate_lockfile?
2639
!Shards.production? && manager.packages.any?
2740
end

src/package.cr

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,19 @@ module Shards
6868
end
6969

7070
def install(version = nil)
71+
# install the shard:
7172
resolver.install(version || self.version)
73+
74+
# link the project's lib path as the shard's lib path, so the dependency
75+
# can access transitive dependencies:
76+
unless @dependency.path
77+
lib_path = File.join(resolver.install_path, "lib")
78+
Shards.logger.debug "Link #{Shards.install_path} to #{lib_path}"
79+
File.symlink("../../lib", lib_path)
80+
end
81+
end
82+
83+
def postinstall
7284
resolver.run_script("postinstall")
7385
rescue ex : Script::Error
7486
resolver.cleanup_install_directory

test/integration/install_test.cr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,15 @@ class InstallCommandTest < Minitest::Test
291291
end
292292
end
293293

294+
def test_runs_postinstall_with_transitive_dependencies
295+
with_shard({ dependencies: {transitive: "*"} }) do
296+
run "shards install"
297+
binary = File.join(application_path, "lib", "transitive", "version")
298+
assert File.exists?(binary)
299+
assert_equal "version @ 0.1.0\n", `#{binary}`
300+
end
301+
end
302+
294303
def test_fails_when_shard_name_doesnt_match
295304
metadata = {
296305
dependencies: {

test/integration/update_test.cr

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,15 @@ class UpdateCommandTest < Minitest::Test
184184
end
185185
end
186186

187+
def test_runs_postinstall_with_transitive_dependencies
188+
with_shard({ dependencies: {transitive: "*"} }, {transitive: "0.1.0"}) do
189+
run "shards update"
190+
binary = File.join(application_path, "lib", "transitive", "version")
191+
assert File.exists?(binary)
192+
assert_equal "version @ 0.1.0\n", `#{binary}`
193+
end
194+
end
195+
187196
def test_installs_executables
188197
metadata = {
189198
dependencies: {

test/integration_helper.cr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@ class Minitest::Test
6565
create_git_repository "unstable", "0.1.0", "0.2.0", "0.3.0.alpha", "0.3.0.beta"
6666
create_git_repository "preview", "0.1.0", "0.2.0", "0.3.0.a", "0.3.0.b", "0.3.0", "0.4.0.a"
6767

68+
# postinstall script with transitive dependency:
69+
create_git_repository "version"
70+
create_file "version", "src/version.cr", %(module Version; STRING = "version @ 0.1.0"; end)
71+
create_git_release "version", "0.1.0"
72+
73+
create_git_repository "transitive"
74+
create_file "transitive", "src/version.cr", %(require "version"; puts Version::STRING)
75+
create_git_release "transitive", "0.2.0", <<-YAML
76+
name: transitive
77+
version: 0.2.0
78+
dependencies:
79+
version:
80+
git: #{git_path(:version)}
81+
scripts:
82+
postinstall: crystal build src/version.cr
83+
YAML
84+
6885
Minitest::Test.created_repositories!
6986
end
7087

0 commit comments

Comments
 (0)