lex-exec 0.1.8 → 0.1.9

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: a0e24d56db8e7444e6ea2af9d29b91a509d95aabac9f057dcc6dd4ec8c2c886e
4
- data.tar.gz: 15efcf34aae86cd254501fd976511d77de01acf5202b8bd0f87156f0cd03d46d
3
+ metadata.gz: 64b870afb59925733e75c02a7266776140f4feba23ac608703d31553409c36ed
4
+ data.tar.gz: 4b9c602faa2f3dd110012d18312f42880940a73d1cb518acac75b0100700eefb
5
5
  SHA512:
6
- metadata.gz: 7381db9bc39961927fff09796a591b3f18e104ade241f3a174bb9a3047a2737af8332cdd3b98c407bc42a5eb29eb6a5c47b53e5e76a9b486f41ed9050480c182
7
- data.tar.gz: 7a73b0059b112190168c19e60b1aff03015fda95ad09de69c88f6f6972f87cefb2d5949b329b7d1fdf5a2a0b2af2167f02625d6f6e331871579881f5173d8369
6
+ metadata.gz: 60981006cc6d04c67e9f33f1c02904828065d6e5e0eec8b43265ba7db7137a8254e6810e1fca2615021a82d1884f3614b11af3d3c1a1ddb2b26b9180d172e48b
7
+ data.tar.gz: 7ea77c999a1596b1993e9501fd24d4800ff4eea3877c948236b309fb9225c0c1190e341856cfe1801e3a7f11cecede97576ec61dd178de8cdc7f2282c2b9f025
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.9] - 2026-04-13
4
+
5
+ ### Added
6
+ - `Git.clone` — shallow/branched clone with depth read from `Legion::Settings.dig(:fleet, :git, :depth)`
7
+ - `Git.fetch` — `git fetch --all --prune` or named remote
8
+ - `Git.checkout` — checkout ref or create new branch with `-b`
9
+ - `Worktree.create/remove/list` — `repo_path:` parameter passes `chdir:` to `Open3.capture3` for shared-worker correctness
10
+ - `Helpers::RepoMaterializer` — strategy-based repo materialization (`materialize/release`); Phase 1 implements `:clone` strategy with `credential_provider` injection
11
+ - `worktree_path` now checks `Legion::Settings.dig(:fleet, :workspace, :worktree_base)` before falling back to `:worktree, :base_dir`
12
+
3
13
  ## [0.1.8] - 2026-04-09
4
14
 
5
15
  ### Changed
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Exec
8
+ module Helpers
9
+ module RepoMaterializer
10
+ class << self
11
+ def materialize(work_item:, credential_provider: nil)
12
+ strategy = resolve_strategy
13
+ case strategy
14
+ when :clone
15
+ materialize_via_clone(work_item: work_item, credential_provider: credential_provider)
16
+ else
17
+ { success: false, error: "Unknown strategy: #{strategy}" }
18
+ end
19
+ end
20
+
21
+ def release(work_item:)
22
+ repo_path = build_repo_path(work_item)
23
+ Helpers::Worktree.remove(task_id: work_item[:task_id], repo_path: repo_path)
24
+ end
25
+
26
+ private
27
+
28
+ def resolve_strategy
29
+ raw = Legion::Settings.dig(:fleet, :materialization, :strategy) if defined?(Legion::Settings)
30
+ (raw || :clone).to_sym
31
+ end
32
+
33
+ def materialize_via_clone(work_item:, credential_provider:)
34
+ url = apply_credentials(work_item[:repo_url], credential_provider)
35
+ depth = Legion::Settings.dig(:fleet, :git, :depth) if defined?(Legion::Settings)
36
+ repo_path = build_repo_path(work_item)
37
+
38
+ begin
39
+ ::FileUtils.mkdir_p(::File.dirname(repo_path))
40
+ rescue StandardError => e
41
+ return { success: false, reason: :mkdir_failed, error: e.message }
42
+ end
43
+
44
+ clone_result = Runners::Git.clone(url: url, path: repo_path, depth: depth)
45
+ unless clone_result[:success]
46
+ return {
47
+ success: false,
48
+ error: clone_result[:stderr] || clone_result[:reason] || clone_result[:error],
49
+ reason: clone_result[:reason],
50
+ stderr: clone_result[:stderr],
51
+ clone_result: clone_result
52
+ }
53
+ end
54
+
55
+ worktree_result = Helpers::Worktree.create(
56
+ task_id: work_item[:task_id],
57
+ branch: work_item[:branch],
58
+ base_ref: work_item[:base_ref] || 'HEAD',
59
+ repo_path: repo_path
60
+ )
61
+ return { success: false, error: worktree_result[:message] } unless worktree_result[:success]
62
+
63
+ { success: true, workspace_path: worktree_result[:path], branch: worktree_result[:branch], repo_path: repo_path }
64
+ end
65
+
66
+ def apply_credentials(url, credential_provider)
67
+ return url unless credential_provider
68
+
69
+ result = credential_provider.call(url)
70
+ raise ArgumentError, 'credential_provider must return a non-empty String URL' unless result.is_a?(String) && !result.empty?
71
+
72
+ result
73
+ end
74
+
75
+ def build_repo_path(work_item)
76
+ base = (Legion::Settings.dig(:fleet, :workspace, :repo_base) if defined?(Legion::Settings))
77
+ base ||= '/tmp/legion-repos'
78
+ repo_name = ::File.basename(work_item[:repo_url], '.git')
79
+ ::File.join(base, work_item[:task_id].to_s, repo_name)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -9,40 +9,59 @@ module Legion
9
9
  module Helpers
10
10
  module Worktree
11
11
  class << self
12
- def create(task_id:, branch: nil, base_ref: 'HEAD')
12
+ def create(task_id:, branch: nil, base_ref: 'HEAD', repo_path: nil)
13
13
  branch ||= "legion/#{task_id}"
14
14
  path = worktree_path(task_id)
15
15
  return { success: false, reason: :already_exists } if Dir.exist?(path)
16
16
 
17
17
  FileUtils.mkdir_p(File.dirname(path))
18
- _stdout, stderr, status = Open3.capture3('git', 'worktree', 'add', path, '-b', branch, base_ref)
18
+ args = ['git', 'worktree', 'add', path, '-b', branch, base_ref]
19
+ opts = repo_path ? { chdir: repo_path } : {}
20
+ _stdout, stderr, status = Open3.capture3(*args, **opts)
19
21
  if status.success?
20
22
  { success: true, path: path, branch: branch }
21
23
  else
22
24
  { success: false, reason: :git_error, message: stderr.strip }
23
25
  end
26
+ rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EACCES => e
27
+ { success: false, reason: :invalid_repo_path, message: e.message }
24
28
  end
25
29
 
26
- def remove(task_id:)
30
+ def remove(task_id:, repo_path: nil)
27
31
  path = worktree_path(task_id)
28
32
  return { success: false, reason: :not_found } unless Dir.exist?(path)
29
33
 
30
- _stdout, stderr, status = Open3.capture3('git', 'worktree', 'remove', path, '--force')
34
+ args = ['git', 'worktree', 'remove', path, '--force']
35
+ opts = repo_path ? { chdir: repo_path } : {}
36
+ _stdout, stderr, status = Open3.capture3(*args, **opts)
31
37
  if status.success?
32
38
  { success: true }
33
39
  else
34
40
  { success: false, reason: :git_error, message: stderr.strip }
35
41
  end
42
+ rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EACCES => e
43
+ { success: false, reason: :invalid_repo_path, message: e.message }
36
44
  end
37
45
 
38
- def list
39
- stdout, _stderr, _status = Open3.capture3('git', 'worktree', 'list', '--porcelain')
40
- worktrees = parse_worktree_list(stdout)
41
- { success: true, worktrees: worktrees }
46
+ def list(repo_path: nil)
47
+ args = ['git', 'worktree', 'list', '--porcelain']
48
+ opts = repo_path ? { chdir: repo_path } : {}
49
+ stdout, stderr, status = Open3.capture3(*args, **opts)
50
+ if status.success?
51
+ worktrees = parse_worktree_list(stdout)
52
+ { success: true, worktrees: worktrees }
53
+ else
54
+ { success: false, reason: :git_error, message: stderr.strip }
55
+ end
56
+ rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EACCES => e
57
+ { success: false, reason: :invalid_repo_path, message: e.message }
42
58
  end
43
59
 
44
60
  def worktree_path(task_id)
45
- base = Legion::Settings.dig(:worktree, :base_dir) if defined?(Legion::Settings)
61
+ base = if defined?(Legion::Settings)
62
+ Legion::Settings.dig(:fleet, :workspace, :worktree_base) ||
63
+ Legion::Settings.dig(:worktree, :base_dir)
64
+ end
46
65
  File.join(base || File.join(Dir.pwd, '.legion-worktrees'), task_id.to_s)
47
66
  end
48
67
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'shellwords'
4
+
3
5
  module Legion
4
6
  module Extensions
5
7
  module Exec
@@ -41,6 +43,26 @@ module Legion
41
43
  cwd: Dir.pwd
42
44
  )
43
45
  end
46
+
47
+ def clone(url:, path:, depth: nil, branch: nil, **)
48
+ resolved_depth = depth
49
+ resolved_depth ||= Legion::Settings.dig(:fleet, :git, :depth) if defined?(Legion::Settings)
50
+ args = ['git clone']
51
+ args << "--depth #{resolved_depth}" if resolved_depth
52
+ args << "--branch #{Shellwords.shellescape(branch)}" if branch
53
+ args << Shellwords.shellescape(url) << Shellwords.shellescape(path)
54
+ Runners::Shell.execute(command: args.join(' '), cwd: Dir.pwd)
55
+ end
56
+
57
+ def fetch(path:, remote: nil, **)
58
+ cmd = remote ? "git fetch #{Shellwords.shellescape(remote)} --prune" : 'git fetch --all --prune'
59
+ Runners::Shell.execute(command: cmd, cwd: path)
60
+ end
61
+
62
+ def checkout(path:, ref:, create: false, **)
63
+ flag = create ? ' -b' : ''
64
+ Runners::Shell.execute(command: "git checkout#{flag} #{Shellwords.shellescape(ref)}", cwd: path)
65
+ end
44
66
  end
45
67
  end
46
68
  end
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Exec
6
- VERSION = '0.1.8'
6
+ VERSION = '0.1.9'
7
7
  end
8
8
  end
9
9
  end
@@ -8,6 +8,7 @@ require_relative 'exec/helpers/result_parser'
8
8
  require_relative 'exec/helpers/audit_log'
9
9
  require_relative 'exec/helpers/checkpoint'
10
10
  require_relative 'exec/helpers/worktree'
11
+ require_relative 'exec/helpers/repo_materializer'
11
12
  require_relative 'exec/runners/shell'
12
13
  require_relative 'exec/runners/git'
13
14
  require_relative 'exec/runners/bundler'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-exec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -131,6 +131,7 @@ files:
131
131
  - lib/legion/extensions/exec/helpers/audit_log.rb
132
132
  - lib/legion/extensions/exec/helpers/checkpoint.rb
133
133
  - lib/legion/extensions/exec/helpers/constants.rb
134
+ - lib/legion/extensions/exec/helpers/repo_materializer.rb
134
135
  - lib/legion/extensions/exec/helpers/result_parser.rb
135
136
  - lib/legion/extensions/exec/helpers/sandbox.rb
136
137
  - lib/legion/extensions/exec/helpers/worktree.rb