From 2224f43e3d710c472e5d7c1f56aabdba365f8d1d Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 00:50:31 +0800 Subject: [PATCH 01/10] =?UTF-8?q?build:=20bump=20github.com/cocoonstack/co?= =?UTF-8?q?coon=2022b7392e16b0=E2=86=921951f352e9f2,=20github.com/gofrs/fl?= =?UTF-8?q?ock=20v0.13.0=E2=86=92v0.13.1,=20github.com/projecteru2/core=20?= =?UTF-8?q?v0.1.3=E2=86=92v0.1.5,=20oras.land/oras-go/v2=20v2.6.1=E2=86=92?= =?UTF-8?q?v2.6.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 15 ++++++++------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index 7d8a932..962daa7 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module github.com/cocoonstack/cocoon-macos go 1.27.0 require ( - github.com/cocoonstack/cocoon v0.6.9-0.20260923161722-22b7392e16b0 - github.com/gofrs/flock v0.13.0 + github.com/cocoonstack/cocoon v0.6.9-0.20260923164306-1951f352e9f2 + github.com/gofrs/flock v0.13.1 github.com/opencontainers/image-spec v1.1.1 - github.com/projecteru2/core v0.1.3 + github.com/projecteru2/core v0.1.5 github.com/spf13/cobra v1.10.2 howett.net/plist v1.0.1 - oras.land/oras-go/v2 v2.6.1 + oras.land/oras-go/v2 v2.6.2 ) require ( @@ -19,12 +19,12 @@ require ( github.com/containernetworking/cni v1.3.0 // indirect github.com/containernetworking/plugins v1.9.1 // indirect github.com/creack/pty v1.1.24 // indirect - github.com/docker/cli v29.6.2+incompatible // indirect + github.com/docker/cli v29.8.0+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.8 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect - github.com/getsentry/sentry-go v0.48.0 // indirect + github.com/getsentry/sentry-go v0.49.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/go-containerregistry v0.21.7 // indirect github.com/google/uuid v1.6.0 // indirect @@ -46,10 +46,11 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect + golang.org/x/mod v0.40.0 // indirect golang.org/x/sync v0.22.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 // indirect google.golang.org/grpc v1.83.2 // indirect google.golang.org/protobuf v1.36.12 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect diff --git a/go.sum b/go.sum index 2015474..ef4f36d 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILM github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo= github.com/cockroachdb/redact v1.1.8 h1:8eVLLj6juKxiKrAEw2b8cJvNqWq++U8WOfQFuL7KTaA= github.com/cockroachdb/redact v1.1.8/go.mod h1:GceHHpJ0rMDpYARL5In88Alq/xMBUtVlz7Qxix6ZVkw= -github.com/cocoonstack/cocoon v0.6.9-0.20260923161722-22b7392e16b0 h1:RJe3v2i/sP1BymsYlJx8+0DqnTRxExvYyi1rslV7tvg= -github.com/cocoonstack/cocoon v0.6.9-0.20260923161722-22b7392e16b0/go.mod h1:B02b6pewrk1DbrRn+iBtDc8LNQWxwot2qF1j4YX5/jg= +github.com/cocoonstack/cocoon v0.6.9-0.20260923164306-1951f352e9f2 h1:3AKsHYAyZVHh2mYXC7dAGvrC/oCf5rDUKDqbB7eEUZU= +github.com/cocoonstack/cocoon v0.6.9-0.20260923164306-1951f352e9f2/go.mod h1:B02b6pewrk1DbrRn+iBtDc8LNQWxwot2qF1j4YX5/jg= github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo= github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= github.com/containernetworking/plugins v1.9.1 h1:8oU6WsIsU3bpnNZuvHp74a6cE1MJwbj2P7s4/yTUNlA= @@ -16,8 +16,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/docker/cli v29.6.2+incompatible h1:/bjePvcbbFTnRrMfWJBY7AjfICdsiLVgHn6LwTVOcqw= -github.com/docker/cli v29.6.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.8.0+incompatible h1:ih0c2jq/nN7QfES8zIfwSzIhRScjs4ehER+kZ60aeSk= +github.com/docker/cli v29.8.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker-credential-helpers v0.9.8 h1:bIREROb7So6PRlq6KTtdS9MPEjC29OQRkFNlvK2OX8Q= github.com/docker/docker-credential-helpers v0.9.8/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -26,16 +26,16 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= -github.com/getsentry/sentry-go v0.48.0 h1:FRZNr7Uk1C86ev1bSJmYlUkL9oyivQA6YOcdYfaaMmY= -github.com/getsentry/sentry-go v0.48.0/go.mod h1:E5UkA5wp1qR2+MDydNYlVeUiNN2xEdjYMidkgf0Qoss= +github.com/getsentry/sentry-go v0.49.0 h1:Ehejknu1l023Ub7QoRBVLAI7g3Jnhqku4oWx4B4Sh5s= +github.com/getsentry/sentry-go v0.49.0/go.mod h1:nuMJAoCfe1u0Bts2ocyNI+TW8HT84vRMqwA5Qq/SKUI= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= -github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= +github.com/gofrs/flock v0.13.1 h1:jjREztyBeSKBZYAC+mgc1laB+xsgy4kYMf3FbKF2UBo= +github.com/gofrs/flock v0.13.1/go.mod h1:sf4BFiHwnvgxa25DlQoDqXQnwRMEOwqxRq37P6MzzmE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -78,8 +78,8 @@ github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTw github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/projecteru2/core v0.1.3 h1:0odzNdtDKRvbMir3xKpqCPJaThHdzfzvou/vGFWquZY= -github.com/projecteru2/core v0.1.3/go.mod h1:4WGV1OlU8TY0hra+N8Ua2s8hbX1AeJmcjnj3PcztnI4= +github.com/projecteru2/core v0.1.5 h1:cS3xfqa6I1bMXtGdbu2A2UzhjiHaP2mogjQWFTkSEF0= +github.com/projecteru2/core v0.1.5/go.mod h1:MDMJmmlJ3ZVvFQl5BnRcKV1/AVCHyiWoI4BI+sCnuYM= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -117,8 +117,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74= -golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY= +golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs= +golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -151,8 +151,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 h1:1VUiZAXyC+zmiFYi+WLtBzr68Cj8wOofHjjrA/kkizc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 h1:KmqdJU4vrNcxy/6qdg3JduZtalEXrJLspVltnR1cE+8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= @@ -193,5 +193,5 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -oras.land/oras-go/v2 v2.6.1 h1:bonOEkjLfp8tt6qXWRRWP6p1F+9octchOf2EqnWB4Zs= -oras.land/oras-go/v2 v2.6.1/go.mod h1:dhtFrFOuZuDtAVeZ9FUnaa5zfzplG3ZnFX9/uH1J/Yk= +oras.land/oras-go/v2 v2.6.2 h1:N04RXngAp1LJKTG6ifz3xHPipasEkWr+hFmInja5YKo= +oras.land/oras-go/v2 v2.6.2/go.mod h1:PlTtg4JTDJkDe8yVHpM2wz7/YDc00GVas+i4jAW2TZ4= From fbe57815a9772b8c6c431311e4bb3972c8c6d888 Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 01:20:36 +0800 Subject: [PATCH 02/10] fix: reject a double quote in the VNC password HMP parses an argument that starts with a double quote as a quoted string, so a password like "abcdef" was stored as abcdef. --- cmd/vm/vnc.go | 4 ++-- cmd/vm/vnc_test.go | 1 + docs/networking.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/vm/vnc.go b/cmd/vm/vnc.go index 870e7af..5798097 100644 --- a/cmd/vm/vnc.go +++ b/cmd/vm/vnc.go @@ -53,8 +53,8 @@ func validateVNCPassword(pw string) error { if len(pw) > 8 { return fmt.Errorf("--vnc-password must be at most 8 bytes, got %d", len(pw)) } - if strings.ContainsFunc(pw, func(r rune) bool { return unicode.IsControl(r) || unicode.IsSpace(r) }) { - return errors.New("--vnc-password must not contain whitespace or control characters") + if strings.ContainsFunc(pw, func(r rune) bool { return unicode.IsControl(r) || unicode.IsSpace(r) || r == '"' }) { + return errors.New("--vnc-password must not contain whitespace, control characters or double quotes") } return nil } diff --git a/cmd/vm/vnc_test.go b/cmd/vm/vnc_test.go index 87ab8aa..d87bbb0 100644 --- a/cmd/vm/vnc_test.go +++ b/cmd/vm/vnc_test.go @@ -50,6 +50,7 @@ func TestValidateVNCPassword(t *testing.T) { {"newline injection rejected", "x\nquit", true}, {"carriage return rejected", "x\rset", true}, {"tab rejected", "a\tb", true}, + {"leading quote rejected", "\"abcdef\"", true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/docs/networking.md b/docs/networking.md index 083086c..e2117b8 100644 --- a/docs/networking.md +++ b/docs/networking.md @@ -83,7 +83,7 @@ retargeted, so the CLI prints that the supplied VNC settings were ignored and ch QEMU's default `None` auth **hangs macOS Screen Sharing**. Pass `--vnc-password <≤8 bytes>` (applied via the QEMU monitor post-launch) so Screen Sharing prompts and connects. The launch is **rejected** -(not truncated) if the password exceeds 8 bytes or contains whitespace or control characters — QEMU's VNC DES +(not truncated) if the password exceeds 8 bytes or contains whitespace, control characters or double quotes — QEMU's VNC DES auth only supports 8-byte passwords. Plain VNC clients (RealVNC/TigerVNC) work without a password on the loopback modes. From 50daeb782427aaed4e636b5797522c281bad68c6 Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 01:21:17 +0800 Subject: [PATCH 03/10] review: tighten comments and log the missed power-down at warn Drop dead gosec/errcheck nolint directives, restated DataDisks and VNC proxy comments, and the nonexistent export op; cut the gc godocs to one fact each; the guest ignoring the power-down is a degraded stop, so it logs at warn like cocoon's stop escalation. --- cmd/image/oci.go | 2 +- cmd/vm/gc.go | 8 ++++---- cmd/vm/gc_test.go | 2 +- cmd/vm/handler.go | 2 +- cmd/vm/lifecycle.go | 2 +- cmd/vm/utils.go | 2 +- cmd/vm/utils_test.go | 8 ++++---- cmd/vm/vnc.go | 1 - qemu/launch.go | 16 ++++++++-------- qemu/snapshot.go | 2 +- 10 files changed, 22 insertions(+), 23 deletions(-) diff --git a/cmd/image/oci.go b/cmd/image/oci.go index 0f253cc..9ac4d83 100644 --- a/cmd/image/oci.go +++ b/cmd/image/oci.go @@ -37,7 +37,7 @@ func pullOCIBlob(ctx context.Context, ref, dest string) error { } blobURL := fmt.Sprintf("https://%s/v2/%s/blobs/%s", repo.Reference.Host(), repo.Reference.Repository, layer.Digest) - f, err := os.Create(dest) //nolint:gosec // dest is an internal temp path (os.CreateTemp), not user input + f, err := os.Create(dest) if err != nil { return err } diff --git a/cmd/vm/gc.go b/cmd/vm/gc.go index d50f941..4f07600 100644 --- a/cmd/vm/gc.go +++ b/cmd/vm/gc.go @@ -50,7 +50,7 @@ func GC(cmd *cobra.Command, _ []string) error { return sweep(cliutil.CommandContext(cmd), cmd, registerNetGC) } -// sweep takes the provisioning lock exclusively and every VM's lock, so a create, clone or rm in flight is never read as residue; a busy lock refuses the run. The cm-family collectors are host-global, so they run only for a root that has owned host devices: a VM dir seen under the lock, or the marker every path that provisions or removes a VM dir persists first. A root without either still sweeps its own temp files. +// sweep holds the provisioning lock exclusively and every VM lock, so an in-flight create, clone or rm is never read as residue. func sweep(ctx context.Context, cmd *cobra.Command, registerNet func(*gc.Orchestrator, *cobra.Command, network.VMInUse) error) error { if _, err := os.Stat(home.Dir(cmd)); err != nil { return fmt.Errorf("no state root at %s: check --state-dir or $COCOON_MACOS_HOME: %w", home.Dir(cmd), err) @@ -86,7 +86,7 @@ func sweep(ctx context.Context, cmd *cobra.Command, registerNet func(*gc.Orchest return o.Run(ctx) } -// withProvisionLock shares the gc lock for the span in which a create or clone owns host devices no record names yet; the file is persistent so a waiter never ends up on an unlinked inode. +// withProvisionLock shares the gc lock for the span in which a create or clone owns host devices no record names yet. func withProvisionLock(ctx context.Context, cmd *cobra.Command, fn func() error) error { gl, err := openGCLock(cmd) if err != nil { @@ -99,13 +99,13 @@ func withProvisionLock(ctx context.Context, cmd *cobra.Command, fn func() error) return fn() } -// markProvisioned records that the state root owning vmsDir has held a VM: applyNet writes it right before the first host device, rm and the incomplete-dir reset before they remove a VM dir, so the evidence outlives the dir while a create that failed before provisioning under a mistyped root leaves nothing behind. +// markProvisioned records that the state root owning vmsDir has held a VM; the marker outlives the last VM dir. func markProvisioned(vmsDir string) error { path := filepath.Join(vmsDir, ".locks", provisionedMarker) if err := utils.EnsureDirs(filepath.Dir(path)); err != nil { return fmt.Errorf("create vm lock dir: %w", err) } - marker, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o600) //nolint:gosec // under the state root's own lock dir + marker, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o600) if err != nil { return fmt.Errorf("mark state root provisioned: %w", err) } diff --git a/cmd/vm/gc_test.go b/cmd/vm/gc_test.go index 1b91fc2..06f703e 100644 --- a/cmd/vm/gc_test.go +++ b/cmd/vm/gc_test.go @@ -169,7 +169,7 @@ func TestGCRefusesABusyVM(t *testing.T) { if err := held.Lock(t.Context()); err != nil { t.Fatal(err) } - defer held.Unlock(t.Context()) //nolint:errcheck + defer held.Unlock(t.Context()) err := sweep(t.Context(), cmd, noNetGC) if err == nil || !strings.Contains(err.Error(), "busy") { diff --git a/cmd/vm/handler.go b/cmd/vm/handler.go index ad8f524..9e3ec48 100644 --- a/cmd/vm/handler.go +++ b/cmd/vm/handler.go @@ -33,7 +33,7 @@ type record struct { NetMode string `json:"net_mode,omitempty"` Hugepages bool `json:"hugepages,omitempty"` ExitOnReboot bool `json:"exit_on_reboot,omitempty"` - DataDisks []string `json:"data_disks,omitempty"` // created data-disk qcow2 paths, attached on AHCI ports 0,1,3,5 + DataDisks []string `json:"data_disks,omitempty"` Disk string `json:"disk"` OpenCore string `json:"opencore"` diff --git a/cmd/vm/lifecycle.go b/cmd/vm/lifecycle.go index 5b0316c..568aea4 100644 --- a/cmd/vm/lifecycle.go +++ b/cmd/vm/lifecycle.go @@ -39,7 +39,7 @@ func Start(cmd *cobra.Command, args []string) error { if err != nil { return err } - // an op that held the lock (export, a racing run) may have restarted qemu; adopt it and only repair a dead vnc proxy + // an op that held the lock (a racing run) may have restarted qemu; adopt it and only repair a dead vnc proxy running, err := reconcileRunningQEMU(dir, r) if err != nil { return err diff --git a/cmd/vm/utils.go b/cmd/vm/utils.go index bc287fe..3026252 100644 --- a/cmd/vm/utils.go +++ b/cmd/vm/utils.go @@ -352,7 +352,7 @@ func powerDown(ctx context.Context, monSock string, r *record, grace time.Durati return false } if err := utils.WaitFor(ctx, grace, 200*time.Millisecond, func() (bool, error) { return !isRunning(r), nil }); err != nil { - logger.Infof(ctx, "guest %s did not halt within %s; signaling qemu", r.Name, grace) + logger.Warnf(ctx, "guest %s did not halt within %s; signaling qemu", r.Name, grace) } return true } diff --git a/cmd/vm/utils_test.go b/cmd/vm/utils_test.go index aa5f159..fcfcc95 100644 --- a/cmd/vm/utils_test.go +++ b/cmd/vm/utils_test.go @@ -164,17 +164,17 @@ func TestStopInstanceAsksTheGuestToPowerDownFirst(t *testing.T) { if err != nil { t.Fatal(err) } - defer ln.Close() //nolint:errcheck + defer ln.Close() got := make(chan string, 1) go func() { conn, err := ln.Accept() if err != nil { return } - defer conn.Close() //nolint:errcheck - fmt.Fprint(conn, hmpPrompt) //nolint:errcheck + defer conn.Close() + fmt.Fprint(conn, hmpPrompt) line, _ := bufio.NewReader(conn).ReadString('\n') - fmt.Fprint(conn, " "+line+hmpPrompt) //nolint:errcheck + fmt.Fprint(conn, " "+line+hmpPrompt) got <- strings.TrimSpace(line) }() disk := filepath.Join(dir, "disk.qcow2") diff --git a/cmd/vm/vnc.go b/cmd/vm/vnc.go index 5798097..cfbd654 100644 --- a/cmd/vm/vnc.go +++ b/cmd/vm/vnc.go @@ -20,7 +20,6 @@ import ( "github.com/cocoonstack/cocoon/utils" ) -// The proxy fronts a CNI VM's netns-local VNC unix socket on a host TCP port (see qemu.Spec.VNCSock). const ( vncSockName = "vnc.sock" vncProxyPID = "vnc-proxy.pid" diff --git a/qemu/launch.go b/qemu/launch.go index 334d2e9..c734ffe 100644 --- a/qemu/launch.go +++ b/qemu/launch.go @@ -22,14 +22,14 @@ const ( // Spec is the per-VM input for launching a macOS guest from a golden qcow2. type Spec struct { CPUs int - Memory string // MiB, e.g. "8192" - VNCDisp int // n => host 127.0.0.1:590n; <0 disables - VNCSock string // when set, bind VNC to this unix socket instead of 127.0.0.1 (CNI: fronted by vncProxy) - VNCPass string // set via the monitor post-launch (macOS Screen Sharing needs password auth) - SSHPort int // host port forwarded to guest :22; 0 disables - Hugepages bool // needs host hugepages reserved; off => default RAM - ExitOnReboot bool // exit QEMU on a guest reboot so an external owner can relaunch it cold - DataDisks []string // extra qcow2 data disks; attached on the AHCI ports MacHDD/OpenCore leave free + Memory string // MiB, e.g. "8192" + VNCDisp int // n => host 127.0.0.1:590n; <0 disables + VNCSock string // when set, bind VNC to this unix socket instead of 127.0.0.1 (CNI: fronted by vncProxy) + VNCPass string // set via the monitor post-launch (macOS Screen Sharing needs password auth) + SSHPort int // host port forwarded to guest :22; 0 disables + Hugepages bool // needs host hugepages reserved; off => default RAM + ExitOnReboot bool // exit QEMU on a guest reboot so an external owner can relaunch it cold + DataDisks []string Disk string OpenCore string diff --git a/qemu/snapshot.go b/qemu/snapshot.go index 5db64cd..9a5bbd2 100644 --- a/qemu/snapshot.go +++ b/qemu/snapshot.go @@ -29,7 +29,7 @@ type snapshotEntry struct { // SnapExists reports whether img carries the snapshot tag. func SnapExists(ctx context.Context, img, tag string) (bool, error) { - out, err := exec.CommandContext(ctx, "qemu-img", "info", "--output=json", img).Output() //nolint:gosec // img is an internal path + out, err := exec.CommandContext(ctx, "qemu-img", "info", "--output=json", img).Output() if err != nil { return false, fmt.Errorf("qemu-img info %s: %w", img, err) } From 5c0d3e8348d66ad91ae5d2c7fd4667e4738d48cf Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 01:21:35 +0800 Subject: [PATCH 04/10] review: take the stdlib v4 UUID for the SMBIOS identity and fold the partition wait predicate --- qemu/inject.go | 6 ++---- qemu/smbios.go | 18 +++--------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/qemu/inject.go b/qemu/inject.go index cda3374..4de2495 100644 --- a/qemu/inject.go +++ b/qemu/inject.go @@ -121,10 +121,8 @@ func withNBDLease(ctx context.Context, path string, fn func() error) error { // waitForPart blocks until the kernel's async partition scan exposes nbdXp1 for the mount. func waitForPart(ctx context.Context, nbd string) error { if err := utils.WaitFor(ctx, 5*time.Second, 100*time.Millisecond, func() (bool, error) { - if _, err := os.Stat(nbd + "p1"); err == nil { - return true, nil - } - return false, nil + _, err := os.Stat(nbd + "p1") + return err == nil, nil }); err != nil { return fmt.Errorf("wait for partition on %s: %w", nbd, err) } diff --git a/qemu/smbios.go b/qemu/smbios.go index 8e8812d..4caff5a 100644 --- a/qemu/smbios.go +++ b/qemu/smbios.go @@ -4,6 +4,8 @@ import ( "crypto/rand" "encoding/hex" "fmt" + "strings" + "uuid" ) const ( @@ -30,15 +32,11 @@ func RandomSMBIOS() (SMBIOS, error) { if err != nil { return SMBIOS{}, err } - uuid, err := randUUID() - if err != nil { - return SMBIOS{}, err - } rom, err := randROM() if err != nil { return SMBIOS{}, err } - return SMBIOS{Model: smbiosModel, Serial: serial, MLB: mlb, UUID: uuid, ROM: rom}, nil + return SMBIOS{Model: smbiosModel, Serial: serial, MLB: mlb, UUID: strings.ToUpper(uuid.NewV4().String()), ROM: rom}, nil } // MAC returns the ROM formatted as the default guest NIC MAC; CNI supplies its own runtime MAC. @@ -61,16 +59,6 @@ func randString(n int) (string, error) { return string(b), nil } -func randUUID() (string, error) { - b, err := randBytes(16) - if err != nil { - return "", err - } - b[6] = (b[6] & 0x0f) | 0x40 // version 4 - b[8] = (b[8] & 0x3f) | 0x80 // variant - return fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:16]), nil -} - func randROM() (string, error) { b, err := randBytes(6) if err != nil { From 3ff87e6072dc2ca87ca5f0550d3185689874fc50 Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 01:21:50 +0800 Subject: [PATCH 05/10] cut: drop the gc live-VM predicate and the empty-Add guard vmInUse never matched: the collectors only ask about IDs Resolve left after dropping every recorded VMID, sweep holds the gc lock and every VM lock so no record appears before Collect, and the bridge collector passes an 8-char prefix. provisionNet's empty-result guard never fired: both Add backends return one config per spec on success and AddRange(0, 1) is one spec. --- cmd/vm/gc.go | 20 +------------------- cmd/vm/net_linux.go | 4 ---- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/cmd/vm/gc.go b/cmd/vm/gc.go index 4f07600..921dd8e 100644 --- a/cmd/vm/gc.go +++ b/cmd/vm/gc.go @@ -80,7 +80,7 @@ func sweep(ctx context.Context, cmd *cobra.Command, registerNet func(*gc.Orchest gc.Register(o, vmGCModule(home.Dir(cmd), dirs)) if len(dirs) == 0 && !utils.FileExists(filepath.Join(home.VMsDir(cmd), ".locks", provisionedMarker)) { log.WithFunc("cmd.vm.sweep").Warnf(ctx, "no VM dir and no provisioning marker under %s: leaving the host's netns and TAPs alone (check --state-dir or $COCOON_MACOS_HOME; a root from before this build regains them at its next create)", home.Dir(cmd)) - } else if err := registerNet(o, cmd, vmInUse(dirs)); err != nil { + } else if err := registerNet(o, cmd, func(context.Context, string) (bool, error) { return false, nil }); err != nil { return err } return o.Run(ctx) @@ -165,24 +165,6 @@ func tryLockVMDirs(ctx context.Context, dirs []string) (func(), error) { return unlock, nil } -func vmInUse(dirs []string) network.VMInUse { - return func(_ context.Context, vmID string) (bool, error) { - for _, dir := range dirs { - r, err := loadRec(dir) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - continue - } - return false, fmt.Errorf("read vm %s for gc: %w", filepath.Base(dir), err) - } - if r.VMID == vmID { - return isRunning(r), nil - } - } - return false, nil - } -} - func vmGCModule(stateDir string, dirs []string) gc.Module[vmGCSnapshot] { return gc.Module[vmGCSnapshot]{ Name: "vm", diff --git a/cmd/vm/net_linux.go b/cmd/vm/net_linux.go index 9bcfd68..fdb1809 100644 --- a/cmd/vm/net_linux.go +++ b/cmd/vm/net_linux.go @@ -5,7 +5,6 @@ package vm import ( "cmp" "context" - "errors" "fmt" "os/exec" "path/filepath" @@ -77,9 +76,6 @@ func provisionNet(cmd *cobra.Command, r *record) (tap, netns, mac string, err er return "", "", "", fmt.Errorf("prepare network: %w", err) } cfgs, err := provider.Add(ctx, r.VMID, vmCfg, network.AddRange(0, 1)...) - if err == nil && len(cfgs) == 0 { - err = errors.New("network add returned no NIC") - } if err != nil { // Add rolls back only what it created itself; the netns from Prepare is ours to drop rctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), vmCleanupTimeout) From 20728307fdd760b0f8a402f31ff935298b7557c9 Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 01:22:13 +0800 Subject: [PATCH 06/10] docs: name the per-VM disk and OpenCore overlay, the start-time network rebuild, and the e2e script cli.md named overlay.qcow2 and a copied OpenCore; the VM dir holds disk.qcow2 and an OpenCore overlay. networking.md gains the #51 rebuild of lost owned networking. The e2e.sh header names the script it is. --- docs/cli.md | 4 ++-- docs/networking.md | 9 ++++++--- scripts/e2e.sh | 10 +++++----- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 741c18f..cb37b0b 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -91,9 +91,9 @@ snapshot and prevents new orphan collection; a read error is not evidence that t ## What `vm run` does -1. `qemu-img create -b overlay.qcow2` — instant copy-on-write clone of the golden image. +1. `qemu-img create -b disk.qcow2` — instant copy-on-write clone of the golden image. 2. Copy a per-VM `OVMF_VARS`. -3. With `--random-smbios`, copy OpenCore per-VM and inject a generated identity into its +3. With `--random-smbios`, bake a per-VM OpenCore overlay and inject a generated identity into its `config.plist` `PlatformInfo/Generic` via a `qemu-nbd` mount — model stays `iMac19,1` (proven to boot Tahoe), only serial/MLB/UUID/ROM are randomized. The identity is recorded and shown by `vm inspect`. diff --git a/docs/networking.md b/docs/networking.md index e2117b8..b160446 100644 --- a/docs/networking.md +++ b/docs/networking.md @@ -55,6 +55,9 @@ stops storming softirqs, and `vm start` (or the relaunch) brings it back up. `vm `create`) deletes the TAP and the netns; a netns or TAP left behind by a create or clone that was killed before its record was written is reclaimed by `cocoon-macos gc`. +`vm start` (and a forced restore's relaunch) rebuilds an auto-created TAP (under `cni` also its netns +and TC redirect) with the recorded MAC when the host lost it, e.g. after a reboot. + ## VNC exposure VNC exposure depends on the net mode: @@ -83,9 +86,9 @@ retargeted, so the CLI prints that the supplied VNC settings were ignored and ch QEMU's default `None` auth **hangs macOS Screen Sharing**. Pass `--vnc-password <≤8 bytes>` (applied via the QEMU monitor post-launch) so Screen Sharing prompts and connects. The launch is **rejected** -(not truncated) if the password exceeds 8 bytes or contains whitespace, control characters or double quotes — QEMU's VNC DES -auth only supports 8-byte passwords. Plain VNC clients (RealVNC/TigerVNC) work without a password on -the loopback modes. +(not truncated) if the password exceeds 8 bytes or contains whitespace, control characters or double +quotes — QEMU's VNC DES auth only supports 8-byte passwords. Plain VNC clients (RealVNC/TigerVNC) work +without a password on the loopback modes. > In-guest macOS Screen Sharing (to the guest's own IP) is **not** enabled headlessly — macOS > requires the Screen Recording TCC grant from the GUI or MDM. Use QEMU's built-in VNC above instead. diff --git a/scripts/e2e.sh b/scripts/e2e.sh index f3024db..7dcb513 100755 --- a/scripts/e2e.sh +++ b/scripts/e2e.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# cm-smoke.sh — cocoon-macos E2E regression, modeled on cocoonv2's issue28-repro.sh. +# scripts/e2e.sh — cocoon-macos E2E regression. # # Two tiers: # [DUMMY] file-level lifecycle on a tiny throwaway qcow2 — NO macOS boot. Fast, runs on any @@ -10,10 +10,10 @@ # Idempotent: cleans state-dir + test bridge + leftover cm* TAPs/netns at start AND end. # # Usage: -# sudo ./cm-smoke.sh # [DUMMY] tier only -# sudo ./cm-smoke.sh --real # [DUMMY] then [REAL] boot of tahoe:26 -# sudo ./cm-smoke.sh --no-net # skip the root/bridge --net auto-create rows -# sudo ./cm-smoke.sh cleanup # tear down and exit +# sudo ./scripts/e2e.sh # [DUMMY] tier only +# sudo ./scripts/e2e.sh --real # [DUMMY] then [REAL] boot of tahoe:26 +# sudo ./scripts/e2e.sh --no-net # skip the root/bridge --net auto-create rows +# sudo ./scripts/e2e.sh cleanup # tear down and exit # # Env overrides: # CM_BIN path to the cocoon-macos binary (default: ./cocoon-macos) From 1641224ade018b06efe7e731e767466a7bd2f117 Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 01:39:11 +0800 Subject: [PATCH 07/10] fix: fail a qmp-input command on a QMP error or a closed socket cmd dropped error replies and EOF, so a failed screendump left OCR reading the previous /tmp/_ocr.ppm and a rejected key or click still exited 0. --- scripts/qmp-input.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/qmp-input.py b/scripts/qmp-input.py index b56839f..4be1475 100644 --- a/scripts/qmp-input.py +++ b/scripts/qmp-input.py @@ -53,7 +53,7 @@ def __init__(self, path: str) -> None: self.f = self.s.makefile("rw") self.f.readline() # greeting self.cmd("qmp_capabilities") - except OSError: + except (OSError, RuntimeError): self.close() raise @@ -71,7 +71,7 @@ def close(self) -> None: self.f = None self.s.close() - def cmd(self, execute: str, **args: object) -> dict | None: + def cmd(self, execute: str, **args: object) -> dict: """Send a QMP command and return its response, skipping async events.""" msg: dict[str, object] = {"execute": execute} if args: @@ -81,9 +81,11 @@ def cmd(self, execute: str, **args: object) -> dict | None: while True: line = self.f.readline() if not line: - return None + raise RuntimeError(f"qmp closed before answering {execute}") obj = json.loads(line) - if "return" in obj or "error" in obj: + if "error" in obj: + raise RuntimeError(f"qmp {execute} failed: {obj['error'].get('desc', obj['error'])}") + if "return" in obj: return obj def send_events(self, events: list[dict]) -> None: From 205e5865993c19b365fcd151fa41f231b9d310ef Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 01:39:25 +0800 Subject: [PATCH 08/10] cut: drop qmp-input's ocrdclick op Its only callers, the picker double-clicks in build-qemu-macos.sh, went away in 5ef9948; nothing in the workspace calls it. --- scripts/qmp-input.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/scripts/qmp-input.py b/scripts/qmp-input.py index 4be1475..42b3738 100644 --- a/scripts/qmp-input.py +++ b/scripts/qmp-input.py @@ -163,21 +163,6 @@ def ocrclick(self, word: str, ymin: int = 0, ymax: int = 10 ** 9) -> bool: print("CLICK %s (%d,%d) conf=%.0f" % (word, cx, cy, conf)) return True - def ocrdclick(self, word: str, yoff: int = 0) -> bool: - """OCR-locate word and double-click at its center (+yoff to hit an icon above its label). - - Boots an OpenCore/OpenCanopy picker entry by name — its arrow keys are - unreliable but the picker takes mouse input. - """ - hits = self.ocr_find(word) - if not hits: - print("NOTFOUND %s" % word) - return False - cx, cy, conf = hits[0] - self.click(cx, cy + yoff, n=2) - print("DCLICK %s (%d,%d) conf=%.0f" % (word, cx, cy + yoff, conf)) - return True - def agree_button(self) -> tuple[int, int] | None: """Locate the macOS SLA "Agree" button: the one immediately right of a same-row "Disagree". @@ -248,9 +233,6 @@ def main() -> None: ymin = int(args[1]) if len(args) > 1 else 0 ymax = int(args[2]) if len(args) > 2 else 10 ** 9 sys.exit(0 if q.ocrclick(args[0], ymin, ymax) else 3) - elif op == "ocrdclick": - yoff = int(args[1]) if len(args) > 1 else 0 - sys.exit(0 if q.ocrdclick(args[0], yoff) else 3) elif op == "agreebtn": btn = q.agree_button() if btn: From 42f6a43be0611411d744836016a624d1f2989beb Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 01:40:05 +0800 Subject: [PATCH 09/10] review: list qmp-input's live ops, fold the agree_button docstring, and take f-strings --- scripts/qmp-input.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/scripts/qmp-input.py b/scripts/qmp-input.py index 42b3738..c71dcd2 100644 --- a/scripts/qmp-input.py +++ b/scripts/qmp-input.py @@ -5,11 +5,16 @@ the 0..32767 range onto the framebuffer) plus key presses and screendumps. Usage: - qmp-input.py SOCK click PX PY # single left click at pixel (PX,PY) - qmp-input.py SOCK dclick PX PY # double click - qmp-input.py SOCK move PX PY # move pointer only - qmp-input.py SOCK key K [K...] # send qcode key(s), e.g. ret down spc - qmp-input.py SOCK screendump FILE # write framebuffer ppm + qmp-input.py SOCK click PX PY # single left click at pixel (PX,PY) + qmp-input.py SOCK dclick PX PY # double click + qmp-input.py SOCK move PX PY # move pointer only + qmp-input.py SOCK key K [K...] # send qcode key(s) one by one, e.g. ret down spc + qmp-input.py SOCK chord K [K...] # press qcode keys together, e.g. meta_l q + qmp-input.py SOCK type TEXT # type ASCII text, adding shift where needed + qmp-input.py SOCK ocrclick WORD [YMIN YMAX] # OCR-locate WORD and click it; exit 3 if not found + qmp-input.py SOCK ocrtext [YMIN [YMAX]] # print the OCR words between YMIN and YMAX + qmp-input.py SOCK agreebtn # click the active SLA Agree button; exit 3 if not found + qmp-input.py SOCK screendump FILE # write framebuffer ppm Screen size defaults to 1280x800; override with QMP_W / QMP_H env vars. """ from __future__ import annotations @@ -156,20 +161,15 @@ def ocr_text(self, ymin: int = 0, ymax: int = 10 ** 9) -> str: def ocrclick(self, word: str, ymin: int = 0, ymax: int = 10 ** 9) -> bool: hits = self.ocr_find(word, ymin, ymax) if not hits: - print("NOTFOUND %s" % word) + print(f"NOTFOUND {word}") return False cx, cy, conf = hits[0] self.click(cx, cy) - print("CLICK %s (%d,%d) conf=%.0f" % (word, cx, cy, conf)) + print(f"CLICK {word} ({cx},{cy}) conf={conf:.0f}") return True def agree_button(self) -> tuple[int, int] | None: - """Locate the macOS SLA "Agree" button: the one immediately right of a same-row "Disagree". - - This excludes the body-text "...read and agree to the terms..."; when a - confirm sheet overlays the license pane, the topmost such pair is the - active modal button (background license buttons sit lower, greyed out). - """ + """Locate the SLA "Agree" button: the topmost one right of a same-row "Disagree", else the lowest "Agree".""" ag = self.ocr_find("Agree") dis = self.ocr_find("Disagree") pairs = [] @@ -179,7 +179,7 @@ def agree_button(self) -> tuple[int, int] | None: pairs.append((ay, ax)) break if pairs: - pairs.sort() # smallest y first = topmost active button row + pairs.sort() return pairs[0][1], pairs[0][0] if ag: # no Disagree paired: take the lowest Agree (plain license button at the bottom) lo = max(ag, key=lambda t: t[1]) @@ -237,7 +237,7 @@ def main() -> None: btn = q.agree_button() if btn: q.click(*btn) - print("AGREE (%d,%d)" % btn) + print(f"AGREE ({btn[0]},{btn[1]})") else: print("NO-AGREE-BUTTON") sys.exit(3) From a96cc7621b576c21e8c2497c21d02cf8aa0f9947 Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 24 Sep 2026 10:55:01 +0800 Subject: [PATCH 10/10] build: pin cocoon to dcec1fa and take cliutil.GroupCommand for the command groups cocoon #264 made GroupCommand the one owner of the group contract (NoArgs + help); the two groups here now use it. Checked on the built binary: vm and image print help with rc 0, vm bogus and image bogus fail with unknown command. --- cmd/image/commands.go | 7 +---- cmd/vm/commands.go | 7 +---- go.mod | 22 ++++++++------- go.sum | 64 +++++++++++++++++++++++++------------------ 4 files changed, 52 insertions(+), 48 deletions(-) diff --git a/cmd/image/commands.go b/cmd/image/commands.go index cbdd859..65fc9dd 100644 --- a/cmd/image/commands.go +++ b/cmd/image/commands.go @@ -7,12 +7,7 @@ import ( ) func Command() *cobra.Command { - imageCmd := &cobra.Command{ - Use: "image", - Short: "Manage macOS disk images (reuses cocoon's cloudimg store)", - Args: cobra.NoArgs, - RunE: cliutil.ShowHelp, - } + imageCmd := cliutil.GroupCommand("image", "Manage macOS disk images (reuses cocoon's cloudimg store)") pull := &cobra.Command{ Use: "pull REF", diff --git a/cmd/vm/commands.go b/cmd/vm/commands.go index 3af6aa6..5a415ac 100644 --- a/cmd/vm/commands.go +++ b/cmd/vm/commands.go @@ -7,12 +7,7 @@ import ( ) func Command() *cobra.Command { - vmCmd := &cobra.Command{ - Use: "vm", - Short: "Manage macOS VMs", - Args: cobra.NoArgs, - RunE: cliutil.ShowHelp, - } + vmCmd := cliutil.GroupCommand("vm", "Manage macOS VMs") createCmd := &cobra.Command{ Use: "create [flags] IMAGE", diff --git a/go.mod b/go.mod index 962daa7..41f0a70 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cocoonstack/cocoon-macos go 1.27.0 require ( - github.com/cocoonstack/cocoon v0.6.9-0.20260923164306-1951f352e9f2 + github.com/cocoonstack/cocoon v0.6.9-0.20260924025202-dcec1fa3f056 github.com/gofrs/flock v0.13.1 github.com/opencontainers/image-spec v1.1.1 github.com/projecteru2/core v0.1.5 @@ -13,10 +13,11 @@ require ( ) require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cockroachdb/errors v1.14.0 // indirect github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect github.com/cockroachdb/redact v1.1.8 // indirect - github.com/containernetworking/cni v1.3.0 // indirect + github.com/containernetworking/cni v1.3.1 // indirect github.com/containernetworking/plugins v1.9.1 // indirect github.com/creack/pty v1.1.24 // indirect github.com/docker/cli v29.8.0+incompatible // indirect @@ -26,10 +27,10 @@ require ( github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/getsentry/sentry-go v0.49.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/go-containerregistry v0.21.7 // indirect + github.com/google/go-containerregistry v0.22.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.19.2 // indirect + github.com/klauspost/compress v1.20.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/mattn/go-colorable v0.1.15 // indirect @@ -46,16 +47,17 @@ require ( github.com/spf13/pflag v1.0.10 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect - golang.org/x/mod v0.40.0 // indirect - golang.org/x/sync v0.22.0 // indirect - golang.org/x/sys v0.47.0 // indirect + go.opentelemetry.io/otel v1.45.0 // indirect + go.opentelemetry.io/otel/trace v1.45.0 // indirect + golang.org/x/sync v0.23.0 // indirect + golang.org/x/sys v0.48.0 // indirect golang.org/x/text v0.41.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260918162117-cecb64721679 // indirect google.golang.org/grpc v1.83.2 // indirect google.golang.org/protobuf v1.36.12 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect - modernc.org/libc v1.74.1 // indirect + modernc.org/libc v1.75.7 // indirect modernc.org/mathutil v1.7.1 // indirect - modernc.org/memory v1.11.0 // indirect - modernc.org/sqlite v1.54.0 // indirect + modernc.org/memory v1.12.1 // indirect + modernc.org/sqlite v1.59.0 // indirect ) diff --git a/go.sum b/go.sum index ef4f36d..a53b3ba 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,17 @@ github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cockroachdb/errors v1.14.0 h1:EfdVEJpN3z8rPMo43Yit59LxoiIa470fSXpZXuEs+ZI= github.com/cockroachdb/errors v1.14.0/go.mod h1:xRa70jZ9sNBQmISt5KmJmAD++E4dQHm89oCRiZGEdq0= github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k= github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo= github.com/cockroachdb/redact v1.1.8 h1:8eVLLj6juKxiKrAEw2b8cJvNqWq++U8WOfQFuL7KTaA= github.com/cockroachdb/redact v1.1.8/go.mod h1:GceHHpJ0rMDpYARL5In88Alq/xMBUtVlz7Qxix6ZVkw= -github.com/cocoonstack/cocoon v0.6.9-0.20260923164306-1951f352e9f2 h1:3AKsHYAyZVHh2mYXC7dAGvrC/oCf5rDUKDqbB7eEUZU= -github.com/cocoonstack/cocoon v0.6.9-0.20260923164306-1951f352e9f2/go.mod h1:B02b6pewrk1DbrRn+iBtDc8LNQWxwot2qF1j4YX5/jg= -github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo= -github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= +github.com/cocoonstack/cocoon v0.6.9-0.20260924025202-dcec1fa3f056 h1:WlBQ1pI36wKAQbD31NMLJEyd22kLmwdORuZMmhGhTm4= +github.com/cocoonstack/cocoon v0.6.9-0.20260924025202-dcec1fa3f056/go.mod h1:hnIOJMDDAZq4qsXlT3uhTAib1G+LN9TTiA4zYySnugw= +github.com/containernetworking/cni v1.3.1 h1:gnHlU/YC1bJcS+CSh+pYQKeuu4O4gsMGW1NMaLhkzW4= +github.com/containernetworking/cni v1.3.1/go.mod h1:OtVXL0yXMfQb+p93OC/vRuhjpxsO1gAV0smjt28UQUQ= github.com/containernetworking/plugins v1.9.1 h1:8oU6WsIsU3bpnNZuvHp74a6cE1MJwbj2P7s4/yTUNlA= github.com/containernetworking/plugins v1.9.1/go.mod h1:fj7kS55qg3o/RgS+WGsF3+ZxwIImMPusQZKzBpcSr4c= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -32,6 +34,8 @@ github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxI github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gofrs/flock v0.13.1 h1:jjREztyBeSKBZYAC+mgc1laB+xsgy4kYMf3FbKF2UBo= @@ -40,10 +44,10 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-containerregistry v0.21.7 h1:/vPFuVXDjtFREsVArW+0h1CIl5urnOhzei4X2DMW9IU= -github.com/google/go-containerregistry v0.21.7/go.mod h1:kjSbt7/zMsKLWfnHrIvKvhXHUw91jbe9DNjPPJ32gXE= -github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oXJCETMO23COyaKGP6fHVpkpWpg= -github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +github.com/google/go-containerregistry v0.22.1 h1:RZuuSYhTvlDvtsK+NkutoCZ//C0X2ebLK8X8l3ULs84= +github.com/google/go-containerregistry v0.22.1/go.mod h1:bJR35SK8XgisYmhg/FMQ/5RK0S/XrOAqLBV5/LR2XE0= +github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo= +github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -53,8 +57,8 @@ github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= -github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA= +github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -105,6 +109,14 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= +go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= +go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= +go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= +go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= +go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= @@ -128,15 +140,15 @@ golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= +golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= +golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= @@ -165,30 +177,30 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM= howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= -modernc.org/cc/v4 v4.29.0 h1:CXgwL8cvxmyzBQZzbSl/6xFtMCryb6u8IOqDci39cgc= -modernc.org/cc/v4 v4.29.0/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= -modernc.org/ccgo/v4 v4.34.6 h1:sBgfIwyN0TQ9C5hwIeuqyeAKyMWnbvj2fvpF4L11uzU= -modernc.org/ccgo/v4 v4.34.6/go.mod h1:SZ8YcN9NG7XVsQYdm6jYBvi8PQP1qi+kqB6OhjqI3Fk= +modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8= +modernc.org/cc/v4 v4.29.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI= +modernc.org/ccgo/v4 v4.35.0 h1:F+TUsmw09QxLzmi3aeYYGxjAXarmZaKgj3mKQHNaA8w= +modernc.org/ccgo/v4 v4.35.0/go.mod h1:qrVGs9S3Sr2Ztcg9ve+kTAYMp5a3YvWjo+SoN06kJ5I= modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM= modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= -modernc.org/gc/v3 v3.1.4 h1:2g65LGVSmFQrXeITAw97x7hCRvZFcyE1uDP+7Vng7JI= -modernc.org/gc/v3 v3.1.4/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= +modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc= +modernc.org/gc/v3 v3.1.5/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= -modernc.org/libc v1.74.1 h1:bdR4VTKFMC4966QSNZ05XLGI/VwzVa2kTUX51Dm0riQ= -modernc.org/libc v1.74.1/go.mod h1:uH4t5bOx3G3g9Xcmj10YKlTcVISlRDwv8VoQJG9n8Os= +modernc.org/libc v1.75.7 h1:o3DTP9/0p9pKmY2WCKQaySW6wIiZhNM7wc2lUoyhfew= +modernc.org/libc v1.75.7/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= -modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= -modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= +modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g= +modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg= modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.54.0 h1:JCxR4qwkJvOaqAoYcgDoO25Nc+ROg6EJ2LfBVzdrgog= -modernc.org/sqlite v1.54.0/go.mod h1:4ntCLuNmnH8+GNqjka1wNg7KJd5/Hi5FYp8K+XQ7GZw= +modernc.org/sqlite v1.59.0 h1:X1es1GpqBlS/5T+vbM4HLUdaa8OtQx468DF2vrx+38A= +modernc.org/sqlite v1.59.0/go.mod h1:+paeT2A3iPRHkQDwG7oA6Tk0zQd5woMEI8q7orfry8k= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=