<pre style='margin:0'>
Clemens Lang (neverpanic) pushed a commit to branch master
in repository macports-base.

</pre>
<p><a href="https://github.com/macports/macports-base/commit/4d9e8a125d328aa3ac6b558570891123eaabbb1c">https://github.com/macports/macports-base/commit/4d9e8a125d328aa3ac6b558570891123eaabbb1c</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit 4d9e8a125d328aa3ac6b558570891123eaabbb1c
</span>Author: Clemens Lang <neverpanic@gmail.com>
AuthorDate: Wed Jul 24 17:22:26 2024 +0200

<span style='display:block; white-space:pre;color:#404040;'>    snapshot: Add JSON export functionality
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>    This will eventually allow users to move to a new machine by running
</span><span style='display:block; white-space:pre;color:#404040;'>    
</span><span style='display:block; white-space:pre;color:#404040;'>     old> sudo port snapshot
</span><span style='display:block; white-space:pre;color:#404040;'>     old> sudo port snapshot --export $ID >snapshot.json
</span><span style='display:block; white-space:pre;color:#404040;'>     new> sudo port snapshot --import <snapshot.json
</span><span style='display:block; white-space:pre;color:#404040;'>     new> sudo port restore --latest
</span>---
 src/macports1.0/snapshot.tcl | 115 ++++++++++++++++++++++++++++++++++++++++++-
 src/port/port.tcl            |   2 +-
 2 files changed, 114 insertions(+), 3 deletions(-)

<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/macports1.0/snapshot.tcl b/src/macports1.0/snapshot.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index f9a1a06fe..4cf1028f9 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/macports1.0/snapshot.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/macports1.0/snapshot.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -32,6 +32,7 @@ package provide snapshot 1.0
</span> 
 package require macports 1.0
 package require registry 1.0
<span style='display:block; white-space:pre;background:#e0ffe0;'>+package require json::write
</span> 
 namespace eval snapshot {
     proc print_usage {} {
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -40,6 +41,8 @@ namespace eval snapshot {
</span>         ui_msg "  port snapshot --list"
         ui_msg "  port snapshot --diff <snapshot-id> \[--all\]"
         ui_msg "  port snapshot --delete <snapshot-id>"
<span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "  port snapshot --export <snapshot-id> >snapshot.json"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        ui_msg "  port snapshot --import <snapshot.json"
</span>     }
 
     proc main {opts} {
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -54,11 +57,11 @@ namespace eval snapshot {
</span>             set operation "create"
         } else {
             set operation ""
<span style='display:block; white-space:pre;background:#ffe0e0;'>-            foreach op {list create diff delete} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            foreach op {list create diff delete export import} {
</span>                 set opname "ports_snapshot_$op"
                 if {[dict exists $opts $opname]} {
                     if {$operation ne ""} {
<span style='display:block; white-space:pre;background:#ffe0e0;'>-                        ui_error "Only one of the --list, --create, --diff, and --delete options can be specified."
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                        ui_error "Only one of the --list, --create, --diff, --delete, --export, or --import options can be specified."
</span>                         error "Incorrect usage, see port snapshot --help."
                     }
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -183,6 +186,16 @@ namespace eval snapshot {
</span>             "delete" {
                 return [delete_snapshot $opts]
             }
<span style='display:block; white-space:pre;background:#e0ffe0;'>+            "export" {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                if {[catch {set snapshot [registry::snapshot get_by_id [dict get $opts ports_snapshot_export]]} result]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ui_error "Failed to obtain snapshot with ID [dict get $opts ports_snapshot_export]: $result"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    return 1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                puts [snapshot2json $snapshot]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            "import" {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }
</span>             default {
                 print_usage
                 return 1
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -190,6 +203,81 @@ namespace eval snapshot {
</span>         }
     }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc snapshot2json {snapshot} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Convert the given snapshot to JSON format and return it
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # The data format is
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #   metadata:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #     type: string "org.macports/snapshot/v1", denoting the version of this format
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #     note: string, the note associated with this snapshot
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #     created_at: string, the local date at which the snapshot was created
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #   ports:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #     list of objects:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       port_name: string, the name of the port
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       requested: int, 1 for ports that are requested, 0 otherwise
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       state: string, "installed" for ports that are active
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       variants: string, list of active variants
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       requested_variants: string, list of variants requested by the user
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #       port_files: list of strings, the files installed by this port
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Args:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #           snapshot - The snapshot object to convert to JSON
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Returns:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        #           string representation of the snapshot object
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # The conversion can take quite a while because we're querying all
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # files installed by the various ports, so display a progress bar
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set fancy_output [expr {![macports::ui_isset ports_debug] && [info exists macports::ui_options(progress_generic)]}]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {$fancy_output} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set progress $macports::ui_options(progress_generic)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            proc noop {args} {}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set progress noop
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set counter 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set total [llength [$snapshot ports]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Add some metadata and a version header that allows us to make
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # backwards-incompatible changes to the output format.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set metadata [json::write object-strings \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            type "org.macports/snapshot/v1" \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            note [$snapshot note] \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            created_at [$snapshot created_at]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        $progress start
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        $progress update $counter $total
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Convert each port into its JSON representation
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set ports [list]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        foreach port [$snapshot ports] {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            incr counter
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            lassign $port port_name requested state variants requested_variants
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set files [snapshot::port_files [$snapshot id] $port_name]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ui_debug "Processing port $counter/$total: $port_name"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            lappend ports [json::write object \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                port_name [json::write string $port_name] \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                requested $requested \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                state [json::write string $state] \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                variants [json::write string $variants] \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                requested_variants [json::write string $requested_variants] \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                port_files [json::write array-strings {*}$files]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            $progress update $counter $total
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # Assemble the metadata and port list into the final JSON object and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        # return it.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set res [json::write object \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            metadata $metadata \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            ports [json::write array {*}$ports]]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        $progress finish
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return $res
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>     proc create {opts} {
 
         registry::write {
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -276,6 +364,29 @@ namespace eval snapshot {
</span>         return $ret
     }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    proc port_files {snapshot_id port_name} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        global registry::tdbc_connection
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {![info exists tdbc_connection]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            registry::tdbc_connect
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        variable port_files_stmt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        if {![info exists port_files_stmt]} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set port_files_stmt [$tdbc_connection prepare {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    SELECT snapshot_files.path FROM snapshot_files
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    INNER JOIN snapshot_ports ON snapshot_files.id = snapshot_ports.id
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    WHERE snapshot_ports.port_name = :port_name
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    AND snapshot_ports.snapshots_id = :snapshot_id
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    ORDER BY snapshot_files.path ASC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            }]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        $tdbc_connection transaction {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+            set results [$port_files_stmt execute]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        set ret [lmap l [$results allrows] {lindex $l 1}]
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        $results close
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+        return $ret
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>     proc _os_mismatch {iplatform iosmajor} {
         global macports::os_platform macports::os_major
         if {$iplatform ne "any" && ($iplatform ne $os_platform
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/port/port.tcl b/src/port/port.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index c66e62ac6..743ec7c56 100755
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/port/port.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/port/port.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -4237,7 +4237,7 @@ set cmd_opts_array [dict create {*}{
</span>     reclaim     {enable-reminders disable-reminders}
     fetch       {no-mirrors}
     bump        {patch}
<span style='display:block; white-space:pre;background:#ffe0e0;'>-    snapshot    {create list {diff 1} all {delete 1} help {note 1}}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    snapshot    {create list {diff 1} all {delete 1} help {note 1} {export 1} import}
</span>     restore     {{snapshot-id 1} all last}
     migrate     {continue all}
 }]
</pre><pre style='margin:0'>

</pre>