Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package mesheryctl for openSUSE:Factory checked in at 2026-05-11 16:55:56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mesheryctl (Old) and /work/SRC/openSUSE:Factory/.mesheryctl.new.1966 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "mesheryctl" Mon May 11 16:55:56 2026 rev:158 rq:1352294 version:1.0.20 Changes: -------- --- /work/SRC/openSUSE:Factory/mesheryctl/mesheryctl.changes 2026-05-07 15:46:44.183455083 +0200 +++ /work/SRC/openSUSE:Factory/.mesheryctl.new.1966/mesheryctl.changes 2026-05-11 17:06:51.504283643 +0200 @@ -1,0 +2,32 @@ +Sun May 10 15:13:02 UTC 2026 - Johannes Kastl <[email protected]> + +- update to 1.0.20: + * General + - [Chore]: Bump meshery/schemas to v1.2.15 @l5io (#19165) + - [Docs] Update README.md @Maanvi212006 (#19160) + - fix(remote-provider): only set X-API-Key for the anonymous + global token @leecalcote (#19166) + - [Server] Fix incorrect catalog path in DefaultLocalProvider + @jahnavigajjala-3 (#19155) + - [Chore]: Bump meshery/schemas to v1.2.14 @l5io (#19147) + - Update administrators and add new member details @hortison + (#19121) + * Meshery CLI + - [mesheryctl] model init: fix misleading logs for connections + and credentials @Joyboy48 (#19066) + * Maintenance + - [CI] Broaden first-time contributor check to include NONE + association @Junnygram (#19156) + - [CI] Fix docs-noob-tester: remove invalid property and + recompile lock file @rishiraj38 (#19159) + - chore(deps): bump @meshery/schemas to v1.2.14 (enumNames in + import-mo… @leecalcote (#19148) + - fix(ui): unblock e2e Import Model + Delete Connections tests + @leecalcote (#19146) + * Documentation + - Revert "Remove kanvas docs from extensions" @ritzorama + (#19182) + - [Docs] Update adapter contributor proto path and generation + command @chaitanyamedidar (#19189) + +------------------------------------------------------------------- Old: ---- mesheryctl-1.0.19.obscpio New: ---- mesheryctl-1.0.20.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ mesheryctl.spec ++++++ --- /var/tmp/diff_new_pack.z2PpF9/_old 2026-05-11 17:06:56.156475096 +0200 +++ /var/tmp/diff_new_pack.z2PpF9/_new 2026-05-11 17:06:56.156475096 +0200 @@ -17,7 +17,7 @@ Name: mesheryctl -Version: 1.0.19 +Version: 1.0.20 Release: 0 Summary: CLI for the meshery cloud native management plane License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.z2PpF9/_old 2026-05-11 17:06:56.228478059 +0200 +++ /var/tmp/diff_new_pack.z2PpF9/_new 2026-05-11 17:06:56.232478224 +0200 @@ -5,7 +5,7 @@ <param name="exclude">.git</param> <param name="exclude">docs/*</param> <param name="exclude">server/meshmodel/*</param> - <param name="revision">v1.0.19</param> + <param name="revision">v1.0.20</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">disable</param> ++++++ mesheryctl-1.0.19.obscpio -> mesheryctl-1.0.20.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/README.md new/mesheryctl-1.0.20/README.md --- old/mesheryctl-1.0.19/README.md 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/README.md 2026-05-08 22:16:47.000000000 +0200 @@ -87,7 +87,7 @@ ## Infrastructure Lifecycle Management -Meshery manages the configuration, deployment, and operation of your Cloud services and Kubernetes clusters while supporting hundreds of different types of cloud native infrastructure integrations. Meshery supports [300+ integrations](https://meshery.io/integrations). +Meshery manages the configuration, deployment, and operation of your Cloud services and Kubernetes clusters while supporting hundreds of different types of cloud native infrastructure integrations. Meshery supports [380+ integrations](https://meshery.io/integrations). <!-- <a href="https://www.youtube.com/watch?v=034nVaQUyME"><img alt="Meshery cloud native management" src="https://raw.githubusercontent.com/meshery/meshery/master/.github/assets/images/readme/meshmap.gif" style="margin-left:10px; margin-bottom:10px;" width="100%" align="center" /></a> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/SECURITY-INSIGHTS.yml new/mesheryctl-1.0.20/SECURITY-INSIGHTS.yml --- old/mesheryctl-1.0.19/SECURITY-INSIGHTS.yml 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/SECURITY-INSIGHTS.yml 2026-05-08 22:16:47.000000000 +0200 @@ -11,9 +11,9 @@ homepage: https://meshery.io roadmap: https://github.com/meshery/meshery/blob/master/ROADMAP.md administrators: - - name: Lee Calcote - affiliation: Layer5 - social: https://layer5.io/community/members/lee-calcote/ + - name: Meshery Maintainers + affiliation: Meshery + social: https://slack.meshery.io primary: true documentation: quickstart-guide: https://docs.meshery.io/installation/quick-start @@ -80,6 +80,9 @@ - name: Sangram Rath affiliation: OD10 social: https://layer5.io/community/members/sangram-rath/ + - name: Mia Grenell + affiliation: The University of Texas at Austin + social: https://layer5.io/community/members/mia-grenell/ license: url: https://github.com/meshery/meshery/blob/master/LICENSE expression: Apache-2.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/go.mod new/mesheryctl-1.0.20/go.mod --- old/mesheryctl-1.0.19/go.mod 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/go.mod 2026-05-08 22:16:47.000000000 +0200 @@ -51,9 +51,9 @@ github.com/meshery/meshery-operator v0.8.11 github.com/meshery/meshkit v1.0.8 github.com/meshery/meshsync v1.0.0 - github.com/meshery/schemas v1.2.13 + github.com/meshery/schemas v1.2.16 github.com/nsf/termbox-go v1.1.1 - github.com/oapi-codegen/runtime v1.3.1 + github.com/oapi-codegen/runtime v1.4.0 github.com/olekukonko/tablewriter v1.1.4 github.com/open-policy-agent/opa v1.12.3 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/go.sum new/mesheryctl-1.0.20/go.sum --- old/mesheryctl-1.0.19/go.sum 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/go.sum 2026-05-08 22:16:47.000000000 +0200 @@ -1494,8 +1494,8 @@ github.com/meshery/meshkit v1.0.8/go.mod h1:YQbXQtWnsvv2kt8nlCU6eJPjj5qWDf3heiLgbtZjKF0= github.com/meshery/meshsync v1.0.0 h1:BIHEVG4BDjqOnSd3vBt9B4IfWJNTfMRAgdwLroBcCfY= github.com/meshery/meshsync v1.0.0/go.mod h1:mFsPXkZRiCSuk5DhxBTrkWdlo6peItRBUoIEEQCovIg= -github.com/meshery/schemas v1.2.13 h1:c1bt4IaWUx/e8ScOIaJVM7hyVXH813g4iYMwLP2sU3Q= -github.com/meshery/schemas v1.2.13/go.mod h1:AOZUVgmhgg3O/AtsAdfeKJytZGPsyQpBrg2hEdtnBBg= +github.com/meshery/schemas v1.2.16 h1:HdZfENOd09oVR8Q8bev0F10QO/HSh2qQII+2FFKaX1s= +github.com/meshery/schemas v1.2.16/go.mod h1:JS5V0zTEHbP5I4VZw0mttkY09+0UnFK6qFt33PqK3aY= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -1581,8 +1581,8 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oapi-codegen/runtime v1.3.1 h1:RgDY6J4OGQLbRXhG/Xpt3vSVqYpHQS7hN4m85+5xB9g= -github.com/oapi-codegen/runtime v1.3.1/go.mod h1:kOdeacKy7t40Rclb1je37ZLFboFxh+YLy0zaPCMibPY= +github.com/oapi-codegen/runtime v1.4.0 h1:KLOSFOp7UzkbS7Cs1ms6NBEKYr0WmH2wZG0KKbd2er4= +github.com/oapi-codegen/runtime v1.4.0/go.mod h1:5sw5fxCDmnOzKNYmkVNF8d34kyUeejJEY8HNT2WaPec= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/init.go new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/init.go --- old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/init.go 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/init.go 2026-05-08 22:16:47.000000000 +0200 @@ -258,7 +258,7 @@ // map file name to template key files: nil, beforeHook: func() { - utils.Log.Info("Adding sample connections...") + utils.Log.Info("Creating connections directory...") }, }, { @@ -266,7 +266,7 @@ // map file name to template key files: nil, beforeHook: func() { - utils.Log.Info("Creating sample credentials...") + utils.Log.Info("Creating credentials directory...") }, }, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.aws-dynamodb-controller-in-yaml.output.golden new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.aws-dynamodb-controller-in-yaml.output.golden --- old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.aws-dynamodb-controller-in-yaml.output.golden 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.aws-dynamodb-controller-in-yaml.output.golden 2026-05-08 22:16:47.000000000 +0200 @@ -3,8 +3,8 @@ Generating model definition... Adding sample components... Creating sample relationships... -Adding sample connections... -Creating sample credentials... +Creating connections directory... +Creating credentials directory... Created test-case-aws-dynamodb-controller model at test-case-aws-dynamodb-controller Next steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.aws-ec2-controller.output.golden new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.aws-ec2-controller.output.golden --- old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.aws-ec2-controller.output.golden 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.aws-ec2-controller.output.golden 2026-05-08 22:16:47.000000000 +0200 @@ -3,8 +3,8 @@ Generating model definition... Adding sample components... Creating sample relationships... -Adding sample connections... -Creating sample credentials... +Creating connections directory... +Creating credentials directory... Created test-case-aws-ec2-controller model at test-case-aws-ec2-controller Next steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.custom-dir-2.aws-ec2-controller.output.golden new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.custom-dir-2.aws-ec2-controller.output.golden --- old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.custom-dir-2.aws-ec2-controller.output.golden 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.custom-dir-2.aws-ec2-controller.output.golden 2026-05-08 22:16:47.000000000 +0200 @@ -3,8 +3,8 @@ Generating model definition... Adding sample components... Creating sample relationships... -Adding sample connections... -Creating sample credentials... +Creating connections directory... +Creating credentials directory... Created test-case-aws-ec2-controller model at test_case_some_other_custom_dir/with/trailing/separator/test-case-aws-ec2-controller Next steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.custom-dir.aws-ec2-controller.output.golden new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.custom-dir.aws-ec2-controller.output.golden --- old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.custom-dir.aws-ec2-controller.output.golden 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.custom-dir.aws-ec2-controller.output.golden 2026-05-08 22:16:47.000000000 +0200 @@ -3,8 +3,8 @@ Generating model definition... Adding sample components... Creating sample relationships... -Adding sample connections... -Creating sample credentials... +Creating connections directory... +Creating credentials directory... Created test-case-aws-ec2-controller model at test_case_some_custom_dir/subdir/one_more_subdir/test-case-aws-ec2-controller Next steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.custom-relative-parent-dir.aws-ec2-controller.output.golden new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.custom-relative-parent-dir.aws-ec2-controller.output.golden --- old/mesheryctl-1.0.19/mesheryctl/internal/cli/root/model/testdata/model.init.custom-relative-parent-dir.aws-ec2-controller.output.golden 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/mesheryctl/internal/cli/root/model/testdata/model.init.custom-relative-parent-dir.aws-ec2-controller.output.golden 2026-05-08 22:16:47.000000000 +0200 @@ -3,8 +3,8 @@ Generating model definition... Adding sample components... Creating sample relationships... -Adding sample connections... -Creating sample credentials... +Creating connections directory... +Creating credentials directory... Created test-case-aws-ec2-controller model at ../test_case_some_custom_dir/subdir/one_more_subdir/test-case-aws-ec2-controller Next steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/provider-ui/package-lock.json new/mesheryctl-1.0.20/provider-ui/package-lock.json --- old/mesheryctl-1.0.19/provider-ui/package-lock.json 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/provider-ui/package-lock.json 2026-05-08 22:16:47.000000000 +0200 @@ -13,11 +13,11 @@ "@emotion/react": "^11.14.0", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.14.1", - "@meshery/schemas": "^1.2.7", + "@meshery/schemas": "^1.2.14", "@mui/icons-material": "^9.0.0", "@mui/material": "^9.0.0", "@mui/system": "^9.0.0", - "@sistent/sistent": "^0.21.4", + "@sistent/sistent": "^0.21.9", "@xstate/react": "^6.1.0", "http-proxy": "^1.18.1", "next": "^16.2.4", @@ -1347,9 +1347,9 @@ } }, "node_modules/@meshery/schemas": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@meshery/schemas/-/schemas-1.2.7.tgz", - "integrity": "sha512-thB6yJv2wYYSWo2bxvFTciW17tvMLemN6xikQYmfw+Wz7g8mBdg8c+5Sd4FCtDA42A/uwlPUbHxvnBiP7/aL5w==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@meshery/schemas/-/schemas-1.2.14.tgz", + "integrity": "sha512-cHrqFCv2IVUOdnBhmpEN7VCq2pZOyi029zQWwaQ9i5pHwES+oyekECgjBRE5t94Sld1GO5uPcWQ/9lT4mgWMOg==", "license": "ISC", "peerDependencies": { "@reduxjs/toolkit": "^2.8.1", @@ -1913,9 +1913,9 @@ } }, "node_modules/@sistent/sistent": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/@sistent/sistent/-/sistent-0.21.4.tgz", - "integrity": "sha512-6iC2cQF8irgToSAEPOrXfb0OVthf/lOYpsy1Lk20ly7RdnroqX3r5fDW79HATTllqtYxH0BJLYcbp/CVkkVv4A==", + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@sistent/sistent/-/sistent-0.21.9.tgz", + "integrity": "sha512-j4H5BY7KLvgO+onkorjCX6O2VpQtf1cNiY7I86KmVw0SW83z9qIHHqaPCJOBVjOh3QIjBUY+wOQeNuNU9VZHvQ==", "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/provider-ui/package.json new/mesheryctl-1.0.20/provider-ui/package.json --- old/mesheryctl-1.0.19/provider-ui/package.json 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/provider-ui/package.json 2026-05-08 22:16:47.000000000 +0200 @@ -20,11 +20,11 @@ "@emotion/react": "^11.14.0", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.14.1", - "@meshery/schemas": "^1.2.7", + "@meshery/schemas": "^1.2.14", "@mui/icons-material": "^9.0.0", "@mui/material": "^9.0.0", "@mui/system": "^9.0.0", - "@sistent/sistent": "^0.21.4", + "@sistent/sistent": "^0.21.9", "@xstate/react": "^6.1.0", "http-proxy": "^1.18.1", "next": "^16.2.4", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/server/models/default_local_provider.go new/mesheryctl-1.0.20/server/models/default_local_provider.go --- old/mesheryctl-1.0.19/server/models/default_local_provider.go 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/server/models/default_local_provider.go 2026-05-08 22:16:47.000000000 +0200 @@ -1223,7 +1223,7 @@ nilUserID := "" // Use the relative directory for patterns - catalogDir := filepath.Join("..", "..", "docs", "catalog") + catalogDir := filepath.Join("..", "..", "docs", "data", "catalog") for _, seedContent := range seedContents { go func(comp string, log logger.Handler) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/server/models/remote_auth.go new/mesheryctl-1.0.20/server/models/remote_auth.go --- old/mesheryctl-1.0.19/server/models/remote_auth.go 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/server/models/remote_auth.go 2026-05-08 22:16:47.000000000 +0200 @@ -170,9 +170,15 @@ Transport: tracing.NewTransport(http.DefaultTransport), // Create tracing transport to pass tracing context } req.Header.Set("Authorization", fmt.Sprintf("bearer %s", token)) - // if token == models.GlobalTokenForAnonymousResults { // disabling because of import cycle - req.Header.Set("X-API-Key", token) // adds the token as special passphrase incase the token is a special passphrase - // } + if token == GlobalTokenForAnonymousResults { + req.Header.Set("X-API-Key", token) + } else { + // Defensive: drop any inbound X-API-Key copied through proxy paths + // (e.g. ExtensionProxy). The cloud's static-token fallback rejects + // anything other than GlobalTokenForAnonymousResults, so leaking a + // caller-supplied value here would only confuse the auth path. + req.Header.Del("X-API-Key") + } req.Header.Set("SystemID", viper.GetString("INSTANCE_ID")) // Adds the system id to the header for event tracking resp, err := c.Do(req) if err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/server/models/remote_auth_test.go new/mesheryctl-1.0.20/server/models/remote_auth_test.go --- old/mesheryctl-1.0.19/server/models/remote_auth_test.go 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/server/models/remote_auth_test.go 2026-05-08 22:16:47.000000000 +0200 @@ -10,6 +10,7 @@ "net/http/httptest" "net/url" "strings" + "sync" "sync/atomic" "testing" "time" @@ -368,3 +369,102 @@ t.Fatalf("expected exactly one refresh request, got %d", got) } } + +// TestRemoteProviderDoRequest_XAPIKeyAnonymousOnly guards the anonymous +// passphrase contract: X-API-Key MUST only be set when the outbound request +// is being made with the global anonymous token, never with a real user JWT. +// Setting X-API-Key with a user JWT pollutes the cloud's static-token +// fallback path and broke anonymous flows in kanvas.new. The test also +// covers the defensive Del path: a caller-supplied X-API-Key on a +// non-anonymous outbound request must be stripped before reaching cloud. +func TestRemoteProviderDoRequest_XAPIKeyAnonymousOnly(t *testing.T) { + var ( + mu sync.Mutex + headers = map[string]string{} + ) + record := func(label, value string) { + mu.Lock() + defer mu.Unlock() + headers[label] = value + } + read := func(label string) string { + mu.Lock() + defer mu.Unlock() + return headers[label] + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/anon": + record("anon", r.Header.Get("X-API-Key")) + w.WriteHeader(http.StatusOK) + case "/user": + record("user", r.Header.Get("X-API-Key")) + w.WriteHeader(http.StatusOK) + case "/user-with-stale-api-key": + record("stale", r.Header.Get("X-API-Key")) + w.WriteHeader(http.StatusOK) + default: + http.NotFound(w, r) + } + })) + defer server.Close() + + provider := newTestRemoteProvider(t, server.URL) + + assertOK := func(resp *http.Response, label string) { + t.Helper() + defer func() { + if cerr := resp.Body.Close(); cerr != nil { + t.Logf("%s: error closing response body: %v", label, cerr) + } + }() + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + t.Fatalf("%s: status %d, body: %s", label, resp.StatusCode, strings.TrimSpace(string(body))) + } + } + + anonReq, err := http.NewRequest(http.MethodGet, server.URL+"/anon", nil) + if err != nil { + t.Fatalf("failed to build anonymous request: %v", err) + } + resp, err := provider.DoRequest(anonReq, GlobalTokenForAnonymousResults) + if err != nil { + t.Fatalf("anon request failed: %v", err) + } + assertOK(resp, "anon request") + + userToken := encodeTestToken(t, oauth2.Token{AccessToken: "real-user-jwt", TokenType: "Bearer"}) + userReq, err := http.NewRequest(http.MethodGet, server.URL+"/user", nil) + if err != nil { + t.Fatalf("failed to build user request: %v", err) + } + resp, err = provider.DoRequest(userReq, userToken) + if err != nil { + t.Fatalf("user request failed: %v", err) + } + assertOK(resp, "user request") + + // Inbound proxy header that should be stripped, not forwarded. + staleReq, err := http.NewRequest(http.MethodGet, server.URL+"/user-with-stale-api-key", nil) + if err != nil { + t.Fatalf("failed to build stale request: %v", err) + } + staleReq.Header.Set("X-API-Key", "leaked-from-inbound-request") + resp, err = provider.DoRequest(staleReq, userToken) + if err != nil { + t.Fatalf("stale request failed: %v", err) + } + assertOK(resp, "stale request") + + if got := read("anon"); got != GlobalTokenForAnonymousResults { + t.Fatalf("anonymous request must carry X-API-Key=%q, got %q", GlobalTokenForAnonymousResults, got) + } + if got := read("user"); got != "" { + t.Fatalf("authenticated request must NOT set X-API-Key, got %q", got) + } + if got := read("stale"); got != "" { + t.Fatalf("authenticated request with inbound X-API-Key must strip it, got %q", got) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/DataFormatter/index.tsx new/mesheryctl-1.0.20/ui/components/DataFormatter/index.tsx --- old/mesheryctl-1.0.19/ui/components/DataFormatter/index.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/DataFormatter/index.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -1,6 +1,13 @@ import * as React from 'react'; -import { CustomTooltip, Typography, Box, IconButton, useTheme, Grid } from '@sistent/sistent'; -import { Launch as LaunchIcon } from '@mui/icons-material'; +import { + CustomTooltip, + Typography, + Box, + IconButton, + LaunchIcon, + useTheme, + Grid, +} from '@sistent/sistent'; import _ from 'lodash'; import { useContext } from 'react'; import { isEmptyAtAllDepths } from '../../utils/objects'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/MesheryAdapterPlayComponent.tsx new/mesheryctl-1.0.20/ui/components/MesheryAdapterPlayComponent.tsx --- old/mesheryctl-1.0.19/ui/components/MesheryAdapterPlayComponent.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/MesheryAdapterPlayComponent.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -1,10 +1,12 @@ import React, { useState, useRef, useEffect } from 'react'; import { + AddIcon, Box, Card, CardActions, CardHeader, Chip, + DeleteIcon, Dialog, DialogActions, DialogContent, @@ -17,6 +19,7 @@ IconButton, Menu, MenuItem, + PlayArrowIcon as PlayIcon, Switch, Table, Tooltip, @@ -28,9 +31,6 @@ NoSsr, TableHead, } from '@sistent/sistent'; -import AddIcon from '@mui/icons-material/Add'; -import DeleteIcon from '@mui/icons-material/Delete'; -import PlayIcon from '@mui/icons-material/PlayArrow'; import MUIDataTable from '@sistent/mui-datatables'; import { useRouter } from 'next/router'; import { Controlled as CodeMirror } from './CodeMirror'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/MesheryChart.tsx new/mesheryctl-1.0.20/ui/components/MesheryChart.tsx --- old/mesheryctl-1.0.19/ui/components/MesheryChart.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/MesheryChart.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -8,6 +8,7 @@ Fade, Popper, styled, + ReplyIcon, } from '@sistent/sistent'; import { NoSsr } from '@sistent/sistent'; import { @@ -25,7 +26,6 @@ LinkedinIcon, FacebookIcon, } from 'react-share'; -import ReplyIcon from '@mui/icons-material/Reply'; const ChartTitle = styled(Typography)(({ theme }) => ({ textAlign: 'center', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/MesheryCredentialComponent.tsx new/mesheryctl-1.0.20/ui/components/MesheryCredentialComponent.tsx --- old/mesheryctl-1.0.19/ui/components/MesheryCredentialComponent.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/MesheryCredentialComponent.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -28,7 +28,7 @@ import { useSelector } from 'react-redux'; import { updateProgress } from '@/store/slices/mesheryUi'; import type { RootState } from '@/store/store'; -import type { MUIDataTableColumn, MUIDataTableMeta } from 'mui-datatables'; +import type { MUIDataTableColumn, MUIDataTableMeta } from '@sistent/mui-datatables'; const CredentialIcon = styled('img')({ width: '24px', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/MesheryPlayComponent.tsx new/mesheryctl-1.0.20/ui/components/MesheryPlayComponent.tsx --- old/mesheryctl-1.0.19/ui/components/MesheryPlayComponent.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/MesheryPlayComponent.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -3,6 +3,7 @@ Button, Divider, MenuItem, + SettingsIcon, TextField, Grid2, Typography, @@ -13,7 +14,6 @@ NoSsr, } from '@sistent/sistent'; import { useRouter } from 'next/router'; -import SettingsIcon from '@mui/icons-material/Settings'; import MesheryAdapterPlayComponent from './MesheryAdapterPlayComponent'; import { useDispatch, useSelector } from 'react-redux'; import { setAdapter } from '@/store/slices/adapter'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/MesherySettingsEnvButtons.tsx new/mesheryctl-1.0.20/ui/components/MesherySettingsEnvButtons.tsx --- old/mesheryctl-1.0.19/ui/components/MesherySettingsEnvButtons.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/MesherySettingsEnvButtons.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -1,5 +1,6 @@ import { Button, + CloudUploadIcon, Typography, FormGroup, TextField, @@ -14,7 +15,6 @@ import { useRef } from 'react'; import AddIconCircleBorder from '../assets/icons/AddIconCircleBorder'; import _PromptComponent from './PromptComponent'; -import CloudUploadIcon from '@mui/icons-material/CloudUpload'; import { useNotification } from '../utils/hooks/useNotification'; import { EVENT_TYPES } from '../lib/event-types'; import { CONNECTION_STATES } from '../utils/Enum'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/ReactSelectWrapper.tsx new/mesheryctl-1.0.20/ui/components/ReactSelectWrapper.tsx --- old/mesheryctl-1.0.19/ui/components/ReactSelectWrapper.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/ReactSelectWrapper.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -2,6 +2,7 @@ import PropTypes from 'prop-types'; import CreateSelect from 'react-select/creatable'; import { + CancelIcon, Typography, TextField, Paper, @@ -11,7 +12,6 @@ styled, NoSsr, } from '@sistent/sistent'; -import CancelIcon from '@mui/icons-material/Cancel'; const StyledNoOptionsMessage = styled(Typography)(({ theme }) => ({ padding: '0.2rem', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/Settings/Registry/ImportModelModal.tsx new/mesheryctl-1.0.20/ui/components/Settings/Registry/ImportModelModal.tsx --- old/mesheryctl-1.0.19/ui/components/Settings/Registry/ImportModelModal.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/Settings/Registry/ImportModelModal.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -4,20 +4,19 @@ FormControl, RadioGroup, Radio, - importModelUiSchema, - importModelSchema, Typography, ModalFooter, Box, Modal, useTheme, } from '@sistent/sistent'; +import { ModelImportRjsfSchemaV1Beta2, ModelImportRjsfUiSchemaV1Beta2 } from '@meshery/schemas'; import { RJSFModalWrapper } from '@/components/General/Modals/Modal'; import CsvStepper from './Stepper/CSVStepper'; import { MESHERY_DOCS_URL } from '@/constants/endpoints'; import { getUnit8ArrayDecodedFile } from '@/utils/utils'; import { useImportMeshModelMutation } from '@/rtk-query/meshModel'; -import React, { useState, useEffect, useContext } from 'react'; +import React, { useState, useEffect, useContext, useRef } from 'react'; import { capitalize } from 'lodash'; import { Loading } from '@/components/DesignLifeCycle/common'; import { NotificationCenterContext } from '@/components/NotificationCenter'; @@ -92,6 +91,146 @@ ); }; +// Canonical RJSF form schemas authored in `meshery/schemas` and validated +// by its `validation/forms_test.go` to be a strict subset of the OpenAPI +// `MesheryModelImportFormPayload` construct. The canonical is the source +// of truth for property shapes, types, descriptions, enum tokens, and +// upload-type labels — pull them through directly. +// +// The canonical describes the full form-data shape (file + url + csv +// branches), but this modal only renders the file-and-url subset; the +// CSV branch is handled by a separate `CsvStepper` modal opened on the +// `csv` upload-type. Keeping the unused fields in the rendered form +// caused two real problems: +// (a) RJSF v6's @rjsf/validator-ajv8 rejected the form on `Next` — +// even though `modelCsv` / `componentCsv` / `relationshipCsv` were +// hidden via `ui:widget: 'hidden'`, their `format: "binary"` and +// the canonical's csv-branch `allOf` required-list still blocked +// `validateForm()`, so the import POST never fired and the test +// hung waiting for the registration event. +// (b) the canonical's schema-level `enumNames` is silently ignored +// by @rjsf/utils' `optionsList()` — RJSF v6 reads the radio +// labels from `ui:enumNames` on the uiSchema instead. +// +// v1.2.16 of @meshery/schemas adds `ModelImportRjsfUiSchemaV1Beta2` and +// changes `modelFile.format` from `"binary"` to `"data-url"` (the format +// RJSF's FileWidget actually produces). We base `importModelUiSchema` on +// the canonical and overlay our consumer overrides on top. +const canonicalProps = ModelImportRjsfSchemaV1Beta2?.properties || {}; +const canonicalUploadType = canonicalProps.uploadType || {}; +const UPLOAD_TYPE_FILE = 'file'; +const UPLOAD_TYPE_URL = 'urlImport'; +const UPLOAD_TYPE_CSV = 'csv'; + +// `allOf` intentionally does NOT mark `modelFile` or `fileName` required +// for the file branch even though the canonical does. CustomFileWidget +// reads the file asynchronously (FileReader → `Promise.then(props.onChange)`) +// and only then lifts the data URL into RJSF's form state, while validation +// runs synchronously on Next-click. Gating submit on `required: ['modelFile']` +// loses the race on every test run and the form sits silently. We trust +// the user-flow guard in `handleImportModelSubmit` instead — it reads +// the file off the DOM input as the synchronous source-of-truth and +// short-circuits if no file is selected. `fileName` is hidden and derived +// from the data URL / DOM fallback at submit time. +const importModelSchema = { + $schema: 'https://json-schema.org/draft-07/schema#', + title: ModelImportRjsfSchemaV1Beta2.title, + type: 'object', + properties: { + uploadType: canonicalUploadType, + modelFile: canonicalProps.modelFile, + url: canonicalProps.url, + fileName: canonicalProps.fileName, + }, + allOf: [ + { + if: { + properties: { uploadType: { const: UPLOAD_TYPE_URL } }, + required: ['uploadType'], + }, + then: { required: ['url'] }, + }, + ], + required: ['uploadType'], +}; + +// Extend the canonical uiSchema (added in @meshery/schemas v1.2.16) with +// consumer-specific overrides: +// - uploadType: add ui:enumNames so RJSF v6 renders friendly radio labels +// (optionsList() reads labels from uiSchema, not schema-level enumNames). +// - fileName: hidden — derived from the uploaded file at submit time. +// - CSV fields: not rendered in this modal (handled by CsvStepper). +const importModelUiSchema = { + ...ModelImportRjsfUiSchemaV1Beta2, + uploadType: { + 'ui:widget': 'radio', + 'ui:enumNames': Array.isArray(canonicalUploadType?.enumNames) + ? [...(canonicalUploadType.enumNames as string[])] + : undefined, + }, + // The canonical schema declares `modelFile` as `type: string` without a + // `format: data-url`, so RJSF falls back to TextWidget and the form never + // renders an `<input type="file">`. Force the file widget here, matching + // the importDesign/importFilter fix shipped in @sistent/sistent v0.21.7. + modelFile: { 'ui:widget': 'file' }, + fileName: { 'ui:widget': 'hidden' }, + modelCsv: { 'ui:widget': 'hidden' }, + componentCsv: { 'ui:widget': 'hidden' }, + relationshipCsv: { 'ui:widget': 'hidden' }, + 'ui:order': ['uploadType', 'modelFile', 'url'], +}; + +// RJSF's file widget encodes the original filename inside the data URL +// (`data:<mime>;name=<file>;base64,<payload>`). Pull it back out so the +// server-side ImportBody.FileName is populated even though the form +// doesn't surface a separate `fileName` input. +const filenameFromDataUrl = (dataUrl: string | undefined): string | undefined => { + if (!dataUrl) return undefined; + const match = dataUrl.match(/;name=([^;]+);/); + return match ? decodeURIComponent(match[1]) : undefined; +}; + +// Decode an RJSF data URL into the byte array the server expects. +// `getUnit8ArrayDecodedFile` is synchronous, so this wrapper is too; +// returns `null` when no data URL is present so callers can fall back +// to the DOM input. +const decodeDataUrlToBytes = (dataUrl: string | undefined): number[] | null => { + if (!dataUrl) return null; + return getUnit8ArrayDecodedFile(dataUrl); +}; + +// Locate the `<input type="file">` rendered for the modelFile field. RJSF +// uses the schema title as the id prefix (`<title>_modelFile`), so we +// can't hard-code `root_modelFile`. Scope the query to the active modal +// dialog to avoid false matches from other file inputs on the page. +const findSelectedModelFile = (): File | undefined => { + if (typeof document === 'undefined') return undefined; + const dialog = document.querySelector<HTMLElement>('[role="dialog"]'); + const root = dialog ?? document; + const inputs = root.querySelectorAll<HTMLInputElement>('input[type="file"][id$="_modelFile"]'); + for (const input of Array.from(inputs)) { + const file = input.files?.[0]; + if (file) return file; + } + return undefined; +}; + +// Read a `File` synchronously-from-the-DOM-but-asynchronously-into-bytes, +// matching the wire shape `getUnit8ArrayDecodedFile` produces from a +// data URL. Used as the synchronous fallback when RJSF's form data +// hasn't received the data URL yet (the FileReader chain inside +// CustomFileWidget can lose the race against a quick Next-click). +const readFileAsBytes = (file: File): Promise<number[]> => + new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + const buffer = reader.result as ArrayBuffer; + resolve(Array.from(new Uint8Array(buffer))); + }; + reader.onerror = () => reject(reader.error); + reader.readAsArrayBuffer(file); + }); + type ImportModelModalProps = { isImportModalOpen: boolean; setIsImportModalOpen: (_open: boolean) => void; @@ -103,8 +242,15 @@ const [isCsvModalOpen, setIsCsvModalOpen] = useState(false); const [importModelReq] = useImportMeshModelMutation(); const [activeStep, setActiveStep] = useState(0); + // Prevents RJSFModalWrapper from advancing the step when the submit + // handler short-circuits (CSV branch, no-file guard, empty-URL guard). + const skipNextRef = useRef(false); const handleNext = () => { + if (skipNextRef.current) { + skipNextRef.current = false; + return; + } if (activeStep === 0) { setActiveStep(1); } @@ -117,53 +263,86 @@ }; const handleImportModelSubmit = async (data) => { - const { uploadType, url, file } = data; + // Canonical field names from ModelImportRjsfSchemaV1Beta2: + // `uploadType` (enum: file/urlImport/csv), `modelFile`, `fileName`, + // `url`, plus the CSV trio handled by CsvStepper. + const { uploadType, url, modelFile, fileName: formFileName } = data; let requestBody = null; - const fileElement = document.getElementById('root_file'); - switch (uploadType) { - case 'File Import': { - const fileName = fileElement.files[0].name; - const fileData = getUnit8ArrayDecodedFile(file); - if (fileData) { + case UPLOAD_TYPE_FILE: { + // Two source-of-truth paths for the file: + // 1. RJSF form data (`data.modelFile` data URL) — this is the + // happy path when the user clicked Next after the upload + // finished round-tripping through CustomFileWidget's async + // FileReader → onChange chain. + // 2. DOM input fallback — Playwright e2e (and any quick + // keystroke after upload) can race the FileReader, so the + // data URL hasn't landed in form state yet on submit. The + // `<input type="file">` rendered by the widget already + // carries the selected `File` synchronously (the browser + // sets `.files` on the input the instant the user picks a + // file), so re-read it here via readFileAsBytes. + const formData = decodeDataUrlToBytes(modelFile); + let fileName = formFileName || filenameFromDataUrl(modelFile); + let fileData: number[] | null = formData; + if (!fileData) { + const inputFile = findSelectedModelFile(); + if (inputFile) { + try { + fileData = await readFileAsBytes(inputFile); + fileName = fileName || inputFile.name; + } catch (err) { + console.error('Error reading file from DOM:', err); + skipNextRef.current = true; + return; + } + } + } + if (fileData && fileName) { + // Server's ImportBody.FileName / .ModelFile are tagged + // `file_name` / `model_file` (snake_case wire format). requestBody = { importBody: { model_file: fileData, url: '', - filename: fileName, + file_name: fileName, }, - uploadType: 'file', + uploadType: UPLOAD_TYPE_FILE, register: true, }; } else { - console.error('Error: File data is empty or invalid'); + console.error('Error: File data or file name is empty or invalid'); + skipNextRef.current = true; return; } break; } - case 'URL Import': { + case UPLOAD_TYPE_URL: { if (url) { requestBody = { importBody: { url: url, }, - uploadType: 'urlImport', + uploadType: UPLOAD_TYPE_URL, register: true, }; } else { console.error('Error: URL is empty'); + skipNextRef.current = true; return; } break; } - case 'CSV Import': { + case UPLOAD_TYPE_CSV: { + skipNextRef.current = true; handleClose(); setIsCsvModalOpen(true); return; } default: { console.error('Error: Invalid upload type'); + skipNextRef.current = true; return; } } @@ -204,7 +383,7 @@ <div> <Typography variant="subtitle1">{option.label}</Typography> <Typography variant="body2" color="textSecondary" textTransform={'none'}> - {schema.enumDescriptions[index]} + {schema.enumDescriptions?.[index]} </Typography> </div> } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/Settings/Registry/Stepper/UrlStepper.tsx new/mesheryctl-1.0.20/ui/components/Settings/Registry/Stepper/UrlStepper.tsx --- old/mesheryctl-1.0.19/ui/components/Settings/Registry/Stepper/UrlStepper.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/Settings/Registry/Stepper/UrlStepper.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -19,6 +19,9 @@ MenuItem, Radio, Grid2, + AppRegistrationIcon, + BrushIcon, + CategoryIcon, } from '@sistent/sistent'; import { @@ -28,12 +31,9 @@ StyledColorBox, StyledDocsRedirectLink, } from './style'; -import BrushIcon from '@mui/icons-material/Brush'; -import CategoryIcon from '@mui/icons-material/Category'; import SourceIcon from '@/assets/icons/SourceIcon'; import FinishFlagIcon from '@/assets/icons/FinishFlagIcon'; import { capitalize } from 'lodash'; -import AppRegistrationIcon from '@mui/icons-material/AppRegistration'; import { DeploymentSelectorIcon } from '@/assets/icons/DeploymentSelectorIcon'; import { CategoryDefinitionV1Beta1OpenApiSchema, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/ViewSwitch.tsx new/mesheryctl-1.0.20/ui/components/ViewSwitch.tsx --- old/mesheryctl-1.0.19/ui/components/ViewSwitch.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/ViewSwitch.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -1,6 +1,4 @@ -import { CustomTooltip, IconButton, useTheme } from '@sistent/sistent'; -import GridOnIcon from '@mui/icons-material/GridOn'; -import TableChartIcon from '@mui/icons-material/TableChart'; +import { CustomTooltip, GridOnIcon, IconButton, TableChartIcon, useTheme } from '@sistent/sistent'; type ViewMode = 'grid' | 'table'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/YamlDialog.tsx new/mesheryctl-1.0.20/ui/components/YamlDialog.tsx --- old/mesheryctl-1.0.19/ui/components/YamlDialog.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/YamlDialog.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -1,16 +1,16 @@ import { + DeleteIcon, Dialog, DialogActions, DialogContent, Divider, + FullScreenIconExit as FullscreenExitIcon, + FullScreenIcon as FullscreenIcon, IconButton, + SaveIcon, Tooltip, } from '@sistent/sistent'; import { UnControlled as CodeMirror } from './CodeMirror'; -import FullscreenExitIcon from '@mui/icons-material/FullscreenExit'; -import DeleteIcon from '@mui/icons-material/Delete'; -import FullscreenIcon from '@mui/icons-material/Fullscreen'; -import SaveIcon from '@mui/icons-material/Save'; import { YamlDialogTitleText, StyledDialog } from './MesheryPatterns/style'; import { StyledCodeMirrorWrapper } from './MesheryPatterns/Cards.styles'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/connections/ConnectionChip.tsx new/mesheryctl-1.0.20/ui/components/connections/ConnectionChip.tsx --- old/mesheryctl-1.0.19/ui/components/connections/ConnectionChip.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/connections/ConnectionChip.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -1,7 +1,6 @@ import React from 'react'; -import { Avatar, useTheme } from '@sistent/sistent'; +import { Avatar, AssignmentTurnedInIcon, useTheme } from '@sistent/sistent'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; -import AssignmentTurnedInIcon from '@mui/icons-material/AssignmentTurnedIn'; import ExploreIcon from '@mui/icons-material/Explore'; import RemoveCircleIcon from '@mui/icons-material/RemoveCircle'; import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/components/connections/ConnectionTable.tsx new/mesheryctl-1.0.20/ui/components/connections/ConnectionTable.tsx --- old/mesheryctl-1.0.19/ui/components/connections/ConnectionTable.tsx 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/components/connections/ConnectionTable.tsx 2026-05-08 22:16:47.000000000 +0200 @@ -384,19 +384,25 @@ }; const handleDeleteConnections = async (selected) => { - if (selected) { - let response = await modalRef.current.show({ - title: `Delete Connections`, - subtitle: `Are you sure that you want to delete the connections?`, - primaryOption: 'DELETE', - showInfoIcon: `Learn more about the [lifecycle of connections and the behavior of state transitions](https://docs.meshery.io/concepts/logical/connections) in Meshery Docs.`, - variant: PROMPT_VARIANTS.DANGER, - }); - if (response === 'DELETE') { - selected.data.map(({ index }) => { - UpdateConnectionStatus(filteredConnections[index].id, CONNECTION_STATES.DELETED); - }); - } + if (!selected?.data?.length) return; + + // Capture the connection IDs up front. The user has to acknowledge the + // confirmation modal before delete fires, and `filteredConnections` can + // be invalidated/reordered by an in-flight refetch in that window — using + // the index after-the-fact dereferenced stale rows and silently no-op'd + // (no PUT, no notification), which surfaced as a hung e2e snackbar wait. + const ids = selected.data.map(({ index }) => filteredConnections?.[index]?.id).filter(Boolean); + if (ids.length === 0) return; + + const response = await modalRef.current.show({ + title: `Delete Connections`, + subtitle: `Are you sure that you want to delete the connections?`, + primaryOption: 'DELETE', + showInfoIcon: `Learn more about the [lifecycle of connections and the behavior of state transitions](https://docs.meshery.io/concepts/logical/connections) in Meshery Docs.`, + variant: PROMPT_VARIANTS.DANGER, + }); + if (response === 'DELETE') { + ids.forEach((id) => UpdateConnectionStatus(id, CONNECTION_STATES.DELETED)); } }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/package-lock.json new/mesheryctl-1.0.20/ui/package-lock.json --- old/mesheryctl-1.0.19/ui/package-lock.json 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/package-lock.json 2026-05-08 22:16:47.000000000 +0200 @@ -21,7 +21,7 @@ "@emotion/react": "^11.14.0", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.14.1", - "@meshery/schemas": "^1.2.12", + "@meshery/schemas": "^1.2.16", "@microlink/react-json-view": "^1.31.20", "@mui/icons-material": "^9.0.0", "@mui/material": "^9.0.0", @@ -37,7 +37,7 @@ "@rjsf/utils": "^6.5.2", "@rjsf/validator-ajv8": "^6.5.2", "@sistent/mui-datatables": "^7.1.1", - "@sistent/sistent": "^0.21.5", + "@sistent/sistent": "^0.21.9", "@tippyjs/react": "^4.2.6", "@uiw/codemirror-theme-material": "^4.25.9", "@uiw/react-codemirror": "^4.25.9", @@ -2060,9 +2060,9 @@ "license": "MIT" }, "node_modules/@meshery/schemas": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@meshery/schemas/-/schemas-1.2.12.tgz", - "integrity": "sha512-qystejxI3PQ0PbFjDeHoq2tU7KRSOLxYe4qXt2pbd3bbQnTiNa4s55bzrueKBjGyXjQSQaGs+FzwUCiuBS4luA==", + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@meshery/schemas/-/schemas-1.2.16.tgz", + "integrity": "sha512-iZ3XFuw7ee5uOnSUIHMnn/TRh3xTKc9eurfjzE9kH3GkhhakhC1nMMQzMTq0NC5FYNMaKiPE4Duc+zhwtLVhog==", "license": "ISC", "peerDependencies": { "@reduxjs/toolkit": "^2.8.1", @@ -3280,9 +3280,9 @@ } }, "node_modules/@sistent/sistent": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@sistent/sistent/-/sistent-0.21.5.tgz", - "integrity": "sha512-88+Iqu7zFeswn9cjSA+ZwNpntGghet89SLi0QQWsdYVojpjHi9ZoDFbAVWdvV+zaa+gY3RLMEx3U6ClmAnm28g==", + "version": "0.21.9", + "resolved": "https://registry.npmjs.org/@sistent/sistent/-/sistent-0.21.9.tgz", + "integrity": "sha512-j4H5BY7KLvgO+onkorjCX6O2VpQtf1cNiY7I86KmVw0SW83z9qIHHqaPCJOBVjOh3QIjBUY+wOQeNuNU9VZHvQ==", "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/package.json new/mesheryctl-1.0.20/ui/package.json --- old/mesheryctl-1.0.19/ui/package.json 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/package.json 2026-05-08 22:16:47.000000000 +0200 @@ -46,7 +46,7 @@ "@emotion/react": "^11.14.0", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.14.1", - "@meshery/schemas": "^1.2.12", + "@meshery/schemas": "^1.2.16", "@microlink/react-json-view": "^1.31.20", "@mui/icons-material": "^9.0.0", "@mui/material": "^9.0.0", @@ -62,7 +62,7 @@ "@rjsf/utils": "^6.5.2", "@rjsf/validator-ajv8": "^6.5.2", "@sistent/mui-datatables": "^7.1.1", - "@sistent/sistent": "^0.21.5", + "@sistent/sistent": "^0.21.9", "@tippyjs/react": "^4.2.6", "@uiw/codemirror-theme-material": "^4.25.9", "@uiw/react-codemirror": "^4.25.9", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/tests/e2e/connections.spec.ts new/mesheryctl-1.0.20/ui/tests/e2e/connections.spec.ts --- old/mesheryctl-1.0.19/ui/tests/e2e/connections.spec.ts 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/tests/e2e/connections.spec.ts 2026-05-08 22:16:47.000000000 +0200 @@ -194,7 +194,15 @@ await getFilteredConnectionsRes; - const row = page.locator('tr').filter({ hasText: 'connected' }).first(); + // Narrow to the data row for the searched cluster — `tr` alone matches + // table headers, MUI menu items, and the chip dropdown popovers, all of + // which can carry the word `connected` from filter labels or other + // rendered chips and silently grab the wrong row. + const row = page + .locator('tbody tr') + .filter({ hasText: clusterMetaData.name }) + .filter({ hasText: 'connected' }) + .first(); // Skip the test if no connected cluster is found (e.g., CI environments without a pre-connected cluster) if ((await row.count()) === 0) { @@ -210,8 +218,27 @@ await page.getByRole('button', { name: 'Delete', exact: true }).click(); // Verify that Confirmation modal opened and delete await expect(page.getByText('Delete Connections')).toBeVisible(); + + // Capture the PUT to the connection-update endpoint so we can surface + // the HTTP status in the test output if the delete fails on the + // backend — the alternative (just waiting on the snackbar) leaves + // 500s/4xxs invisible: the success toast never fires and the test + // hangs to its 60s timeout with no clue what happened. + const updateRes = page.waitForResponse( + (resp) => + /\/api\/integrations\/connections\/[0-9a-f-]{36}$/.test(resp.url()) && + resp.request().method() === 'PUT', + { timeout: 60_000 }, + ); + await page.getByRole('button', { name: 'DELETE', exact: true }).click(); + const response = await updateRes; + expect( + response.status(), + `connection update PUT returned ${response.status()} ${response.statusText()} — body: ${await response.text()}`, + ).toBe(200); + await waitForSnackBar(page, 'Connection status updated'); }); }); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mesheryctl-1.0.19/ui/tests/e2e/models.spec.ts new/mesheryctl-1.0.20/ui/tests/e2e/models.spec.ts --- old/mesheryctl-1.0.19/ui/tests/e2e/models.spec.ts 2026-05-07 02:03:16.000000000 +0200 +++ new/mesheryctl-1.0.20/ui/tests/e2e/models.spec.ts 2026-05-08 22:16:47.000000000 +0200 @@ -107,7 +107,7 @@ test('Import a Model via File Import', async ({ page }) => { await page.getByTestId('TabBar-Button-ImportModel').click(); - await page.getByRole('heading', { name: 'File Import' }).click(); + await page.getByRole('heading', { name: 'File Import', exact: true }).click(); await page.setInputFiles('input[type="file"]', model_import.MODEL_FILE_IMPORT); @@ -122,7 +122,7 @@ test('Import a Model via Url Import', async ({ page }) => { await page.getByTestId('TabBar-Button-ImportModel').click(); - await page.getByRole('heading', { name: 'URL Import' }).click(); + await page.getByRole('heading', { name: 'URL Import', exact: true }).click(); await page.getByRole('textbox', { name: 'URL' }).click(); await page.getByRole('textbox', { name: 'URL' }).fill(model_import.MODEL_URL_IMPORT); @@ -138,7 +138,7 @@ test('Import a Model via CSV Import', async ({ page }) => { await page.getByTestId('TabBar-Button-ImportModel').click(); - await page.getByRole('heading', { name: 'CSV Import' }).click(); + await page.getByRole('heading', { name: 'CSV Import', exact: true }).click(); await page.getByRole('button', { name: 'Next' }).click(); ++++++ mesheryctl.obsinfo ++++++ --- /var/tmp/diff_new_pack.z2PpF9/_old 2026-05-11 17:07:00.180640704 +0200 +++ /var/tmp/diff_new_pack.z2PpF9/_new 2026-05-11 17:07:00.188641033 +0200 @@ -1,5 +1,5 @@ name: mesheryctl -version: 1.0.19 -mtime: 1778112196 -commit: beaf1d857db6a05d670419f95079cbde0bca7bdf +version: 1.0.20 +mtime: 1778271407 +commit: 9c6a1e085d7a8a3c892fb098d4f7e7dc372ba538 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/mesheryctl/vendor.tar.gz /work/SRC/openSUSE:Factory/.mesheryctl.new.1966/vendor.tar.gz differ: char 135, line 1
