inquirex-tools 0.9.5 → 0.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d8f0e3e2dc5bcc9d45d3c9cba64f8a7d96400b6d1296d316ff56460ece229166
4
- data.tar.gz: 3ee07acd2e8e731115e0a5552bbbdaeea2979198195e396fc946765f8b6deef6
3
+ metadata.gz: c0382f5f2ef1fed61354873914a92e24e3b25dbead0959aac9de8f48702bdfdf
4
+ data.tar.gz: 25c18df8bb5cb9a7bcad98668431ff7587df5444b7797917f8c3201308168170
5
5
  SHA512:
6
- metadata.gz: 81ca8eed0f60037439fd3c8ab6aafe9b35114082f2c02db488bf279a3d52577c7fb7c8c7e04a41ab48e0c8ab6be1e8e71e324d32b566cbc865ce861e4855601e
7
- data.tar.gz: 3adc9867dab085361badf7b00ff38ef9965345efa2ef610bddb27e35e2a34945ebaa2459bdf068e18118cd2121ec67f952564f2f9df562e00d53df49ebb63578
6
+ metadata.gz: 0dc205efda474da503f17a2ac1259b12193996621bba9041ff696e67563bde5b050dd0ca52bb5149b261b694fdb941b29755dd05a9fb71a81c335c65ce903e8d
7
+ data.tar.gz: ddd1e89483f67689fc64b4d4618d19b0a667dfd91da9778b26ee56d7ff36c54c657540bf15441c7fb53273d15b4403d5d4705f1f61292e8772142fab65d6535c
data/.envrc CHANGED
@@ -1 +1,3 @@
1
1
  PATH_add bin
2
+
3
+ [[ -f ../.envrc ]] && source ../.envrc
@@ -0,0 +1,29 @@
1
+ # Many Repos Setup
2
+
3
+ ## Problem
4
+
5
+ Inquirex Organization houses several interlinked public repositories under https://github.com/inquirex that are often worked on in tandem, and checked out into the same folder.
6
+
7
+ For instance, seeting up the entire tree requires the following:
8
+
9
+ ```bash
10
+ cd ~/
11
+ mkdir -p ~/github/inquirex
12
+ cd ~/github/inquirex
13
+ declare -a repos=(
14
+ inquirex
15
+ inquirex-llm
16
+ inquirex-tty
17
+ inquirex-webui
18
+ inquirex-widget
19
+ inquirex-tools
20
+ inquirex-claude
21
+ )
22
+ for repo in ${repos[@]}; do
23
+ git clone git@github.com:inquirex/${repo}
24
+ done
25
+
26
+ cd inquirex-tools && bin/setup
27
+
28
+ ```
29
+
@@ -22,9 +22,14 @@ module Inquirex
22
22
  # so nothing here handles a credential in plain text.
23
23
  DEFAULT_TOKEN_READER = -> { `gh auth token 2>/dev/null`.strip }
24
24
 
25
- # Runs the generator in one repository, discarding its very chatty output.
25
+ # Runs the generator in one repository, discarding its very chatty
26
+ # output, with this gem's bundle scrubbed — see
27
+ # {Inquirex::Tools.unbundled}. The generator is a gem of its own and must
28
+ # resolve against the installation that provides it, not against ours.
26
29
  DEFAULT_RUNNER = lambda { |dir, env|
27
- system(env, "github_changelog_generator", chdir: dir, out: File::NULL, err: File::NULL)
30
+ Inquirex::Tools.unbundled do
31
+ system(env, "github_changelog_generator", chdir: dir, out: File::NULL, err: File::NULL)
32
+ end
28
33
  }
29
34
 
30
35
  # @param workspace [Workspace]
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "workspace"
4
+
5
+ module Inquirex
6
+ module Tools
7
+ # Raises the "newest published gem" pin that a package's CI installs from
8
+ # rubygems.org.
9
+ #
10
+ # `inquirex-webui` reparses its printer's output with the real gem, which
11
+ # CI installs from the registry rather than from a checkout. That pin
12
+ # therefore cannot name the version in the source tree — it does not exist
13
+ # yet — so it trails by one release and has to be raised by hand afterwards.
14
+ #
15
+ # Nobody remembers. The pin sat at 0.9.4 while the printer started emitting
16
+ # `min`, `max` and `step_size`, added to the gem in 0.9.5, and the
17
+ # round-trip failed with "`min` is not allowed inside a step block":
18
+ # SafeSource is default-deny, so a step setter it has never heard of is
19
+ # rejected outright. The failure names the printer, not the pin.
20
+ #
21
+ # Running immediately after a successful publish is what makes this safe.
22
+ # At that moment the version is on the registry by construction, so the
23
+ # pin's one rule — never run ahead of rubygems.org — cannot be violated.
24
+ #
25
+ # @example Raise the pin to the version just published
26
+ # Inquirex::Tools::PinUpdater.new.call("0.9.6") # => ["inquirex-webui/.github/workflows/ci.yml"]
27
+ class PinUpdater
28
+ # A pinned family version in a workflow's `env:` block. Anchored to the
29
+ # whole line so a value mentioned in a comment is left alone.
30
+ PIN = /^(?<lead>\s*INQUIREX_GEM_VERSION:\s*)(?<quote>["']?)\d+\.\d+\.\d+\k<quote>\s*$/
31
+
32
+ # @param workspace [Workspace] the ecosystem checkout to rewrite
33
+ # @param out [IO] where report output goes
34
+ def initialize(workspace: Workspace.new, out: $stdout)
35
+ @workspace = workspace
36
+ @out = out
37
+ end
38
+
39
+ # Rewrites every pin to `version`.
40
+ #
41
+ # @param version [String] the version that was just published
42
+ # @param dry_run [Boolean] report what would change and write nothing
43
+ # @return [Array<String>] workspace-relative paths that changed
44
+ def call(version, dry_run: false)
45
+ changed = workflows.select { |path| rewrite(path, version, dry_run:) }
46
+ report(changed, version, dry_run:)
47
+ changed.map { |path| path.delete_prefix("#{workspace.root}/") }
48
+ end
49
+
50
+ private
51
+
52
+ # @return [Workspace] the ecosystem checkout being rewritten
53
+ attr_reader :workspace
54
+
55
+ # @return [IO] where report output goes
56
+ attr_reader :out
57
+
58
+ # @return [Array<String>] every workflow file in every lockstep package
59
+ def workflows
60
+ Workspace::LOCKSTEP.keys.flat_map do |name|
61
+ Dir.glob(File.join(workspace.root, name, ".github", "workflows", "*.y{a,}ml"))
62
+ end.sort
63
+ end
64
+
65
+ # @return [Boolean] whether the file needed rewriting
66
+ def rewrite(path, version, dry_run:)
67
+ source = File.read(path)
68
+ updated = source.gsub(PIN) { "#{::Regexp.last_match(:lead)}\"#{version}\"" }
69
+ return false if updated == source
70
+
71
+ File.write(path, updated) unless dry_run
72
+ true
73
+ end
74
+
75
+ # @return [void]
76
+ def report(changed, version, dry_run:)
77
+ return if changed.empty?
78
+
79
+ verb = dry_run ? "Would raise" : "Raised"
80
+ out.puts
81
+ out.puts "#{verb} the published-gem pin to #{version}:"
82
+ changed.each { |path| out.puts " #{path.delete_prefix("#{workspace.root}/")}" }
83
+ # The publisher does not commit — it has no business deciding what a
84
+ # release commit looks like — so say plainly that this is not done yet.
85
+ out.puts "Commit that; CI installs the pinned version from the registry.".dark unless dry_run
86
+ end
87
+ end
88
+ end
89
+ end
@@ -40,9 +40,11 @@ module Inquirex
40
40
  ROTATE_INTERVAL = 2
41
41
 
42
42
  # Shells out with the child's stdout and stderr left attached, so a
43
- # failing package's own output is what the operator sees.
43
+ # failing package's own output is what the operator sees, and with this
44
+ # gem's bundle scrubbed so the recipe resolves its own — see
45
+ # {Inquirex::Tools.unbundled}.
44
46
  DEFAULT_RUNNER = lambda { |dir, argv|
45
- Dir.chdir(dir) { system("just", *argv) }
47
+ Inquirex::Tools.unbundled { Dir.chdir(dir) { system("just", *argv) } }
46
48
  }
47
49
 
48
50
  # Reads the current RubyGems TOTP. `op` resolves the account from
@@ -92,16 +94,20 @@ module Inquirex
92
94
  # without publishing anything.
93
95
  # @param otp_reader [#call] returns the current 2FA code, or nil
94
96
  # @param published_checker [#call] (name, kind, version) => true/false/nil
97
+ # @param pin_updater [#call] (version, dry_run:) raises the published-gem
98
+ # pin once the version is certainly on the registry
95
99
  def initialize(workspace: Workspace.new,
96
100
  out: $stdout,
97
101
  runner: DEFAULT_RUNNER,
98
102
  otp_reader: DEFAULT_OTP_READER,
99
- published_checker: DEFAULT_PUBLISHED_CHECKER)
103
+ published_checker: DEFAULT_PUBLISHED_CHECKER,
104
+ pin_updater: nil)
100
105
  @workspace = workspace
101
106
  @out = out
102
107
  @runner = runner
103
108
  @otp_reader = otp_reader
104
109
  @published_checker = published_checker
110
+ @pin_updater = pin_updater || PinUpdater.new(workspace:, out:)
105
111
  end
106
112
 
107
113
  # Publishes every lockstep package, or prints what it would publish.
@@ -155,6 +161,9 @@ module Inquirex
155
161
 
156
162
  out.puts
157
163
  out.puts summary(dry_run:, published:, skipped:)
164
+ # Only now is the version certainly on the registry, which is the one
165
+ # rule the pin has to obey.
166
+ pin_updater.call(workspace.version_of("inquirex"), dry_run:)
158
167
  true
159
168
  end
160
169
 
@@ -175,6 +184,9 @@ module Inquirex
175
184
  # @return [#call] asks a registry whether a version is already public
176
185
  attr_reader :published_checker
177
186
 
187
+ # @return [#call] raises the published-gem pin after a release
188
+ attr_reader :pin_updater
189
+
178
190
  # @return [Boolean] whether the package published cleanly
179
191
  def run(name, dir, argv)
180
192
  return true if runner.call(dir, argv)
@@ -34,9 +34,11 @@ module Inquirex
34
34
  RELEASE_BRANCH = "main"
35
35
 
36
36
  # Shells out with the child's stdout and stderr left attached, so a
37
- # failing package's own `gh` output is what the operator sees.
37
+ # failing package's own `gh` output is what the operator sees, and with
38
+ # this gem's bundle scrubbed so the recipe resolves its own — see
39
+ # {Inquirex::Tools.unbundled}.
38
40
  DEFAULT_RUNNER = lambda { |dir, argv|
39
- Dir.chdir(dir) { system("just", *argv) }
41
+ Inquirex::Tools.unbundled { Dir.chdir(dir) { system("just", *argv) } }
40
42
  }
41
43
 
42
44
  # @param workspace [Workspace] the ecosystem checkout to release from
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Inquirex
4
4
  module Tools
5
- VERSION = "0.9.5"
5
+ VERSION = "0.10.0"
6
6
  end
7
7
  end
@@ -60,8 +60,12 @@ module Inquirex
60
60
  [status.success?, output]
61
61
  }
62
62
 
63
- # Runs a package's own checks in its own directory.
64
- DEFAULT_RUNNER = ->(dir, argv) { Dir.chdir(dir) { system(*argv) } }
63
+ # Runs a package's own checks in its own directory, with this gem's
64
+ # bundle scrubbed so those checks resolve the package's own Gemfile
65
+ # rather than inheriting ours — see {Inquirex::Tools.unbundled}.
66
+ DEFAULT_RUNNER = lambda { |dir, argv|
67
+ Inquirex::Tools.unbundled { Dir.chdir(dir) { system(*argv) } }
68
+ }
65
69
 
66
70
  # @param workspace [Workspace] the ecosystem checkout to rewrite
67
71
  # @param out [IO] where report output goes
@@ -4,6 +4,7 @@ require_relative "tools/version"
4
4
  require_relative "tools/workspace"
5
5
  require_relative "tools/version_checker"
6
6
  require_relative "tools/version_bumper"
7
+ require_relative "tools/pin_updater"
7
8
  require_relative "tools/publisher"
8
9
  require_relative "tools/releaser"
9
10
  require_relative "tools/changelogs"
@@ -33,5 +34,34 @@ module Inquirex
33
34
  # Inquirex::Tools::VersionChecker.new.preflight # => true when every package is ready
34
35
  module Tools
35
36
  class Error < StandardError; end
37
+
38
+ # Runs a block with Bundler's environment removed.
39
+ #
40
+ # Every orchestrator here shells into a *different* package and runs
41
+ # `bundle exec` there. `bundle exec inquirex versions-bump` exports
42
+ # BUNDLE_GEMFILE, RUBYOPT and friends, and a child process inherits them —
43
+ # so without this, each sibling resolves against **this gem's** Gemfile
44
+ # instead of its own.
45
+ #
46
+ # That failure is loud but misleading. `versions-bump` reported five of six
47
+ # packages failing `just check-all`: `inquirex` and `inquirex-llm` raised
48
+ # `NoMethodError: undefined method 'skip' for module SimpleCov`, because
49
+ # this gem pins simplecov ~> 0.22 while theirs resolve 1.0.3 where
50
+ # `SimpleCov.skip` exists; `inquirex-tty` raised `LoadError: cannot load
51
+ # such file -- inquirex`, because this gem deliberately does not depend on
52
+ # the gems it manages. Every one of those reads as a defect in the package
53
+ # being checked. None of them was.
54
+ #
55
+ # Bundler is not declared as a dependency — it is present whenever this
56
+ # runs under `bundle exec`, and when it is absent there is no environment
57
+ # to scrub, so the block simply runs.
58
+ #
59
+ # @yield with a clean environment
60
+ # @return [Object] whatever the block returns
61
+ def self.unbundled(&)
62
+ return yield unless defined?(::Bundler) && ::Bundler.respond_to?(:with_unbundled_env)
63
+
64
+ ::Bundler.with_unbundled_env(&)
65
+ end
36
66
  end
37
67
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inquirex-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
@@ -50,7 +50,6 @@ executables:
50
50
  extensions: []
51
51
  extra_rdoc_files: []
52
52
  files:
53
- - ".DS_Store"
54
53
  - ".env.encrypted"
55
54
  - ".env.example"
56
55
  - ".envrc"
@@ -64,7 +63,8 @@ files:
64
63
  - README.md
65
64
  - Rakefile
66
65
  - docs/badges/coverage_badge.svg
67
- - docs/monorepo-migration.md
66
+ - docs/plans/001-⚪️-many-repo-setup/spec.md
67
+ - docs/plans/002-⚪️-monorepo-migration/spec.md
68
68
  - exe/inquirex
69
69
  - exe/inquirexor
70
70
  - justfile
@@ -77,6 +77,7 @@ files:
77
77
  - lib/inquirex/tools/commands/version.rb
78
78
  - lib/inquirex/tools/commands/versions_bump.rb
79
79
  - lib/inquirex/tools/commands/versions_check.rb
80
+ - lib/inquirex/tools/pin_updater.rb
80
81
  - lib/inquirex/tools/publisher.rb
81
82
  - lib/inquirex/tools/releaser.rb
82
83
  - lib/inquirex/tools/version.rb
@@ -106,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
107
  - !ruby/object:Gem::Version
107
108
  version: '0'
108
109
  requirements: []
109
- rubygems_version: 4.0.17
110
+ rubygems_version: 4.0.18
110
111
  specification_version: 4
111
112
  summary: Release tooling for the Inquirex package family
112
113
  test_files: []
data/.DS_Store DELETED
Binary file