The update checker used http.Get (http.DefaultClient, no timeout) inside
a synchronous ticker loop, so an endpoint that accepts the connection but
never responds blocks the loop forever -- update checking silently dies
until restart. The response body was also only closed on the success
path, leaking the connection on the non-200 and read-error returns.
Extract the fetch into fetchAppUpdate(client, url): the caller passes a
client with a 10s timeout, and the body is closed via defer on every
path. Add tests covering success, body-close on non-200, and timeout.
Closes#445.