Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package hcloud-cli for openSUSE:Factory checked in at 2026-09-28 10:39:12 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/hcloud-cli (Old) and /work/SRC/openSUSE:Factory/.hcloud-cli.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "hcloud-cli" Mon Sep 28 10:39:12 2026 rev:24 rq:1380299 version:1.69.0 Changes: -------- --- /work/SRC/openSUSE:Factory/hcloud-cli/hcloud-cli.changes 2026-09-17 15:20:09.221827983 +0200 +++ /work/SRC/openSUSE:Factory/.hcloud-cli.new.383539/hcloud-cli.changes 2026-09-28 10:40:27.200871568 +0200 @@ -1,0 +2,18 @@ +Fri Sep 25 04:50:48 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 1.69.0: + * Features + - network: implement list-members command (#1491) (d4e3726) + * Bug Fixes + - primary-ip: add fallback for unknown assignee types (#1493) + (0856baa) + * Dependencies + - deps: update dependency jdx/mise to v2026.9.11 (#1486) + - deps: update dependency github:goreleaser/goreleaser to + v2.18.2 (#1488) + - deps: update module github.com/dustin/go-humanize to v1.1.0 + (#1490) + - deps: update module github.com/hetznercloud/hcloud-go/v2 to + v2.49.0 (#1489) + +------------------------------------------------------------------- Old: ---- hcloud-cli-1.68.0.obscpio New: ---- hcloud-cli-1.69.0.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ hcloud-cli.spec ++++++ --- /var/tmp/diff_new_pack.gOA2S8/_old 2026-09-28 10:40:28.181912705 +0200 +++ /var/tmp/diff_new_pack.gOA2S8/_new 2026-09-28 10:40:28.183912789 +0200 @@ -19,7 +19,7 @@ %define executable_name hcloud Name: hcloud-cli -Version: 1.68.0 +Version: 1.69.0 Release: 0 Summary: A command-line interface for Hetzner Cloud License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.gOA2S8/_old 2026-09-28 10:40:28.214914089 +0200 +++ /var/tmp/diff_new_pack.gOA2S8/_new 2026-09-28 10:40:28.216914173 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/hetznercloud/cli.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">refs/tags/v1.68.0</param> + <param name="revision">refs/tags/v1.69.0</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.gOA2S8/_old 2026-09-28 10:40:28.235914970 +0200 +++ /var/tmp/diff_new_pack.gOA2S8/_new 2026-09-28 10:40:28.238915096 +0200 @@ -3,6 +3,6 @@ <param name="url">https://github.com/hetznercloud/cli</param> <param name="changesrevision">a9f3077b036d9145ef75e17b51483fa4481c0a9f</param></service><service name="tar_scm"> <param name="url">https://github.com/hetznercloud/cli.git</param> - <param name="changesrevision">177c1b5eeb9e2410b19531ed85a8ecd9d96920f8</param></service></servicedata> + <param name="changesrevision">88350084112192df1317e3cc101419ddcb85c689</param></service></servicedata> (No newline at EOF) ++++++ hcloud-cli-1.68.0.obscpio -> hcloud-cli-1.69.0.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/CHANGELOG.md new/hcloud-cli-1.69.0/CHANGELOG.md --- old/hcloud-cli-1.68.0/CHANGELOG.md 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/CHANGELOG.md 2026-09-24 16:48:58.000000000 +0200 @@ -1,5 +1,17 @@ # Changelog +## [v1.69.0](https://github.com/hetznercloud/cli/releases/tag/v1.69.0) + +[Compare to previous version](https://github.com/hetznercloud/cli/compare/v1.68.0...v1.69.0) + +### Features + +- **network**: implement list-members command (#1491) ([d4e3726](https://github.com/hetznercloud/cli/commit/d4e3726cbebba9f5a61389573fc518f03bc3a7ad)) + +### Bug Fixes + +- **primary-ip**: add fallback for unknown assignee types (#1493) ([0856baa](https://github.com/hetznercloud/cli/commit/0856baa767793f9e9d86a870fcd6432b4aedb954)) + ## [v1.68.0](https://github.com/hetznercloud/cli/releases/tag/v1.68.0) [Compare to previous version](https://github.com/hetznercloud/cli/compare/v1.67.0...v1.68.0) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/docs/reference/manual/hcloud_network.md new/hcloud-cli-1.69.0/docs/reference/manual/hcloud_network.md --- old/hcloud-cli-1.68.0/docs/reference/manual/hcloud_network.md 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/docs/reference/manual/hcloud_network.md 2026-09-24 16:48:58.000000000 +0200 @@ -37,6 +37,7 @@ * [hcloud network enable-protection](hcloud_network_enable-protection.md) - Enable resource protection for a Network * [hcloud network expose-routes-to-vswitch](hcloud_network_expose-routes-to-vswitch.md) - Expose routes to connected vSwitch * [hcloud network list](hcloud_network_list.md) - List Networks +* [hcloud network list-members](hcloud_network_list-members.md) - List Network Members * [hcloud network remove-label](hcloud_network_remove-label.md) - Remove a label from a Network * [hcloud network remove-route](hcloud_network_remove-route.md) - Remove a route from a Network * [hcloud network remove-subnet](hcloud_network_remove-subnet.md) - Remove a subnet from a Network diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/docs/reference/manual/hcloud_network_list-members.md new/hcloud-cli-1.69.0/docs/reference/manual/hcloud_network_list-members.md --- old/hcloud-cli-1.68.0/docs/reference/manual/hcloud_network_list-members.md 1970-01-01 01:00:00.000000000 +0100 +++ new/hcloud-cli-1.69.0/docs/reference/manual/hcloud_network_list-members.md 2026-09-24 16:48:58.000000000 +0200 @@ -0,0 +1,55 @@ +## hcloud network list-members + +List Network Members + +### Synopsis + +Displays a list of Network Members. + +Output can be controlled with the -o flag. Use -o noheader to suppress the +table header. Displayed columns and their order can be set with +-o columns=alias_ips,id (see available columns below). + +Columns: + - alias_ips + - id + - ip + - status + - subnet + - type + +``` +hcloud network list-members [options] <network> +``` + +### Options + +``` + -h, --help help for list-members + -o, --output stringArray output options: noheader|columns=...|json|yaml + -l, --selector string Selector to filter by labels + -s, --sort strings Determine the sorting of the result + --status strings Only Members with one of these statuses are displayed + --subnet strings Only Members attached to one of these subnets are displayed + --type strings Only Members with one of these types are displayed +``` + +### Options inherited from parent commands + +``` + --config string Config file path (default "~/.config/hcloud/cli.toml") + --context string Currently active context + --debug Enable debug output + --debug-file string File to write debug output to + --endpoint string Hetzner Cloud API endpoint (default "https://api.hetzner.cloud/v1") + --hetzner-endpoint string Hetzner API endpoint (default "https://api.hetzner.com/v1") + --http-timeout duration Timeout for HTTP requests (default 0 = no timeout) + --no-experimental-warnings If true, experimental warnings are not shown + --poll-interval duration Interval at which to poll information, for example action progress (default 500ms) + --quiet If true, only print error messages +``` + +### SEE ALSO + +* [hcloud network](hcloud_network.md) - Manage Networks + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/go.mod new/hcloud-cli-1.69.0/go.mod --- old/hcloud-cli-1.68.0/go.mod 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/go.mod 2026-09-24 16:48:58.000000000 +0200 @@ -7,12 +7,12 @@ require ( github.com/BurntSushi/toml v1.6.0 github.com/cheggaaa/pb/v3 v3.2.1 - github.com/dustin/go-humanize v1.0.1 + github.com/dustin/go-humanize v1.1.0 github.com/fatih/color v1.19.0 github.com/fatih/structs v1.1.0 github.com/goccy/go-yaml v1.19.2 github.com/guptarohit/asciigraph v0.10.0 - github.com/hetznercloud/hcloud-go/v2 v2.48.0 + github.com/hetznercloud/hcloud-go/v2 v2.49.0 github.com/jedib0t/go-pretty/v6 v6.8.3 github.com/spf13/cast v1.10.0 github.com/spf13/cobra v1.10.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/go.sum new/hcloud-cli-1.69.0/go.sum --- old/hcloud-cli-1.68.0/go.sum 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/go.sum 2026-09-24 16:48:58.000000000 +0200 @@ -19,8 +19,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dustin/go-humanize v1.1.0 h1:dbKTrvD0klcbBV/h4AWJdMuZogJACoMlvWIWZ5b2xWg= +github.com/dustin/go-humanize v1.1.0/go.mod h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI= github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -37,8 +37,8 @@ github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/guptarohit/asciigraph v0.10.0 h1:LmbFXSHZOhaQxjJYexdRk7TzoC5sJ7vDTEjP1YUbKgY= github.com/guptarohit/asciigraph v0.10.0/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= -github.com/hetznercloud/hcloud-go/v2 v2.48.0 h1:P3biOxqEufOxp2xBEZTZGIl56xK4sFbwLkCW/52AbiE= -github.com/hetznercloud/hcloud-go/v2 v2.48.0/go.mod h1:J9QH6j8pRH0K3+HlqgOlQ8abXagWTD/GpTkfra2et+g= +github.com/hetznercloud/hcloud-go/v2 v2.49.0 h1:QXONxfgXIF99PFJknkVw+LrQQB4PB5IbEjEDh4Hfmig= +github.com/hetznercloud/hcloud-go/v2 v2.49.0/go.mod h1:J9QH6j8pRH0K3+HlqgOlQ8abXagWTD/GpTkfra2et+g= github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/internal/cmd/network/list_members.go new/hcloud-cli-1.69.0/internal/cmd/network/list_members.go --- old/hcloud-cli-1.68.0/internal/cmd/network/list_members.go 1970-01-01 01:00:00.000000000 +0100 +++ new/hcloud-cli-1.69.0/internal/cmd/network/list_members.go 2026-09-24 16:48:58.000000000 +0200 @@ -0,0 +1,140 @@ +package network + +import ( + "fmt" + "net" + "slices" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/hetznercloud/cli/internal/cmd/base" + "github.com/hetznercloud/cli/internal/cmd/cmpl" + "github.com/hetznercloud/cli/internal/cmd/output" + "github.com/hetznercloud/cli/internal/cmd/util" + "github.com/hetznercloud/cli/internal/hcapi2" + "github.com/hetznercloud/cli/internal/state" + "github.com/hetznercloud/cli/internal/state/config" + "github.com/hetznercloud/hcloud-go/v2/hcloud" + "github.com/hetznercloud/hcloud-go/v2/hcloud/schema" +) + +var networkMemberTypeStrings = []string{ + string(hcloud.NetworkMemberTypeLoadBalancer), + string(hcloud.NetworkMemberTypeServer), +} + +var networkMemberStatusStrings = []string{ + string(hcloud.NetworkMemberStatusAttaching), + string(hcloud.NetworkMemberStatusDetaching), + string(hcloud.NetworkMemberStatusError), + string(hcloud.NetworkMemberStatusOK), + string(hcloud.NetworkMemberStatusUpdating), +} + +var ListMembersCmd = &base.ListCmd[*hcloud.NetworkMember, schema.NetworkMember]{ + ResourceNamePlural: "Network Members", + JSONKeyGetByName: "members", + + DefaultColumns: []string{"type", "id", "status", "ip", "alias_ips", "subnet"}, + SortOption: config.OptionSortNetworkMember, + + ValidArgsFunction: func(client hcapi2.Client) cobra.CompletionFunc { + return cmpl.SuggestCandidatesF(client.Network().Names) + }, + + PositionalArgumentOverride: []string{"network"}, + + AdditionalFlags: func(cmd *cobra.Command) { + cmd.Flags().StringSlice("type", nil, "Only Members with one of these types are displayed") + _ = cmd.RegisterFlagCompletionFunc("type", cmpl.SuggestCandidates(networkMemberTypeStrings...)) + + cmd.Flags().StringSlice("status", nil, "Only Members with one of these statuses are displayed") + _ = cmd.RegisterFlagCompletionFunc("status", cmpl.SuggestCandidates(networkMemberStatusStrings...)) + + cmd.Flags().StringSlice("subnet", nil, "Only Members attached to one of these subnets are displayed") + }, + + FetchWithArgs: func(s state.State, flags *pflag.FlagSet, listOpts hcloud.ListOpts, sorts []string, args []string) ([]*hcloud.NetworkMember, error) { + networkIDOrName := args[0] + + types, _ := flags.GetStringSlice("type") + subnets, _ := flags.GetStringSlice("subnet") + statuses, _ := flags.GetStringSlice("status") + + opts := hcloud.NetworkMemberListOpts{ListOpts: listOpts} + if len(sorts) > 0 { + opts.Sort = sorts + } + + if len(types) > 0 { + for _, typ := range types { + if !slices.Contains(networkMemberTypeStrings, typ) { + return nil, fmt.Errorf("invalid type: %s", typ) + } + opts.Type = append(opts.Type, hcloud.NetworkMemberType(typ)) + } + } + + if len(subnets) > 0 { + for _, subnet := range subnets { + _, ipNet, err := net.ParseCIDR(subnet) + if err != nil { + return nil, fmt.Errorf("invalid subnet: %s", subnet) + } + opts.Subnet = append(opts.Subnet, ipNet) + } + } + + if len(statuses) > 0 { + for _, status := range statuses { + if !slices.Contains(networkMemberStatusStrings, status) { + return nil, fmt.Errorf("invalid status: %s", status) + } + opts.Status = append(opts.Status, hcloud.NetworkMemberStatus(status)) + } + } + + network, _, err := s.Client().Network().Get(s, networkIDOrName) + if err != nil { + return nil, err + } + if network == nil { + return nil, fmt.Errorf("Network not found: %s", networkIDOrName) + } + + return s.Client().Network().AllMembersWithOpts(s, network, opts) + }, + + OutputTable: func(t *output.Table[*hcloud.NetworkMember], _ hcapi2.Client) { + t. + AddAllowedFields(&hcloud.NetworkMember{}). + AddFieldFn("ip", func(member *hcloud.NetworkMember) string { + if member.IP == nil { + return util.NA("") + } + return member.IP.String() + }). + AddFieldFn("alias_ips", func(member *hcloud.NetworkMember) string { + var aliasIPs []string + for _, aliasIP := range member.AliasIPs { + aliasIPs = append(aliasIPs, aliasIP.String()) + } + return util.NA(strings.Join(aliasIPs, "\n")) + }). + AddFieldFn("subnet", func(member *hcloud.NetworkMember) string { + if member.Subnet == nil { + return util.NA("") + } + return member.Subnet.String() + }) + }, + + Schema: hcloud.SchemaFromNetworkMember, + + Configure: func(_ state.State, cmd *cobra.Command) *cobra.Command { + cmd.Use = "list-members [options] <network>" + return cmd + }, +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/internal/cmd/network/list_members_test.go new/hcloud-cli-1.69.0/internal/cmd/network/list_members_test.go --- old/hcloud-cli-1.68.0/internal/cmd/network/list_members_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/hcloud-cli-1.69.0/internal/cmd/network/list_members_test.go 2026-09-24 16:48:58.000000000 +0200 @@ -0,0 +1,78 @@ +package network_test + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + "github.com/hetznercloud/cli/internal/cmd/network" + "github.com/hetznercloud/cli/internal/testutil" + "github.com/hetznercloud/hcloud-go/v2/hcloud" +) + +func TestListMembers(t *testing.T) { + fx := testutil.NewFixture(t) + defer fx.Finish() + + cmd := network.ListMembersCmd.CobraCommand(fx.State()) + + networkRes := &hcloud.Network{ + ID: 123, + Name: "test-network", + IPRange: &net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(16, 32)}, + } + + members := []*hcloud.NetworkMember{ + { + Type: hcloud.NetworkMemberTypeServer, + ID: 42, + IP: net.IPv4(10, 0, 0, 2), + Status: hcloud.NetworkMemberStatusOK, + AliasIPs: []net.IP{net.IPv4(10, 0, 0, 3), net.IPv4(10, 0, 0, 4)}, + Subnet: &net.IPNet{ + IP: net.IPv4(10, 0, 0, 0), + Mask: net.CIDRMask(24, 32), + }, + }, + { + Type: hcloud.NetworkMemberTypeLoadBalancer, + ID: 7, + IP: net.IPv4(10, 0, 0, 5), + Status: hcloud.NetworkMemberStatusAttaching, + Subnet: &net.IPNet{ + IP: net.IPv4(10, 0, 0, 0), + Mask: net.CIDRMask(24, 32), + }, + }, + } + + fx.ExpectEnsureToken() + fx.Client.NetworkClient.EXPECT(). + Get(gomock.Any(), "test-network"). + Return(networkRes, nil, nil) + fx.Client.NetworkClient.EXPECT(). + AllMembersWithOpts( + gomock.Any(), + networkRes, + hcloud.NetworkMemberListOpts{ + ListOpts: hcloud.ListOpts{PerPage: 50}, + Sort: []string{"id:asc"}, + }, + ). + Return(members, nil) + + out, errOut, err := fx.Run(cmd, []string{"test-network"}) + + expOut := `TYPE ID STATUS IP ALIAS IPS SUBNET +server 42 ok 10.0.0.2 10.0.0.3 10.0.0.0/24 + 10.0.0.4 +load_balancer 7 attaching 10.0.0.5 - 10.0.0.0/24 +` + + require.NoError(t, err) + assert.Empty(t, errOut) + assert.Equal(t, expOut, out) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/internal/cmd/network/network.go new/hcloud-cli-1.69.0/internal/cmd/network/network.go --- old/hcloud-cli-1.68.0/internal/cmd/network/network.go 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/internal/cmd/network/network.go 2026-09-24 16:48:58.000000000 +0200 @@ -43,5 +43,9 @@ AddSubnetCmd.CobraCommand(s), RemoveSubnetCmd.CobraCommand(s), ) + + util.AddGroup(cmd, "member", "Members", + ListMembersCmd.CobraCommand(s), + ) return cmd } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/internal/cmd/primaryip/list.go new/hcloud-cli-1.69.0/internal/cmd/primaryip/list.go --- old/hcloud-cli-1.68.0/internal/cmd/primaryip/list.go 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/internal/cmd/primaryip/list.go 2026-09-24 16:48:58.000000000 +0200 @@ -6,6 +6,8 @@ "time" "github.com/spf13/pflag" + "golang.org/x/text/cases" + "golang.org/x/text/language" "github.com/hetznercloud/cli/internal/cmd/base" "github.com/hetznercloud/cli/internal/cmd/output" @@ -59,6 +61,11 @@ switch primaryIP.AssigneeType { case "server": assignee = fmt.Sprintf("Server %s", client.Server().ServerName(primaryIP.AssigneeID)) + case "unassigned", "": + break + default: + // fallback: foo_bar 123 -> Foo Bar 123 + assignee = fmt.Sprintf("%s %d", cases.Title(language.English).String(strings.ReplaceAll(primaryIP.AssigneeType, "_", " ")), primaryIP.AssigneeID) } } return util.NA(assignee) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/internal/hcapi2/mock/zz_hcapi_mock.go new/hcloud-cli-1.69.0/internal/hcapi2/mock/zz_hcapi_mock.go --- old/hcloud-cli-1.68.0/internal/hcapi2/mock/zz_hcapi_mock.go 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/internal/hcapi2/mock/zz_hcapi_mock.go 2026-09-24 16:48:58.000000000 +0200 @@ -2529,6 +2529,36 @@ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "All", reflect.TypeOf((*MockNetworkClient)(nil).All), ctx) } +// AllMembers mocks base method. +func (m *MockNetworkClient) AllMembers(ctx context.Context, network *hcloud.Network) ([]*hcloud.NetworkMember, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AllMembers", ctx, network) + ret0, _ := ret[0].([]*hcloud.NetworkMember) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AllMembers indicates an expected call of AllMembers. +func (mr *MockNetworkClientMockRecorder) AllMembers(ctx, network any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllMembers", reflect.TypeOf((*MockNetworkClient)(nil).AllMembers), ctx, network) +} + +// AllMembersWithOpts mocks base method. +func (m *MockNetworkClient) AllMembersWithOpts(ctx context.Context, network *hcloud.Network, opts hcloud.NetworkMemberListOpts) ([]*hcloud.NetworkMember, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AllMembersWithOpts", ctx, network, opts) + ret0, _ := ret[0].([]*hcloud.NetworkMember) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AllMembersWithOpts indicates an expected call of AllMembersWithOpts. +func (mr *MockNetworkClientMockRecorder) AllMembersWithOpts(ctx, network, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AllMembersWithOpts", reflect.TypeOf((*MockNetworkClient)(nil).AllMembersWithOpts), ctx, network, opts) +} + // AllWithOpts mocks base method. func (m *MockNetworkClient) AllWithOpts(ctx context.Context, opts hcloud.NetworkListOpts) ([]*hcloud.Network, error) { m.ctrl.T.Helper() @@ -2717,6 +2747,22 @@ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockNetworkClient)(nil).List), ctx, opts) } +// ListMembers mocks base method. +func (m *MockNetworkClient) ListMembers(ctx context.Context, network *hcloud.Network, opts hcloud.NetworkMemberListOpts) ([]*hcloud.NetworkMember, *hcloud.Response, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListMembers", ctx, network, opts) + ret0, _ := ret[0].([]*hcloud.NetworkMember) + ret1, _ := ret[1].(*hcloud.Response) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// ListMembers indicates an expected call of ListMembers. +func (mr *MockNetworkClientMockRecorder) ListMembers(ctx, network, opts any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListMembers", reflect.TypeOf((*MockNetworkClient)(nil).ListMembers), ctx, network, opts) +} + // Name mocks base method. func (m *MockNetworkClient) Name(arg0 int64) string { m.ctrl.T.Helper() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/internal/state/config/options.go new/hcloud-cli-1.69.0/internal/state/config/options.go --- old/hcloud-cli-1.68.0/internal/state/config/options.go 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/internal/state/config/options.go 2026-09-24 16:48:58.000000000 +0200 @@ -259,6 +259,15 @@ nil, ) + OptionSortNetworkMember = newOpt( + "sort.network.member", + "Default sorting for Network Member resource", + []string{"id:asc"}, + (DefaultPreferenceFlags&^OptionFlagPFlag)|OptionFlagSlice|OptionFlagHidden, + nil, + nil, + ) + OptionSortPlacementGroup = newOpt( "sort.placement-group", "Default sorting for Placement Group resource", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/internal/version/version.go new/hcloud-cli-1.69.0/internal/version/version.go --- old/hcloud-cli-1.68.0/internal/version/version.go 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/internal/version/version.go 2026-09-24 16:48:58.000000000 +0200 @@ -4,7 +4,7 @@ var ( // version is a semver version (https://semver.org). - version = "1.68.0" // x-releaser-pleaser-version + version = "1.69.0" // x-releaser-pleaser-version // versionPrerelease is a semver version pre-release identifier (https://semver.org). // diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/mise.toml new/hcloud-cli-1.69.0/mise.toml --- old/hcloud-cli-1.68.0/mise.toml 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/mise.toml 2026-09-24 16:48:58.000000000 +0200 @@ -2,6 +2,6 @@ go = "1.26.8" "go:go.uber.org/mock/mockgen" = "v0.6.0" "github:golangci/golangci-lint" = "2.13.2" -"github:goreleaser/goreleaser" = "v2.18.1" +"github:goreleaser/goreleaser" = "v2.18.2" "github:anchore/quill" = "v0.7.1" "github:jstemmer/go-junit-report" = "v2.1.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/test/e2e/e2e_test.go new/hcloud-cli-1.69.0/test/e2e/e2e_test.go --- old/hcloud-cli-1.68.0/test/e2e/e2e_test.go 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/test/e2e/e2e_test.go 2026-09-24 16:48:58.000000000 +0200 @@ -50,6 +50,7 @@ var buf bytes.Buffer cmd.SetArgs(args) cmd.SetOut(&buf) + cmd.SetErr(t.Output()) err := cmd.Execute() return buf.String(), err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-cli-1.68.0/test/e2e/network_test.go new/hcloud-cli-1.69.0/test/e2e/network_test.go --- old/hcloud-cli-1.68.0/test/e2e/network_test.go 2026-09-14 17:14:03.000000000 +0200 +++ new/hcloud-cli-1.69.0/test/e2e/network_test.go 2026-09-24 16:48:58.000000000 +0200 @@ -65,6 +65,20 @@ ) }) + t.Run("list-members", func(t *testing.T) { + t.Run("non-existing-network", func(t *testing.T) { + out, err := runCommand(t, "network", "list-members", "non-existing-network") + require.EqualError(t, err, "Network not found: non-existing-network") + assert.Empty(t, out) + }) + + t.Run("no-members", func(t *testing.T) { + out, err := runCommand(t, "network", "list-members", strconv.FormatInt(networkID, 10)) + require.NoError(t, err) + assert.Equal(t, "TYPE ID STATUS IP ALIAS IPS SUBNET\n", out) + }) + }) + t.Run("change-ip-range", func(t *testing.T) { t.Run("non-existing-network", func(t *testing.T) { out, err := runCommand(t, "network", "change-ip-range", "--ip-range", "10.0.2.0/16", "non-existing-network") ++++++ hcloud-cli.obsinfo ++++++ --- /var/tmp/diff_new_pack.gOA2S8/_old 2026-09-28 10:40:28.595930066 +0200 +++ /var/tmp/diff_new_pack.gOA2S8/_new 2026-09-28 10:40:28.599930234 +0200 @@ -1,5 +1,5 @@ name: hcloud-cli -version: 1.68.0 -mtime: 1789398843 -commit: 177c1b5eeb9e2410b19531ed85a8ecd9d96920f8 +version: 1.69.0 +mtime: 1790261338 +commit: 88350084112192df1317e3cc101419ddcb85c689 ++++++ vendor.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/.envrc new/vendor/github.com/dustin/go-humanize/.envrc --- old/vendor/github.com/dustin/go-humanize/.envrc 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/dustin/go-humanize/.envrc 2026-09-24 16:48:58.000000000 +0200 @@ -0,0 +1,4 @@ +# shellcheck shell=bash +set -eu + +use flake diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/.gitignore new/vendor/github.com/dustin/go-humanize/.gitignore --- old/vendor/github.com/dustin/go-humanize/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/dustin/go-humanize/.gitignore 2026-09-24 16:48:58.000000000 +0200 @@ -0,0 +1 @@ +.direnv diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/.travis.yml new/vendor/github.com/dustin/go-humanize/.travis.yml --- old/vendor/github.com/dustin/go-humanize/.travis.yml 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/dustin/go-humanize/.travis.yml 1970-01-01 01:00:00.000000000 +0100 @@ -1,21 +0,0 @@ -sudo: false -language: go -go_import_path: github.com/dustin/go-humanize -go: - - 1.13.x - - 1.14.x - - 1.15.x - - 1.16.x - - stable - - master -matrix: - allow_failures: - - go: master - fast_finish: true -install: - - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). -script: - - diff -u <(echo -n) <(gofmt -d -s .) - - go vet . - - go install -v -race ./... - - go test -v -race ./... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/README.markdown new/vendor/github.com/dustin/go-humanize/README.markdown --- old/vendor/github.com/dustin/go-humanize/README.markdown 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/dustin/go-humanize/README.markdown 2026-09-24 16:48:58.000000000 +0200 @@ -1,4 +1,4 @@ -# Humane Units [](https://travis-ci.org/dustin/go-humanize) [](https://godoc.org/github.com/dustin/go-humanize) +# Humane Units [](https://travis-ci.org/dustin/go-humanize) [](https://godoc.org/github.com/dustin/go-humanize) [](https://dustin.github.io/go-humanize/) Just a few functions for helping humanize times and sizes. @@ -8,6 +8,9 @@ See [godoc](https://pkg.go.dev/github.com/dustin/go-humanize) for complete documentation. +See the [test coverage report](https://dustin.github.io/go-humanize/) for +coverage details. + ## Sizes This lets you take numbers like `82854982` and convert them to useful diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/SECURITY.md new/vendor/github.com/dustin/go-humanize/SECURITY.md --- old/vendor/github.com/dustin/go-humanize/SECURITY.md 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/dustin/go-humanize/SECURITY.md 2026-09-24 16:48:58.000000000 +0200 @@ -0,0 +1,5 @@ +# Security Policy + +To report a security issue, please disclose it at [security advisory](https://github.com/dustin/go-humanize/security/advisories/new). + +We will respond within 7 working days of your submission. If the issue is confirmed as a vulnerability, we will open a Security Advisory and acknowledge your contributions as part of it. This project follows a 90 day disclosure timeline. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/bytes.go new/vendor/github.com/dustin/go-humanize/bytes.go --- old/vendor/github.com/dustin/go-humanize/bytes.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/dustin/go-humanize/bytes.go 2026-09-24 16:48:58.000000000 +0200 @@ -3,6 +3,7 @@ import ( "fmt" "math" + "math/bits" "strconv" "strings" "unicode" @@ -65,39 +66,90 @@ return math.Log(n) / math.Log(b) } -func humanateBytes(s uint64, base float64, sizes []string) string { +func countDigits(n int64) int { + digits := 0 + for n != 0 { + n /= 10 + digits += 1 + } + return digits +} + +func humanateBytes(s uint64, base float64, minDigits int, sizes []string) string { if s < 10 { return fmt.Sprintf("%d B", s) } e := math.Floor(logn(float64(s), base)) suffix := sizes[int(e)] - val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 - f := "%.0f %s" - if val < 10 { - f = "%.1f %s" + val := float64(s) / math.Pow(base, e) + + // Determine how many decimal places to keep based on the unrounded + // value's integer-digit count, then round exactly once to that + // precision. Rounding first to a fixed precision and then again when + // formatting (double rounding) can flip the result at a boundary due + // to floating point error (see #103). + intDigits := countDigits(int64(val)) + digits := max(minDigits-intDigits, 0) + rounding := math.Pow10(digits) + val = math.Floor(val*rounding+0.5) / rounding + + // Rounding may have carried into an additional integer digit (e.g. + // 9.996 -> 10.0), which means fewer decimal places should be shown + // than originally computed. + if newIntDigits := countDigits(int64(val)); newIntDigits > intDigits { + digits = max(digits-(newIntDigits-intDigits), 0) } + ff := "%%.%df %%s" + f := fmt.Sprintf(ff, digits) return fmt.Sprintf(f, val, suffix) } -// Bytes produces a human readable representation of an SI size. +// Bytes produces a human-readable representation of an SI size. // // See also: ParseBytes. // // Bytes(82854982) -> 83 MB func Bytes(s uint64) string { sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} - return humanateBytes(s, 1000, sizes) + return humanateBytes(s, 1000, 2, sizes) } -// IBytes produces a human readable representation of an IEC size. +// BytesN produces a human-readable representation of an SI size. +// n specifies the total number of digits to output, including the decimal part. +// If n is less than or equal to the number of digits in the integer part, the decimal part will be omitted. +// +// See also: ParseBytes. +// +// BytesN(82854982, 3) -> 82.9 MB +// BytesN(82854982, 4) -> 82.85 MB +func BytesN(s uint64, n int) string { + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} + return humanateBytes(s, 1000, n, sizes) +} + +// IBytes produces a human-readable representation of an IEC size. // // See also: ParseBytes. // // IBytes(82854982) -> 79 MiB func IBytes(s uint64) string { sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} - return humanateBytes(s, 1024, sizes) + return humanateBytes(s, 1024, 2, sizes) +} + +// IBytesN produces a human-readable representation of an IEC size. +// n specifies the total number of digits to output, including the decimal part. +// If n is less than or equal to the number of digits in the integer part, the decimal part will be omitted. +// +// See also: ParseBytes. +// +// IBytesN(82854982, 4) -> 79.02 MiB +// IBytesN(123456789, 3) -> 118 MiB +// IBytesN(123456789, 6) -> 117.738 MiB +func IBytesN(s uint64, n int) string { + sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"} + return humanateBytes(s, 1024, n, sizes) } // ParseBytes parses a string representation of bytes into the number @@ -132,6 +184,18 @@ extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) if m, ok := bytesSizeTable[extra]; ok { + // Parse whole-number inputs exactly. A float64 cannot represent + // integers above 2^53, and the multiplication below would also reject + // otherwise-valid values near math.MaxUint64, so route integers through + // uint64 arithmetic with an overflow check. + if !strings.ContainsRune(num, '.') { + if v, perr := strconv.ParseUint(num, 10, 64); perr == nil { + if hi, lo := bits.Mul64(v, m); hi == 0 { + return lo, nil + } + return 0, fmt.Errorf("too large: %v", s) + } + } f *= float64(m) if f >= math.MaxUint64 { return 0, fmt.Errorf("too large: %v", s) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/comma.go new/vendor/github.com/dustin/go-humanize/comma.go --- old/vendor/github.com/dustin/go-humanize/comma.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/dustin/go-humanize/comma.go 2026-09-24 16:48:58.000000000 +0200 @@ -13,34 +13,48 @@ // // e.g. Comma(834142) -> 834,142 func Comma(v int64) string { - sign := "" + // Shortcut for [0, 7] + if v&^0b111 == 0 { + return string([]byte{byte(v) + 48}) + } // Min int64 can't be negated to a usable value, so it has to be special cased. if v == math.MinInt64 { return "-9,223,372,036,854,775,808" } + // Counting the number of digits. + var count byte = 0 + for n := v; n != 0; n = n / 10 { + count++ + } + count += (count - 1) / 3 if v < 0 { - sign = "-" v = 0 - v + count++ } + output := make([]byte, count) + j := len(output) - 1 - parts := []string{"", "", "", "", "", "", ""} - j := len(parts) - 1 - - for v > 999 { - parts[j] = strconv.FormatInt(v%1000, 10) - switch len(parts[j]) { - case 2: - parts[j] = "0" + parts[j] - case 1: - parts[j] = "00" + parts[j] - } - v = v / 1000 + var counter byte = 0 + for v > 9 { + output[j] = byte(v%10) + 48 + v = v / 10 j-- + if counter == 2 { + counter = 0 + output[j] = ',' + j-- + } else { + counter++ + } } - parts[j] = strconv.Itoa(int(v)) - return sign + strings.Join(parts[j:], ",") + + output[j] = byte(v) + 48 + if j == 1 { + output[0] = '-' + } + return string(output) } // Commaf produces a string form of the given number in base 10 with @@ -48,6 +62,16 @@ // // e.g. Commaf(834142.32) -> 834,142.32 func Commaf(v float64) string { + if math.IsNaN(v) { + return "NaN" + } + if math.IsInf(v, 1) { + return "+Inf" + } + if math.IsInf(v, -1) { + return "-Inf" + } + buf := &bytes.Buffer{} if v < 0 { buf.Write([]byte{'-'}) @@ -76,17 +100,30 @@ return buf.String() } -// CommafWithDigits works like the Commaf but limits the resulting -// string to the given number of decimal places. +// CommafWithDigits works like the Commaf but always formats the +// resulting string to exactly the given number of decimal places, +// truncating extra digits or padding with zeros as needed. // // e.g. CommafWithDigits(834142.32, 1) -> 834,142.3 +// e.g. CommafWithDigits(1000, 2) -> 1,000.00 func CommafWithDigits(f float64, decimals int) string { - return stripTrailingDigits(Commaf(f), decimals) + s := stripTrailingDigits(Commaf(f), decimals) + if decimals <= 0 { + return s + } + + if dot := strings.IndexByte(s, '.'); dot < 0 { + s += "." + strings.Repeat("0", decimals) + } else if have := len(s) - dot - 1; have < decimals { + s += strings.Repeat("0", decimals-have) + } + return s } // BigComma produces a string form of the given big.Int in base 10 // with commas after every three orders of magnitude. -func BigComma(b *big.Int) string { +func BigComma(bin *big.Int) string { + b := new(big.Int).Set(bin) sign := "" if b.Sign() < 0 { sign = "-" @@ -114,3 +151,19 @@ parts[j] = strconv.Itoa(int(b.Int64())) return sign + strings.Join(parts[j:], ",") } + +// ParseComma parses a string produced by Comma. +// It strips commas and returns the int64 value, or an error if the +// string cannot be parsed. +func ParseComma(s string) (int64, error) { + s = strings.ReplaceAll(s, ",", "") + return strconv.ParseInt(s, 10, 64) +} + +// ParseCommaf parses a string produced by Commaf. +// It strips commas and returns the float64 value, or an error if the +// string cannot be parsed. +func ParseCommaf(s string) (float64, error) { + s = strings.ReplaceAll(s, ",", "") + return strconv.ParseFloat(s, 64) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/commaf.go new/vendor/github.com/dustin/go-humanize/commaf.go --- old/vendor/github.com/dustin/go-humanize/commaf.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/dustin/go-humanize/commaf.go 2026-09-24 16:48:58.000000000 +0200 @@ -12,6 +12,13 @@ // BigCommaf produces a string form of the given big.Float in base 10 // with commas after every three orders of magnitude. func BigCommaf(v *big.Float) string { + if v.IsInf() { + if v.Sign() < 0 { + return "-Inf" + } + return "+Inf" + } + buf := &bytes.Buffer{} if v.Sign() < 0 { buf.Write([]byte{'-'}) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/flake.lock new/vendor/github.com/dustin/go-humanize/flake.lock --- old/vendor/github.com/dustin/go-humanize/flake.lock 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/dustin/go-humanize/flake.lock 2026-09-24 16:48:58.000000000 +0200 @@ -0,0 +1,58 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1789370336, + "narHash": "sha256-obwZbWHP7htCIoq2Z0g/uTMcEgN6yqcMNIcOMe6P2qI=", + "rev": "c7def046b9a883d46974757852106483d741586f", + "type": "tarball", + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1073483.c7def046b9a8/nixexprs.tar.zst" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/flake.nix new/vendor/github.com/dustin/go-humanize/flake.nix --- old/vendor/github.com/dustin/go-humanize/flake.nix 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/dustin/go-humanize/flake.nix 2026-09-24 16:48:58.000000000 +0200 @@ -0,0 +1,15 @@ +{ + description = "go-humanize"; + nixConfig.bash-prompt = "\[go-humanize\]$ "; + + inputs.flake-utils.url = "github:numtide/flake-utils"; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem + (system: + let pkgs = nixpkgs.legacyPackages.${system}; in + { + devShells.default = import ./shell.nix { inherit pkgs; }; + } + ); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/ftoa.go new/vendor/github.com/dustin/go-humanize/ftoa.go --- old/vendor/github.com/dustin/go-humanize/ftoa.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/dustin/go-humanize/ftoa.go 2026-09-24 16:48:58.000000000 +0200 @@ -29,7 +29,7 @@ return s[:i] } i++ - if i+digits >= len(s) { + if digits >= len(s)-i { return s } return s[:i+digits] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/number.go new/vendor/github.com/dustin/go-humanize/number.go --- old/vendor/github.com/dustin/go-humanize/number.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/dustin/go-humanize/number.go 2026-09-24 16:48:58.000000000 +0200 @@ -42,11 +42,12 @@ ) // FormatFloat produces a formatted number as string based on the following user-specified criteria: +// // * thousands separator // * decimal separator // * decimal precision // -// Usage: s := RenderFloat(format, n) +// Usage: s := FormatFloat(format, n) // The format parameter tells how to render the number n. // // See examples: http://play.golang.org/p/LXc1Ddm1lJ @@ -111,7 +112,7 @@ // +0000 if formatIndx[0] == 0 { if format[formatIndx[0]] != '+' { - panic("RenderFloat(): invalid positive sign directive") + panic("FormatFloat(): invalid positive sign directive") } positiveStr = "+" formatIndx = formatIndx[1:] @@ -125,7 +126,7 @@ // 000,000.00 if len(formatIndx) == 2 { if (formatIndx[1] - formatIndx[0]) != 4 { - panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers") + panic("FormatFloat(): thousands separator directive must be followed by 3 digit-specifiers") } thousandStr = string(format[formatIndx[0]]) formatIndx = formatIndx[1:] @@ -160,7 +161,7 @@ intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision]) // generate integer part string - intStr := strconv.FormatInt(int64(intf), 10) + intStr := strconv.FormatFloat(intf, 'f', 0, 64) // add thousand separator if required if len(thousandStr) > 0 { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/shell.nix new/vendor/github.com/dustin/go-humanize/shell.nix --- old/vendor/github.com/dustin/go-humanize/shell.nix 1970-01-01 01:00:00.000000000 +0100 +++ new/vendor/github.com/dustin/go-humanize/shell.nix 2026-09-24 16:48:58.000000000 +0200 @@ -0,0 +1,11 @@ +{ pkgs ? import <nixpkgs> { } }: +with pkgs; +mkShell { + buildInputs = [ + go + gotools + ]; + + shellHook = '' + ''; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/si.go new/vendor/github.com/dustin/go-humanize/si.go --- old/vendor/github.com/dustin/go-humanize/si.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/dustin/go-humanize/si.go 2026-09-24 16:48:58.000000000 +0200 @@ -33,12 +33,39 @@ var revSIPrefixTable = revfmap(siPrefixTable) +// minSIExponent and maxSIExponent are the smallest and largest exponents +// siPrefixTable can name. They are derived from the table rather than written +// down so that adding a prefix (as ronto/quecto/ronna/quetta were added) keeps +// them correct. +var minSIExponent, maxSIExponent = func() (float64, float64) { + min, max := math.Inf(1), math.Inf(-1) + for k := range siPrefixTable { + if k < min { + min = k + } + if k > max { + max = k + } + } + return min, max +}() + +// siParseAliases lets ParseSI accept extra spellings of prefixes that look +// identical to the canonical ones in siPrefixTable. Output still uses the +// canonical character; this is input-side only. +var siParseAliases = map[string]float64{ + "μ": 1e-6, // GREEK SMALL LETTER MU, often typed instead of µ (U+00B5) +} + // revfmap reverses the map and precomputes the power multiplier func revfmap(in map[float64]string) map[string]float64 { rv := map[string]float64{} for k, v := range in { rv[v] = math.Pow(10, k) } + for a, v := range siParseAliases { + rv[a] = v + } return rv } @@ -49,6 +76,9 @@ for _, v := range siPrefixTable { ri += v } + for a := range siParseAliases { + ri += a + } ri += `]?)(.*)` riParseRegex = regexp.MustCompile(ri) @@ -69,11 +99,23 @@ exponent := math.Floor(logn(mag, 10)) exponent = math.Floor(exponent/3) * 3 + // Outside the table there is no prefix that can name the magnitude, so + // clamp to the nearest one there is and let the returned value carry the + // remaining orders of magnitude. Without this the lookup below misses, + // the prefix comes back as the empty string, and the value is left scaled + // for a prefix that was never returned - so the caller silently gets a + // number off by as much as 10^300 with no error. + if exponent < minSIExponent { + exponent = minSIExponent + } else if exponent > maxSIExponent { + exponent = maxSIExponent + } + value := mag / math.Pow(10, exponent) // Handle special case where value is exactly 1000.0 // Should return 1 M instead of 1000 k - if value == 1000.0 { + if value == 1000.0 && exponent+3 <= maxSIExponent { exponent += 3 value = mag / math.Pow(10, exponent) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/dustin/go-humanize/times.go new/vendor/github.com/dustin/go-humanize/times.go --- old/vendor/github.com/dustin/go-humanize/times.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/dustin/go-humanize/times.go 2026-09-24 16:48:58.000000000 +0200 @@ -4,6 +4,7 @@ "fmt" "math" "sort" + "strings" "time" ) @@ -113,5 +114,12 @@ escaped = ch == '%' } } - return fmt.Sprintf(mag.Format, args...) + s := fmt.Sprintf(mag.Format, args...) + if lbl == "" { + // When the label is empty, trim the whitespace its "%s" + // placeholder would otherwise leave behind (e.g. a trailing + // space in "3 weeks %s"). + s = strings.TrimRight(s, " ") + } + return s } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/internal/version/version.go new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/internal/version/version.go --- old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/internal/version/version.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/internal/version/version.go 2026-09-24 16:48:58.000000000 +0200 @@ -1,4 +1,4 @@ package version // Version is the library's version following Semantic Versioning. -const Version = "2.48.0" // x-releaser-pleaser-version +const Version = "2.49.0" // x-releaser-pleaser-version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/network.go new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/network.go --- old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/network.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/network.go 2026-09-24 16:48:58.000000000 +0200 @@ -84,6 +84,47 @@ Delete bool } +// NetworkMember represents a resource attached to a [Network]. +type NetworkMember struct { + Type NetworkMemberType + ID int64 + IP net.IP + Status NetworkMemberStatus + AliasIPs []net.IP + Subnet *net.IPNet +} + +// NetworkMemberType specifies the type of a resource attached to a [Network]. +type NetworkMemberType string + +// List of available [NetworkMember] types. +const ( + // NetworkMemberTypeServer is a [Server] attached to a [Network]. + NetworkMemberTypeServer NetworkMemberType = "server" + // NetworkMemberTypeLoadBalancer is a [LoadBalancer] attached to a [Network]. + NetworkMemberTypeLoadBalancer NetworkMemberType = "load_balancer" +) + +// NetworkMemberStatus specifies the status of a resource attached to a [Network]. +type NetworkMemberStatus string + +// List of available [NetworkMember] statuses. +const ( + // NetworkMemberStatusOK indicates the resource is attached and its network + // configuration is up to date. + NetworkMemberStatusOK NetworkMemberStatus = "ok" + // NetworkMemberStatusAttaching indicates the resource is being attached to the [Network]. + NetworkMemberStatusAttaching NetworkMemberStatus = "attaching" + // NetworkMemberStatusDetaching indicates the resource is being detached from the [Network]. + NetworkMemberStatusDetaching NetworkMemberStatus = "detaching" + // NetworkMemberStatusUpdating indicates the resource network configuration is being updated. + NetworkMemberStatusUpdating NetworkMemberStatus = "updating" + // NetworkMemberStatusError indicates the last operation on this member failed. Its + // network configuration might be out of date, and the resource might not be + // reachable within the network. + NetworkMemberStatusError NetworkMemberStatus = "error" +) + // NetworkClient is a client for the network API. type NetworkClient struct { client *Client @@ -436,3 +477,66 @@ return ActionFromSchema(respBody.Action), resp, nil } + +// NetworkMemberListOpts specifies options for listing members of a [Network]. +type NetworkMemberListOpts struct { + ListOpts + Type []NetworkMemberType + Subnet []*net.IPNet + Status []NetworkMemberStatus + Sort []string +} + +func (l NetworkMemberListOpts) Values() url.Values { + vals := l.ListOpts.Values() + for _, t := range l.Type { + vals.Add("type", string(t)) + } + for _, s := range l.Subnet { + vals.Add("subnet", s.String()) + } + for _, s := range l.Status { + vals.Add("status", string(s)) + } + for _, s := range l.Sort { + vals.Add("sort", s) + } + return vals +} + +// ListMembers returns a list of [NetworkMember] attached to a specific [Network] for the given options. +// +// See https://docs.hetzner.cloud/reference/cloud#tag/networks/list_network_members +func (c *NetworkClient) ListMembers(ctx context.Context, network *Network, opts NetworkMemberListOpts) ([]*NetworkMember, *Response, error) { + const opPath = "/networks/%d/members?%s" + ctx = ctxutil.SetOpPath(ctx, opPath) + + reqPath := fmt.Sprintf(opPath, network.ID, opts.Values().Encode()) + + respBody, resp, err := getRequest[schema.NetworkMemberListResponse](ctx, c.client, reqPath) + if err != nil { + return nil, resp, err + } + + return allFromSchemaFunc(respBody.Members, NetworkMemberFromSchema), resp, nil +} + +// AllMembersWithOpts returns all [NetworkMember] attached to a specific [Network] for the given options. +// +// See https://docs.hetzner.cloud/reference/cloud#tag/networks/list_network_members +func (c *NetworkClient) AllMembersWithOpts(ctx context.Context, network *Network, opts NetworkMemberListOpts) ([]*NetworkMember, error) { + if opts.ListOpts.PerPage == 0 { + opts.ListOpts.PerPage = 50 + } + return iterPages(func(page int) ([]*NetworkMember, *Response, error) { + opts.Page = page + return c.ListMembers(ctx, network, opts) + }) +} + +// AllMembers returns all [NetworkMember] attached to a specific [Network]. +// +// See https://docs.hetzner.cloud/reference/cloud#tag/networks/list_network_members +func (c *NetworkClient) AllMembers(ctx context.Context, network *Network) ([]*NetworkMember, error) { + return c.AllMembersWithOpts(ctx, network, NetworkMemberListOpts{}) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/schema/network.go new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/schema/network.go --- old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/schema/network.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/schema/network.go 2026-09-24 16:48:58.000000000 +0200 @@ -153,3 +153,19 @@ type NetworkActionChangeProtectionResponse struct { Action Action `json:"action"` } + +// NetworkMember defines the schema of a resource attached to a network. +type NetworkMember struct { + Type string `json:"type"` + ID int64 `json:"id"` + IP string `json:"ip"` + Status string `json:"status"` + AliasIPs []string `json:"alias_ips"` + Subnet string `json:"subnet"` +} + +// NetworkMemberListResponse defines the schema of the response when +// listing members of a network. +type NetworkMemberListResponse struct { + Members []NetworkMember `json:"members"` +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/schema_gen.go new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/schema_gen.go --- old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/schema_gen.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/schema_gen.go 2026-09-24 16:48:58.000000000 +0200 @@ -218,6 +218,10 @@ SchemaFromNetworkRoute(NetworkRoute) schema.NetworkRoute + NetworkMemberFromSchema(schema.NetworkMember) *NetworkMember + + SchemaFromNetworkMember(*NetworkMember) schema.NetworkMember + LoadBalancerFromSchema(schema.LoadBalancer) *LoadBalancer // goverter:map OutgoingTraffic | mapZeroUint64ToNil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_network_client_iface.go new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_network_client_iface.go --- old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_network_client_iface.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_network_client_iface.go 2026-09-24 16:48:58.000000000 +0200 @@ -42,4 +42,16 @@ DeleteRoute(ctx context.Context, network *Network, opts NetworkDeleteRouteOpts) (*Action, *Response, error) // ChangeProtection changes the resource protection level of a network. ChangeProtection(ctx context.Context, network *Network, opts NetworkChangeProtectionOpts) (*Action, *Response, error) + // ListMembers returns a list of [NetworkMember] attached to a specific [Network] for the given options. + // + // See https://docs.hetzner.cloud/reference/cloud#tag/networks/list_network_members + ListMembers(ctx context.Context, network *Network, opts NetworkMemberListOpts) ([]*NetworkMember, *Response, error) + // AllMembersWithOpts returns all [NetworkMember] attached to a specific [Network] for the given options. + // + // See https://docs.hetzner.cloud/reference/cloud#tag/networks/list_network_members + AllMembersWithOpts(ctx context.Context, network *Network, opts NetworkMemberListOpts) ([]*NetworkMember, error) + // AllMembers returns all [NetworkMember] attached to a specific [Network]. + // + // See https://docs.hetzner.cloud/reference/cloud#tag/networks/list_network_members + AllMembers(ctx context.Context, network *Network) ([]*NetworkMember, error) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_schema.go new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_schema.go --- old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_schema.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_schema.go 2026-09-24 16:48:58.000000000 +0200 @@ -115,6 +115,11 @@ return c.NetworkFromSchema(in) } +// NetworkMemberFromSchema converts schema.NetworkMember to *NetworkMember. +func NetworkMemberFromSchema(in schema.NetworkMember) *NetworkMember { + return c.NetworkMemberFromSchema(in) +} + // NetworkRouteFromSchema converts schema.NetworkRoute to NetworkRoute. func NetworkRouteFromSchema(in schema.NetworkRoute) NetworkRoute { return c.NetworkRouteFromSchema(in) @@ -290,6 +295,11 @@ return c.SchemaFromNetwork(in) } +// SchemaFromNetworkMember converts *NetworkMember to schema.NetworkMember. +func SchemaFromNetworkMember(in *NetworkMember) schema.NetworkMember { + return c.SchemaFromNetworkMember(in) +} + // SchemaFromNetworkRoute converts NetworkRoute to schema.NetworkRoute. func SchemaFromNetworkRoute(in NetworkRoute) schema.NetworkRoute { return c.SchemaFromNetworkRoute(in) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_schema_converter.go new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_schema_converter.go --- old/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_schema_converter.go 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/github.com/hetznercloud/hcloud-go/v2/hcloud/zz_schema_converter.go 2026-09-24 16:48:58.000000000 +0200 @@ -341,6 +341,22 @@ hcloudNetwork.ExposeRoutesToVSwitch = source.ExposeRoutesToVSwitch return &hcloudNetwork } +func (c *converterImpl) NetworkMemberFromSchema(source schema.NetworkMember) *NetworkMember { + var hcloudNetworkMember NetworkMember + hcloudNetworkMember.Type = NetworkMemberType(source.Type) + hcloudNetworkMember.ID = source.ID + hcloudNetworkMember.IP = ipFromString(source.IP) + hcloudNetworkMember.Status = NetworkMemberStatus(source.Status) + if source.AliasIPs != nil { + hcloudNetworkMember.AliasIPs = make([]net.IP, len(source.AliasIPs)) + for i := 0; i < len(source.AliasIPs); i++ { + hcloudNetworkMember.AliasIPs[i] = ipFromString(source.AliasIPs[i]) + } + } + netIPNet := ipNetFromString(source.Subnet) + hcloudNetworkMember.Subnet = &netIPNet + return &hcloudNetworkMember +} func (c *converterImpl) NetworkRouteFromSchema(source schema.NetworkRoute) NetworkRoute { var hcloudNetworkRoute NetworkRoute netIPNet := ipNetFromString(source.Destination) @@ -837,6 +853,23 @@ } return schemaNetwork } +func (c *converterImpl) SchemaFromNetworkMember(source *NetworkMember) schema.NetworkMember { + var schemaNetworkMember schema.NetworkMember + if source != nil { + schemaNetworkMember.Type = string((*source).Type) + schemaNetworkMember.ID = (*source).ID + schemaNetworkMember.IP = stringFromIP((*source).IP) + schemaNetworkMember.Status = string((*source).Status) + if (*source).AliasIPs != nil { + schemaNetworkMember.AliasIPs = make([]string, len((*source).AliasIPs)) + for i := 0; i < len((*source).AliasIPs); i++ { + schemaNetworkMember.AliasIPs[i] = stringFromIP((*source).AliasIPs[i]) + } + } + schemaNetworkMember.Subnet = c.pNetIPNetToString((*source).Subnet) + } + return schemaNetworkMember +} func (c *converterImpl) SchemaFromNetworkRoute(source NetworkRoute) schema.NetworkRoute { var schemaNetworkRoute schema.NetworkRoute schemaNetworkRoute.Destination = c.pNetIPNetToString(source.Destination) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vendor/modules.txt new/vendor/modules.txt --- old/vendor/modules.txt 2026-09-14 17:14:03.000000000 +0200 +++ new/vendor/modules.txt 2026-09-24 16:48:58.000000000 +0200 @@ -25,8 +25,8 @@ # github.com/cpuguy83/go-md2man/v2 v2.0.7 ## explicit; go 1.12 github.com/cpuguy83/go-md2man/v2/md2man -# github.com/dustin/go-humanize v1.0.1 -## explicit; go 1.16 +# github.com/dustin/go-humanize v1.1.0 +## explicit; go 1.21 github.com/dustin/go-humanize # github.com/fatih/color v1.19.0 ## explicit; go 1.25.0 @@ -56,7 +56,7 @@ # github.com/guptarohit/asciigraph v0.10.0 ## explicit; go 1.11 github.com/guptarohit/asciigraph -# github.com/hetznercloud/hcloud-go/v2 v2.48.0 +# github.com/hetznercloud/hcloud-go/v2 v2.49.0 ## explicit; go 1.25.0 github.com/hetznercloud/hcloud-go/v2/hcloud github.com/hetznercloud/hcloud-go/v2/hcloud/exp/actionutil
