Add remote listing support to sgard list via -r flag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 09:22:59 -07:00
parent 3e0aabef4a
commit bffe7bde12
3 changed files with 72 additions and 21 deletions

View File

@@ -111,3 +111,4 @@ Phase 6: Manifest Signing (to be planned).
| 2026-03-25 | — | `sgard info` command: shows detailed file information (status, hash, timestamps, mode, encryption, targeting). 5 tests. | | 2026-03-25 | — | `sgard info` command: shows detailed file information (status, hash, timestamps, mode, encryption, targeting). 5 tests. |
| 2026-03-25 | — | Deploy sgardd to rift: Dockerfile, docker-compose, mc-proxy L4 route on :9443, Metacrypt TLS cert, DNS. | | 2026-03-25 | — | Deploy sgardd to rift: Dockerfile, docker-compose, mc-proxy L4 route on :9443, Metacrypt TLS cert, DNS. |
| 2026-03-25 | — | `sgard remote set/show`: persistent remote config in `<repo>/remote.yaml` (addr, tls, tls_ca). | | 2026-03-25 | — | `sgard remote set/show`: persistent remote config in `<repo>/remote.yaml` (addr, tls, tls_ca). |
| 2026-03-26 | — | `sgard list` remote support: uses `resolveRemoteConfig()` to list server manifest via `PullManifest` RPC. Client `List()` method added. |

View File

@@ -8,6 +8,7 @@ import (
"io" "io"
"github.com/kisom/sgard/garden" "github.com/kisom/sgard/garden"
"github.com/kisom/sgard/manifest"
"github.com/kisom/sgard/server" "github.com/kisom/sgard/server"
"github.com/kisom/sgard/sgardpb" "github.com/kisom/sgard/sgardpb"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
@@ -273,6 +274,22 @@ func (c *Client) doPull(ctx context.Context, g *garden.Garden) (int, error) {
return blobCount, nil return blobCount, nil
} }
// List fetches the server's manifest and returns its entries without
// downloading any blobs. Automatically re-authenticates if needed.
func (c *Client) List(ctx context.Context) ([]manifest.Entry, error) {
var entries []manifest.Entry
err := c.retryOnAuth(ctx, func() error {
resp, err := c.rpc.PullManifest(ctx, &sgardpb.PullManifestRequest{})
if err != nil {
return fmt.Errorf("list remote: %w", err)
}
m := server.ProtoToManifest(resp.GetManifest())
entries = m.Files
return nil
})
return entries, err
}
// Prune requests the server to remove orphaned blobs. Returns the count removed. // Prune requests the server to remove orphaned blobs. Returns the count removed.
// Automatically re-authenticates if needed. // Automatically re-authenticates if needed.
func (c *Client) Prune(ctx context.Context) (int, error) { func (c *Client) Prune(ctx context.Context) (int, error) {

View File

@@ -1,41 +1,74 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"github.com/kisom/sgard/garden" "github.com/kisom/sgard/garden"
"github.com/kisom/sgard/manifest"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var listRemoteFlag bool
var listCmd = &cobra.Command{ var listCmd = &cobra.Command{
Use: "list", Use: "list",
Short: "List all tracked files", Short: "List all tracked files",
Long: "List all tracked files locally, or on the remote server with -r.",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
g, err := garden.Open(repoFlag) if listRemoteFlag {
if err != nil { return listRemote()
return err
} }
return listLocal()
entries := g.List()
for _, e := range entries {
switch e.Type {
case "file":
hash := e.Hash
if len(hash) > 8 {
hash = hash[:8]
}
fmt.Printf("%-6s %s\t%s\n", "file", e.Path, hash)
case "link":
fmt.Printf("%-6s %s\t-> %s\n", "link", e.Path, e.Target)
case "directory":
fmt.Printf("%-6s %s\n", "dir", e.Path)
}
}
return nil
}, },
} }
func listLocal() error {
g, err := garden.Open(repoFlag)
if err != nil {
return err
}
printEntries(g.List())
return nil
}
func listRemote() error {
ctx := context.Background()
c, cleanup, err := dialRemote(ctx)
if err != nil {
return err
}
defer cleanup()
entries, err := c.List(ctx)
if err != nil {
return err
}
printEntries(entries)
return nil
}
func printEntries(entries []manifest.Entry) {
for _, e := range entries {
switch e.Type {
case "file":
hash := e.Hash
if len(hash) > 8 {
hash = hash[:8]
}
fmt.Printf("%-6s %s\t%s\n", "file", e.Path, hash)
case "link":
fmt.Printf("%-6s %s\t-> %s\n", "link", e.Path, e.Target)
case "directory":
fmt.Printf("%-6s %s\n", "dir", e.Path)
}
}
}
func init() { func init() {
listCmd.Flags().BoolVarP(&listRemoteFlag, "use-remote", "r", false, "list files on the remote server")
rootCmd.AddCommand(listCmd) rootCmd.AddCommand(listCmd)
} }