ocran 1.4.5 → 1.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +6 -0
- data/README.md +2 -0
- data/lib/ocran/direction.rb +42 -3
- data/lib/ocran/option.rb +9 -1
- data/lib/ocran/runner.rb +53 -0
- data/lib/ocran/runtime_environment.rb +14 -6
- data/lib/ocran/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9898f2fb5230aa7ee4f9bec0b47da2609bed63b37042b7ccb2b0dce9a694f25b
|
|
4
|
+
data.tar.gz: a3ae88ab734250742900487fa9a4dd3b862da930f11c9f656959b197efc7d24c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94b73edf3033cbda84a6d8f5d446b23acdffac467ac06c2881821c3caccb479bf5673e69662c96735d17cc2bfc0db43d0b44a0613e094c1e4251eac3cf915919
|
|
7
|
+
data.tar.gz: a6fb23433b4fca4ed3379a8486b6202a7c1c0272799034ee28543ab8c137f7d3fd8b04ad23f1a87f2012f98ec65505297e6bc51a90247426676170a1cc909151
|
data/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
=== 1.4.6
|
|
2
|
+
- Applications no longer carry gems that only OCRAN itself activated. The gem scan read Gem.loaded_specs while the build was already running instead of the snapshot taken after the dependency run, so whatever OCRAN had activated by then was packed as if the application had loaded it: OCRAN's own gem and its fiddle dependency, activated by the `ocran` command of an installed OCRAN (since OCRA 1.3.2, which started packing every gem in Gem.loaded_specs; about 390 KB of stubs, lzma.exe and sources per application), and since 1.3.16, whose refactoring moved the build ahead of the gem scan, also tempfile, tmpdir, fileutils, delegate and etc from the stub builder and fiddle from DLL detection. The scan now uses the snapshot and leaves out the running OCRAN's gem and its runtime dependencies. Only activation is discounted: a gem the application does load is still packed. The same snapshot decides which gems' SxS assembly manifests are included on Windows.
|
|
3
|
+
- Gems that Bundler installed into a configured path are found without Bundler (issue #61). ruby/setup-ruby with `bundler-cache: true` - the README's GitHub Actions example - installs the Gemfile's gems into vendor/bundle, a place plain RubyGems never looks, so `ocran app.rb` died with a LoadError on the first gem during the dependency run, `--gemfile` included. Before loading the script OCRAN now asks Bundler, in a child process and without evaluating the Gemfile, where the application's bundle is installed, and adds that directory to the gem path when it is not the system gems. The bundle is not set up, so gems the Gemfile does not list stay loadable as before; a bundle installed into the system gems and `bundle exec` are unaffected.
|
|
4
|
+
- On Windows, an `--output` name without an extension now gets .exe (names ending in .exe or .com are used as given). The name used to be taken verbatim, so `ocran myapp.rb --output myapp` - the README's GitHub Actions example, whose comment promised myapp.exe - produced an extensionless `myapp` that Windows does not run. Other platforms keep the name as given. New CI workflow test-readme-actions.yml runs that example step for step on Windows, Linux and both macOS architectures against OCRAN from the commit under test.
|
|
5
|
+
- Repository: removed a Ruby 3.3 bundle (vendor/bundle, 4,586 files), .bundle/config and a stray .ape-1.10 binary that a merge commit had checked in on 2026-08-09, and ignore them from now on. The bundle's binstubs call `ruby3.3`, which CI runners do not have, so every Ruby 3.3 test job failed before running a test once the bundler cache no longer replaced them; the jobs' cache-version is bumped to drop the caches that restored them. None of it was ever part of a published gem.
|
|
6
|
+
|
|
1
7
|
=== 1.4.5
|
|
2
8
|
- Windows: detected DLLs are now also bundled into bin, next to ruby.exe, whenever they are not already resolvable from where they get packed. The Windows loader resolves a native extension's imports from the extension's own directory, ruby.exe's application directory (bin) plus its ruby_builtin_dlls SxS assembly, and the system directories - PATH is not consulted on hardened systems, and the AddDllDirectory route gems take through ruby_installer/runtime does not exist in a packed app. A DLL loaded from a gem's own tree (e.g. FreeTDS, which tiny_tds ships under ports/), from a devkit's msys64 tree inside the Ruby prefix, or from outside the prefix entirely was packed only at that original location, which the loader never searches, so the packaged application died at require time with a misleading LoadError on machines where a rich PATH did not mask the gap - the out-of-prefix case was skipped entirely by a guard that made its copy_to_bin branch unreachable. DLLs from the Windows directory keep coming from the target system and are never bundled. Companion DLLs found next to native extensions (e.g. libssl-3-x64.dll beside openssl.so in archdir) go into bin as well: a copy in archdir only helps extensions in archdir itself, while the same extension packed at a gem path (openssl and psych are gems since Ruby 3.x) resolves its imports from bin.
|
|
3
9
|
- `--cosmo-ruby`: a native gem now also counts as provided by the payload when the payload can resolve the gem's primary feature, not only when the payload has a gemspec of that name. A gemspec is not what makes a library requirable - an extension linked into the APE, or a library in the interpreter's embedded stdlib, answers `require` with nothing under /zip/lib/ruby/gems/*/specifications - so gems such as cgi and pathname (both compiled into CosmoRuby, and both ordinary native gems on Ruby 3.4+) were reported incompatible and refused builds that work. The probe runs the payload once using $LOAD_PATH.resolve_feature_path, which searches exactly as require does, built-in extensions included, but executes none of the code it finds.
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# OCRAN
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
[OCRAN (One-Click Ruby Application Next)](https://github.com/largo/ocran) packages Ruby applications for
|
|
4
6
|
distribution. It bundles your script, the Ruby interpreter, gems, and native
|
|
5
7
|
libraries into a single self-contained artifact that runs without requiring
|
data/lib/ocran/direction.rb
CHANGED
|
@@ -191,8 +191,7 @@ module Ocran
|
|
|
191
191
|
end
|
|
192
192
|
end
|
|
193
193
|
if defined?(Gem)
|
|
194
|
-
|
|
195
|
-
specs += Gem.loaded_specs.each_value.reject { |spec| foreign.include?(spec.name) }
|
|
194
|
+
specs += application_loaded_specs
|
|
196
195
|
# Now, we also detect gems that are not included in Gem.loaded_specs.
|
|
197
196
|
# Therefore, we look for any loaded file from a gem path.
|
|
198
197
|
specs += GemSpecQueryable.detect_gems_from(features, verbose: @option.verbose?)
|
|
@@ -202,6 +201,46 @@ module Ocran
|
|
|
202
201
|
specs
|
|
203
202
|
end
|
|
204
203
|
|
|
204
|
+
# The gems RubyGems had activated when the dependency run ended, less
|
|
205
|
+
# those that are not the application's.
|
|
206
|
+
#
|
|
207
|
+
# Read from the snapshot taken with the loaded features, not from
|
|
208
|
+
# Gem.loaded_specs, which by now also holds what OCRAN activated for the
|
|
209
|
+
# build itself - fiddle for DLL detection, tempfile with tmpdir,
|
|
210
|
+
# fileutils, delegate and etc for the builder - and which every
|
|
211
|
+
# application used to get packed since 1.3.16 moved the build ahead of
|
|
212
|
+
# the gem scan. OCRAN's own gem and its runtime dependencies (fiddle) are
|
|
213
|
+
# dropped as well: the `ocran` command activates them before the
|
|
214
|
+
# dependency run starts, and since OCRA 1.3.2 they were packed into
|
|
215
|
+
# every application built from an installed OCRAN, although nothing in a
|
|
216
|
+
# packaged application loads OCRAN. Only activation is discounted: an
|
|
217
|
+
# application that does load one of them still gets it packed through
|
|
218
|
+
# detect_gems_from.
|
|
219
|
+
def application_loaded_specs
|
|
220
|
+
@application_loaded_specs ||= begin
|
|
221
|
+
excluded = foreign_bundle_gem_names | ocran_gem_names
|
|
222
|
+
@post_env.loaded_specs.reject { |spec| excluded.include?(spec.name) }
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# The names of the gem this OCRAN runs from and of the gems it activated
|
|
227
|
+
# as its runtime dependencies. Empty when OCRAN does not run from an
|
|
228
|
+
# installed gem, as from a source checkout.
|
|
229
|
+
def ocran_gem_names
|
|
230
|
+
specs = @post_env.loaded_specs.to_h { |spec| [spec.name, spec] }
|
|
231
|
+
own = specs.each_value.find { |spec| Pathname(__dir__).subpath?(spec.gem_dir) }
|
|
232
|
+
names = Set.new
|
|
233
|
+
queue = own ? [own] : []
|
|
234
|
+
while (spec = queue.shift)
|
|
235
|
+
next unless names.add?(spec.name)
|
|
236
|
+
|
|
237
|
+
spec.runtime_dependencies.each do |dep|
|
|
238
|
+
queue << specs[dep.name] if specs.key?(dep.name)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
names
|
|
242
|
+
end
|
|
243
|
+
|
|
205
244
|
# The gems RubyGems had already activated for a bundle that is not the
|
|
206
245
|
# application's, by the time OCRAN started.
|
|
207
246
|
#
|
|
@@ -560,7 +599,7 @@ module Ocran
|
|
|
560
599
|
archdir = Pathname(RbConfig::CONFIG["archdir"])
|
|
561
600
|
sxs_manifest_dirs << archdir if archdir.exist? && archdir.subpath?(exec_prefix)
|
|
562
601
|
if defined?(Gem)
|
|
563
|
-
|
|
602
|
+
application_loaded_specs.each do |spec|
|
|
564
603
|
next if spec.extensions.empty?
|
|
565
604
|
ext_dir = Pathname(spec.extension_dir)
|
|
566
605
|
sxs_manifest_dirs << ext_dir if ext_dir.exist? && ext_dir.subpath?(exec_prefix)
|
data/lib/ocran/option.rb
CHANGED
|
@@ -277,7 +277,15 @@ EOF
|
|
|
277
277
|
|
|
278
278
|
@options[:output_executable] =
|
|
279
279
|
if output_override
|
|
280
|
-
|
|
280
|
+
# Windows runs a program only by its extension, so a name given
|
|
281
|
+
# without .exe - `--output myapp`, as a build script shared with
|
|
282
|
+
# Linux and macOS writes it - gets one there. .exe and .com names
|
|
283
|
+
# are used as given.
|
|
284
|
+
if Gem.win_platform? && !output_override.extname?(".exe") && !output_override.extname?(".com")
|
|
285
|
+
Pathname("#{output_override}.exe")
|
|
286
|
+
else
|
|
287
|
+
output_override
|
|
288
|
+
end
|
|
281
289
|
else
|
|
282
290
|
executable = script
|
|
283
291
|
# If debug mode is enabled, append "-debug" to the filename
|
data/lib/ocran/runner.rb
CHANGED
|
@@ -49,6 +49,7 @@ module Ocran
|
|
|
49
49
|
else
|
|
50
50
|
warn_about_foreign_bundle
|
|
51
51
|
end
|
|
52
|
+
expose_bundle_install_path
|
|
52
53
|
say "Loading script to check dependencies"
|
|
53
54
|
$PROGRAM_NAME = @option.script.to_s
|
|
54
55
|
end
|
|
@@ -110,6 +111,58 @@ module Ocran
|
|
|
110
111
|
(app_gemfile ? "; pass --gemfile #{app_gemfile} to package against that bundle" : "")
|
|
111
112
|
end
|
|
112
113
|
|
|
114
|
+
# `bundle install` puts the gems where Bundler is configured to put them,
|
|
115
|
+
# and a configured path - `bundle config set path vendor/bundle`, which is
|
|
116
|
+
# what ruby/setup-ruby's `bundler-cache: true` does - is a place only
|
|
117
|
+
# Bundler looks. The dependency run loads the script as plain Ruby, so
|
|
118
|
+
# its first `require` of a gem from such a bundle raised LoadError
|
|
119
|
+
# although the bundle was installed (github issue #61).
|
|
120
|
+
#
|
|
121
|
+
# Add the bundle's install directory to the gem path. Its gems become
|
|
122
|
+
# visible exactly as if they had been installed into Ruby's own gem
|
|
123
|
+
# directory, and nothing more: setting the bundle up would also hide
|
|
124
|
+
# every gem the Gemfile does not list, breaking scripts that work today.
|
|
125
|
+
# A bundle installed into the system gems, the ordinary case outside CI,
|
|
126
|
+
# changes nothing, and under `bundle exec` Bundler has already put its
|
|
127
|
+
# gems on the gem path.
|
|
128
|
+
def expose_bundle_install_path
|
|
129
|
+
return if @pre_env.bundler_setup_loaded?
|
|
130
|
+
|
|
131
|
+
gemfile = @option.application_gemfile or return
|
|
132
|
+
path = bundle_install_path(gemfile) or return
|
|
133
|
+
return if Gem.path.any? { |gem_path| same_file?(gem_path, path) }
|
|
134
|
+
|
|
135
|
+
verbose "Adding #{path}, where the bundle of #{gemfile} is installed, to the gem path"
|
|
136
|
+
Gem.paths = { "GEM_HOME" => Gem.dir, "GEM_PATH" => (Gem.path + [path]).join(File::PATH_SEPARATOR) }
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Ruby run in a child process to print where Bundler installs the bundle
|
|
140
|
+
# of BUNDLE_GEMFILE, or nothing when that is the system gems.
|
|
141
|
+
BUNDLE_PATH_QUERY = <<~RUBY
|
|
142
|
+
require "bundler"
|
|
143
|
+
print Bundler.bundle_path unless Bundler.configured_bundle_path.use_system_gems?
|
|
144
|
+
RUBY
|
|
145
|
+
private_constant :BUNDLE_PATH_QUERY
|
|
146
|
+
|
|
147
|
+
# The directory the bundle of the given Gemfile is installed into when
|
|
148
|
+
# that is not the system gems, or nil. Bundler decides it from its
|
|
149
|
+
# settings - .bundle/config beside the Gemfile, the user's configuration
|
|
150
|
+
# and BUNDLE_* variables - without evaluating the Gemfile.
|
|
151
|
+
#
|
|
152
|
+
# Asked in a child process: loading Bundler here, before the dependency
|
|
153
|
+
# run, would put Bundler's files among the script's loaded features and
|
|
154
|
+
# pack them into every application with a Gemfile.
|
|
155
|
+
def bundle_install_path(gemfile)
|
|
156
|
+
env = { "BUNDLE_GEMFILE" => gemfile.to_s, "RUBYOPT" => nil, "BUNDLER_SETUP" => nil }
|
|
157
|
+
path = IO.popen(env, [RbConfig.ruby, "-e", BUNDLE_PATH_QUERY], err: File::NULL, &:read)
|
|
158
|
+
return nil unless $?.success? && !path.empty? && File.directory?(path)
|
|
159
|
+
|
|
160
|
+
path
|
|
161
|
+
rescue SystemCallError => e
|
|
162
|
+
verbose "Could not ask Bundler where the bundle of #{gemfile} is installed: #{e.message}"
|
|
163
|
+
nil
|
|
164
|
+
end
|
|
165
|
+
|
|
113
166
|
# The Gemfile this process was started against, if any.
|
|
114
167
|
def active_bundler_gemfile
|
|
115
168
|
return ENV["BUNDLE_GEMFILE"] unless ENV["BUNDLE_GEMFILE"].to_s.empty?
|
|
@@ -15,18 +15,26 @@ module Ocran
|
|
|
15
15
|
# under.
|
|
16
16
|
BUNDLER_SETUP_FEATURE = %r{[\\/]bundler[\\/]setup\.rb\z}
|
|
17
17
|
|
|
18
|
-
attr_reader :env, :load_path, :loaded_features, :pwd, :
|
|
18
|
+
attr_reader :env, :load_path, :loaded_features, :pwd, :loaded_specs
|
|
19
19
|
|
|
20
20
|
def initialize
|
|
21
21
|
@env = ENV.to_hash.freeze
|
|
22
22
|
@load_path = $LOAD_PATH.dup.freeze
|
|
23
23
|
@loaded_features = $LOADED_FEATURES.dup.freeze
|
|
24
24
|
@pwd = Dir.pwd.freeze
|
|
25
|
-
# The gems RubyGems had activated at this point.
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
|
|
25
|
+
# The specs of the gems RubyGems had activated at this point. Taken
|
|
26
|
+
# with the loaded features for the same reason: OCRAN goes on to
|
|
27
|
+
# activate gems of its own while it builds (fiddle for DLL detection,
|
|
28
|
+
# tempfile for the builder, ...), and those are not the application's.
|
|
29
|
+
# Under `bundle exec` the snapshot taken before the dependency run is
|
|
30
|
+
# the entire bundle, activated before OCRAN's first line runs, which is
|
|
31
|
+
# what tells the build environment's gems apart from the application's.
|
|
32
|
+
@loaded_specs = (defined?(Gem) ? Gem.loaded_specs.values : []).freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The names of the gems in #loaded_specs.
|
|
36
|
+
def activated_gems
|
|
37
|
+
@loaded_specs.map(&:name)
|
|
30
38
|
end
|
|
31
39
|
|
|
32
40
|
# Whether Bundler had already set this process up when the snapshot was
|
data/lib/ocran/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ocran
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.4.
|
|
4
|
+
version: 1.4.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andi Idogawa
|
|
@@ -116,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
117
|
version: '0'
|
|
118
118
|
requirements: []
|
|
119
|
-
rubygems_version: 4.0.
|
|
119
|
+
rubygems_version: 4.0.20
|
|
120
120
|
specification_version: 4
|
|
121
121
|
summary: OCRAN (One-Click Ruby Application Next) packages Ruby applications for distribution
|
|
122
122
|
on Windows, Linux, and macOS.
|