mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 16:46:17 +00:00
style(setup): address actionable golangci-lint findings
Fixes the lint hits that pointed at real bugs or dead code; leaves the remaining style-only suggestions (rangeValCopy micro-copies, gocyclo informational, intentional name choices like SetupStateMachine) alone. - pkg/models/clockdisplay.go: restore <clockDisplay> XMLName tag on both ClockDisplay and ClockDisplayRequest. The earlier `xml:"-"` clashed with ClockDisplayUpdatedEvent.ClockDisplay's `xml:"clockDisplay"` tag (SA5008). Custom MarshalXML/UnmarshalXML still own the wire format. - pkg/service/setup/setup.go: drop the now-unused checkRemoteServices helper (replaced by applyProbeToSummary) and rename the unused deviceIP parameter of populatePlannedNetworkConfig to _. - pkg/service/setup/setup_session.go: collapse sendStep's (string, error) return to plain error — every caller already discarded the string. - pkg/service/setup/init_plan.go: rename shadowed err variables to rwErr / genErr / invalidErr / nilErr / stepErr. - cmd/soundtouch-cli/cmd_setup.go: drop redundant int(syscall.Stdin) conversion (already int) and rename a shadowed err to pairErr. go build ./..., go vet ./..., and tests for the touched packages all green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
29a462da2b
commit
a1ae10650f
@@ -626,7 +626,7 @@ func promptBasicAuth() (string, string, error) {
|
||||
|
||||
fmt.Fprint(os.Stderr, "Password: ")
|
||||
|
||||
pass, err := term.ReadPassword(int(syscall.Stdin))
|
||||
pass, err := term.ReadPassword(syscall.Stdin)
|
||||
|
||||
fmt.Fprintln(os.Stderr)
|
||||
|
||||
@@ -1373,9 +1373,9 @@ func runPairBare(c *cli.Context, deviceIP, accountID string) error {
|
||||
|
||||
fmt.Printf("→ setMargeAccount accountID=%s (no SETUP bracket)\n", accountID)
|
||||
|
||||
if err := session.SetMargeAccount(ctx, accountID, ""); err != nil {
|
||||
PrintError(fmt.Sprintf("setMargeAccount: %v", err))
|
||||
return err
|
||||
if pairErr := session.SetMargeAccount(ctx, accountID, ""); pairErr != nil {
|
||||
PrintError(fmt.Sprintf("setMargeAccount: %v", pairErr))
|
||||
return pairErr
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
// other callers don't have to be rewritten; custom MarshalXML /
|
||||
// UnmarshalXML methods bridge to the nested format on the wire.
|
||||
type ClockDisplay struct {
|
||||
XMLName xml.Name `xml:"-"`
|
||||
XMLName xml.Name `xml:"clockDisplay"`
|
||||
DeviceID string
|
||||
Enabled bool
|
||||
Format string // public-facing values: "12", "24", "auto"
|
||||
@@ -234,7 +234,7 @@ func (c *ClockDisplay) IsEmpty() bool {
|
||||
// settings. Fields use the same public names as the response struct;
|
||||
// MarshalXML produces the nested wire format the device requires.
|
||||
type ClockDisplayRequest struct {
|
||||
XMLName xml.Name `xml:"-"`
|
||||
XMLName xml.Name `xml:"clockDisplay"`
|
||||
Enabled *bool
|
||||
Format string
|
||||
Brightness *int
|
||||
|
||||
@@ -137,9 +137,9 @@ func (m *Manager) ExecuteInitPlan(ctx context.Context, plan InitPlan, progress P
|
||||
emit(StepURLRewrite, "telnet URL rewrite", StatusRunning, nil)
|
||||
|
||||
urls := defaultTelnetURLs(plan.ServiceURL)
|
||||
if _, err := m.migrateViaTelnet(plan.DeviceIP, plan.ServiceURL, urls); err != nil {
|
||||
emit(StepURLRewrite, "telnet URL rewrite", StatusFailed, err)
|
||||
return plan, fmt.Errorf("URL rewrite: %w", err)
|
||||
if _, rwErr := m.migrateViaTelnet(plan.DeviceIP, plan.ServiceURL, urls); rwErr != nil {
|
||||
emit(StepURLRewrite, "telnet URL rewrite", StatusFailed, rwErr)
|
||||
return plan, fmt.Errorf("URL rewrite: %w", rwErr)
|
||||
}
|
||||
|
||||
emit(StepURLRewrite, "telnet URL rewrite", StatusOK, nil)
|
||||
@@ -154,10 +154,10 @@ func (m *Manager) ExecuteInitPlan(ctx context.Context, plan InitPlan, progress P
|
||||
|
||||
known := listKnownAccountIDs(m)
|
||||
|
||||
id, err := GenerateAccountID(known)
|
||||
if err != nil {
|
||||
emit(StepGenerateAccountID, "generate account ID", StatusFailed, err)
|
||||
return plan, fmt.Errorf("generate account ID: %w", err)
|
||||
id, genErr := GenerateAccountID(known)
|
||||
if genErr != nil {
|
||||
emit(StepGenerateAccountID, "generate account ID", StatusFailed, genErr)
|
||||
return plan, fmt.Errorf("generate account ID: %w", genErr)
|
||||
}
|
||||
|
||||
plan.AccountID = id
|
||||
@@ -165,19 +165,19 @@ func (m *Manager) ExecuteInitPlan(ctx context.Context, plan InitPlan, progress P
|
||||
emit(StepGenerateAccountID, "generate account ID="+id, StatusOK, nil)
|
||||
}
|
||||
} else if !IsValidAccountID(plan.AccountID) {
|
||||
err := fmt.Errorf("invalid AccountID %q: must be exactly 7 digits", plan.AccountID)
|
||||
emit(StepGenerateAccountID, "validate account ID", StatusFailed, err)
|
||||
invalidErr := fmt.Errorf("invalid AccountID %q: must be exactly 7 digits", plan.AccountID)
|
||||
emit(StepGenerateAccountID, "validate account ID", StatusFailed, invalidErr)
|
||||
|
||||
return plan, err
|
||||
return plan, invalidErr
|
||||
}
|
||||
|
||||
emit(StepDialWebSocket, "dial websocket", StatusRunning, nil)
|
||||
|
||||
if m.NewSetupSession == nil {
|
||||
err := errors.New("Manager.NewSetupSession is nil — call NewManager or set it explicitly")
|
||||
emit(StepDialWebSocket, "dial websocket", StatusFailed, err)
|
||||
nilErr := errors.New("Manager.NewSetupSession is nil — call NewManager or set it explicitly")
|
||||
emit(StepDialWebSocket, "dial websocket", StatusFailed, nilErr)
|
||||
|
||||
return plan, err
|
||||
return plan, nilErr
|
||||
}
|
||||
|
||||
session, err := m.NewSetupSession(plan.DeviceIP, info.DeviceID, plan.StepTimeout)
|
||||
@@ -229,9 +229,9 @@ func (m *Manager) ExecuteInitPlan(ctx context.Context, plan InitPlan, progress P
|
||||
|
||||
emit(st.kind, st.name, StatusRunning, nil)
|
||||
|
||||
if err := st.fn(ctx); err != nil {
|
||||
emit(st.kind, st.name, StatusFailed, err)
|
||||
return plan, fmt.Errorf("%s: %w", st.name, err)
|
||||
if stepErr := st.fn(ctx); stepErr != nil {
|
||||
emit(st.kind, st.name, StatusFailed, stepErr)
|
||||
return plan, fmt.Errorf("%s: %w", st.name, stepErr)
|
||||
}
|
||||
|
||||
emit(st.kind, st.name, StatusOK, nil)
|
||||
|
||||
@@ -357,7 +357,7 @@ func (m *Manager) GetMigrationSummary(deviceIP, targetURL, proxyURL string, opti
|
||||
return summary, nil
|
||||
}
|
||||
|
||||
func (m *Manager) populatePlannedNetworkConfig(summary *MigrationSummary, deviceIP, targetURL string) {
|
||||
func (m *Manager) populatePlannedNetworkConfig(summary *MigrationSummary, _, targetURL string) {
|
||||
parsedURL, err := url.Parse(targetURL)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -744,27 +744,6 @@ func applyURLOverrides(cfg *PrivateCfg, options map[string]string) {
|
||||
}
|
||||
}
|
||||
|
||||
// checkRemoteServices checks for remote services files on the device
|
||||
func (m *Manager) checkRemoteServices(summary *MigrationSummary, deviceIP string) {
|
||||
client := m.NewSSH(deviceIP)
|
||||
locations := []string{
|
||||
"/etc/remote_services",
|
||||
"/mnt/nv/remote_services",
|
||||
"/tmp/remote_services",
|
||||
}
|
||||
|
||||
for _, loc := range locations {
|
||||
if _, err := client.Run(fmt.Sprintf("[ -e %s ]", loc)); err == nil {
|
||||
summary.RemoteServicesFound = append(summary.RemoteServicesFound, loc)
|
||||
|
||||
summary.RemoteServicesEnabled = true
|
||||
if loc != "/tmp/remote_services" {
|
||||
summary.RemoteServicesPersistent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkCACertTrusted checks if the local CA certificate is already in
|
||||
// the device's trust store. The CALabel grep works regardless of whether
|
||||
// Manager.Crypto is configured — only the secondary "match cert payload"
|
||||
|
||||
@@ -141,10 +141,12 @@ func (s *SetupSession) Close() error {
|
||||
// sendStep wraps body in the canonical <msg><header url="…" method="…">…
|
||||
// envelope, sends it, and drains incoming frames until one references the
|
||||
// same requestID, status path, or url attribute — that frame is the ack.
|
||||
// Pushed <updates> and <SoundTouchSdkInfo> frames are ignored.
|
||||
func (s *SetupSession) sendStep(ctx context.Context, route, method, body string) (string, error) {
|
||||
// Pushed <updates> and <SoundTouchSdkInfo> frames are ignored. The ack
|
||||
// payload is consumed for error detection (<error …/>) only and never
|
||||
// returned — every caller discards it.
|
||||
func (s *SetupSession) sendStep(ctx context.Context, route, method, body string) error {
|
||||
if s.conn == nil {
|
||||
return "", errors.New("setup session: connection closed")
|
||||
return errors.New("setup session: connection closed")
|
||||
}
|
||||
|
||||
id := s.reqID.Add(1)
|
||||
@@ -162,7 +164,7 @@ func (s *SetupSession) sendStep(ctx context.Context, route, method, body string)
|
||||
_ = s.conn.SetWriteDeadline(deadline)
|
||||
|
||||
if err := s.conn.WriteMessage(websocket.TextMessage, []byte(envelope)); err != nil {
|
||||
return "", fmt.Errorf("send %s: %w", route, err)
|
||||
return fmt.Errorf("send %s: %w", route, err)
|
||||
}
|
||||
|
||||
idNeedle := fmt.Sprintf(`requestID="%d"`, id)
|
||||
@@ -174,7 +176,7 @@ func (s *SetupSession) sendStep(ctx context.Context, route, method, body string)
|
||||
|
||||
_, data, err := s.conn.ReadMessage()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("await ack for %s: %w", route, err)
|
||||
return fmt.Errorf("await ack for %s: %w", route, err)
|
||||
}
|
||||
|
||||
text := string(data)
|
||||
@@ -187,19 +189,18 @@ func (s *SetupSession) sendStep(ctx context.Context, route, method, body string)
|
||||
|
||||
// Device-side errors surface as <error …/> in the body.
|
||||
if strings.Contains(strings.ToLower(text), "<error") {
|
||||
return text, fmt.Errorf("device rejected %s: %s", route, strings.TrimSpace(text))
|
||||
return fmt.Errorf("device rejected %s: %s", route, strings.TrimSpace(text))
|
||||
}
|
||||
|
||||
if strings.Contains(text, idNeedle) || strings.Contains(text, statusNeedle) || strings.Contains(text, urlNeedle) {
|
||||
return text, nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start sends SETUP_START.
|
||||
func (s *SetupSession) Start(ctx context.Context) error {
|
||||
_, err := s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_START"/>`)
|
||||
return err
|
||||
return s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_START"/>`)
|
||||
}
|
||||
|
||||
// IdentifyEnter sends SETUP_IDENTIFY_DEVICE_ENTER. timeoutMs defaults to
|
||||
@@ -210,29 +211,24 @@ func (s *SetupSession) IdentifyEnter(ctx context.Context, timeoutMs int) error {
|
||||
}
|
||||
|
||||
body := fmt.Sprintf(`<setupState state="SETUP_IDENTIFY_DEVICE_ENTER" timeout="%d"/>`, timeoutMs)
|
||||
_, err := s.sendStep(ctx, "setup", "POST", body)
|
||||
|
||||
return err
|
||||
return s.sendStep(ctx, "setup", "POST", body)
|
||||
}
|
||||
|
||||
// SetLanguage POSTs sysLanguage. Code 2 = English.
|
||||
func (s *SetupSession) SetLanguage(ctx context.Context, code int) error {
|
||||
body := fmt.Sprintf(`<sysLanguage>%d</sysLanguage>`, code)
|
||||
_, err := s.sendStep(ctx, "language", "POST", body)
|
||||
|
||||
return err
|
||||
return s.sendStep(ctx, "language", "POST", body)
|
||||
}
|
||||
|
||||
// Enter sends SETUP_ENTER.
|
||||
func (s *SetupSession) Enter(ctx context.Context) error {
|
||||
_, err := s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_ENTER"/>`)
|
||||
return err
|
||||
return s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_ENTER"/>`)
|
||||
}
|
||||
|
||||
// IdentifyLeave sends SETUP_IDENTIFY_DEVICE_LEAVE.
|
||||
func (s *SetupSession) IdentifyLeave(ctx context.Context) error {
|
||||
_, err := s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_IDENTIFY_DEVICE_LEAVE"/>`)
|
||||
return err
|
||||
return s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_IDENTIFY_DEVICE_LEAVE"/>`)
|
||||
}
|
||||
|
||||
// SetName POSTs a device-name change. An empty name is a no-op.
|
||||
@@ -242,9 +238,8 @@ func (s *SetupSession) SetName(ctx context.Context, name string) error {
|
||||
}
|
||||
|
||||
body := fmt.Sprintf(`<name>%s</name>`, xmlBodyEscape(name))
|
||||
_, err := s.sendStep(ctx, "name", "POST", body)
|
||||
|
||||
return err
|
||||
return s.sendStep(ctx, "name", "POST", body)
|
||||
}
|
||||
|
||||
// SetMargeAccount sends the canonical PairDeviceWithAccount envelope.
|
||||
@@ -264,22 +259,19 @@ func (s *SetupSession) SetMargeAccount(ctx context.Context, accountID, authToken
|
||||
`<PairDeviceWithAccount><accountId>%s</accountId><userAuthToken>%s</userAuthToken></PairDeviceWithAccount>`,
|
||||
xmlBodyEscape(accountID), xmlBodyEscape(authToken),
|
||||
)
|
||||
_, err := s.sendStep(ctx, "setMargeAccount", "POST", body)
|
||||
|
||||
return err
|
||||
return s.sendStep(ctx, "setMargeAccount", "POST", body)
|
||||
}
|
||||
|
||||
// Leave sends SETUP_LEAVE.
|
||||
func (s *SetupSession) Leave(ctx context.Context) error {
|
||||
_, err := s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_LEAVE"/>`)
|
||||
return err
|
||||
return s.sendStep(ctx, "setup", "POST", `<setupState state="SETUP_LEAVE"/>`)
|
||||
}
|
||||
|
||||
// PushCustomerSupportInfo triggers the post-setup telemetry sync. Harmless
|
||||
// on our local service.
|
||||
func (s *SetupSession) PushCustomerSupportInfo(ctx context.Context) error {
|
||||
_, err := s.sendStep(ctx, "pushCustomerSupportInfoToMarge", "GET", "")
|
||||
return err
|
||||
return s.sendStep(ctx, "pushCustomerSupportInfoToMarge", "GET", "")
|
||||
}
|
||||
|
||||
// xmlAttrEscape escapes the small set of characters that would break an
|
||||
|
||||
Reference in New Issue
Block a user