Class: Inquirex::Tools::Workspace

Inherits:
Object
  • Object
show all
Defined in:
lib/inquirex/tools/workspace.rb

Overview

Locates the Inquirex ecosystem checkout and reads each package's version.

As gem executables these tools run from an rbenv shim, so __dir__ says nothing about where the repositories live. The root is discovered by walking up from the working directory looking for the sibling checkouts, or taken from INQUIREX_ROOT.

Constant Summary collapse

LOCKSTEP =

Every package in the family, all carrying the same version. They share one serialization format — the gem defines the DSL and emits step JSON, inquirex-widget (npm; formerly @kigster/inquirex-js) renders it, inquirex-webui prints it back to DSL, inquirex-llm extends the vocabulary, inquirex-tty walks a flow in the terminal, and inquirex-tools is the tooling that ships alongside them. A verb present in one and absent in another does not fail loudly; it drops data.

Nothing is excluded. One version number across the whole family means "which combination has this bug?" is a lookup rather than an investigation.

{
  "inquirex"        => :gem,
  "inquirex-llm"    => :gem,
  "inquirex-tools"  => :gem,
  "inquirex-tty"    => :gem,
  "inquirex-widget" => :npm,
  "inquirex-webui"  => :npm
}.freeze
CHANGELOG_ONLY =

Repositories that carry a generated CHANGELOG.md but are not versioned in lockstep.

%w[qualified-at].freeze
GEM_VERSION_RE =
/VERSION\s*=\s*"([^"]+)"/
MARKERS =

Directories whose presence identifies the ecosystem root.

%w[inquirex inquirex-widget].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root: nil) ⇒ Workspace

Returns a new instance of Workspace.

Parameters:

  • root (String, nil) (defaults to: nil)

    explicit root; discovered when nil

Raises:

  • (Error)

    when no root can be found



48
49
50
51
# File 'lib/inquirex/tools/workspace.rb', line 48

def initialize(root: nil)
  @root = root || ENV["INQUIREX_ROOT"] || self.class.discover
  raise Error, "cannot find the Inquirex workspace — run inside it or set INQUIREX_ROOT" if @root.nil?
end

Instance Attribute Details

#rootString (readonly)

Returns absolute path to the ecosystem root.

Returns:

  • (String)

    absolute path to the ecosystem root



44
45
46
# File 'lib/inquirex/tools/workspace.rb', line 44

def root
  @root
end

Class Method Details

.discoverString?

Walks up from the working directory looking for a directory that holds the marker checkouts.

Returns:

  • (String, nil)


57
58
59
60
61
62
63
64
65
66
67
# File 'lib/inquirex/tools/workspace.rb', line 57

def self.discover
  dir = Dir.pwd
  loop do
    return dir if MARKERS.all? { |m| Dir.exist?(File.join(dir, m)) }

    parent = File.dirname(dir)
    return nil if parent == dir

    dir = parent
  end
end

Instance Method Details

#git_state(name) ⇒ Hash

Returns :clean, :branch, :tags — all nil when not a checkout.

Parameters:

  • name (String)

    repository directory name

Returns:

  • (Hash)

    :clean, :branch, :tags — all nil when not a checkout



115
116
117
118
119
120
121
122
123
124
# File 'lib/inquirex/tools/workspace.rb', line 115

def git_state(name)
  dir = File.join(@root, name)
  return { clean: nil, branch: nil, tags: [] } unless File.exist?(File.join(dir, ".git"))

  {
    clean:  `git -C #{dir} status --porcelain 2>/dev/null`.strip.empty?,
    branch: `git -C #{dir} rev-parse --abbrev-ref HEAD 2>/dev/null`.strip,
    tags:   `git -C #{dir} tag --points-at HEAD 2>/dev/null`.split("\n").map(&:strip).reject(&:empty?)
  }
end

#version_file(name) ⇒ String?

Returns path to the file carrying its version.

Parameters:

  • name (String)

    package name

Returns:

  • (String, nil)

    path to the file carrying its version



71
72
73
74
75
76
# File 'lib/inquirex/tools/workspace.rb', line 71

def version_file(name)
  case LOCKSTEP[name]
  when :npm then existing(File.join(name, "package.json"))
  when :gem then gem_version_file(name)
  end
end

#version_of(name) ⇒ String?

Returns the current version, or nil when unreadable.

Parameters:

  • name (String)

    package name

Returns:

  • (String, nil)

    the current version, or nil when unreadable



80
81
82
83
84
85
# File 'lib/inquirex/tools/workspace.rb', line 80

def version_of(name)
  file = version_file(name)
  return nil unless file && File.exist?(file)

  LOCKSTEP[name] == :gem ? File.read(file)[GEM_VERSION_RE, 1] : JSON.parse(File.read(file))["version"]
end

#versionsHash{String => String, nil}

Returns every lockstep package's version.

Returns:

  • (Hash{String => String, nil})

    every lockstep package's version



88
# File 'lib/inquirex/tools/workspace.rb', line 88

def versions = LOCKSTEP.keys.to_h { |n| [n, version_of(n)] }

#write_version(name, version, kind) ⇒ void

This method returns an undefined value.

Rewrites one package's version. Edits the single version line rather than re-emitting the file, so formatting and comments survive.

Parameters:

  • name (String)

    package name

  • version (String)

    new version

  • kind (Symbol)

    :gem or :npm — which version line to rewrite. Passed by the caller rather than looked up here so a caller that already knows the kind does not pay for a second lookup.

Raises:



99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/inquirex/tools/workspace.rb', line 99

def write_version(name, version, kind)
  file = version_file(name)
  raise Error, "no version file for #{name}" unless file

  src = File.read(file)
  updated = case kind
            when :gem
              src.sub(GEM_VERSION_RE, %(VERSION = "#{version}"))
            when :npm
              src.sub(/"version":\s*"[^"]*"/, %("version": "#{version}"))
            end
  File.write(file, updated)
end