@@ -7,7 +7,7 @@ import 'package:chunked_downloader/chunked_downloader.dart';
77import 'package:dio/dio.dart' ;
88import 'package:flutter/foundation.dart' ;
99import 'package:localization/localization.dart' ;
10- import 'package:wsl2distromanager/api/archive .dart' ;
10+ import 'package:wsl2distromanager/api/layer_processor .dart' ;
1111import 'package:wsl2distromanager/api/safe_paths.dart' ;
1212import 'package:wsl2distromanager/components/helpers.dart' ;
1313import 'package:wsl2distromanager/components/logging.dart' ;
@@ -145,12 +145,10 @@ class DockerImage {
145145 String ? distroName;
146146 final Dio dio;
147147 final ChunkedDownloaderFactory chunkedDownloaderFactory;
148- final ArchiveService archiveService;
149148
150149 DockerImage (
151150 {Dio ? dio,
152151 ChunkedDownloaderFactory ? chunkedDownloaderFactory,
153- ArchiveService ? archiveService,
154152 String ? registryUrl,
155153 this .authUrl = 'https://auth.docker.io/token' ,
156154 this .svcUrl = 'registry.docker.io' })
@@ -174,8 +172,7 @@ class DockerImage {
174172 chunkSize: chunkSize ?? 1024 * 1024 ,
175173 onProgress: onProgress,
176174 onDone: onDone,
177- onError: onError)),
178- archiveService = archiveService ?? ArchiveService () {
175+ onError: onError)) {
179176 String ? mirror = prefs.getString ('DockerMirror' );
180177 if (mirror != null && mirror.isNotEmpty) {
181178 this .registryUrl = mirror;
@@ -349,8 +346,85 @@ class DockerImage {
349346 ImageManifest .fromMap (await _getManifest (image, token, digest));
350347
351348 final config = imageManifest.config.digest;
352- await _downloadBlob (image, token, config,
353- SafePath (path).file ('config.json' ), (p0, p1) {});
349+ final configPath = SafePath (path).file ('config.json' );
350+ await _downloadBlob (image, token, config, configPath, (p0, p1) {});
351+
352+ // Parse config.json for metadata (V2)
353+ try {
354+ if (await File (configPath).exists ()) {
355+ final configContent = await File (configPath).readAsString ();
356+ final configJson = json.decode (configContent);
357+
358+ if (configJson['config' ] != null ) {
359+ final parsedConfig = configJson['config' ];
360+ final env = parsedConfig['Env' ];
361+ final cmd = parsedConfig['Cmd' ];
362+ final entrypoint = parsedConfig['Entrypoint' ];
363+ final user = parsedConfig['User' ];
364+
365+ // Handle User
366+ if (user != null && user is String && user.isNotEmpty) {
367+ var userStr = user;
368+ if (userStr.contains (':' )) {
369+ userStr = userStr.split (':' )[0 ];
370+ }
371+ if (int .tryParse (userStr) == null ) {
372+ prefs.setString ('StartUser_$distroName ' , userStr);
373+ } else {
374+ Notify .message (
375+ 'Not implemented yet: Docker USER is a number.' );
376+ }
377+ }
378+
379+ // Handle Env, Entrypoint, Cmd
380+ var entrypointCmd = '' ;
381+ if (entrypoint != null && entrypoint is List ) {
382+ entrypointCmd = entrypoint.map ((e) => e.toString ()).join (' ' );
383+ }
384+
385+ String exportEnv = '' ;
386+ if (env != null && env is List ) {
387+ exportEnv = env.map ((e) => 'export $e ;' ).join (' ' );
388+ }
389+
390+ // Use distroName for StartCmd so it applies to the instance
391+ if (cmd != null && cmd is List ) {
392+ prefs.setString ('StartCmd_$distroName ' ,
393+ '$exportEnv $entrypointCmd ; ${cmd .map ((e ) => e .toString ()).join (' ' )}' );
394+ } else if (entrypointCmd.isNotEmpty) {
395+ prefs.setString (
396+ 'StartCmd_$distroName ' , '$exportEnv $entrypointCmd ' );
397+ }
398+ }
399+
400+ // Handle history for UserCmds/GroupCmds
401+ // These need to be saved under image filename to be picked up by create_dialog
402+ String imageName = filename (image, tag);
403+ List <String > userCmds = [];
404+ List <String > groupCmds = [];
405+
406+ if (configJson['history' ] != null &&
407+ configJson['history' ] is List ) {
408+ for (var item in configJson['history' ]) {
409+ final createdBy = item['created_by' ] as String ? ;
410+ if (createdBy != null ) {
411+ if (createdBy.contains ('adduser' ) ||
412+ createdBy.contains ('useradd' )) {
413+ userCmds.add (createdBy);
414+ }
415+ if (createdBy.contains ('groupadd' ) ||
416+ createdBy.contains ('addgroup' )) {
417+ groupCmds.add (createdBy);
418+ }
419+ }
420+ }
421+ }
422+ prefs.setStringList ('UserCmds_$imageName ' , userCmds);
423+ prefs.setStringList ('GroupCmds_$imageName ' , groupCmds);
424+ }
425+ } catch (e, stack) {
426+ logDebug (e, stack, 'Failed to parse V2 config' );
427+ }
354428 } catch (e, stackTrace) {
355429 if (kDebugMode) {
356430 print (e);
@@ -456,8 +530,8 @@ class DockerImage {
456530 // Set image specific commands
457531 String name = filename (image, tag);
458532 if (cmd != null ) {
459- prefs.setString (
460- 'StartCmd_$ name ' , ' $exportEnv $entrypointCmd ; ${cmd .join (' ' )}' );
533+ prefs.setString ('StartCmd_$ distroName ' ,
534+ '$exportEnv $entrypointCmd ; ${cmd .join (' ' )}' );
461535 }
462536 prefs.setStringList ('UserCmds_$name ' , userCmds);
463537 prefs.setStringList ('GroupCmds_$name ' , groupCmds);
@@ -570,38 +644,17 @@ class DockerImage {
570644 int retry = 0 ;
571645
572646 final parentPath = SafePath (tmpImagePath);
573- String outTar = parentPath.file ('$imageName .tar' );
574647 String outTarGz = SafePath (distroPath).file ('$imageName .tar.gz' );
575648 while (retry < 2 ) {
576649 try {
577- // More than one layer
578- List <String > paths = [];
579- if (layers != 1 ) {
580- for (var i = 0 ; i < layers; i++ ) {
581- // Read archives layers
582- if (kDebugMode) {
583- print ('Extracting layer $i of $layers ' );
584- }
585- // progress(i, layers, -1, -1);
586- Notify .message ('Extracting layer $i of $layers ' );
587-
588- // Extract layer
589- final layerTarGz = parentPath.file ('layer_$i .tar.gz' );
590- await archiveService.extract (layerTarGz, parentPath.path);
591- paths.add (parentPath.file ('layer_$i .tar' ));
592- }
593-
594- // Archive as tar then gzip to disk
595- await archiveService.merge (paths, outTar);
596- await archiveService.compress (outTar, outTarGz);
597-
598- Notify .message ('writingtodisk-text' .i18n ());
599- } else if (layers == 1 ) {
600- // Just copy the file
601- File (SafePath (tmpImagePath).file ('layer_0.tar.gz' ))
602- .copySync (outTarGz);
650+ List <String > layerPaths = [];
651+ for (var i = 0 ; i < layers; i++ ) {
652+ layerPaths.add (parentPath.file ('layer_$i .tar.gz' ));
603653 }
604654
655+ await LayerProcessor ()
656+ .mergeLayers (layerPaths, outTarGz, (msg) => Notify .message (msg));
657+
605658 retry = 2 ;
606659 break ;
607660 } catch (e, stackTrace) {
0 commit comments