Releases: nicglazkov/gcloud-dot
Release list
v1.1.11
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
Say where the product stands with Windows hardening rules
A Defender attack surface reduction rule blocked gcloud-dot-tray.exe being
started, and the alert looked like the product misbehaving. It was not: the
parent was WmiPrvSE.exe, because this project's own remote test harness started
the tray over SSH with Invoke-CimMethod on Win32_Process, and blocking process
creation that originates from WMI is precisely what that rule is for. One such
event exists in the machine's entire Defender history, timestamped to the
harness session; the startup shortcut route users actually take has never
tripped anything across multiple reboots.
An earlier session had already collided with the same rule without recognising
it: Win32_Process.Create returning 2 was read as a WMI quirk and worked around
with schtasks. It was this block.
SECURITY.md now states the position outright: nothing shipped creates a
process via WMI or PSExec, nothing uses -EncodedCommand or -WindowStyle
Hidden, and every way the tray starts gives it an ordinary parent. The one
shipped use of WMI at all is a read-only Get-CimInstance query during legacy
migration, which creates nothing.
Notice a dead tray icon and put a new one up
Explorer crashed on the Windows machine, an access violation in a shell DLL
four days into the tray's six day run, and from then on the icon took clicks
and did nothing: no menu, no window, no way to quit. The process behind it was
demonstrably healthy the whole time, answering its five second tick and
consuming a quit request the moment one was written. The user's entirely
reasonable read was "the app is stuck".
The library re-registers the icon when the shell broadcasts TaskbarCreated,
but after a crash restart that is not always the whole story, so the app stops
assuming. Once a tick it pushes the tooltip it is already showing, one cheap
shell message whose reply says whether the icon still exists, and on any error
it rebuilds the tray from scratch, menu and all. Worst case after a shell
crash is now five seconds without an icon.
Debugging this took an evening of Defender timestamps and file mtimes, because
a GUI process has no stderr anyone can see. The tray now keeps a small log
beside its state file, capped and trimmed, noting startup, icon rebuilds,
failed window opens, sign in failures, and update outcomes. tray.log next to
state.json.
Two more findings from the same post mortem. The staging directory still held
the previous installer, because a Windows tray upgrading itself is killed by
that installer partway through apply and the cleanup at the end never runs:
startup now sweeps it, and the tray no longer waits to be killed at all. It
starts the installer detached, reports the handover, and lets the installer
replace the files and start a fresh tray, which is what already happens on a
machine where the upgrade goes through the command line. The command line
itself keeps its synchronous path, since the installer does not kill it.
1.1.11: the tray survives a shell crash
Full changelog: v1.1.10...v1.1.11
v1.1.10
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
winget manifests for 1.1.9
Find Homebrew where it lives, not on PATH
Clicking Update now on a Homebrew install would have failed. The app spawned
brew by name, and a tray started at login is started by launchd, which hands
it /usr/bin:/bin:/usr/sbin:/sbin and nothing else. Homebrew puts its command
in /opt/homebrew/bin or /usr/local/bin, neither of which is on that list,
so the spawn failed with "No such file or directory" and the window reported
that the update could not be started, on a machine with Homebrew right there.
It now looks in the two prefixes Homebrew installs itself into before falling
back to whatever PATH offers, which is what gcloud::find already does and for
exactly the same reason.
Caught before shipping the click rather than after: the PATH of the running
tray on the Intel machine was read directly, and brew was not on it.
Full changelog: v1.1.9...v1.1.10
v1.1.9
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
Let a macOS install with no bundle upgrade itself
install.sh puts two bare binaries in a directory. Nothing on macOS was
prepared for that: the updater always asked for the disk image, and the disk
image path always looked for a .app to swap. Running gcloud-dot upgrade
from a shell install answered "this release has no download for this platform",
on a release that carries a download for exactly that case.
The asset now follows the shape on disk rather than the operating system. A
bundle takes the disk image; bare binaries take the macOS tarball and are
replaced the same way Linux replaces its own, which is what they are. The
restart no longer assumes open -a has a bundle to open either.
Found by running the shell installer end to end for the first time. It has been
published and linked from the site since the first release.
Take the ellipsis out of the Windows installer
The rule against them covers everything a user reads, and every sweep so far
had looked at shell scripts, markdown, HTML and Rust. PowerShell was not on
that list, so downloading... sat in the one installer nobody had grepped.
1.1.9: the shell installers work all the way through
Full changelog: v1.1.8...v1.1.9
v1.1.8
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
winget manifests for 1.1.7
Stop gcloud-dot history panicking on a nonsense sample
It sorted the measured session lengths with partial_cmp().unwrap() to show
their range. partial_cmp is None for NaN, so one NaN anywhere in the list
took the whole command down.
Clock arithmetic should never produce one, but "should never" was doing all the
work in that sentence, and the samples live in a file on disk where anything
can reach them. total_cmp orders every float there is and cannot fail.
This was the only .unwrap() left in the workspace outside tests. There are
now none.
Put checking for updates in the menu, and make the worker obey it
check_for_updates was honoured by the app and reachable by nobody. It is the
one setting here that touches the network, so somebody who would rather this
app did not call GitHub once a day should be able to say so without being told
that a state file exists.
Adding the switch exposed a worse problem behind it. The checker was started at
launch when the setting was on, which meant turning it off left the running
thread checking and reporting, and turning it on and off again left a thread
behind each time. There is now one thread for the life of the process, reading
a flag the menu writes. Switched off it holds the next check due, so switching
back on asks straight away rather than serving out the rest of a day nobody was
counting.
Turning it off also clears any update already found, since continuing to offer
one is not what was asked for.
Document the settings that have no control in the app
Quiet hours, the warning thresholds, and the service account key age are all
read and acted on, and until now appeared in no menu and no document. The only
way to discover them was to read the source.
They are in the README with their defaults, alongside a note that the file has
to be edited while the app is stopped, since it writes its own copy back on the
next change.
A test keeps that table honest by reading the README and comparing it against
Settings::default(). Written after getting warn_at_minutes wrong in the
first draft of the very table it now checks.
Also adds gcloud-dot check, which the command table had never listed.
Write release notes from the commit messages
Every release so far published one line: a link to the compare view. GitHub
builds its generated notes from merged pull requests, and this repository is
pushed to directly, so there was nothing for it to find.
That matters more here than it would elsewhere, because the update banner in
the app offers "See what changed" and sends people to exactly that page. An
empty page is a promise the app makes and the release breaks.
The commit messages already say what changed and why, so they are the notes.
Nothing is written by hand twice. Every existing release has been backfilled.
Audit the dependency tree on every push
cargo audit now runs in CI and fails on a vulnerability. There are none. The
thirteen warnings it does report were being checked whenever somebody thought
to look, which is not a schedule.
SECURITY.md described the one unsound crate and none of the rest. It now
accounts for all thirteen and says where they come from: twelve are GTK 3
bindings reaching the tree through both tao and tray-icon, which each pin
gtk 0.18, so neither can be worked around by dropping the other. The
thirteenth is ttf-parser, by way of ab_glyph, which draws the countdown
into the tray icon and only ever parses fonts compiled into the binary.
Also takes wry 0.56.1.
1.1.8: no panics, a switch for update checks, and notes worth reading
Full changelog: v1.1.7...v1.1.8
v1.1.7
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
winget manifests for 1.1.6
Make an empty cask checksum impossible to publish
Twice now the Homebrew cask has gone out with an empty sha256, which is the
worst way for it to fail: brew refuses every install with a checksum mismatch
before downloading anything, so the cask is broken for everybody while looking
entirely plausible in the diff.
The second time had a cause worth writing down. The hash was read from the
release's download URL, which sits behind a CDN that was still serving the copy
of SHA256SUMS.txt from before the disk image was added to it. The read
succeeded and returned nothing.
So the script reads through gh, which does not go through that cache, refuses
to touch the cask unless the hash is 64 hex characters, and reads the file back
after editing rather than trusting the edit.
1.1.7: recognise a Windows install again
Resolving the executable through its symlinks fixed macOS in 1.1.3 and quietly
broke Windows, where Path::canonicalize returns a verbatim path prefixed
\\?\ and the installer records an ordinary one in the registry. Comparing the
two said no every time, so an installed copy reported itself as self managed.
Nothing behaved differently, because both routes replace themselves by running
the same installer, which is the only reason this was not noticed sooner. It
was still wrong, and upgrade --check said so out loud.
The comparison now allows for the verbatim prefix and for Windows not caring
about case. It is a pure function, compiled and tested on every platform rather
than only on the one it affects, since a rule exercised nowhere is a rule
nobody notices breaking.
Full changelog: v1.1.6...v1.1.7
v1.1.6
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
Attach the webview to the GTK box on Linux, so the window draws
The details window has never rendered on Linux. It opened, sized itself
correctly, ran a WebKitWebProcess, and drew nothing: the page background
appeared and no element on top of it ever did.
WebViewBuilder::build is documented as X11 only. wry's own examples all take
the other route on Linux, attaching to the GTK box tao already puts in the
window, and that is what this now does.
Diagnosed by elimination rather than by guessing. Loading the exact same HTML
in a plain WebKit2 view on the same machine rendered it perfectly, backdrop
filters and all, which ruled out the styling, the software renderer, and the
missing DRI3 device that the logs were complaining about, and left the way the
webview was attached as the only thing that differed.
Found by clicking through the app over a remote desktop session on Ubuntu.
Nothing short of running the GUI there would have shown it: the window builds,
reports no error, and is simply empty.
1.1.6: the Linux window draws
Ships the webview attachment fix in the commit before this one. On Linux the
details window has been opening empty since the window existed.
Full changelog: v1.1.5...v1.1.6
v1.1.5
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
Fill in the placeholders on the check path too
gcloud-dot upgrade gives an apt user a command they can run. upgrade --check was still handing them ./gcloud-dot_<version>_<arch>.deb, because
the fix went in at one of the two call sites and the second one asked for the
generic form directly.
The check output also now says what to run when something else owns the files,
rather than pointing at gcloud-dot upgrade, which in that case exists only to
refuse.
The test asserts this of the function rather than of either path, since asking
it of one path is what let this through.
1.1.5: the check path fills in its placeholders
Ships the fix in the commit before this one, where upgrade --check was
still quoting and at an apt user while upgrade itself had
been giving them something runnable since 1.1.4.
Full changelog: v1.1.4...v1.1.5
v1.1.4
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
winget manifests for 1.1.3
1.1.4: stop people quitting the app when they meant to close the window
The action row read Sign in, Check now, Quit. People hit Quit meaning "dismiss
this", and the app stopped: no dot in the menu bar, no warning before the next
expiry, and no obvious way back.
The reflex is not the mistake. A panel with buttons along the bottom invites
the reading that the last one dismisses it, so the last one is now Close, which
is where that reflex lands and which only hides the window. Escape does the
same. Quit moved out of the row into the footer, set apart from the version and
the website link, and asks first in words that name what stops happening rather
than repeating the verb back.
A refresh arriving mid confirmation is now suppressed as well, since rebuilding
the body would put a live Quit button under a cursor already travelling towards
Cancel.
Also: an apt user was being told to run
sudo apt install ./gcloud-dot_<version>_<arch>.deb, which is not a command
anybody can run. Once the release is known the placeholders can be filled in,
so it now gives the curl that fetches the file and the apt that installs it,
for the version on offer and this machine's architecture. Seen on Ubuntu, where
the refusal itself was working correctly.
Match the update notification to how this copy was installed
It told everyone to "open the window and choose Update now", including installs
a package manager owns, where that button does not exist because the window
shows a command instead. A notification describing a button that is not on the
screen is worse than one that says nothing.
Caught by screenshotting the Ubuntu desktop and reading the notification that
was sitting on it, on a machine where the app had been installed with dpkg.
Full changelog: v1.1.3...v1.1.4
v1.1.3
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
1.1.3: resolve the command through its symlink before deciding who owns it
Homebrew puts gcloud-dot on PATH as a link into the app bundle. The updater
asked its question of the link rather than of the files an upgrade would
replace, and got a different answer depending on the architecture.
On an Intel Mac the link is /usr/local/bin/gcloud-dot, which matched a rule
meant for Linux system prefixes, so gcloud-dot upgrade answered an ordinary
cask install with "sudo apt install", on a machine with no apt.
On Apple Silicon it is /opt/homebrew/bin/gcloud-dot, which matched nothing and
fell through to self managed. That is the worse of the two: the app would have
replaced a bundle Homebrew owns, leaving brew describing a version no longer on
disk, which is the exact failure this whole design exists to prevent. It went
unnoticed because every test so far invoked the binary inside the bundle by its
full path, where the question does not arise.
Resolving the executable through its symlinks answers for the bundle, and the
Caskroom check then finds it on both architectures. The system prefix rule is
now Linux only as well, since there is no dpkg on macOS and /usr/local is where
Intel Homebrew lives.
Found on an Intel iMac. Nothing on an arm64 machine would have shown it.
Full changelog: v1.1.2...v1.1.3
v1.1.2
Install
Pick your platform on the website,
or take a file below. Every download is listed in SHA256SUMS.txt.
Already running it? It updates itself: open the window and choose Update now,
or run gcloud-dot upgrade.
What changed
Make the two manual release steps repeatable
The macOS disk image is built and notarized here rather than in CI, because
notarization needs an Apple key that is not in the repository's secrets. That
left two things to remember by hand, and the second one was missed on the first
release of the day: CI writes SHA256SUMS.txt before the image exists, and the
in-app updater refuses any download whose hash is not published there, so a DMG
uploaded without amending that file produces an app that declines to install
its own update.
make publish-macos now does both and prints what a client will see.
The winget manifests are generated for the same reason. Three files repeat the
version and one carries a checksum, which is read from the release's own
SHA256SUMS.txt so the manifest describes the artifact people will download
rather than one built locally alongside it.
1.1.2: keep looking for updates, and sweep what the last one left behind
The update check ran once, twenty seconds after launch. This app is built to
sit in the menu bar for weeks, so a release landing on a Tuesday was not
mentioned until something happened to restart the tray, which for a well
behaved background app might be never. It now checks again once a day.
That turns a one-off notification into a daily one, which is its own problem: a
notification a day about the same release teaches people to dismiss this app's
notifications without reading them, and the ones about expiring credentials are
the whole point. So each version is announced once however often it is seen,
while the banner in the window stays, because a banner is a statement rather
than an interruption.
Also sweeps the executable a Windows upgrade renamed aside. The installer tries
to delete it, but at that moment the process holding it is usually still alive,
so the delete is deferred to the next reboot. Doing it at startup, when nothing
holds the file, closes that gap for anyone who does not reboot.
Full changelog: v1.1.1...v1.1.2