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 =

Packages that must always carry the same version. They share one serialization format — the gem defines the DSL and emits step JSON, inquirex-js renders it, inquirex-webui prints it back to DSL, inquirex-llm extends the vocabulary, 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.

inquirex-tty is deliberately absent: a developer tool outside the wire contract, versioned on its own cadence.

{
  "inquirex" => :gem,
  "inquirex-llm" => :gem,
  "inquirex-tools" => :gem,
  "inquirex-js" => :npm,
  "inquirex-webui" => :npm
}.freeze
EXCLUDED =

Excluded from lockstep, documented so the omission reads as deliberate.

%w[inquirex-tty].freeze
CHANGELOG_ONLY =

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

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

Directories whose presence identifies the ecosystem root.

%w[inquirex inquirex-js].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



112
113
114
115
116
117
118
119
120
121
# File 'lib/inquirex/tools/workspace.rb', line 112

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) ⇒ 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

Raises:



96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/inquirex/tools/workspace.rb', line 96

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

  src = File.read(file)
  updated =
    if LOCKSTEP[name] == :gem
      src.sub(GEM_VERSION_RE, %(VERSION = "#{version}"))
    else
      src.sub(/"version":\s*"[^"]*"/, %("version": "#{version}"))
    end
  File.write(file, updated)
end