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 +7 -0
- data/CLAUDE.md +55 -0
- data/LICENSE.txt +21 -0
- data/README.md +59 -0
- data/Rakefile +8 -0
- data/examples/rls/.gitignore +8 -0
- data/examples/rls/Gemfile +9 -0
- data/examples/rls/README.md +9 -0
- data/examples/rls/Rakefile +4 -0
- data/examples/rls/bin/console +11 -0
- data/examples/rls/bin/setup +8 -0
- data/examples/rls/exe/rls +7 -0
- data/examples/rls/lib/rls/cli.rb +48 -0
- data/examples/rls/lib/rls/version.rb +5 -0
- data/examples/rls/lib/rls.rb +8 -0
- data/examples/rls/rls.gemspec +28 -0
- data/examples/rls/sig/rls.rbs +4 -0
- data/examples/s3-ls/.gitignore +8 -0
- data/examples/s3-ls/Gemfile +9 -0
- data/examples/s3-ls/Gemfile.lock +91 -0
- data/examples/s3-ls/README.md +19 -0
- data/examples/s3-ls/Rakefile +4 -0
- data/examples/s3-ls/bin/console +11 -0
- data/examples/s3-ls/bin/setup +8 -0
- data/examples/s3-ls/exe/s3-ls +7 -0
- data/examples/s3-ls/lib/s3/ls/cli.rb +78 -0
- data/examples/s3-ls/lib/s3/ls/version.rb +7 -0
- data/examples/s3-ls/lib/s3/ls.rb +10 -0
- data/examples/s3-ls/s3-ls.gemspec +36 -0
- data/examples/s3-ls/sig/s3/ls.rbs +6 -0
- data/exe/uprb +7 -0
- data/lib/uprb/cli.rb +176 -0
- data/lib/uprb/require_replacer.rb +171 -0
- data/lib/uprb/require_tracker.rb +91 -0
- data/lib/uprb/static_require_tracker.rb +197 -0
- data/lib/uprb/version.rb +5 -0
- data/lib/uprb.rb +15 -0
- data/sig/uprb.rbs +4 -0
- data/tmp/.keep +0 -0
- metadata +95 -0
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,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,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,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,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,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,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,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
|