cinnabar 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ba73ee68ae237057d44f55990de599fae1290bdb0ad22ed9f4a3cc6688e8a9c
4
- data.tar.gz: 0ca77910329aa2c50e2ee0f6562ae1873ab31db0926dedc856aafeb9e1b98c03
3
+ metadata.gz: 0d07b1f0ca422a414fa67b7017920940bd12eadfe4f2d104de088e6ce67c9def
4
+ data.tar.gz: a2065bf84722d56727ce1d12602ef3d498d0026ba420f3e213ce5b9d24fdd558
5
5
  SHA512:
6
- metadata.gz: 9123046f853c2533f6adbab221418066af5c351b283c346e31937ecd7a8010b897860cb3fb1365386d3d6abfdf7e822e405aa79ab63420fd1553cb5605f08b9f
7
- data.tar.gz: 54b6ac22db5f6b5bb7287329d9b74876f03a8e76738968276cdd45b6d2ce22281b084c203ae93e66615bead81a2ce94cebbfdfd518c84956017a6f3a3e51949b
6
+ metadata.gz: ca95b7d444e89dd2c169d16b1255779c482dd4ed36bda09f7050a2d6c99f1049e27e52c660b4a2c35f0045b0c668a2954f7b982006257f3f74dcdb0a71a9d03c
7
+ data.tar.gz: 7da5135f3602cf662ea4bbc53e86cada66dc5d5601223600beaf53ac56d05262d7bb518a18cbfa2c22fc9128184032882c885e2906c438c3a7c7a00de8c3a69f
data/docs/ChangeLog.md ADDED
@@ -0,0 +1,5 @@
1
+ # ChangeLog
2
+
3
+ ## v0.0.2 (2026-01-05)
4
+
5
+ - fix ruby v4.0.0 compatibility
data/docs/Readme.md CHANGED
@@ -40,7 +40,7 @@ env:
40
40
  RUBYOPT: "--disable=gems"
41
41
  default_ci_shell: ruby cinnabar/ci.rb {0}
42
42
  # optional values: debug, info, warn, error, fatal, unknown
43
- RUBY_LOG: "debug"
43
+ RUBY_LOG: debug
44
44
 
45
45
  jobs:
46
46
  build:
@@ -56,7 +56,7 @@ jobs:
56
56
  with:
57
57
  repository: 2moe/cinnabar
58
58
  path: cinnabar
59
- ref: v0.0.1
59
+ ref: v0.0.2
60
60
 
61
61
  - name: (example) run cargo command
62
62
  run: |
@@ -85,7 +85,7 @@ jobs:
85
85
  stdout.to_i == 5 #=> true
86
86
  ```
87
87
 
88
- #### `.async_run`
88
+ #### `.async_run` + log
89
89
 
90
90
  ```ruby,yaml
91
91
  - run: |
@@ -98,6 +98,10 @@ jobs:
98
98
  } .to_argv
99
99
  .async_run
100
100
 
101
+ # log_dbg, log_info, log_warn, log_err, log_fatal, log_unk
102
+ "You can now do other things without waiting for
103
+ the process to complete.".log_dbg
104
+
101
105
  stdout, status = task.wait_with_output
102
106
  stdout.log_info
103
107
  raise "wasi" unless status.success?
@@ -117,7 +121,7 @@ jobs:
117
121
  # opts = { stdin_data:, stdin_binmode: false }
118
122
  opts = { stdin_data: }
119
123
 
120
- accel = %w[kvm hvf tcg].join ':'
124
+ accel = %w[kvm hvf whpx].join ':'
121
125
  task = {
122
126
  'qemu-system-x86_64': (),
123
127
  machine: "accel=#{accel}",
@@ -7,9 +7,8 @@
7
7
  # @see https://github.com/2moe/cinnabar
8
8
  module Cinnabar; end
9
9
 
10
+ require 'pathname'
11
+
10
12
  # To ensure compatibility with "--disable=gems" (allowing users to pre-require),
11
13
  # add conditional checks before requiring these libraries.
12
14
  require 'sinlog' unless defined? Sinlog::VERSION
13
- # require 'argvise' unless defined? Argvise::VERSION
14
-
15
- require 'pathname'
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE: This file can be run independently;
4
+ # Although **00_pre.rb** has already imported the relevant libraries, they still need to be imported here.
5
+ require 'pathname'
6
+
7
+ module Cinnabar
8
+ # Build a Proc that converts a directory-like value into a {Kernel.Pathname}.
9
+ #
10
+ # This is handy when you want to pass a converter into higher-order APIs
11
+ # (e.g., map/filter pipelines).
12
+ #
13
+ # @param dir [String, #to_s] a directory path (or any object convertible to String)
14
+ # @return [Proc] a lambda that maps `dir` to `Pathname(dir)`
15
+ #
16
+ # @example Convert a list of directories to Pathname objects
17
+ #
18
+ # conv = Cinnabar.to_path_proc
19
+ # %w[/tmp /var].map(&conv)
20
+ # #=> [#<Pathname:/tmp>, #<Pathname:/var>]
21
+ def self.to_path_proc = ->(dir) { Pathname(dir) }
22
+ end
23
+
24
+ # Adds `String#to_path` as a convenience helper to convert strings into {Kernel.Pathname}.
25
+ #
26
+ # This module supports two integration styles:
27
+ #
28
+ # - {Cinnabar::StrToPath::Mixin}: globally extends {String} (monkey patch).
29
+ # - {Cinnabar::StrToPath::Refin}: lexically-scoped extension via refinements.
30
+ #
31
+ # @note This feature relies on Ruby's {Pathname} class.
32
+ # Make sure `require "pathname"` is loaded before calling `to_path`.
33
+ module Cinnabar::StrToPath
34
+ # Implementation of the `#to_path` method intended to be mixed into {String}.
35
+ module Ext
36
+ # Convert the receiver (a String) into a {Pathname}.
37
+ #
38
+ # @return [Pathname] `Pathname(self)`
39
+ #
40
+ # @example
41
+ #
42
+ # "lib".to_path
43
+ # #=> #<Pathname:lib>
44
+ def to_path = ::Kernel.Pathname(self)
45
+ end
46
+
47
+ # This is a "mixin switch": `include Cinnabar::StrToPath::Mixin` will modify
48
+ # {::String} for the entire process (i.e., a monkey patch).
49
+ #
50
+ # @note Side effect: this will affect *all* strings in the process, including
51
+ # third-party code. If you want scoped behavior, prefer {Refin}.
52
+ #
53
+ # @example
54
+ #
55
+ # include Cinnabar::StrToPath::Mixin
56
+ #
57
+ # __dir__.to_path
58
+ # # Same as `Pathname(__dir__)`
59
+ module Mixin
60
+ # Hook invoked when this module is included.
61
+ #
62
+ # @param _host [Module] the including host (unused)
63
+ # @return [void]
64
+ def self.included(_host) = ::String.include Ext
65
+ end
66
+
67
+ # Adds `String#to_path` via Ruby refinements (lexically scoped).
68
+ #
69
+ # This avoids global monkey patches. The method is only visible within scopes
70
+ # where `using Cinnabar::StrToPath::Refin` is active.
71
+ #
72
+ # @example
73
+ #
74
+ # using Cinnabar::StrToPath::Refin
75
+ #
76
+ # __dir__.to_path
77
+ # # Same as `Pathname(__dir__)`
78
+ module Refin
79
+ # Refinement for {String} to import {Ext#to_path}.
80
+ refine ::String do
81
+ import_methods Ext
82
+ end
83
+ end
84
+ end
85
+
86
+ module Cinnabar::Path
87
+ module_function
88
+
89
+ # Appends a directory to Ruby's load path (`$LOAD_PATH` / `$:`)
90
+ # if it is not already included.
91
+ #
92
+ # @param dir [String] directory path to add into `$LOAD_PATH`
93
+ # @return [void]
94
+ # @example
95
+ #
96
+ # Cinnabar::Path.append_load_path '/opt/ruby/4.0.0/lib/ruby/4.0.0'
97
+ def append_load_path(dir)
98
+ # $: is an alias of $LOAD_PATH
99
+ $: << dir unless $:.include?(dir)
100
+ end
101
+
102
+ # Finds the gem's "lib directory" (via `gem which -V`) and append it to $LOAD_PATH.
103
+ #
104
+ # This method maintains a small cache file to avoid running `gem which` repeatedly.
105
+ # Cache format (per line):
106
+ # <gem_name><two spaces><lib_dir>
107
+ #
108
+ # If the gem cannot be located, it will try to install it and retry up to `max_retries`.
109
+ #
110
+ # @param gem_name [String, Symbol] gem name to locate (default: 'logger')
111
+ # @param cache_file [String] cache file path (default: 'tmp/load_path.txt')
112
+ # @param max_retries [Integer] maximum retries for gem install + re-check (default: 2)
113
+ # @return [void]
114
+ def find_and_append_load_path(gem_name = 'logger', cache_file: 'tmp/load_path.txt', max_retries: 2)
115
+ pkg = gem_name.to_s
116
+ cache_data = decode_cache_file(cache_file)
117
+
118
+ # If cached, append immediately and return.
119
+ case val = cache_data&.[](pkg)
120
+ when nil then ()
121
+ else return append_load_path(val)
122
+ end
123
+
124
+ # Locate the gem's lib directory, installing the gem if necessary.
125
+ lib_dir = gem_dir_with_retry(pkg, max_retries)
126
+
127
+ # Update cache and write it back to disk.
128
+ cache_data[pkg] = lib_dir
129
+ encoded = encode_cache_hash(cache_data)
130
+
131
+ # Ensure parent directory exists, then write the cache file.
132
+ Kernel.Pathname(cache_file)
133
+ .tap { _1.dirname.mkpath }
134
+ .write(encoded)
135
+
136
+ # Finally, append the located directory to $LOAD_PATH.
137
+ append_load_path(lib_dir)
138
+ end
139
+
140
+ # Decodes cache file into a Hash.
141
+ #
142
+ # It ignores:
143
+ #
144
+ # - leading spaces (lstrip)
145
+ # - empty lines
146
+ # - comment lines that start with '#'
147
+ #
148
+ # Each line is split by "two spaces" into:
149
+ # `key value`
150
+ #
151
+ # @param file [String] cache file path
152
+ # @return [Hash{String => String}] mapping from gem name to lib dir
153
+ def decode_cache_file(file)
154
+ # If cache file does not exist, treat as empty cache.
155
+ return {} unless File.exist?(file)
156
+
157
+ File.foreach(file)
158
+ .lazy
159
+ .map(&:lstrip) # allow indentation; normalize leading spaces
160
+ .map(&:chomp) # remove trailing newline
161
+ .reject(&:empty?) # drop blank lines
162
+ .reject { _1.start_with? '#' } # drop comments
163
+ .map { |line| line.split(' ', 2) } # split into [key, value] by two spaces
164
+ .to_h
165
+ end
166
+
167
+ # Encodes a Hash into the cache file format.
168
+ #
169
+ # @param data [Hash] mapping from gem name to lib dir
170
+ # @return [String] encoded cache content
171
+ # @raise [ArgumentError] if `data` is not a Hash
172
+ #
173
+ # @example
174
+ #
175
+ # CiPath = Cinnabar::Path
176
+ #
177
+ # gem_home = "#{Dir.home}/.local/share/gem"
178
+ # data = {
179
+ # "logger" => "#{gem_home}/gems/logger-1.7.0/lib",
180
+ # "irb" => "#{gem_home}/gems/irb-1.16.0/lib",
181
+ # "reline" => "#{gem_home}/gems/reline-0.6.3/lib",
182
+ # }
183
+ # str = CiPath.encode_cache_hash(data)
184
+ def encode_cache_hash(data)
185
+ Kernel.raise ArgumentError, 'data must be a hash' unless data.is_a? ::Hash
186
+
187
+ # Use "two spaces" as a stable delimiter (same as decode).
188
+ data.map { |k, v| "#{k} #{v}" }.join("\n")
189
+ end
190
+
191
+ # Resolves gem's lib directory by invoking:
192
+ # `gem which -V <pkg>`
193
+ #
194
+ # `gem which -V` prints the resolved file path; we strip it and take its dirname.
195
+ #
196
+ # @param pkg [String] gem name
197
+ # @return [String] directory containing the resolved file
198
+ # @raise [RuntimeError] if gem which returns empty
199
+ #
200
+ # @note Please do not use `Gem::Specification` in this method,
201
+ # as this function and the script must remain compatible with `--disable=gems`.
202
+ def gem_dir(pkg)
203
+ path = IO.popen(%w[gem which -V] << pkg.to_s, &:read).to_s.strip
204
+ Kernel.raise "gem which returned empty for #{pkg}" if path.empty?
205
+
206
+ File.dirname(path)
207
+ end
208
+ private_class_method :gem_dir
209
+
210
+ # Locates the lib directory for the given gem_name; if it fails, retries by installing the gem.
211
+ #
212
+ # Behavior:
213
+ #
214
+ # - If `gem_dir` raises, print a warning and run `gem install <pkg>`.
215
+ # - Retry up to `max_retries`.
216
+ #
217
+ # @param gem_name [String, Symbol] gem name
218
+ # @param max_retries [Integer] max retry count (default: 2)
219
+ # @return [String] resolved lib directory
220
+ # @raise [RuntimeError] when install fails or retries exceed max
221
+ #
222
+ #
223
+ # @example
224
+ #
225
+ # CiPath = Cinnabar::Path
226
+ #
227
+ # dir_str = CiPath.gem_dir_with_retry("logger")
228
+ def gem_dir_with_retry(gem_name, max_retries = 2)
229
+ pkg = gem_name.to_s
230
+ attempts = 0
231
+
232
+ begin
233
+ logger_dir = gem_dir(pkg)
234
+ rescue StandardError => e
235
+ # Inform user about the failure and the planned automatic install attempt.
236
+ #
237
+ # Do not use `Sinlog.warn` or any "advanced" logger here!
238
+ # As this function is intended for lower-level APIs.
239
+ Kernel.warn "[WARN] #{e}; Try installing #{pkg}"
240
+
241
+ # Attempt to install; raise if installation fails.
242
+ Kernel.system "gem install #{pkg}" or Kernel.raise 'Failed to install'
243
+
244
+ attempts += 1
245
+ Kernel.raise 'Already retried 3 times' if attempts > max_retries
246
+
247
+ retry
248
+ end
249
+
250
+ logger_dir
251
+ end
252
+ # private_class_method :gem_dir_with_retry
253
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cinnabar
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.2'
5
5
  end
data/lib/cinnabar.rb CHANGED
@@ -6,5 +6,6 @@
6
6
  require_relative 'cinnabar/00_pre'
7
7
  require_relative 'cinnabar/cmd_runner'
8
8
  require_relative 'cinnabar/net'
9
+ require_relative 'cinnabar/path'
9
10
  require_relative 'cinnabar/pipe'
10
11
  require_relative 'cinnabar/version'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cinnabar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - 2moe
@@ -31,12 +31,14 @@ files:
31
31
  - ".rubocop.yml"
32
32
  - ".yardopts"
33
33
  - License
34
+ - docs/ChangeLog.md
34
35
  - docs/Readme-zh.md
35
36
  - docs/Readme.md
36
37
  - lib/cinnabar.rb
37
38
  - lib/cinnabar/00_pre.rb
38
39
  - lib/cinnabar/cmd_runner.rb
39
40
  - lib/cinnabar/net.rb
41
+ - lib/cinnabar/path.rb
40
42
  - lib/cinnabar/pipe.rb
41
43
  - lib/cinnabar/version.rb
42
44
  homepage: https://github.com/2moe/cinnabar
@@ -44,6 +46,7 @@ licenses:
44
46
  - Apache-2.0
45
47
  metadata:
46
48
  homepage_uri: https://github.com/2moe/cinnabar
49
+ documentation_uri: https://2moe.github.io/cinnabar
47
50
  rdoc_options: []
48
51
  require_paths:
49
52
  - lib
@@ -58,7 +61,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
58
61
  - !ruby/object:Gem::Version
59
62
  version: '0'
60
63
  requirements: []
61
- rubygems_version: 3.7.2
64
+ rubygems_version: 4.0.3
62
65
  specification_version: 4
63
66
  summary: CI Utils
64
67
  test_files: []