uprb 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5935681ab015986fc8fa6aa5aad5d660da25e6126602888b95929f8456216f8b
4
+ data.tar.gz: c59b6d98633c40d506852dca47e4bf1895baaece4fcb596c4bf1e2cf62392dea
5
+ SHA512:
6
+ metadata.gz: cfeb194da679b6b6826c21f7207128869d835496268051d27e292e09f74edaaff60ca04381c5db3b63e268770924904d5cbfa0bca84055460fe4ef934e09f568
7
+ data.tar.gz: f09e358a5ffd2b2d22c1ca1dc26669be9d21d8bdc141aa59ed13e6480203c4e1b97c246944495d88a7e8cf80a53fb9029baa3e41f33bdfea7d4e44d8821f770f
data/CLAUDE.md ADDED
@@ -0,0 +1,55 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. User-facing docs live in `README.md`.
4
+
5
+ ## Commands
6
+
7
+ - `bin/setup` — `bundle install`.
8
+ - `bundle exec rake` — run the full test suite.
9
+ - `bundle exec ruby -Itest test/test_uprb_cli.rb -n test_pack_builds_executable` — run a single test by name.
10
+ - `bundle exec exe/uprb pack path/to/script.rb path/to/out` — run the CLI from the working tree.
11
+
12
+ Ruby 3.1+ (`uprb.gemspec`). CI: 3.4.7.
13
+
14
+ ## What uprb does
15
+
16
+ Packs a Ruby script into a deterministic, fast-starting executable by **freezing every observed `require` at pack time**. At runtime, the frozen mapping is preferred; anything else falls through to Ruby's normal `require`. If the environment changes, the executable is expected to break — the only remediation is repack.
17
+
18
+ ## Architecture
19
+
20
+ - `lib/uprb/require_tracker.rb` — aliases `Kernel#require` / `Kernel#require_relative` and records `name => $LOADED_FEATURES` path on every successful call.
21
+ - `lib/uprb/static_require_tracker.rb` — the **default** tracer. Two passes: (1) parse the entry with Prism and actually `require` each literal require/autoload target — libraries load normally but the entry's other top-level code (e.g. `App.start(ARGV)`) does not run, and everything pulled in transitively is recorded via `RequireTracker`; (2) `StaticWalker` recursively parses every reachable `.rb` file to pick up literal requires and autoload paths that the runtime never hit (rescued `LoadError` alternates, unused autoloads, feature-flag branches). The merged map uses dynamic results on conflict since those reflect the actually-resolved path. Interpolated-string requires triggered only by runtime execution (e.g. `require "foo/#{name}"` inside a method called by the entry) are still missed — those need `--dynamic`.
22
+ - `lib/uprb/require_replacer.rb` — `pack` builds the mapping via `build_mapping` (default: `StaticRequireTracker.trace`; `--dynamic`: `execute_with_tracker` + `StaticWalker` as a second pass so the runtime-only captures are augmented with literal/autoload paths not exercised by the execution). Then compiles the main script and every `.rb` entry into `RubyVM::InstructionSequence` binaries and appends a `Marshal` payload after `__END__`. A prepended `FixedRequire` module serves embedded ISeqs; non-`.rb` requires (C extensions etc.) are resolved via a `REQUIRE_MAP` of original absolute paths; unknown names defer to `super`. `execute_with_tracker` captures stdout/stderr to `Tempfile`, clears `ARGV`, sets `$PROGRAM_NAME`, and `Kernel#load`s the source in-process.
23
+ - `lib/uprb/cli.rb`, `exe/uprb` — CLI: `pack`, `gem install`, `gem pack`. Options: `--path DIR` (gem subcommands; default `Gem.bindir`).
24
+
25
+ The output gets a shebang only when the source does. With a source shebang, the wrapper's shebang is the absolute `RbConfig.ruby` path plus `--disable-gems` by default. Two orthogonal opt-outs:
26
+
27
+ - `--skip-disable-gems` drops the `--disable-gems` flag so the output starts Ruby normally (rubygems / `RUBYOPT` / Bundler Gemfile autodetection all active) — intended for vendoring-only use cases that accept normal startup cost.
28
+ - `--skip-ruby-path-replace` keeps the source file's shebang ruby invocation verbatim (e.g. `/usr/bin/env ruby`) instead of rewriting it to `RbConfig.ruby` — intended for vendoring that wants a portable Ruby reference in the output.
29
+
30
+ The two flags can be combined (e.g. `#!/usr/bin/env ruby` with no `--disable-gems`). When the source has no shebang, the packed output also has none and is not chmod'd executable — invoke via `ruby packed_file`. This is uniform across all flag combinations.
31
+
32
+ ## Hard constraints on the generated executable
33
+
34
+ Any change to the output generator must preserve these — violating any is a bug:
35
+
36
+ - Absolute `RbConfig.ruby` path in the shebang by default; never `/usr/bin/env ruby`, never a PATH lookup. `--skip-ruby-path-replace` is the one documented opt-out, for vendoring-only outputs that preserve the source's shebang ruby invocation
37
+ - `--disable-gems` in the shebang by default; rubygems is not available at runtime. `--skip-disable-gems` is the one documented opt-out, for vendoring-only outputs that accept normal Ruby startup cost
38
+ - No `-I`, no `$LOAD_PATH` modification, no `RUBYLIB`, no Bundler
39
+ - `.rb` dependencies are always embedded as ISeq binaries inside the `Marshal` payload; C extensions and other non-`.rb` requires are always referenced by their **original absolute path** via `REQUIRE_MAP` — never copied, never relocated
40
+
41
+ ## Non-goals
42
+
43
+ Declining these is a design choice, not a todo:
44
+
45
+ - Bundling Ruby itself; vendoring C extensions; cross-machine portability
46
+ - Surviving Ruby/gem upgrades, graceful degradation
47
+ - Plugin systems or dynamic resolution beyond the frozen mapping
48
+
49
+ (Compiled `.rb` sources are embedded in the output, so it is *partially* self-contained for pure-Ruby deps. C extensions and the Ruby interpreter are still referenced by absolute path, so the output remains tied to the machine it was packed on.)
50
+
51
+ The output is expected to break when the Ruby path moves, C extension paths change, gems are up/downgraded, or new code paths hit unseen `require`s. The only remediation is `uprb pack <src> <dest>`.
52
+
53
+ ## Testing notes
54
+
55
+ `test/test_uprb_cli.rb` shells out to `exe/uprb` via `Open3` and writes artifacts under `./tmp/` (gitignored). `aws-sdk-core` is a dev dependency so the suite can verify that a library pulling in rubygems vendored stdlib still packs successfully; the packed output itself is not runnable under `--disable-gems` and is not executed by the test. `examples/rls` and `examples/s3-ls` are sample gems, not part of the suite. `examples/s3-ls` depends on `aws-sdk-s3` and is kept as a reference — with a bare `uprb gem pack` it won't run under `--disable-gems`, but `uprb gem pack s3-ls --with-rubygems --dynamic` runs it up to the AWS credential/region resolution path (see `examples/s3-ls/README.md`).
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 hogelog
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # uprb
2
+
3
+ uprb is a Ruby script packer. It builds a single executable from a Ruby
4
+ script with a fast, deterministic startup.
5
+
6
+ This is not a native binary compiler. The packed executable still needs
7
+ a Ruby interpreter on the machine that runs it, and it is tied to the
8
+ Ruby and gems that were active when you packed.
9
+
10
+ The packed executable starts Ruby with `--disable-gems` by default.
11
+ Gems that depend on rubygems at load time will not work as-is — use
12
+ `--with-rubygems` to embed rubygems into the output, or
13
+ `--skip-disable-gems` to drop the flag entirely (vendoring only; gives
14
+ up the fast-startup headline).
15
+
16
+ ## Usage
17
+
18
+ Pack a script into a single executable:
19
+
20
+ ```bash
21
+ uprb pack path/to/script.rb path/to/output
22
+ ```
23
+
24
+ Options:
25
+
26
+ - `-f`, `--force`: overwrite the destination without prompting
27
+ - `-r`, `--require LIB`: pre-`require` `LIB` so it is embedded and loaded before the script runs. Repeatable.
28
+ - `--with-rubygems`: shortcut for `--require rubygems`. Needed when the script references `Gem::Version` / `Gem::Requirement` / `Gem::Platform` etc. Trade-off: larger output.
29
+ - `--dynamic`: execute the entry script at pack time so runtime-only requires (e.g. interpolated `require`s) are captured. The entry actually runs, which is a problem for scripts that do I/O on startup — arguments after `--` are forwarded as `ARGV` so you can steer into a safe path (e.g. `-- --help`).
30
+ - `--skip-disable-gems`: drop `--disable-gems` from the shebang. The output then behaves like a normal Ruby invocation — rubygems, `RUBYOPT`, and Bundler's Gemfile autodetection all run as usual. Use this when you want single-file vendoring of `.rb` dependencies and don't care about startup latency.
31
+ - `--skip-ruby-path-replace`: keep the source file's shebang ruby invocation (e.g. `/usr/bin/env ruby`) instead of rewriting it to an absolute `RbConfig.ruby` path. `--disable-gems` is still appended (unless `--skip-disable-gems` is also passed), so this is orthogonal to `--skip-disable-gems`. Use for vendoring when you want a portable Ruby reference in the output.
32
+
33
+ If the source has no shebang, the packed output also has no shebang and is not chmod'd executable — invoke via `ruby packed_file`. This applies regardless of flags.
34
+
35
+ Pack executables from an installed gem:
36
+
37
+ ```bash
38
+ uprb gem pack GEM_NAME
39
+ ```
40
+
41
+ Install a gem and pack its executables:
42
+
43
+ ```bash
44
+ uprb gem install GEM_NAME
45
+ ```
46
+
47
+ Both `gem` subcommands accept `--path DIR` (destination directory),
48
+ `-f`/`--force`, `-r`/`--require LIB`, `--with-rubygems`, `--dynamic`,
49
+ `--skip-disable-gems`, and `--skip-ruby-path-replace`.
50
+
51
+ By default, `uprb` asks `overwrite? [y/N]` on stderr when the
52
+ destination already exists. When stdin is not a TTY (e.g. CI), `uprb`
53
+ refuses to overwrite unless `--force` is given.
54
+
55
+ ## Install
56
+
57
+ ```bash
58
+ gem install uprb
59
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ task default: :test
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in rls.gemspec
6
+ gemspec
7
+
8
+ gem "irb"
9
+ gem "rake", "~> 13.0"
@@ -0,0 +1,9 @@
1
+ # rls
2
+
3
+ Example ls command written by ruby.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ rls
9
+ ```
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "rls"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+ require "rls"
6
+
7
+ Rls::CLI.start(ARGV)
@@ -0,0 +1,48 @@
1
+ module Rls
2
+ class CLI
3
+ USAGE = <<~USAGE.chomp
4
+ Usage:
5
+ rls [path]
6
+ USAGE
7
+
8
+ class << self
9
+ def start(argv)
10
+ new(argv).start
11
+ end
12
+ end
13
+
14
+ def initialize(argv)
15
+ @argv = argv.dup
16
+ end
17
+
18
+ def start
19
+ arg = @argv.shift
20
+
21
+ case arg
22
+ when "--help", "-h"
23
+ STDOUT.puts(USAGE)
24
+ when nil
25
+ ls_command
26
+ else
27
+ ls_command(arg)
28
+ end
29
+ rescue Rls::Error => e
30
+ STDERR.puts("rls: #{e.message}")
31
+ rescue StandardError => e
32
+ STDERR.puts("rls: #{e.class}: #{e.message}")
33
+ end
34
+
35
+ private
36
+
37
+ def ls_command(path = ".")
38
+ raise Rls::Error, "unexpected arguments: #{@argv.join(' ')}" unless @argv.empty?
39
+
40
+ abs_path = File.expand_path(path)
41
+ raise Rls::Error, "not a directory: #{path}" unless File.directory?(abs_path)
42
+
43
+ Dir.children(abs_path).sort.each do |entry|
44
+ STDOUT.puts(entry)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rls
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rls/version"
4
+ require_relative "rls/cli"
5
+
6
+ module Rls
7
+ class Error < StandardError; end
8
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/rls/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "rls"
7
+ spec.version = Rls::VERSION
8
+ spec.authors = ["hogelog"]
9
+ spec.email = ["konbu.komuro@gmail.com"]
10
+
11
+ spec.summary = "Example ls command gem"
12
+ spec.description = "Example ls command gem"
13
+ spec.homepage = "https://github.com/hogelog/uprb"
14
+ spec.required_ruby_version = ">= 3.2.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+
18
+ gemspec = File.basename(__FILE__)
19
+ spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
20
+ ls.readlines("\x0", chomp: true).reject do |f|
21
+ (f == gemspec) ||
22
+ f.start_with?(*%w[bin/ Gemfile .gitignore])
23
+ end
24
+ end
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) {|f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,4 @@
1
+ module Rls
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in s3-ls.gemspec
6
+ gemspec
7
+
8
+ gem "irb"
9
+ gem "rake", "~> 13.0"
@@ -0,0 +1,91 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ s3-ls (0.1.0)
5
+ aws-sdk-s3 (>= 1.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ aws-eventstream (1.4.0)
11
+ aws-partitions (1.1202.0)
12
+ aws-sdk-core (3.241.3)
13
+ aws-eventstream (~> 1, >= 1.3.0)
14
+ aws-partitions (~> 1, >= 1.992.0)
15
+ aws-sigv4 (~> 1.9)
16
+ base64
17
+ bigdecimal
18
+ jmespath (~> 1, >= 1.6.1)
19
+ logger
20
+ aws-sdk-kms (1.120.0)
21
+ aws-sdk-core (~> 3, >= 3.241.3)
22
+ aws-sigv4 (~> 1.5)
23
+ aws-sdk-s3 (1.211.0)
24
+ aws-sdk-core (~> 3, >= 3.241.3)
25
+ aws-sdk-kms (~> 1)
26
+ aws-sigv4 (~> 1.5)
27
+ aws-sigv4 (1.12.1)
28
+ aws-eventstream (~> 1, >= 1.0.2)
29
+ base64 (0.3.0)
30
+ bigdecimal (4.0.1)
31
+ date (3.5.1)
32
+ erb (6.0.1)
33
+ io-console (0.8.2)
34
+ irb (1.16.0)
35
+ pp (>= 0.6.0)
36
+ rdoc (>= 4.0.0)
37
+ reline (>= 0.4.2)
38
+ jmespath (1.6.2)
39
+ logger (1.7.0)
40
+ pp (0.6.3)
41
+ prettyprint
42
+ prettyprint (0.2.0)
43
+ psych (5.3.1)
44
+ date
45
+ stringio
46
+ rake (13.3.1)
47
+ rdoc (7.0.3)
48
+ erb
49
+ psych (>= 4.0.0)
50
+ tsort
51
+ reline (0.6.3)
52
+ io-console (~> 0.5)
53
+ stringio (3.2.0)
54
+ tsort (0.2.0)
55
+
56
+ PLATFORMS
57
+ arm64-darwin-25
58
+ ruby
59
+
60
+ DEPENDENCIES
61
+ irb
62
+ rake (~> 13.0)
63
+ s3-ls!
64
+
65
+ CHECKSUMS
66
+ aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b
67
+ aws-partitions (1.1202.0) sha256=c8aa0f134a23464c61cfd00edfb4b6d968b01847a8b591d4dcc0c63a4897c301
68
+ aws-sdk-core (3.241.3) sha256=c7c445ecf1c601c860fd537458b2eb8df0c5df01e63c371849e6594e6b1d4f47
69
+ aws-sdk-kms (1.120.0) sha256=a206ac6f62efbe971f802e8399d2702496a5c5bc800abcf94ead87bdddfdfd80
70
+ aws-sdk-s3 (1.211.0) sha256=2ae5feb09ff4862462824f267b76601ed16922a15de56cf51e4fa99bc5b3f519
71
+ aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00
72
+ base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
73
+ bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7
74
+ date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
75
+ erb (6.0.1) sha256=28ecdd99c5472aebd5674d6061e3c6b0a45c049578b071e5a52c2a7f13c197e5
76
+ io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
77
+ irb (1.16.0) sha256=2abe56c9ac947cdcb2f150572904ba798c1e93c890c256f8429981a7675b0806
78
+ jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1
79
+ logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
80
+ pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
81
+ prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
82
+ psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
83
+ rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
84
+ rdoc (7.0.3) sha256=dfe3d0981d19b7bba71d9dbaeb57c9f4e3a7a4103162148a559c4fc687ea81f9
85
+ reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
86
+ s3-ls (0.1.0)
87
+ stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
88
+ tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
89
+
90
+ BUNDLED WITH
91
+ 4.0.3
@@ -0,0 +1,19 @@
1
+ # s3-ls
2
+
3
+ Example S3 ls command gem.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ s3-ls s3://bucket[/prefix]
9
+ ```
10
+
11
+ ## Packing with uprb
12
+
13
+ `aws-sdk-core` pulls in rubygems vendored stdlib, so embed rubygems with `--with-rubygems`. Its credential provider chain uses runtime-only interpolated requires (e.g. `aws-sdk-core/json/#{name}_engine`), so pack in `--dynamic` mode so the entry actually runs and those requires fire:
14
+
15
+ ```bash
16
+ uprb gem pack s3-ls --with-rubygems --dynamic --path ~/local/bin/
17
+ ```
18
+
19
+ `--dynamic` also augments its trace with a static walk of every reachable `.rb`, so literal requires in unexecuted credential-provider branches get embedded too. If a runtime path still falls through to something that wasn't observed or statically reachable, repack with additional `-r` flags for the missing libs.
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "s3/ls"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+ require "s3/ls/cli"
6
+
7
+ S3::Ls::CLI.start(ARGV)
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "aws-sdk-s3"
5
+ require_relative "../ls"
6
+
7
+ module S3
8
+ module Ls
9
+ class CLI
10
+ USAGE = <<~USAGE.chomp
11
+ Usage:
12
+ s3-ls s3://bucket[/prefix]
13
+ s3-ls -h | --help
14
+ USAGE
15
+
16
+ def self.start(argv)
17
+ new(argv).start
18
+ end
19
+
20
+ def initialize(argv)
21
+ @argv = argv.dup
22
+ end
23
+
24
+ def start
25
+ if @argv.first == "-h" || @argv.first == "--help"
26
+ $stdout.puts USAGE
27
+ return
28
+ end
29
+
30
+ client = Aws::S3::Client.new
31
+
32
+ if @argv.empty?
33
+ list_buckets(client)
34
+ else
35
+ uri = @argv.shift
36
+ bucket, prefix = parse_s3_uri(uri)
37
+ list_objects(client, bucket, prefix)
38
+ end
39
+ rescue S3::Ls::Error => e
40
+ $stderr.puts "s3-ls: #{e.message}"
41
+ exit 1
42
+ rescue StandardError => e
43
+ $stderr.puts "s3-ls: #{e.class}: #{e.message}"
44
+ exit 1
45
+ end
46
+
47
+ private
48
+
49
+ def list_objects(client, bucket, prefix)
50
+ client.list_objects_v2(bucket: bucket, prefix: prefix).each_page do |page|
51
+ page.contents.each do |object|
52
+ $stdout.puts "s3://#{bucket}/#{object.key}"
53
+ end
54
+ end
55
+ end
56
+
57
+ def list_buckets(client)
58
+ client.list_buckets.buckets.each do |bucket|
59
+ $stdout.puts "s3://#{bucket.name}"
60
+ end
61
+ end
62
+
63
+ def parse_s3_uri(input)
64
+ uri = URI.parse(input)
65
+ raise S3::Ls::Error, "invalid s3 uri: #{input}" unless uri.scheme == "s3"
66
+ raise S3::Ls::Error, "missing bucket in s3 uri: #{input}" if uri.host.to_s.empty?
67
+
68
+ bucket = uri.host
69
+ prefix = uri.path
70
+ prefix = prefix.sub(%r{\A/}, "")
71
+ prefix = nil if prefix.empty?
72
+ [bucket, prefix]
73
+ rescue URI::InvalidURIError
74
+ raise S3::Ls::Error, "invalid s3 uri: #{input}"
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module S3
4
+ module Ls
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ls/version"
4
+ require_relative "ls/cli"
5
+
6
+ module S3
7
+ module Ls
8
+ class Error < StandardError; end
9
+ end
10
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/s3/ls/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "s3-ls"
7
+ spec.version = S3::Ls::VERSION
8
+ spec.authors = ["hogelog"]
9
+ spec.email = ["konbu.komuro@gmail.com"]
10
+
11
+ spec.summary = "Example S3 ls command gem"
12
+ spec.description = "Example S3 ls command gem"
13
+ spec.homepage = "https://github.com/hogelog/uprb"
14
+ spec.required_ruby_version = ">= 3.2.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = spec.homepage
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ gemspec = File.basename(__FILE__)
22
+ spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
23
+ ls.readlines("\x0", chomp: true).reject do |f|
24
+ (f == gemspec) ||
25
+ f.start_with?(*%w[bin/ Gemfile .gitignore])
26
+ end
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) {|f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.add_dependency "aws-sdk-s3", ">= 1.0"
33
+
34
+ # For more information and examples about making a new gem, check out our
35
+ # guide at: https://bundler.io/guides/creating_gem.html
36
+ end
@@ -0,0 +1,6 @@
1
+ module S3
2
+ module Ls
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
data/exe/uprb ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
+ require "uprb/cli"
6
+
7
+ Uprb::CLI.start(ARGV)