rascal 0.3.9 → 0.4.0

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: ffca96c320440170db495b607004508d9f60aac4d6f387e803084aff3480344f
4
- data.tar.gz: ca02687246280f362e0035a697a9061eec1edf46de2ebccdc67fc8999f4902d5
3
+ metadata.gz: d916ce222f77cd89927ad3ac8ceb56da9d34d87f2f808938b6c796ce92ab3d8b
4
+ data.tar.gz: 070e9c46b5f33074260638ba1b2e5bb19844c515461788ae904d4b1e72690a7e
5
5
  SHA512:
6
- metadata.gz: 88d02bffc72f022deea6798ff1ff34561874367b4d30c796162eae4cbad6610018682ad9b7f1992e319d823a6745e5125c723aa0c97938b64ecf79695ca16652
7
- data.tar.gz: cf2c78e81c688d8529828b06f7544bc03887005fbd456a09b23c64ccde685a5eecfa6859427a748c8abfca6e3529a1fc351679cdaa4b9aefa261e5a5e6ffc3dc
6
+ metadata.gz: 7cd38cd621c764d5d8548e4093abc1be639713efc2294b7e80b7fec5d7cdb37648569258c6b8725b604ddcbc756b5916d26b2e6577f8e33a03b100d522b5d957
7
+ data.tar.gz: '00299e9e4c118a754e0b5227c04d2400d7424a551cddaebc24a1fb85c5649e6c7913835e26754f73d663a4ab50b345ed322198f6f4552c3e3f21d318196cf511'
@@ -17,6 +17,8 @@ jobs:
17
17
  gemfile: Gemfile
18
18
  - ruby: 3.4.1
19
19
  gemfile: Gemfile
20
+ - ruby: 4.0.6
21
+ gemfile: Gemfile
20
22
  env:
21
23
  BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
22
24
  steps:
data/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ All notable changes to this project will be documented here.
4
4
 
5
5
  Rascal follows semantic versioning. This has little consequence pre 1.0, so expect breaking changes.
6
6
 
7
+ ## 0.4.0 (2026-07-30)
8
+
9
+ - Add `rascal run ENVIRONMENT -- COMMAND`, which runs a single command in an
10
+ environment without a TTY and exits with the command's status. Useful for
11
+ scripts, git hooks and AI agents, which cannot drive `rascal shell`.
12
+ - Add `rascal environments`, which lists available environment names on stdout.
13
+
14
+
7
15
  ## 0.3.9 (2026-06-19)
8
16
 
9
17
  - Support variables within service definitions.
data/Gemfile.common CHANGED
@@ -9,6 +9,7 @@ gem 'rake', '~> 13.0'
9
9
  gem 'rspec'
10
10
  gem 'cucumber'
11
11
  gem 'aruba'
12
+ gem 'logger'
12
13
 
13
14
  gem 'guard-rspec'
14
15
  gem 'guard-cucumber'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rascal (0.3.9)
4
+ rascal (0.4.0)
5
5
  thor (>= 1.0.0)
6
6
 
7
7
  GEM
@@ -18,7 +18,7 @@ GEM
18
18
  builder (3.3.0)
19
19
  childprocess (3.0.0)
20
20
  coderay (1.1.3)
21
- contracts (0.17)
21
+ contracts (0.17.3)
22
22
  cucumber (9.2.1)
23
23
  builder (~> 3.2)
24
24
  cucumber-ci-environment (> 9, < 11)
@@ -67,6 +67,7 @@ GEM
67
67
  listen (3.8.0)
68
68
  rb-fsevent (~> 0.10, >= 0.10.3)
69
69
  rb-inotify (~> 0.9, >= 0.9.10)
70
+ logger (1.7.0)
70
71
  lumberjack (1.2.8)
71
72
  method_source (1.0.0)
72
73
  mini_mime (1.1.5)
@@ -108,6 +109,7 @@ DEPENDENCIES
108
109
  cucumber
109
110
  guard-cucumber
110
111
  guard-rspec
112
+ logger
111
113
  rake (~> 13.0)
112
114
  rascal!
113
115
  rspec
data/README.md CHANGED
@@ -101,6 +101,37 @@ Start a docker container (plus required services) and open an interactive shell.
101
101
  Currently requires a "bash" to exist.
102
102
 
103
103
 
104
+ #### rascal run <job> -- <command>
105
+
106
+ Run a single command in a docker container (plus required services), then exit
107
+ with the command's exit status.
108
+
109
+ ```sh
110
+ rascal run rspec -- bundle exec rspec spec/models/user_spec.rb
111
+ ```
112
+
113
+ Unlike `rascal shell`, this allocates no TTY and needs no interaction, so it can
114
+ be used from scripts, git hooks, editors and AI agents. Everything after `--` is
115
+ passed through as a list of arguments, so arguments may contain spaces (`-n 'a
116
+ test name'`). To use shell syntax such as `&&` or `cd`, wrap it explicitly:
117
+
118
+ ```sh
119
+ rascal run rspec -- bash -c 'cd subproject && bundle exec rspec'
120
+ ```
121
+
122
+ `before_shell` and `after_shell` still run around the command, but do not affect
123
+ the exit status. Note that a failing `before_shell` command (e.g. `bundle check`
124
+ in an environment that was never bundled) does not abort the run.
125
+
126
+ Currently requires a "bash" to exist.
127
+
128
+
129
+ #### rascal environments
130
+
131
+ Print the names of all available (non-hidden) environments, one per line, so that
132
+ scripts can discover them without parsing an error message.
133
+
134
+
104
135
  #### rascal clean <job> | --all [--volumes]
105
136
 
106
137
  Stop and remove all created containers, services, networks for either the given or all jobs.
@@ -0,0 +1,15 @@
1
+ require 'rascal'
2
+
3
+ module Rascal
4
+ module CLI
5
+ class Environments < Base
6
+ include IOHelper
7
+
8
+ def run
9
+ environment_definition.available_environment_names.each do |name|
10
+ say name
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -36,6 +36,24 @@ module Rascal
36
36
  end
37
37
  end
38
38
 
39
+ desc 'environments', 'List the names of all available environments'
40
+ def environments
41
+ handle_error do
42
+ Environments.new(self, options).run
43
+ end
44
+ end
45
+
46
+ # Thor::Actions already defines #run, hence the mapping.
47
+ map 'run' => '_run'
48
+ stop_on_unknown_option! :_run
49
+ desc 'run ENVIRONMENT COMMAND', 'Run a command in the given environment and exit with its status'
50
+ def _run(environment_name = nil, *command)
51
+ handle_error do
52
+ command = command.drop(1) if command.first == '--'
53
+ Run.new(self, options, environment_name, command).run
54
+ end
55
+ end
56
+
39
57
  desc 'clean ENVIRONMENT', 'Stop and remove docker containers for the given environment'
40
58
  method_option :volumes, type: :boolean, default: false, desc: 'Remove (cache) volumes'
41
59
  method_option :all, type: :boolean, default: false, desc: 'Clean all environments'
@@ -0,0 +1,29 @@
1
+ require 'rascal'
2
+
3
+ module Rascal
4
+ module CLI
5
+ class Run < Base
6
+ def initialize(thor, options, environment_name, command)
7
+ @environment_name = environment_name
8
+ @command = command
9
+ super(thor, options)
10
+ end
11
+
12
+ def run
13
+ if (environment = find_environment(@environment_name))
14
+ fail_with_error('Missing command. Example: rascal run job -- bundle exec rake') if @command.empty?
15
+ status = environment.run_command(*@command)
16
+ exit(exit_code_for(status))
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ # A command killed by a signal has no exit status. Report it the way a
23
+ # shell would, so callers still see a non-zero status.
24
+ def exit_code_for(status)
25
+ status.exitstatus || (status.termsig ? 128 + status.termsig : 1)
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/rascal/cli.rb CHANGED
@@ -2,10 +2,12 @@ require 'thor'
2
2
 
3
3
  module Rascal
4
4
  module CLI
5
- autoload :Base, 'rascal/cli/base'
6
- autoload :Clean, 'rascal/cli/clean'
7
- autoload :Main, 'rascal/cli/main'
8
- autoload :Shell, 'rascal/cli/shell'
9
- autoload :Update, 'rascal/cli/update'
5
+ autoload :Base, 'rascal/cli/base'
6
+ autoload :Clean, 'rascal/cli/clean'
7
+ autoload :Environments, 'rascal/cli/environments'
8
+ autoload :Main, 'rascal/cli/main'
9
+ autoload :Run, 'rascal/cli/run'
10
+ autoload :Shell, 'rascal/cli/shell'
11
+ autoload :Update, 'rascal/cli/update'
10
12
  end
11
13
  end
@@ -65,16 +65,14 @@ module Rascal
65
65
  )
66
66
  end
67
67
 
68
- def run_and_attach(*command, env: {}, network: nil, volumes: [], working_dir: nil, allow_failure: false)
68
+ def run_and_attach(*command, env: {}, network: nil, volumes: [], working_dir: nil, allow_failure: false, tty: true)
69
69
  Docker.interface.run_and_attach(
70
70
  'container',
71
71
  'run',
72
72
  '--rm',
73
73
  '-a', 'STDOUT',
74
74
  '-a', 'STDERR',
75
- '-a', 'STDIN',
76
- '--interactive',
77
- '--tty',
75
+ *(['-a', 'STDIN', '--interactive', '--tty'] if tty),
78
76
  *(['-w', working_dir] if working_dir),
79
77
  *(volumes.flat_map { |v| ['-v', v.to_param] }),
80
78
  *env_args(env),
@@ -30,6 +30,7 @@ module Rascal
30
30
  unless allow_failure || exit_status.success?
31
31
  raise Error, "docker container run failed"
32
32
  end
33
+ exit_status
33
34
  end
34
35
 
35
36
  private
@@ -1,3 +1,5 @@
1
+ require 'shellwords'
2
+
1
3
  module Rascal
2
4
  class Environment
3
5
  attr_reader :name, :network, :container, :env_variables, :services, :volumes, :working_dir, :before_shell
@@ -27,6 +29,27 @@ module Rascal
27
29
  )
28
30
  end
29
31
 
32
+ # Runs a single command in the environment, without a TTY, and returns its
33
+ # Process::Status. Unlike #run_shell this is suitable for non-interactive
34
+ # use (scripts, CI-like runs, AI agents), which need the command's real
35
+ # exit status.
36
+ #
37
+ # The command is a list of arguments, which is escaped before being handed
38
+ # to bash, so arguments may contain spaces and shell metacharacters. Use
39
+ # e.g. `bash -c "foo && bar"` to run something the shell must interpret.
40
+ def run_command(*command)
41
+ download_missing
42
+ start_services
43
+ @container.run_and_attach('bash', '-c', command_script(Shellwords.join(command)),
44
+ env: @env_variables,
45
+ network: @network,
46
+ volumes: @volumes,
47
+ working_dir: @working_dir,
48
+ allow_failure: true,
49
+ tty: false
50
+ )
51
+ end
52
+
30
53
  def clean(clean_volumes: false)
31
54
  @services.each(&:clean)
32
55
  @network.clean
@@ -42,6 +65,19 @@ module Rascal
42
65
 
43
66
  private
44
67
 
68
+ # Runs before_shell/after_shell around the given command, but exits with the
69
+ # command's status. Without saving it, an `after_shell` entry would determine
70
+ # the exit status, and failures would go unnoticed.
71
+ def command_script(command)
72
+ [
73
+ *@before_shell,
74
+ command,
75
+ '__rascal_status=$?',
76
+ *@after_shell,
77
+ 'exit $__rascal_status',
78
+ ].join(';')
79
+ end
80
+
45
81
  def download_missing
46
82
  @container.download_missing
47
83
  @services.each(&:download_missing)
@@ -1,3 +1,3 @@
1
1
  module Rascal
2
- VERSION = "0.3.9"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rascal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.9
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Kraze
@@ -54,7 +54,9 @@ files:
54
54
  - lib/rascal/cli.rb
55
55
  - lib/rascal/cli/base.rb
56
56
  - lib/rascal/cli/clean.rb
57
+ - lib/rascal/cli/environments.rb
57
58
  - lib/rascal/cli/main.rb
59
+ - lib/rascal/cli/run.rb
58
60
  - lib/rascal/cli/shell.rb
59
61
  - lib/rascal/cli/update.rb
60
62
  - lib/rascal/docker.rb
@@ -97,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
99
  - !ruby/object:Gem::Version
98
100
  version: '0'
99
101
  requirements: []
100
- rubygems_version: 3.6.9
102
+ rubygems_version: 4.0.10
101
103
  specification_version: 4
102
104
  summary: Spin up CI environments locally.
103
105
  test_files: []