gowalk-cicd 1.0.195 → 1.0.196

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.
@@ -50,7 +50,7 @@ def candidate(engine, old, body, name, staged, reservation):
50
50
  current = engine.inspect(old['Id'])
51
51
  unchanged(old, current)
52
52
  if current['State']['Running']:
53
- engine.request('POST', '/containers/' + old['Id'] + '/stop?t=10')
53
+ engine.stop(old['Id'])
54
54
  replacement = engine.inspect(name, missing=True)
55
55
  if replacement is None:
56
56
  result = engine.request('POST', '/containers/create?name=' + quote(name, safe=''), body)
@@ -71,6 +71,8 @@ def confirmed(engine, method, path, proof):
71
71
 
72
72
 
73
73
  def rollback(engine, old, name, reservation):
74
+ if engine.pending_stop is not None:
75
+ engine.stop(old['Id'])
74
76
  replacement = engine.inspect(name, missing=True)
75
77
  if replacement is not None and replacement['Id'] != old['Id']:
76
78
  if (replacement['Config'].get('Labels', {}).get(MARKER) != old['Id']
@@ -13,14 +13,15 @@ SOCKET = '/var/run/docker.sock'
13
13
  class Engine:
14
14
  def __init__(self):
15
15
  self.deadline = time.monotonic() + 90
16
+ self.pending_stop = None
16
17
 
17
- def request(self, method, path, body=None, missing=False, raw=False):
18
+ def request(self, method, path, body=None, missing=False, raw=False, timeout=10):
18
19
  remaining = self.deadline - time.monotonic()
19
20
  if remaining <= 0:
20
21
  raise ValueError('rollout_deadline')
21
- connection = http.client.HTTPConnection('localhost', timeout=min(10, remaining))
22
+ connection = http.client.HTTPConnection('localhost', timeout=min(timeout, remaining))
22
23
  connection.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
23
- connection.sock.settimeout(min(10, remaining))
24
+ connection.sock.settimeout(min(timeout, remaining))
24
25
  try:
25
26
  connection.sock.connect(SOCKET)
26
27
  data = None if body is None else json.dumps(body).encode()
@@ -38,6 +39,15 @@ class Engine:
38
39
  def inspect(self, identifier, missing=False):
39
40
  return self.request('GET', '/containers/' + quote(identifier, safe='') + '/json', missing=missing)
40
41
 
42
+ def stop(self, identifier):
43
+ # Docker continues an accepted stop after a lost HTTP reply. Keep that uncertainty
44
+ # until the same-ID stop completes and its stopped state is positively observed.
45
+ self.pending_stop = identifier
46
+ self.request('POST', '/containers/' + identifier + '/stop?t=10', timeout=20)
47
+ if self.inspect(identifier)['State']['Running']:
48
+ raise ValueError('container_stop_unconfirmed')
49
+ self.pending_stop = None
50
+
41
51
 
42
52
  def unshared(engine, original):
43
53
  identifiers = [row['Id'] for row in engine.request('GET', '/containers/json?all=1')]
@@ -67,9 +77,16 @@ def probe(engine, identifier, test):
67
77
  created = engine.request('POST', '/containers/' + identifier + '/exec',
68
78
  {'AttachStdout': False, 'AttachStderr': False, 'Cmd': command})
69
79
  engine.request('POST', '/exec/' + created['Id'] + '/start', {'Detach': False, 'Tty': False}, raw=True)
70
- result = engine.request('GET', '/exec/' + created['Id'] + '/json')
71
- if result['Running'] or result['ExitCode'] != 0:
72
- raise ValueError('original_probe_failed')
80
+ deadline = min(time.monotonic() + 5, engine.deadline)
81
+ while time.monotonic() < deadline:
82
+ result = engine.request('GET', '/exec/' + created['Id'] + '/json',
83
+ timeout=max(0.001, deadline - time.monotonic()))
84
+ if not result['Running']:
85
+ if result['ExitCode'] != 0:
86
+ raise ValueError('original_probe_failed')
87
+ return
88
+ time.sleep(min(0.1, max(0, deadline - time.monotonic())))
89
+ raise ValueError('original_probe_timeout')
73
90
 
74
91
 
75
92
  def healthy(engine, identifier, deadline):
@@ -113,7 +113,12 @@ def unchanged(before, after, reservation_changes=False):
113
113
 
114
114
  def verified(row, body, original):
115
115
  config = {key: value for key, value in body.items() if key not in ('HostConfig', 'NetworkingConfig')}
116
- if (row['Config'] != config or row['Image'] != original['Image'] or row['HostConfig'] != body['HostConfig']):
116
+ hosts = [dict(value) for value in (row['HostConfig'], body['HostConfig'])]
117
+ # Docker reports the default OOM setting as null while running and false before start.
118
+ for host in hosts:
119
+ if host.get('OomKillDisable') is None:
120
+ host['OomKillDisable'] = False
121
+ if row['Config'] != config or row['Image'] != original['Image'] or hosts[0] != hosts[1]:
117
122
  raise ValueError('replacement_config_changed')
118
123
  actual = row['NetworkSettings']['Networks']
119
124
  expected = body['NetworkingConfig']['EndpointsConfig']
@@ -81,6 +81,12 @@ marker-bound replacement, restore/start the original and verify its original hea
81
81
  by identity/state readback. `rollback_unconfirmed` means recovery needs another exact readback/replay;
82
82
  retained containers and records stay available, and unrelated containers are never removed to clear a name.
83
83
 
84
+ Docker may report the default `OomKillDisable` as either null or false; these alone compare equivalently,
85
+ while an enabled OOM-kill override must match exactly. Stop requests retain their ten-second grace with
86
+ a twenty-second transport allowance inside the transaction budget. An uncertain stop must settle on
87
+ that exact container before rollback can restart it or certify its health. Original-probe verification
88
+ waits for the exec's confirmed exit status, for at most five seconds within the remaining transaction.
89
+
84
90
  Replay with the same original ID resumes an interrupted retained-container transaction. After successful
85
91
  cleanup it finds the marker-bound healthy replacement and returns `already_applied`. It refuses absent,
86
92
  ambiguous or changed runtime evidence. Start with one canary, verify application health externally and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gowalk-cicd",
3
- "version": "1.0.195",
3
+ "version": "1.0.196",
4
4
  "description": "Zero-config GitHub Actions delivery for iOS TestFlight and Android Google Play.",
5
5
  "type": "module",
6
6
  "bin": {