From 7e790bf0402a3f5eda17155a8eac47589cec97db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 07:06:27 +0000 Subject: [PATCH 01/11] chore(deps): bump softprops/action-gh-release from 2 to 3 Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e63cb11..8f9db2a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -205,7 +205,7 @@ jobs: echo "tag=$REF" >> $GITHUB_OUTPUT - name: Create GitHub Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 with: tag_name: ${{ steps.tag.outputs.tag }} name: ${{ steps.tag.outputs.tag }} From 0a648dac7cb88cb24b867461b5ea9cc9b7a49c4f Mon Sep 17 00:00:00 2001 From: "Asad Iqbal (Saadi)" Date: Wed, 15 Jul 2026 16:19:58 +0500 Subject: [PATCH 02/11] =?UTF-8?q?chore:=20back-merge=20main=20=E2=86=92=20?= =?UTF-8?q?develop=20after=20#323=20promote=20(#335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 7494ab8c7dbd22fc9d426db1c3a9c45d95ab206e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:31:56 +0500 Subject: [PATCH 03/11] chore(deps): bump the k8s group with 3 updates (#331) Bumps the k8s group with 3 updates: [k8s.io/api](https://github.com/kubernetes/api), [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) and [k8s.io/client-go](https://github.com/kubernetes/client-go). Updates `k8s.io/api` from 0.36.1 to 0.36.2 - [Commits](https://github.com/kubernetes/api/compare/v0.36.1...v0.36.2) Updates `k8s.io/apimachinery` from 0.36.1 to 0.36.2 - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.36.1...v0.36.2) Updates `k8s.io/client-go` from 0.36.1 to 0.36.2 - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.36.1...v0.36.2) --- updated-dependencies: - dependency-name: k8s.io/api dependency-version: 0.36.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s - dependency-name: k8s.io/apimachinery dependency-version: 0.36.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s - dependency-name: k8s.io/client-go dependency-version: 0.36.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: k8s ... Signed-off-by: dependabot[bot] Co-authored-by: Asad Iqbal (Saadi) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 98ef7643..5f23f413 100644 --- a/go.mod +++ b/go.mod @@ -22,9 +22,9 @@ require ( golang.org/x/term v0.45.0 golang.org/x/text v0.40.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.36.1 - k8s.io/apimachinery v0.36.1 - k8s.io/client-go v0.36.1 + k8s.io/api v0.36.2 + k8s.io/apimachinery v0.36.2 + k8s.io/client-go v0.36.2 ) require ( @@ -65,7 +65,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/klog/v2 v2.140.0 // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect - k8s.io/streaming v0.36.1 // indirect + k8s.io/streaming v0.36.2 // indirect k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect diff --git a/go.sum b/go.sum index e311c3d2..9e7fd08e 100644 --- a/go.sum +++ b/go.sum @@ -167,18 +167,18 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.36.1 h1:XbL/EMj8K2aJpJtePmqUyQMsM0D4QI2pvl7YKJ20FTY= -k8s.io/api v0.36.1/go.mod h1:KOWo4ey3TINlXjeHVuwB3i+tXXnu+UcwFBHlI/9dvEo= -k8s.io/apimachinery v0.36.1 h1:G63Gjx2W+q0YD+72Vo8oY0nDnePVwnuzTmmy5ENrVSA= -k8s.io/apimachinery v0.36.1/go.mod h1:ibYOR00vW/I1kzvi5SF0dRuJ52BvKtfvRdOn35GPQ+8= -k8s.io/client-go v0.36.1 h1:FN/K8QIT2CEDt+2WB2HnWrUANZ50AP5GII43/SP2JR0= -k8s.io/client-go v0.36.1/go.mod h1:s6rAnCtTGYDQnpNjEhSaISV+2O8jwruZ6m3QOYBFbtU= +k8s.io/api v0.36.2 h1:TF6YDLIzKfccK7cq9YpTcGX8TJmEkHVRv78DM51fRYY= +k8s.io/api v0.36.2/go.mod h1:F4LbMO4brjZYh7yFkXWhynSvtB7YauxV4c+HHkNRGNg= +k8s.io/apimachinery v0.36.2 h1:0PE/W/WNy1UX61NLbXY5TMbJ6UwLL6E6lAPkYrKFxbQ= +k8s.io/apimachinery v0.36.2/go.mod h1:fvf/HOLXq9RId0rnDIbN1OEBvHXdQbLMM8nu0LcBUf4= +k8s.io/client-go v0.36.2 h1:bfgxmFKc9CgqsgX4xKLAAdmTQlWee7Ob/HlDOrJ5TBI= +k8s.io/client-go v0.36.2/go.mod h1:1vgO4OAlfPnoLcb+Rze2GF5rAr14w8qjrYMoyXJzQj0= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg= k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= -k8s.io/streaming v0.36.1 h1:L+K68n4Gg940BGNNYtUBvL1WTLL0YnKT3s+P1MNAmR4= -k8s.io/streaming v0.36.1/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s= +k8s.io/streaming v0.36.2 h1:NSKthPPg9UFSKsRauVJUVGH2Dvn8fhKmY4qrMkw/p98= +k8s.io/streaming v0.36.2/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= From 3fb66cf78808aa1c8772c88516f10ee3e2a64587 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:32:02 +0500 Subject: [PATCH 04/11] chore(deps): bump github.com/schollz/progressbar/v3 (#333) Bumps [github.com/schollz/progressbar/v3](https://github.com/schollz/progressbar) from 3.19.0 to 3.19.1. - [Release notes](https://github.com/schollz/progressbar/releases) - [Commits](https://github.com/schollz/progressbar/compare/v3.19.0...v3.19.1) --- updated-dependencies: - dependency-name: github.com/schollz/progressbar/v3 dependency-version: 3.19.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: Asad Iqbal (Saadi) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 5f23f413..b7db74eb 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/AlecAivazis/survey/v2 v2.3.7 github.com/fatih/color v1.19.0 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 - github.com/schollz/progressbar/v3 v3.19.0 + github.com/schollz/progressbar/v3 v3.19.1 github.com/spf13/cobra v1.8.1 golang.org/x/term v0.45.0 golang.org/x/text v0.40.0 @@ -44,7 +44,7 @@ require ( github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/moby/spdystream v0.5.1 // indirect diff --git a/go.sum b/go.sum index 9e7fd08e..bcf30c18 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,8 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= @@ -92,8 +92,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= -github.com/schollz/progressbar/v3 v3.19.0 h1:Ea18xuIRQXLAUidVDox3AbwfUhD0/1IvohyTutOIFoc= -github.com/schollz/progressbar/v3 v3.19.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= +github.com/schollz/progressbar/v3 v3.19.1 h1:iv8BgwOvdML/S3p84uBpy/IMigv4U9594vPZYa2EdrU= +github.com/schollz/progressbar/v3 v3.19.1/go.mod h1:LFL7jqimKxfhero4K1eCkUr/6R39AgQeiPCJtlTWIW8= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -136,7 +136,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From 55ccbc4ab2e8c15b04ae252956c2aad7de0fa664 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:32:13 +0500 Subject: [PATCH 05/11] chore(deps): bump helm/kind-action from 1.10.0 to 1.14.0 (#328) Bumps [helm/kind-action](https://github.com/helm/kind-action) from 1.10.0 to 1.14.0. - [Release notes](https://github.com/helm/kind-action/releases) - [Commits](https://github.com/helm/kind-action/compare/v1.10.0...v1.14.0) --- updated-dependencies: - dependency-name: helm/kind-action dependency-version: 1.14.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: Asad Iqbal (Saadi) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 1a9eada2..e48d5c67 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -58,7 +58,7 @@ jobs: # kubeconfig resolution. kind ships a default StorageClass # (local-path), so the test's PVC binds. - name: Create kind cluster - uses: helm/kind-action@v1.10.0 + uses: helm/kind-action@v1.14.0 # Run the unit suite + the integration suite each under coverage, then merge # them (go tool covdata). The merge credits the real-cluster I/O seams — From cfe73d250c4df63310b6ce88c7e4a1d362f2e129 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:59:35 +0500 Subject: [PATCH 06/11] chore(deps): bump actions/add-to-project from 1.0.2 to 2.0.0 (#327) Bumps [actions/add-to-project](https://github.com/actions/add-to-project) from 1.0.2 to 2.0.0. - [Release notes](https://github.com/actions/add-to-project/releases) - [Commits](https://github.com/actions/add-to-project/compare/v1.0.2...v2.0.0) --- updated-dependencies: - dependency-name: actions/add-to-project dependency-version: 2.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: Asad Iqbal (Saadi) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/add-to-kanban.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/add-to-kanban.yml b/.github/workflows/add-to-kanban.yml index 45aa70ac..28640d55 100644 --- a/.github/workflows/add-to-kanban.yml +++ b/.github/workflows/add-to-kanban.yml @@ -10,7 +10,7 @@ jobs: add-to-project: runs-on: ubuntu-latest steps: - - uses: actions/add-to-project@v1.0.2 + - uses: actions/add-to-project@v2.0.0 with: project-url: https://github.com/orgs/tracebloc/projects/2 github-token: ${{ secrets.PROJECTS_KANBAN_TOKEN }} From 84683f0b751d7664cab81fed943feadd601fa773 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 16:59:42 +0500 Subject: [PATCH 07/11] chore(deps): bump actions/stale from 9 to 10 (#332) Bumps [actions/stale](https://github.com/actions/stale) from 9 to 10. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v9...v10) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '10' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: Asad Iqbal (Saadi) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale-backlog.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale-backlog.yml b/.github/workflows/stale-backlog.yml index 318e3729..c4adb55d 100644 --- a/.github/workflows/stale-backlog.yml +++ b/.github/workflows/stale-backlog.yml @@ -13,7 +13,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 with: days-before-issue-stale: 42 # 6 weeks of no activity → warning days-before-issue-close: 14 # +2 weeks of silence → close From ccd5820bb99adc24f0c35b35a39ef1ac58a2da73 Mon Sep 17 00:00:00 2001 From: "Asad Iqbal (Saadi)" Date: Wed, 15 Jul 2026 17:21:03 +0500 Subject: [PATCH 08/11] fix(ci): point Dependabot at develop, not main (#336) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All work in this repo flows feature → develop → main (promote PRs). Dependabot defaults to the repo default branch (main), so the first batch of bumps (#327–#334) targeted main directly — bypassing develop and set to regress on the next promote (develop carries the older deps). Set target-branch: "develop" on both ecosystems so bumps ride the normal review + promote path. Co-authored-by: Claude Opus 4.8 --- .github/dependabot.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index cdfeaa04..a73ba661 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,11 +13,19 @@ # # Org-wide rollout beyond backend + cli is an open decision — flagged # to @saadqbal on the PR that added this file. +# +# target-branch is "develop", NOT the default (main): all work in this +# repo flows feature → develop → main (promote PRs). Dependabot defaults +# to the repo default branch (main); left unset, bumps landed straight on +# main, bypassing develop and regressing on the next promote (the develop +# side has the older deps). Point them at develop so they ride the normal +# review + promote path. version: 2 updates: - package-ecosystem: "gomod" directory: "/" + target-branch: "develop" schedule: interval: "weekly" labels: @@ -34,6 +42,7 @@ updates: - package-ecosystem: "github-actions" directory: "/" + target-branch: "develop" schedule: interval: "monthly" labels: From 239e8a5ebcb277795a78cb484953c1d962815bf9 Mon Sep 17 00:00:00 2001 From: "Asad Iqbal (Saadi)" Date: Wed, 15 Jul 2026 18:01:06 +0500 Subject: [PATCH 09/11] ci: bump artifact actions to v7 (upload + download together) (#337) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Supersedes Dependabot #329, which bumped actions/upload-artifact 4→7 but left actions/download-artifact@v4 in release.yml's publish job. GitHub requires matching upload/download majors — a v7↔v4 pair breaks the release artifact merge (download-artifact can't read v7-format uploads). Bump all four refs together: upload-artifact in build.yml, mutation.yml, release.yml, and download-artifact in release.yml. CI/release-only; no CLI-binary change. Co-authored-by: Claude Opus 4.8 --- .github/workflows/build.yml | 2 +- .github/workflows/mutation.yml | 2 +- .github/workflows/release.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 530a78ab..271a645c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -285,7 +285,7 @@ jobs: "$BIN" dataset push --help >/dev/null - name: Upload binary as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: tracebloc-${{ matrix.os }}-${{ matrix.arch }} path: dist/tracebloc-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.ext }} diff --git a/.github/workflows/mutation.yml b/.github/workflows/mutation.yml index 0bc26267..118b4bbf 100644 --- a/.github/workflows/mutation.yml +++ b/.github/workflows/mutation.yml @@ -110,7 +110,7 @@ jobs: - name: Upload machine-readable report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: gremlins-report path: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e63cb11..027eafd1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -153,7 +153,7 @@ jobs: cat "$BIN_NAME.sha256" - name: Upload per-matrix artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: dist-${{ matrix.os }}-${{ matrix.arch }} path: dist/ @@ -174,7 +174,7 @@ jobs: ref: ${{ inputs.ref || github.ref }} - name: Download all matrix artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: path: dist/ merge-multiple: true From f810a1626d068caf1d1d755e3c8497cf86d475cd Mon Sep 17 00:00:00 2001 From: "Asad Iqbal (Saadi)" Date: Thu, 16 Jul 2026 12:09:19 +0500 Subject: [PATCH 10/11] Merge pull request #339 from tracebloc/fix/338-heartbeat-single-client fix(cli): home heartbeat fetches one client, not the whole account (#338) --- internal/api/client.go | 27 ++++++++++++++ internal/api/client_get_test.go | 47 +++++++++++++++++++++++ internal/cli/client.go | 39 +++++++++---------- internal/cli/client_test.go | 55 +++++++++++++++++++-------- internal/cli/delete_test.go | 66 ++++++++++++++++----------------- 5 files changed, 166 insertions(+), 68 deletions(-) create mode 100644 internal/api/client_get_test.go diff --git a/internal/api/client.go b/internal/api/client.go index 15a95bd5..d5ed292a 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -505,6 +505,33 @@ func (c *Client) RevokeClient(ctx context.Context, id int) error { // against a misbehaving `next` chain, set well above any real account. const maxListPages = 100 +// GetClient fetches a single client by its dashboard id (GET /edge-device/{id}/). +// The detail route is the same one PatchClientClusterID/RevokeClient address, and +// returns one ProvisionedClient. This is the O(1) way to check ONE client's +// status — unlike ListClients, which pages through the whole account (the +// home-screen heartbeat must not do that under its ~1.2s budget, cli#338). +// A 404 returns (nil, nil) so the caller can distinguish "no such client" from +// a transport/backend error. +func (c *Client) GetClient(ctx context.Context, id int) (*ProvisionedClient, error) { + path := fmt.Sprintf("/edge-device/%d/", id) + url := c.BaseURL + path + status, raw, err := c.get(ctx, path) + if err != nil { + return nil, err + } + if status == http.StatusNotFound { + return nil, nil + } + if status < 200 || status >= 300 { + return nil, &APIError{StatusCode: status, Body: string(raw), URL: url} + } + var out ProvisionedClient + if err := json.Unmarshal(raw, &out); err != nil { + return nil, fmt.Errorf("decoding get-client response: %w", err) + } + return &out, nil +} + // ListClients returns ALL clients in the caller's account (GET /edge-device/). // The endpoint is DRF-paginated, so this follows `next` to the end — list, // `use `, and create-time collision detection must see every client, not diff --git a/internal/api/client_get_test.go b/internal/api/client_get_test.go new file mode 100644 index 00000000..d71728ae --- /dev/null +++ b/internal/api/client_get_test.go @@ -0,0 +1,47 @@ +package api + +import ( + "context" + "net/http" + "testing" +) + +// TestGetClient covers the single-client detail fetch (GET /edge-device/{id}/) +// that backs the home-screen heartbeat (cli#338): a 2xx decodes one client, a +// 404 is (nil, nil) — "no such client", not an error — and any other non-2xx is +// an APIError. It also pins the path so the heartbeat can't regress to a list. +func TestGetClient(t *testing.T) { + t.Run("2xx decodes a single client + hits the detail path", func(t *testing.T) { + c := stubClient(t, func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/edge-device/1070/" { + t.Errorf("path = %q, want /edge-device/1070/ (detail route, not the list)", r.URL.Path) + } + _, _ = w.Write([]byte(`{"id":1070,"first_name":"asad-macbook","status":1,"namespace":"asad-macbook-3"}`)) + }) + pc, err := c.GetClient(context.Background(), 1070) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if pc == nil || pc.ID != 1070 || pc.Status != 1 { + t.Fatalf("GetClient = %+v, want id=1070 status=1", pc) + } + }) + + t.Run("404 -> (nil, nil), not an error", func(t *testing.T) { + c := stubClient(t, func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNotFound) }) + pc, err := c.GetClient(context.Background(), 999) + if pc != nil || err != nil { + t.Fatalf("404 must be (nil, nil); got (%+v, %v)", pc, err) + } + }) + + t.Run("non-2xx (500) -> APIError", func(t *testing.T) { + c := stubClient(t, func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(`{"detail":"boom"}`)) + }) + if _, err := c.GetClient(context.Background(), 1); err == nil { + t.Error("a 500 must return an error") + } + }) +} diff --git a/internal/cli/client.go b/internal/cli/client.go index 8a449194..126175f5 100644 --- a/internal/cli/client.go +++ b/internal/cli/client.go @@ -864,30 +864,31 @@ func runClientStatus(ctx context.Context, p *ui.Printer, wait bool, timeout time } } -// lookupClientStatus finds the account client whose numeric id matches active and -// returns its backend status code. found=false means no such client (deleted, or -// signed into the wrong account). A list error is returned verbatim so --wait can -// treat it as transient and retry. +// lookupClientStatus fetches the active client directly and returns its backend +// status code. found=false means no such client (deleted, or signed into the +// wrong account). A lookup error is returned verbatim so --wait can treat it as +// transient and retry. Fetches the single client by id (GET /edge-device/{id}/) +// rather than listing the whole account — the home-screen heartbeat runs this +// under a ~1.2s budget, and paging every client blew it (cli#338). func lookupClientStatus(ctx context.Context, client *api.Client, active string) (status int, found bool, err error) { - clients, err := client.ListClients(ctx) + id, err := strconv.Atoi(active) if err != nil { - return 0, false, err + // A non-numeric active id can never match a backend client, so report it + // as not-found — exactly what the old ListClients+match path did — rather + // than a permanent error. A --wait loop fail-fasts on a missing client but + // treats errors as transient, so returning an error here would make it + // poll a permanent parse failure to the timeout (Bugbot: poll/retry loops + // must fail-fast on non-transient errors). + return 0, false, nil } - if c := findClientByID(clients, active); c != nil { - return c.Status, true, nil + c, err := client.GetClient(ctx, id) + if err != nil { + return 0, false, err } - return 0, false, nil -} - -// findClientByID returns the account client whose numeric dashboard id equals id -// (the string form stored as the active-client pointer), or nil if none match. -func findClientByID(clients []api.ProvisionedClient, id string) *api.ProvisionedClient { - for i := range clients { - if strconv.Itoa(clients[i].ID) == id { - return &clients[i] - } + if c == nil { + return 0, false, nil // 404 — no such client } - return nil + return c.Status, true, nil } // EdgeDevice.status codes mirrored from the backend (metaApi User.py). diff --git a/internal/cli/client_test.go b/internal/cli/client_test.go index cb0b8477..29e2ceb2 100644 --- a/internal/cli/client_test.go +++ b/internal/cli/client_test.go @@ -1223,8 +1223,8 @@ func setActiveClientID(t *testing.T, id string) { // reports the active client online, and says so. func TestClientStatus_WaitOnline_Exit0(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { - _, _ = w.Write([]byte(`[{"id":5,"first_name":"c","namespace":"c","status":1}]`)) // 1 = online + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { + _, _ = w.Write([]byte(`{"id":5,"first_name":"c","namespace":"c","status":1}`)) // 1 = online return } t.Errorf("unexpected %s %s", r.Method, r.URL.Path) @@ -1245,8 +1245,8 @@ func TestClientStatus_WaitOnline_Exit0(t *testing.T) { // so the loop never sleeps. func TestClientStatus_WaitTimeout_Exit1(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { - _, _ = w.Write([]byte(`[{"id":5,"first_name":"c","namespace":"c","status":0}]`)) // 0 = offline + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { + _, _ = w.Write([]byte(`{"id":5,"first_name":"c","namespace":"c","status":0}`)) // 0 = offline } }) setActiveClientID(t, "5") @@ -1265,8 +1265,8 @@ func TestClientStatus_WaitTimeout_Exit1(t *testing.T) { // TestClientStatus_OneShot: without --wait, report the current state and exit 0. func TestClientStatus_OneShot(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { - _, _ = w.Write([]byte(`[{"id":5,"first_name":"c","namespace":"c","status":2}]`)) // 2 = pending + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { + _, _ = w.Write([]byte(`{"id":5,"first_name":"c","namespace":"c","status":2}`)) // 2 = pending } }) setActiveClientID(t, "5") @@ -1297,7 +1297,7 @@ func TestClientStatus_NoActiveClient(t *testing.T) { // proves we didn't poll to exhaustion. func TestClientStatus_WaitFailsFastOn426(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { w.WriteHeader(http.StatusUpgradeRequired) // 426 _, _ = w.Write([]byte(`{"error":"upgrade_required","min_version":"1.2.3"}`)) } @@ -1320,8 +1320,8 @@ func TestClientStatus_WaitFailsFastOn426(t *testing.T) { // matching the one-shot path, rather than polling to the timeout. func TestClientStatus_WaitFailsFastOnMissingClient(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { - _, _ = w.Write([]byte(`[{"id":9,"first_name":"other","namespace":"other","status":1}]`)) // active id 5 absent + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { + w.WriteHeader(http.StatusNotFound) // active id 5 absent → 404 } }) setActiveClientID(t, "5") @@ -1335,13 +1335,36 @@ func TestClientStatus_WaitFailsFastOnMissingClient(t *testing.T) { } } +// TestClientStatus_WaitFailsFastOnNonNumericID (Bugbot, #338 follow-up): a +// corrupt (non-numeric) active client id can never match a backend client, so +// --wait must fail fast on a missing client rather than retry a permanent parse +// error to the timeout. No backend call is expected (the id never parses). +func TestClientStatus_WaitFailsFastOnNonNumericID(t *testing.T) { + withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { + t.Errorf("no backend call expected for a non-numeric id, got %s %s", r.Method, r.URL.Path) + }) + setActiveClientID(t, "not-a-number") + // Long timeout: the test would hang if a non-numeric id were treated as a + // transient error instead of a missing client. + err := runClientStatus(context.Background(), ui.New(&bytes.Buffer{}), true, 10*time.Minute) + if got := ExitCodeFromError(err); got != 1 { + t.Fatalf("exit code = %d, want 1", got) + } + if err != nil && strings.Contains(err.Error(), "timed out") { + t.Errorf("a non-numeric id must fail fast, not time out: %v", err) + } + if err == nil || !strings.Contains(err.Error(), "isn't in your account") { + t.Errorf("want a missing-client error, got: %v", err) + } +} + // TestClientStatus_WaitTimeoutSurfacesListError (Bugbot #146-F): when every // status check fails, the timeout message must name the real error, not a bare // "unreachable". A 1ns timeout means the deadline passes on the first failure. func TestClientStatus_WaitTimeoutSurfacesListError(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { - w.WriteHeader(http.StatusInternalServerError) // persistent list failure + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { + w.WriteHeader(http.StatusInternalServerError) // persistent status-check failure } }) setActiveClientID(t, "5") @@ -1362,7 +1385,7 @@ func TestClientStatus_WaitTimeoutSurfacesListError(t *testing.T) { // the full timeout. A 10-minute timeout would hang the test if it didn't. func TestClientStatus_WaitFailsFastOn401(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { w.WriteHeader(http.StatusUnauthorized) // dead credential } }) @@ -1398,13 +1421,13 @@ func TestClientStatus_TimeoutWithoutWaitRejected(t *testing.T) { func TestClientStatus_WaitTimeoutClearsStaleError(t *testing.T) { calls := 0 withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { calls++ if calls == 1 { w.WriteHeader(http.StatusBadGateway) // one transient blip return } - _, _ = w.Write([]byte(`[{"id":5,"first_name":"c","namespace":"c","status":0}]`)) // then offline + _, _ = w.Write([]byte(`{"id":5,"first_name":"c","namespace":"c","status":0}`)) // then offline } }) setActiveClientID(t, "5") @@ -1430,8 +1453,8 @@ func TestClientStatus_WaitTimeoutClearsStaleError(t *testing.T) { // during --wait exits quietly with code 130 — not a bare "Error: context canceled". func TestClientStatus_WaitCtrlCIsSilent(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { - _, _ = w.Write([]byte(`[{"id":5,"first_name":"c","namespace":"c","status":0}]`)) // offline + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { + _, _ = w.Write([]byte(`{"id":5,"first_name":"c","namespace":"c","status":0}`)) // offline } }) setActiveClientID(t, "5") diff --git a/internal/cli/delete_test.go b/internal/cli/delete_test.go index f70bb56c..c8d89a70 100644 --- a/internal/cli/delete_test.go +++ b/internal/cli/delete_test.go @@ -137,9 +137,9 @@ func TestDelete_Yes_FullSequence(t *testing.T) { revokePath := "" withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": // Guard's status lookup: report the client OFFLINE so it doesn't block. - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): revokePath = r.URL.Path if got := r.Header.Get("Authorization"); got != "Bearer tok" { @@ -200,8 +200,8 @@ func TestDelete_Yes_FullSequence(t *testing.T) { func TestDelete_RevokeNon403_ContinuesTeardown(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusNotFound) // 404: stale pointer / backend predates /revoke default: @@ -248,8 +248,8 @@ func TestDelete_RevokeNon403_ContinuesTeardown(t *testing.T) { func TestDelete_RevokeNon403_Degraded_HonestClosing(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusNotFound) // 404: best-effort revoke fails default: @@ -329,8 +329,8 @@ func TestDelete_RevokeUnauthorized_FailsFast(t *testing.T) { func TestDelete_KeepData_SparesDataDir(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) default: @@ -375,8 +375,8 @@ func TestDelete_KeepData_SparesDataDir(t *testing.T) { func TestDelete_WipeFails_StillClearsPointer(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } @@ -407,8 +407,8 @@ func TestDelete_WipeFails_StillClearsPointer(t *testing.T) { func TestDelete_TeardownFailure_HonestClosing(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } @@ -441,7 +441,7 @@ func TestDelete_Guard426_FailsFast(t *testing.T) { if r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke") { revoked = true } - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { w.WriteHeader(http.StatusUpgradeRequired) // 426 _, _ = w.Write([]byte(`{"error":"upgrade_required","min_version":"1.2.3"}`)) } @@ -472,7 +472,7 @@ func TestDelete_GuardAuthError_FailsFast(t *testing.T) { if r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke") { revoked = true } - if r.Method == http.MethodGet && r.URL.Path == "/edge-device/" { + if r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/" { w.WriteHeader(http.StatusUnauthorized) // 401 — token revoked/expired _, _ = w.Write([]byte(`{"detail":"invalid token"}`)) } @@ -500,8 +500,8 @@ func TestDelete_GuardAuthError_FailsFast(t *testing.T) { func TestDelete_SelfRemovalFails_HonestClosing(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } @@ -532,8 +532,8 @@ func TestDelete_SelfRemovalFails_HonestClosing(t *testing.T) { func TestDelete_KubeconfigContext_ReachHelm(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } @@ -559,9 +559,9 @@ func TestDelete_RunningJob_RefusesUnlessForce(t *testing.T) { newHandler := func() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": // status 1 = online (a running client). - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":1}]`)) + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":1}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } @@ -619,8 +619,8 @@ func TestDelete_RunningJob_RefusesUnlessForce(t *testing.T) { func TestDelete_ShowsRetainedAndLeftCopy(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } @@ -653,7 +653,7 @@ func TestDelete_TypedNameMismatch_Cancels(t *testing.T) { if r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke") { revoked = true } - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) }) setActiveForDelete(t, "5", "gpu-box-01", "gpu-box-01") fn := &fakeNodeboot{executable: filepath.Join(t.TempDir(), "tracebloc")} @@ -675,8 +675,8 @@ func TestDelete_TypedNameMismatch_Cancels(t *testing.T) { func TestDelete_BrewManagedBinary_Hint(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } @@ -704,8 +704,8 @@ func TestDelete_BrewManagedBinary_Hint(t *testing.T) { func TestDelete_NoNamespace_WarnsAndSkipsUninstall(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } @@ -733,8 +733,8 @@ func TestDelete_NoNamespace_WarnsAndSkipsUninstall(t *testing.T) { func TestDelete_RevokeForbidden_OffboardCopy(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodGet && r.URL.Path == "/edge-device/admins/": _, _ = w.Write([]byte(`[]`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): @@ -814,8 +814,8 @@ func writeBinaryWithTBAlias(t *testing.T) string { func TestDelete_OwnTBAlias_Removed(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } @@ -839,8 +839,8 @@ func TestDelete_OwnTBAlias_Removed(t *testing.T) { func TestDelete_ForeignTBAlias_Left(t *testing.T) { withClientBackend(t, func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/edge-device/": - _, _ = w.Write([]byte(`[{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}]`)) + case r.Method == http.MethodGet && r.URL.Path == "/edge-device/5/": + _, _ = w.Write([]byte(`{"id":5,"first_name":"gpu-box-01","namespace":"gpu-box-01","status":0}`)) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/revoke"): w.WriteHeader(http.StatusOK) } From 44b50b104bb724ede088bd9d0e38ca0e70e48246 Mon Sep 17 00:00:00 2001 From: "Asad Iqbal (Saadi)" Date: Thu, 16 Jul 2026 13:30:53 +0500 Subject: [PATCH 11/11] chore(deps): bump github.com/spf13/cobra from 1.8.1 to 1.10.2 (#340) Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.1 to 1.10.2. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.8.1...v1.10.2) --- updated-dependencies: - dependency-name: github.com/spf13/cobra dependency-version: 1.10.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index b7db74eb..3f89f67c 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/fatih/color v1.19.0 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/schollz/progressbar/v3 v3.19.1 - github.com/spf13/cobra v1.8.1 + github.com/spf13/cobra v1.10.2 golang.org/x/term v0.45.0 golang.org/x/text v0.40.0 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index bcf30c18..9c664976 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -94,9 +94,8 @@ github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEV github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/schollz/progressbar/v3 v3.19.1 h1:iv8BgwOvdML/S3p84uBpy/IMigv4U9594vPZYa2EdrU= github.com/schollz/progressbar/v3 v3.19.1/go.mod h1:LFL7jqimKxfhero4K1eCkUr/6R39AgQeiPCJtlTWIW8= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=