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 +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/legion/extensions/exec/helpers/repo_materializer.rb +86 -0
- data/lib/legion/extensions/exec/helpers/worktree.rb +28 -9
- data/lib/legion/extensions/exec/runners/git.rb +22 -0
- data/lib/legion/extensions/exec/version.rb +1 -1
- data/lib/legion/extensions/exec.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 64b870afb59925733e75c02a7266776140f4feba23ac608703d31553409c36ed
|
|
4
|
+
data.tar.gz: 4b9c602faa2f3dd110012d18312f42880940a73d1cb518acac75b0100700eefb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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 =
|
|
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
|
|
@@ -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.
|
|
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
|