lex-exec 0.1.5 → 0.1.7

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: 9a079df771b24d55acbeee57d43f988f69d03959dd2b50781d1d968517e0efbf
4
- data.tar.gz: 8044b380d16e17f0ed5931c40001fcafe605399e1a5bfe85c0783e7570f15730
3
+ metadata.gz: 7d8694405797d859fb276bf9ffb3fdd2b9ae66ed3f3c1cc35a1b6fb5b43541cd
4
+ data.tar.gz: 2b46aac5a8817673d6ac641d04b4b880c2531f5c79a1b076ef77b83053d1a008
5
5
  SHA512:
6
- metadata.gz: 9a3d5a024a74c3500fd417c9e601b81881bff68ac5facb79afd3f012893145c2fd8e24c744ee732c3dff9696fccff770e46c3a32bf9b780379640fabcbf17a09
7
- data.tar.gz: 5ac1a85eaee2d7376e7060126e8a660ebfe63b8753b39ccee03c310993befda664b909408cb49ab86bbc63f1c6a1d2cf9abe8fcc27b92c24cbbcd286df03ef0d
6
+ metadata.gz: 9cd2bd04a54f2c430d9d51cf11b6d356fdcce17664fcddd83d1e2d5a4a63e1f89c2b25aa89855f406ab4b3f69c4b3611d98c47b1ffbf8d6eacce214efeb7b2a5
7
+ data.tar.gz: 4b2fba88d6554fbdf73ab0206f6ada2cf730094d366ee96bfcef61c96240dd0d9e1c5deeda3c4729b99d42a28f0cc14126673a5c001ccda37d1874254137dce6
@@ -10,8 +10,8 @@ jobs:
10
10
  ci:
11
11
  uses: LegionIO/.github/.github/workflows/ci.yml@main
12
12
 
13
- lint:
14
- uses: LegionIO/.github/.github/workflows/lint-patterns.yml@main
13
+ excluded-files:
14
+ uses: LegionIO/.github/.github/workflows/excluded-files.yml@main
15
15
 
16
16
  security:
17
17
  uses: LegionIO/.github/.github/workflows/security-scan.yml@main
@@ -27,7 +27,7 @@ jobs:
27
27
  uses: LegionIO/.github/.github/workflows/stale.yml@main
28
28
 
29
29
  release:
30
- needs: [ci, lint]
30
+ needs: [ci, excluded-files]
31
31
  if: github.event_name == 'push' && github.ref == 'refs/heads/main'
32
32
  uses: LegionIO/.github/.github/workflows/release.yml@main
33
33
  secrets:
data/.rubocop.yml CHANGED
@@ -1,56 +1,2 @@
1
- AllCops:
2
- TargetRubyVersion: 3.4
3
- NewCops: enable
4
- SuggestExtensions: false
5
-
6
- Layout/LineLength:
7
- Max: 160
8
-
9
- Layout/SpaceAroundEqualsInParameterDefault:
10
- EnforcedStyle: space
11
-
12
- Layout/HashAlignment:
13
- EnforcedHashRocketStyle: table
14
- EnforcedColonStyle: table
15
-
16
- Metrics/MethodLength:
17
- Max: 50
18
-
19
- Metrics/ClassLength:
20
- Max: 1500
21
-
22
- Metrics/ModuleLength:
23
- Max: 1500
24
-
25
- Metrics/BlockLength:
26
- Max: 40
27
- Exclude:
28
- - 'spec/**/*'
29
-
30
- Metrics/AbcSize:
31
- Max: 60
32
-
33
- Metrics/CyclomaticComplexity:
34
- Max: 15
35
-
36
- Metrics/PerceivedComplexity:
37
- Max: 17
38
-
39
- Style/Documentation:
40
- Enabled: false
41
-
42
- Style/SymbolArray:
43
- Enabled: true
44
-
45
- Style/FrozenStringLiteralComment:
46
- Enabled: true
47
- EnforcedStyle: always
48
-
49
- Naming/FileName:
50
- Enabled: false
51
-
52
- Naming/PredicateMethod:
53
- Enabled: false
54
-
55
- Naming/PredicatePrefix:
56
- Enabled: false
1
+ inherit_gem:
2
+ rubocop-legion: config/lex.yml
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.7] - 2026-04-09
4
+
5
+ ### Added
6
+ - Python venv integration: route bare `python3`/`pip3` commands to Legion-managed venv (`~/.legionio/python`)
7
+ - `python3`, `python`, `pip3`, `pip` added to sandbox allowlist
8
+ - Runtime venv resolution via `Constants.venv_python`/`.venv_pip`/`.venv_exists?` (no longer frozen at load time)
9
+
10
+ ## [0.1.6] - 2026-03-30
11
+
12
+ ### Changed
13
+ - update to rubocop-legion 0.1.7, resolve all offenses
14
+
3
15
  ## [0.1.5] - 2026-03-29
4
16
 
5
17
  ### Changed
data/Gemfile CHANGED
@@ -8,6 +8,7 @@ group :test do
8
8
  gem 'rspec', '~> 3.13'
9
9
  gem 'rspec_junit_formatter'
10
10
  gem 'rubocop', '~> 1.75'
11
+ gem 'rubocop-legion', '~> 0.1'
11
12
  gem 'rubocop-rspec'
12
13
  gem 'simplecov'
13
14
  end
@@ -5,14 +5,24 @@ module Legion
5
5
  module Exec
6
6
  module Helpers
7
7
  module Constants
8
- DEFAULT_TIMEOUT = 120_000 # 120 seconds in ms
9
- MAX_TIMEOUT = 600_000 # 10 minutes in ms
10
- MAX_OUTPUT_BYTES = 1_048_576 # 1 MB
8
+ DEFAULT_TIMEOUT = 120_000 # 120 seconds in ms
9
+ MAX_TIMEOUT = 600_000 # 10 minutes in ms
10
+ MAX_OUTPUT_BYTES = 1_048_576 # 1 MB
11
11
 
12
- ALLOWED_COMMANDS = %w[
12
+ LEGION_PYTHON_VENV = File.expand_path('~/.legionio/python').freeze
13
+
14
+ BASE_ALLOWED_COMMANDS = %w[
13
15
  bundle git gh ruby rspec rubocop ls cat mkdir cp mv rm touch echo wc head tail
16
+ python3 pip3
17
+ ].freeze
18
+
19
+ VENV_ALLOWED_COMMANDS = [
20
+ "#{LEGION_PYTHON_VENV}/bin/python3",
21
+ "#{LEGION_PYTHON_VENV}/bin/pip3"
14
22
  ].freeze
15
23
 
24
+ ALLOWED_COMMANDS = (BASE_ALLOWED_COMMANDS + VENV_ALLOWED_COMMANDS).freeze
25
+
16
26
  BLOCKED_PATTERNS = [
17
27
  %r{rm\s+-rf\s+/},
18
28
  /rm\s+-rf\s+~/,
@@ -25,6 +35,20 @@ module Legion
25
35
  ].freeze
26
36
 
27
37
  AUDIT_FIELDS = %i[command cwd exit_code duration_ms executed_at truncated].freeze
38
+
39
+ module_function
40
+
41
+ def venv_python
42
+ "#{LEGION_PYTHON_VENV}/bin/python3"
43
+ end
44
+
45
+ def venv_pip
46
+ "#{LEGION_PYTHON_VENV}/bin/pip3"
47
+ end
48
+
49
+ def venv_exists?
50
+ File.exist?("#{LEGION_PYTHON_VENV}/pyvenv.cfg")
51
+ end
28
52
  end
29
53
  end
30
54
  end
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Exec
6
6
  module Runners
7
- module Bundler
7
+ module Bundler # rubocop:disable Legion/Extension/RunnerIncludeHelpers
8
8
  module_function
9
9
 
10
10
  def install(path:, **)
@@ -17,7 +17,7 @@ module Legion
17
17
  cwd: path,
18
18
  timeout: 300_000
19
19
  )
20
- return result unless result[:stdout] || result[:stderr]
20
+ return result unless result[:stdout] || result[:stderr] # rubocop:disable Legion/Extension/RunnerReturnHash
21
21
 
22
22
  raw = result[:stdout] || result[:stderr] || ''
23
23
  parsed = Helpers::ResultParser.parse_rspec(raw)
@@ -27,7 +27,7 @@ module Legion
27
27
  def exec_rubocop(path:, autocorrect: false, **)
28
28
  cmd = autocorrect ? 'bundle exec rubocop -A' : 'bundle exec rubocop'
29
29
  result = Runners::Shell.execute(command: cmd, cwd: path, timeout: 120_000)
30
- return result unless result[:stdout]
30
+ return result unless result[:stdout] # rubocop:disable Legion/Extension/RunnerReturnHash
31
31
 
32
32
  parsed = Helpers::ResultParser.parse_rubocop(result[:stdout] || '')
33
33
  result.merge(parsed: parsed)
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Exec
6
6
  module Runners
7
- module Git
7
+ module Git # rubocop:disable Legion/Extension/RunnerIncludeHelpers
8
8
  module_function
9
9
 
10
10
  def init(path:, **)
@@ -28,7 +28,7 @@ module Legion
28
28
 
29
29
  def status(path:, **)
30
30
  result = Runners::Shell.execute(command: 'git status --porcelain', cwd: path)
31
- return result unless result[:success]
31
+ return result unless result[:success] # rubocop:disable Legion/Extension/RunnerReturnHash
32
32
 
33
33
  parsed = Helpers::ResultParser.parse_git_status(result[:stdout] || '')
34
34
  result.merge(parsed: parsed)
@@ -14,6 +14,11 @@ module Legion
14
14
  check = default_sandbox.allowed?(command)
15
15
  return { success: false, error: :blocked, reason: check[:reason] } unless check[:allowed]
16
16
 
17
+ # Rewrite bare `python3` / `python` / `pip3` / `pip` invocations to use
18
+ # the Legion-managed venv interpreter so scripts always run inside the
19
+ # correct environment with pre-installed packages (python-pptx, etc.).
20
+ command = rewrite_python_command(command)
21
+
17
22
  start_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
18
23
  timeout_secs = [timeout, Helpers::Constants::MAX_TIMEOUT].min / 1000.0
19
24
 
@@ -21,7 +26,7 @@ module Legion
21
26
  stdout, stderr, status = Timeout.timeout(timeout_secs) do
22
27
  Open3.capture3(env, command, chdir: cwd)
23
28
  end
24
- rescue Timeout::Error
29
+ rescue Timeout::Error => _e
25
30
  return { success: false, error: :timeout, timeout_ms: timeout }
26
31
  rescue ArgumentError => e
27
32
  return { success: false, error: e.message }
@@ -41,7 +46,7 @@ module Legion
41
46
  audit_log.record(command: command, cwd: cwd, exit_code: exit_code,
42
47
  duration_ms: duration_ms, truncated: truncated)
43
48
 
44
- Legion::Logging.debug("[lex-exec] exit=#{exit_code} duration=#{duration_ms}ms cmd=#{command}")
49
+ Legion::Logging.debug("[lex-exec] exit=#{exit_code} duration=#{duration_ms}ms cmd=#{command}") # rubocop:disable Legion/HelperMigration/DirectLogging
45
50
 
46
51
  {
47
52
  success: exit_code.zero?,
@@ -59,6 +64,20 @@ module Legion
59
64
 
60
65
  private
61
66
 
67
+ # Replace bare `python3`, `python`, `pip3`, `pip` at the start of a command
68
+ # with the absolute venv paths — but only when the venv actually exists.
69
+ # Full absolute paths that already point into the venv are left unchanged.
70
+ def rewrite_python_command(command)
71
+ return command unless Helpers::Constants.venv_exists? # rubocop:disable Legion/Extension/RunnerReturnHash
72
+
73
+ python = Helpers::Constants.venv_python
74
+ pip = Helpers::Constants.venv_pip
75
+
76
+ command
77
+ .sub(/\Apython3(\s|\z)/, "#{python}\\1")
78
+ .sub(/\Apip3(\s|\z)/, "#{pip}\\1")
79
+ end
80
+
62
81
  def default_sandbox
63
82
  @default_sandbox ||= Helpers::Sandbox.new
64
83
  end
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Exec
6
- VERSION = '0.1.5'
6
+ VERSION = '0.1.7'
7
7
  end
8
8
  end
9
9
  end
@@ -16,7 +16,7 @@ require_relative 'exec/client'
16
16
  module Legion
17
17
  module Extensions
18
18
  module Exec
19
- extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
19
+ extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core, false
20
20
  end
21
21
  end
22
22
  end
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.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity