rascal 0.4.0 → 0.4.1

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: d916ce222f77cd89927ad3ac8ceb56da9d34d87f2f808938b6c796ce92ab3d8b
4
- data.tar.gz: 070e9c46b5f33074260638ba1b2e5bb19844c515461788ae904d4b1e72690a7e
3
+ metadata.gz: 8fa4e3608306dedc697f6da5f37017038e694e2c9182cee77fe79f7a448fdfa3
4
+ data.tar.gz: 38b938c35bbe6dd09df61fae8f292083cbb1177b954516f79e02d980591100ed
5
5
  SHA512:
6
- metadata.gz: 7cd38cd621c764d5d8548e4093abc1be639713efc2294b7e80b7fec5d7cdb37648569258c6b8725b604ddcbc756b5916d26b2e6577f8e33a03b100d522b5d957
7
- data.tar.gz: '00299e9e4c118a754e0b5227c04d2400d7424a551cddaebc24a1fb85c5649e6c7913835e26754f73d663a4ab50b345ed322198f6f4552c3e3f21d318196cf511'
6
+ metadata.gz: c8025fa47c2c66853a8ae0080c2230379f35d123346669864ec2f0d899be13a59f2ff9a8af0677784a49bc1b626dbfcad6e0c4985a4f65626861b2f59f8b8fe6
7
+ data.tar.gz: 931c0a18f53b506c3f8e3d72803505ec32db08c1c070b06b0c34d8d397068d18e37f1cce34ac9f4c944e9a679962d3b4dd1a1cc142cf23c3c10b74debc3a31d0
data/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ 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.1 (2026-07-30)
8
+
9
+ - Recreate a service container whose network no longer exists, instead of failing
10
+ to start it.
11
+
12
+
7
13
  ## 0.4.0 (2026-07-30)
8
14
 
9
15
  - Add `rascal run ENVIRONMENT -- COMMAND`, which runs a single command in an
@@ -23,23 +23,35 @@ module Rascal
23
23
  end
24
24
 
25
25
  def running?
26
- if id
27
- container_info = Docker.interface.run(
28
- 'container',
29
- 'inspect',
30
- id,
31
- output: :json,
32
- ).first
33
- !!container_info.dig('State', 'Running')
34
- else
35
- false
36
- end
26
+ !!container_info&.dig('State', 'Running')
37
27
  end
38
28
 
39
29
  def exists?
40
30
  !!id
41
31
  end
42
32
 
33
+ # Docker resolves a container's network by the id it recorded when the
34
+ # container was created, and refuses to start it once that network is
35
+ # gone:
36
+ #
37
+ # failed to set up container networking: network 36243f82141a... not found
38
+ #
39
+ # `docker network prune` (and `docker system prune`) leaves exactly that
40
+ # behind, because it removes networks whose containers are all stopped —
41
+ # the normal state of an environment between runs. Reconnecting does not
42
+ # help, since docker resolves the recorded id before it looks at the
43
+ # container's current endpoints, so the container has to go. Nothing in a
44
+ # service container is worth preserving; #start creates a new one.
45
+ def remove_if_network_missing(network)
46
+ return unless exists?
47
+ attached = attached_network_ids
48
+ return if attached.empty? || attached.include?(network.id)
49
+
50
+ say "Removing container for #{@name}, its network no longer exists"
51
+ remove_container
52
+ @id = nil
53
+ end
54
+
43
55
  def start(network: nil, network_alias: nil, volumes: [], env: {}, command: [])
44
56
  say "Starting container for #{@name}"
45
57
  create(network: network, network_alias: network_alias, volumes: volumes, env: env, command: command) unless exists?
@@ -123,6 +135,24 @@ module Rascal
123
135
 
124
136
  private
125
137
 
138
+ # A fresh `docker container inspect`, or nil if there is no container.
139
+ def container_info
140
+ return unless id
141
+ Docker.interface.run(
142
+ 'container',
143
+ 'inspect',
144
+ id,
145
+ output: :json,
146
+ ).first
147
+ end
148
+
149
+ # Ids of the networks the container is attached to. Empty for a container
150
+ # that was created but never started.
151
+ def attached_network_ids
152
+ networks = container_info&.dig('NetworkSettings', 'Networks') || {}
153
+ networks.each_value.filter_map { |n| n['NetworkID'] }.reject(&:empty?).uniq
154
+ end
155
+
126
156
  def image_exists?
127
157
  Docker.interface.run(
128
158
  'image',
@@ -39,11 +39,15 @@ module Rascal
39
39
  end
40
40
  end
41
41
 
42
+ # The full id, not the short one docker prints by default: a container
43
+ # records the network it is attached to by full id, and
44
+ # Container#remove_if_network_missing compares the two.
42
45
  def id
43
46
  @id ||= Docker.interface.run(
44
47
  'network',
45
48
  'ls',
46
49
  '--quiet',
50
+ '--no-trunc',
47
51
  '--filter', "name=^#{@prefixed_name}$",
48
52
  output: :id,
49
53
  )
@@ -17,6 +17,7 @@ module Rascal
17
17
 
18
18
  def start_if_stopped(network: nil)
19
19
  unless @container.running?
20
+ @container.remove_if_network_missing(network)
20
21
  if (container_id = @container.id)
21
22
  network.disconnect(container_id)
22
23
  end
@@ -1,3 +1,3 @@
1
1
  module Rascal
2
- VERSION = "0.4.0"
2
+ VERSION = "0.4.1"
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.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Kraze
@@ -99,7 +99,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
99
99
  - !ruby/object:Gem::Version
100
100
  version: '0'
101
101
  requirements: []
102
- rubygems_version: 4.0.10
102
+ rubygems_version: 3.6.9
103
103
  specification_version: 4
104
104
  summary: Spin up CI environments locally.
105
105
  test_files: []