docker-diff 0.0.8__py3-none-any.whl → 0.0.9__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docker-diff
3
- Version: 0.0.8
3
+ Version: 0.0.9
4
4
  Summary: Docker Image Comparison Database Manager
5
5
  License: This is free and unencumbered software released into the public domain.
6
6
 
@@ -0,0 +1,10 @@
1
+ docker_diff-0.0.9.dist-info/licenses/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
2
+ docker_diff_pkg/__init__.py,sha256=aG5WUO74Z5ax04A5iwh6thCpHcFXBsdH5v-ph-uUJuI,321
3
+ docker_diff_pkg/_version.py,sha256=X2FLFwBoUmgJPsOV-1l-SxIXNSTX1TQ036Kf2j9Mc68,704
4
+ docker_diff_pkg/cli.py,sha256=zrUzf36Ld1HWW1jVneO6COnes7EOszw6ibUAUjIleyg,24989
5
+ docker_diff_pkg/schema.sql,sha256=xI3kOohbUEzwFhmIGQs2_abN_tJn1BBdAya7tVbw6n8,5477
6
+ docker_diff-0.0.9.dist-info/METADATA,sha256=xZxoN25AcmUNEq1YZ9R5w5DWAnvc6qo0H0PfSTX9iu0,4549
7
+ docker_diff-0.0.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ docker_diff-0.0.9.dist-info/entry_points.txt,sha256=nEDTWZsgB5fRJgeXHJbXJyaHKMmKyN9Qypf3X78tosw,57
9
+ docker_diff-0.0.9.dist-info/top_level.txt,sha256=GB5Qrc3AH1pk0543cQz9SRNr5Fatrj-c0Cdp3rCvTM8,16
10
+ docker_diff-0.0.9.dist-info/RECORD,,
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.0.8'
32
- __version_tuple__ = version_tuple = (0, 0, 8)
31
+ __version__ = version = '0.0.9'
32
+ __version_tuple__ = version_tuple = (0, 0, 9)
33
33
 
34
34
  __commit_id__ = commit_id = None
docker_diff_pkg/cli.py CHANGED
@@ -222,25 +222,103 @@ class DockerImageDB:
222
222
 
223
223
  # Get file listing using docker run
224
224
  try:
225
- result = subprocess.run([
226
- 'docker', 'run', '--rm', image_name,
227
- 'find', '/', '-type', 'f', '-exec', 'stat', '-c', '%n|%s|%Y', '{}', ';'
228
- ], capture_output=True, text=True)
229
-
230
- files = []
231
- for line in result.stdout.strip().split('\n'):
232
- if '|' in line:
233
- parts = line.split('|')
234
- if len(parts) >= 3:
235
- files.append({
236
- 'path': parts[0],
237
- 'size': int(parts[1]) if parts[1].isdigit() else 0,
238
- 'mtime': int(parts[2]) if parts[2].isdigit() else None
239
- })
240
-
241
- self.add_files_for_image(image_id, files)
242
- print(f" Stored {len(files)} files for {image_name}")
243
- return image_id
225
+ # Stream file listing using Docker SDK if available; fallback to streaming subprocess
226
+ # Clear existing files first (we'll insert incrementally)
227
+ self._exec("DELETE FROM files WHERE image_id = ?", (image_id,))
228
+
229
+ insert_sql = """
230
+ INSERT INTO files
231
+ (image_id, file_path, file_size, file_mode, modified_time, file_type, checksum)
232
+ VALUES (?, ?, ?, ?, ?, ?, ?)
233
+ """
234
+
235
+ batch: List[Tuple[Any, ...]] = []
236
+ batch_size = 500
237
+ inserted = 0
238
+
239
+ def flush_batch():
240
+ nonlocal batch, inserted
241
+ if batch:
242
+ self._executemany(insert_sql, batch)
243
+ inserted += len(batch)
244
+ batch = []
245
+ if inserted % 1000 == 0:
246
+ print(f" Discovered {inserted} files...", flush=True)
247
+
248
+ def parse_line(line: str):
249
+ parts = line.strip().split("|")
250
+ if len(parts) >= 3:
251
+ path = parts[0]
252
+ size = int(parts[1]) if parts[1].isdigit() else 0
253
+ mtime = int(parts[2]) if parts[2].isdigit() else None
254
+ batch.append((image_id, path, size, None, mtime, "file", None))
255
+ if len(batch) >= batch_size:
256
+ flush_batch()
257
+
258
+ cmd = ['find', '/', '-type', 'f', '-exec', 'stat', '-c', '%n|%s|%Y', '{}', ';']
259
+
260
+ try:
261
+ try:
262
+ import docker # type: ignore
263
+ client = docker.from_env()
264
+ # Ensure image is available; pull only if not present
265
+ try:
266
+ client.images.get(image_name)
267
+ except Exception:
268
+ client.images.pull(image_name)
269
+
270
+ container = client.containers.create(image=image_name, command=cmd, detach=True)
271
+ try:
272
+ container.start()
273
+ remainder = ""
274
+ for chunk in container.logs(stream=True, stdout=True, stderr=False, follow=True):
275
+ if not chunk:
276
+ continue
277
+ text = chunk.decode("utf-8", errors="ignore")
278
+ remainder += text
279
+ while True:
280
+ nl = remainder.find("\n")
281
+ if nl == -1:
282
+ break
283
+ line = remainder[:nl]
284
+ remainder = remainder[nl + 1 :]
285
+ if line:
286
+ parse_line(line)
287
+ if remainder.strip():
288
+ parse_line(remainder.strip())
289
+ flush_batch()
290
+ finally:
291
+ try:
292
+ container.remove(force=True)
293
+ except Exception:
294
+ pass
295
+
296
+ print(f" Stored {inserted} files for {image_name}")
297
+ return image_id
298
+
299
+ except Exception as docker_err:
300
+ # Fallback to streaming via subprocess
301
+ proc = subprocess.Popen(
302
+ ['docker', 'run', '--rm', image_name, *cmd],
303
+ stdout=subprocess.PIPE,
304
+ stderr=subprocess.PIPE,
305
+ text=True,
306
+ bufsize=1,
307
+ )
308
+ assert proc.stdout is not None
309
+ for line in proc.stdout:
310
+ if line:
311
+ parse_line(line)
312
+ proc.wait()
313
+ if proc.returncode != 0:
314
+ err = (proc.stderr.read() if proc.stderr else "").strip()
315
+ raise subprocess.SubprocessError(err or f"docker run exited with {proc.returncode}")
316
+ flush_batch()
317
+ print(f" Stored {inserted} files for {image_name}")
318
+ return image_id
319
+
320
+ except Exception as e:
321
+ raise subprocess.SubprocessError(str(e))
244
322
 
245
323
  except subprocess.SubprocessError as e:
246
324
  print(f"Error scanning {image_name}: {e}")
@@ -1,10 +0,0 @@
1
- docker_diff-0.0.8.dist-info/licenses/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
2
- docker_diff_pkg/__init__.py,sha256=aG5WUO74Z5ax04A5iwh6thCpHcFXBsdH5v-ph-uUJuI,321
3
- docker_diff_pkg/_version.py,sha256=j-ar4gJGiWIawqKXhvv9hGJWLfwu0tISl-0GV97B7a0,704
4
- docker_diff_pkg/cli.py,sha256=C6LYpgFGmiZsBZ8MO4TJKmtKMvg1EqB6qYlpilbq6pI,21549
5
- docker_diff_pkg/schema.sql,sha256=xI3kOohbUEzwFhmIGQs2_abN_tJn1BBdAya7tVbw6n8,5477
6
- docker_diff-0.0.8.dist-info/METADATA,sha256=7B6M1QDA_F4YtmzjQduM7KTbmP4_6_P0ii-4QklOqNs,4549
7
- docker_diff-0.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- docker_diff-0.0.8.dist-info/entry_points.txt,sha256=nEDTWZsgB5fRJgeXHJbXJyaHKMmKyN9Qypf3X78tosw,57
9
- docker_diff-0.0.8.dist-info/top_level.txt,sha256=GB5Qrc3AH1pk0543cQz9SRNr5Fatrj-c0Cdp3rCvTM8,16
10
- docker_diff-0.0.8.dist-info/RECORD,,