Skip to content

Replace our own icon font, Chrome client, GitHub layer and e2e driver with libraries - #45

Merged
linghengqian merged 12 commits into
mainfrom
refactor/reuse-libraries
Oct 30, 2026
Merged

linghengqian merged 12 commits into
mainfrom
refactor/reuse-libraries

Conversation

@linghengqian

Copy link
Copy Markdown
Member
  • Replace what the repository wrote itself with maintained libraries, update mygo to 0.7.3, and drive the end-to-end tests, the memory measurement and the screenshots through the automation plugin of mygo. The bar for a library was more than 1,000 stars on GitHub, more than 200k downloads in the registry (for Go modules the importers on pkg.go.dev, which counts no downloads) and a release in the last six months; the standard library, golang.org/x and what mygo ships were exempt. 139 files, +7,707 / -16,103 lines
  • Icons: vfxtool iconfont and vfxtool renamefont (a TrueType subsetter and variable-font instancer, 19 files and 9,609 lines with their tests), the download of the font in the sandbox image, its cache in CI and the cache directory for registered fonts (ownFontCache) are gone. mygo draws SVG icons itself (ui.Icon), so shared/icons.json pins the npm package @iconify-json/material-symbols 1.2.97 (Apache-2.0, 1.3 MB, 936k downloads a month) with the SHA-512 of its tarball; vfxtool prepare downloads it once with go-getter, which checks the checksum, keeps it in the cache directory (VFOX_PLUS_ICONS; VFOX_PLUS_NPM_REGISTRY names a mirror) and cuts the outlined and the filled SVG of every symbol in use into mygo/assets/icons, and a symbol that the package lacks fails the build and is named
  • vfxtool prepare --fetch only downloads: the sandbox runs it in its deps task, into a cache volume (SANDBOX_NPM_REGISTRY names a mirror), and runs the rest offline, and mygo.yml caches the package instead of the font. No SVG file is committed: 0e45738 draws the icons from the 110 SVG files of google/material-design-icons and d8fcb03 stops committing them, so read the diff of the whole branch. The icons are still those of Google; extension, extension_off and explore were redrawn upstream, and the snapshots of the interface differ by fewer than 300 pixels each, nearly all of them in the extension icon. The icons sit on the centre of the text line, where the font put them 2 px low, and the memory use is the same
  • core/i18n parsed a subset of ICU MessageFormat itself (320 lines, with plural rules for English and Chinese only); the i18n package of mygo, which the application links already and which needs nothing but golang.org/x/text/language, parses all of it and picks the plural category from the CLDR. A number that is an argument is written the way of the language ("2,641 versions"), an apostrophe before {, } or # quotes, as in ICU (the catalogues have no such apostrophe), and the language of the desktop is read with i18n.Base, which also takes zh_CN.UTF-8 and the like
  • http.CrossOriginProtection replaces the comparison of the Origin and the Referer in requireSameOrigin: it reads Sec-Fetch-Site first and the Origin where a browser sends no such header (the Referer is not read any more), and requests without either header, like curl, pass as before. term.IsTerminal replaces isTerminal, which took any character device for a terminal, /dev/null too. golang.org/x/tools/cover replaces the parser of coverage profiles in vfxtool coverage and also merges the blocks that two packages report
  • chromedp replaces the WebSocket client (RFC 6455), the DevTools Protocol client and the Chrome launcher of vfxtool webheap, and the fake Chrome that its tests ran against: chromedp starts the browser, speaks the protocol over a pipe and removes the profile, and the tests drive a real Chrome (CHROME_BIN names it; they are skipped where there is none). The screenshots of a page, of two scenes and of the dark theme come out byte for byte as before, and the heap is read the same way (three samples, the median). go mod tidy drops brotli, which only the font cutter used, and cobra, pflag, mousetrap, otiai10/copy and x/text, and lists go-licenses, go-getter and x/tools as the direct dependencies that they are
  • The memory report reads and writes through the typed client of go-github: the layer that routed REST paths and returned decoded JSON (API, GitHubAPI, toAny, asInt, asString, ApiError; about 370 lines) is gone, and the functions take a Repository and read *github.Artifact, *github.WorkflowRun, *github.PullRequest and the like. Retries are a RoundTripper (repository.go): GET, PATCH and DELETE are sent up to three times after an HTTP 5xx or a broken connection, waiting 2 and 4 seconds, with their body; a POST is sent once, and a refusal or a rate limit is passed on, as before. The comments of a pull request are paged by go-github (ListCommentsIter; the old code stopped after 1000), the zip of an artifact is fetched from the address that the API redirects to, without the token, and read up to 64 KiB. The tests talk to a fake of the API over HTTP (httptest) instead of calling a fake Go interface, an error of the API at any of 11 stages ends the report without a write, and the comments are byte-identical to those of the old code for three merged pull requests, checked against the real API. No library for the retries qualifies: go-retryablehttp, backoff and retry-go had their last release more than six months ago, go-retry has 722 stars
  • Update mygo from 0.6.3 to 0.7.3, which asks for Go 1.27.3 in go.mod and in the documents; the 36 snapshots of the interface are pixel-identical to those of 0.6.3. The memory table was measured again, and main and the branch measured the same in the same sandbox that day (49 and 58 MB of PSS against 50 and 58), so the 45 and 53 of the old table came from an earlier state of the application and the update costs nothing. The desktop binary is 18 MB, not 12 MB; the headless one is 12 MB
  • 71 links in 14 documents lacked their closing bracket and showed as text; vfxtool docs-check fails on such a link now (markdownlint does not see it, for the text is no link). 8 bare URLs followed by a closing parenthesis, in the ASCII or in the full-width form, are in <...>, because GitHub takes a bare URL up to the next space and the punctuation after it became part of the address. A "|" in the code of a table cell cut the cell and the Windows cross-compile row lost arm64, and an anchor of the memory table pointed to a heading that the README no longer has. .markdownlint.yaml and the markdownlint-cli 0.50.0 image, pinned by its digest, run in the lint job of repository.yml, and contributing.md says how to run it; markdownlint-cli (1,102 stars, 4.4M downloads a month) was chosen over markdownlint-cli2 (966 stars), and Prettier would rewrite 38 of the 58 documents, so there is no formatter
  • End-to-end tests: the application has no scenario code any more. ui/e2e_driver.go and ui/e2e_scenarios.go (about 800 lines), the hooks of the frame and the "e2e: SHOT" and "e2e: MEASURE" lines of the output are gone; mygo/automation_e2e.go links plugins/automation (new in mygo 0.7.3: one JSON object per line over a Unix socket that MYGO_AUTOMATION names) with the build tag e2e, and ui/e2e.go keeps the profile of the offline test plugin and VFOX_PLUS_E2E_WINDOW, the size of the window of the compact layout
  • The scenarios are Go in shared/tooling: internal/automation is the client of the plugin (windows, idle, act, input, capture, tree), internal/appsession starts the application with a new home, the fake or the real vfox and a socket, and drives it by the labels and the keys of the elements of its window, and e2erun (fake, real), measure and screenshots use it. The commands, the workflows and the lines of vfxtool measure are the same, and a wait that times out prints the elements that the window showed. internal/appsession has 95 % coverage, against a fake application on a socket and against the test binary run as the application, which is ended by SIGTERM or killed; internal/automation has 96 %
  • vfxtool build --e2e builds with the tag e2e alone: the plugin needs the inspector of the native user interface, which the release build leaves out. That build is 0.4 MB larger, so the memory table says 51 / 63 / 59 / 73 MB for it (it said 50 / 62 / 58 / 73) and says which build is measured; the comment of memory.yml can show up to 1 MB more for this pull request, below the 5 MB and 5 % that it calls a change
  • The screenshots are still taken from the X root window, with the title bar and the wallpaper, because the capture of the plugin shows the content only and is twice as large. The compact ones start the application in a window of that size, so there are five runs instead of three. In the sandbox 16 of the 27 pictures are identical to those of the old driver, 8 differ by a few pixels (the random path of the sandbox, a percent, the caret) and 3 only by the position of the window, which is centred now
  • The application says what a screen reader reads, which the scenarios depend on: the row of a task says its status (it is one button, whose children are hidden), a closed dialog leaves the frame at once (the last frame kept it in the tree), and the widgets of a list item are in it by its title, not by the name it is called. kit.Registry stays for the unit tests of the components
  • Under rootless Docker what a task writes as tester belongs to a subordinate uid of the host (101000), and a directory it made, such as the stage of a package, has no write permission for others, so the user could not empty it and the second vfxtool sandbox all ended with "unlinkat dist/sandbox/mygo/package/stage-x64/vfox-plus.exe: permission denied". When the host is refused, the sandbox deletes the results with rm -rf in a throw-away container of its image (root in the container is the user), and vfxtool sandbox clean deletes the results before the images, because the container needs one. The tests stand in for Docker and for root; against the real thing, a vfxtool built before the change failed on files of uid 101000 as before, and the new one printed "Deleting ... in a container" and the second vfxtool sandbox all ran green
  • The setting System of the language matched the locales of the environment (LANGUAGE, LC_ALL, LC_MESSAGES and LANG, as GLib reads them) against the catalogues, and Windows sets none of them, so a Chinese Windows showed English until the language was chosen in the settings. Once the window can be opened, the application asks mygo for the languages of the user (Application.PreferredLanguages: the display languages on Windows, the same variables as before on Linux) and puts them before the locales of the environment; mygo can tell them only when it runs, because the call waits for the main loop, so ui.New cannot ask. Eight cases are tested with the languages of the system given
  • Looked at and kept, because no library qualifies (checked on 2026-10-30): a file lock (gofrs/flock, 767 stars), an atomic file write (google/renameio, 681 stars), a QR code in the terminal (mdp/qrterminal, 559 stars; skip2/go-qrcode has 3.0k stars but no release, and its last commit is from 2024-03; boombuler/barcode had its last release in 2025-07), the XDG directories (adrg/xdg, last release 2024-10), and the Material colour utilities and the androidx shapes in ui/mcu and ui/shapes (the best Go port has 7 stars). A qualifying library in another language, fontTools (5.3k stars), was not adopted because the tooling is Go only; SVG icons removed the need for the font cutter. Dependencies that already fail the bar and stay: skip2/go-qrcode, google/go-licenses (v2.0.1, 2025-09) and ZXing core 3.5.4 on Android (2025-11-11)
  • The workflows changed in two files: mygo.yml (the cache of the npm package of the icons instead of the one of the font, and two comments) and repository.yml (the markdownlint step)
  • vfxtool sandbox all passes on the last commit, run twice (golangci-lint 0 issues with and without the tag e2e, coverage of core and state 92.6 %, of ui 90.1 %, the scenarios fake and real, the .deb installed into a clean Ubuntu container); the second run is the one that deleted the files of uid 101000 in a container. The last four commits were also built and tested one by one from copies made with git archive (shared/tooling with the Windows cross-build and vet, mygo with and without the tag e2e, docs-check and markdownlint). Not run: the workflows on GitHub, and anything on Windows (the build for Windows is only cross-compiled and vetted, so the language fix is untested there)

- mygo draws SVG icons itself (`ui.Icon`), so the Material Symbols come as
  the 24 px SVGs of google/material-design-icons, the revision that the font
  was cut from, outlined and filled, in `shared/icons`
- `vfxtool iconfont` (a TrueType subsetter and variable-font instancer, about
  7,000 lines) and `vfxtool renamefont` are gone, with the download of the
  font in the sandbox image, the cache of it in CI and the cache directory
  that mygo needed for registered fonts (`ownFontCache`)
- `vfxtool prepare` copies the icons, the catalogues and the license, and
  calls the license collector itself instead of running a copy of vfxtool
- the icons sit on the centre of the text line, where the font put them 2 px
  low; the memory use is the same (measured on Home and with Node.js listed)
- `core/i18n` parsed a subset of ICU MessageFormat itself (320 lines, with
  plural rules for English and Chinese only); the i18n package of mygo, which
  the application links already and which needs nothing but
  golang.org/x/text/language, parses all of it and picks the plural category
  from the CLDR
- a number that is an argument is written the way of the language
  (`2,641 versions`), and an apostrophe before `{`, `}` or `#` quotes, as in
  ICU; the catalogues have no such apostrophe
- the language of the desktop is read with `i18n.Base`, which also takes
  `zh_CN.UTF-8` and the like
- `requireSameOrigin` compared the Origin and the Referer by hand;
  `http.CrossOriginProtection` reads Sec-Fetch-Site first and the Origin
  where a browser sends no such header (the Referer is not read any more);
  requests without either header, like curl, pass as before
- `isTerminal` took any character device for a terminal, /dev/null too;
  `term.IsTerminal` asks the system
- `vfxtool webheap` carried a WebSocket client (RFC 6455), a DevTools
  Protocol client and a Chrome launcher of its own, and its tests a fake
  Chrome with a WebSocket server; chromedp starts the browser, speaks the
  protocol over a pipe and removes the profile, and the tests drive a real
  Chrome (they are skipped where there is none)
- the screenshots of a page, of two scenes and of the dark theme come out byte
  for byte as before, and the heap is read the same way (three samples, the
  median)
- `vfxtool coverage` reads the profile with golang.org/x/tools/cover, which
  also merges the blocks that two packages report
- `go mod tidy` drops brotli, which only the font cutter used, and lists
  go-licenses and go-getter as the direct dependencies that they are
- mygo v0.6.3 -> v0.7.3, which asks for Go 1.27.3 in go.mod and in the docs
- the 36 snapshots of the interface are pixel-identical to those of 0.6.3
- measure the memory table again: 50 MB of PSS on the Home page and 58 MB with the
  versions of Node.js listed; main measures 49 and 58 in the same sandbox today, so
  the 45 and 53 of the table came from an earlier state of the application, and the
  update costs nothing
- the desktop binary is 18 MB, not 12 MB; the headless one is 12 MB
- the Technology link of the table pointed to an anchor that the README no longer has
- shared/icons.json pins @iconify-json/material-symbols 1.2.97 (Apache-2.0, 1.3 MB,
  936k downloads a month) with the SHA-512 of its tarball
- vfxtool prepare downloads it once with go-getter, which checks the checksum, keeps
  it in the cache directory (VFOX_PLUS_ICONS; VFOX_PLUS_NPM_REGISTRY names a mirror)
  and cuts the outlined and the filled SVG of every symbol in use into
  mygo/assets/icons; a symbol that the package lacks fails the build, naming it
- vfxtool prepare --fetch only downloads: the sandbox runs it in its deps task, into a
  cache volume (SANDBOX_NPM_REGISTRY names a mirror), and runs the rest offline;
  CI caches the package
- shared/icons (110 SVG files and their license) is gone; licenses.txt gives the origin
  of the icons and the license text of the repository, which is the same
- the icons are still those of Google; extension, extension_off and explore were
  redrawn upstream, and the snapshots of the interface differ by fewer than 300 pixels
  each, nearly all of them in the extension icon
- no Go module qualifies for this: cogentcore.org/core/icons is the Outlined style,
  a 48 MB module with 104 importers
- the layer that routed REST paths and returned decoded JSON (API, GitHubAPI, toAny,
  asInt, asString, ApiError; about 370 lines) is gone: the functions take a Repository
  and read *github.Artifact, *github.WorkflowRun, *github.PullRequest and the like
- retries are a RoundTripper (repository.go): GET, PATCH and DELETE are sent up to three
  times after an HTTP 5xx or a broken connection, waiting 2 and 4 seconds, with their
  body; a POST is sent once, and a refusal or a rate limit is passed on, as before
- the comments of a pull request are paged by go-github (ListCommentsIter); the old code
  stopped after 1000
- the zip of an artifact is fetched from the address that the API redirects to, without
  the token, and read up to 64 KiB
- the tests talk to a fake of the API over HTTP (httptest) instead of calling a fake Go
  interface, and an error of the API at any of 11 stages ends the report without a write
- the comments are byte-identical to those of the old code for three merged pull
  requests, checked against the real API
- no library for the retries qualifies: go-retryablehttp, backoff and retry-go had their
  last release more than six months ago, go-retry has 722 stars
- 71 links in 14 documents lacked their closing bracket, as in [`x`(../x.md), and
  showed as text; vfxtool docs-check now fails on such a link (markdownlint does not
  see it, for the text is no link)
- 8 bare URLs followed by ")" or by the full-width ")" are in <...>: GitHub takes a
  bare URL up to the next space, so the punctuation after it became part of the address
- a '|' in the code of a table cell cut the cell, and the Windows cross-compile row lost
  arm64; the trailing space of a code span is said in words; an anchor of the memory
  table pointed to a heading that the README no longer has
- .markdownlint.yaml and the markdownlint-cli 0.50.0 image (pinned by its digest) run in
  the lint job of repository.yml; contributing.md says how to run it and what matters
- markdownlint-cli (1,102 stars, 4.4M downloads a month) was chosen over markdownlint-cli2
  (966 stars); Prettier would rewrite 38 of the 58 documents, so there is no formatter
- shared/tooling/internal/automation speaks the protocol of plugins/automation (new in
  mygo 0.7.3: one JSON object per line over a Unix socket) as a Go client: windows, idle,
  act, input, capture and tree; a failure that the application reports is an *Error with
  the code of the protocol
- the helpers on a tree read a window the way a person does: find and filter nodes, whether
  a node is within another, what a text says, the title of a list item, the field that
  follows the title of a setting, the percent of a download that the window shows
- the tests talk to a fake application on a socket and cover 96 % of the statements
- nothing uses it yet: the next commits drive the end-to-end scenarios, the memory
  measurement and the screenshots through it
- under rootless Docker what a task writes as tester belongs to a subordinate uid of the
  host (101000), and a directory it made, such as the stage of a package, has no write
  permission for others: the user cannot empty it, so the second vfxtool sandbox all ended
  with "unlinkat dist/sandbox/mygo/package/stage-x64/vfox-plus.exe: permission denied"
- removeResults tries the host first; when it is refused, a throw-away container of the
  sandbox image (else the base image, else the clean Ubuntu) deletes the directory with
  rm -rf, for root in the container is the user, who may delete what the subordinate uid
  owns; without any of the images it says what to do
- vfxtool sandbox clean deletes the results before it deletes the images, for the
  container needs one
- the Docker commands are variables, so the tests, which stand in for Docker and for
  root, need neither
- checked against the real thing: a vfxtool built from the previous commit failed on files
  of uid 101000 as before, this one printed "Deleting ... in a container" and the second
  vfxtool sandbox all ran green
- the setting "System" matched the locales of the environment (LANGUAGE, LC_ALL,
  LC_MESSAGES and LANG, as GLib reads them) against the catalogues; Windows sets none of
  them, so a Chinese Windows showed English until the language was chosen in the settings
- once the window can be opened (App.WhenReady), the application asks mygo for the
  languages of the user (Application.PreferredLanguages: on Windows the display languages
  of GetUserPreferredUILanguages, on Linux the same variables as before) and puts them
  before the locales of the environment; mygo can tell them only when it runs, for the call
  waits for the main loop, so ui.New cannot ask
- eight cases are tested with the languages of the system given: Chinese Windows without
  a variable, Traditional Chinese (which has the one Chinese catalogue), the first language
  that has a catalogue, none that has, the environment alone, the system before the
  environment, and a language of the settings, which wins
- the call into Windows is compiled and vetted for Windows here, but was not run on one
…plugin of mygo

- the application has no scenario code any more: ui/e2e_driver.go and ui/e2e_scenarios.go
  (about 800 lines), the hooks of the frame and the "e2e: SHOT" and "e2e: MEASURE" lines
  of its output are gone; mygo/automation_e2e.go links plugins/automation with the build
  tag e2e, and ui/e2e.go keeps the profile of the offline test plugin and
  VFOX_PLUS_E2E_WINDOW, the size of the window of the compact layout
- the scenarios are Go in shared/tooling: appsession starts the application with a new
  home, the fake or the real vfox and a socket, and drives it by the elements of the window
  (the labels and the keys that the components give them); e2erun (fake, real), measure
  and screenshots use it. The commands, the workflows and the numbers are the same, and a
  wait that times out prints the elements that the window showed
- vfxtool build --e2e builds with the tag e2e alone: the plugin needs the inspector, which
  the release build leaves out. That build is 0.4 MB larger, and the memory table now
  says 51 / 63 / 59 / 73 MB for it (it said 50 / 62 / 58 / 73)
- the screenshots are still taken from the X root window, for the capture of the plugin
  has no title bar and is twice as large; the compact ones start the application in a
  window of that size, so there are five runs instead of three. In the sandbox 16 of the
  27 pictures are identical to those of the old driver, 8 differ by a few pixels (the
  random path of the sandbox, a percent, the caret) and 3 only by the position of the
  window
- the application says what a screen reader reads: the row of a task says its status (it
  is one button, whose children are hidden), a closed dialog leaves the frame at once (the
  last frame kept it in the tree), and the widgets of a list item are in it by its title,
  not by the name it is called
- kit.Registry stays for the unit tests of the components; the tests of appsession (95 %)
  run against a fake application on a socket and against the test binary run as the
  application, which is ended by SIGTERM or killed
- the documents in both languages say how the scenarios, the measuring and the screenshots
  work now
@linghengqian linghengqian added the enhancement New feature or request label Oct 30, 2026
@linghengqian
linghengqian marked this pull request as ready for review October 30, 2026 01:43
@linghengqian
linghengqian merged commit d115252 into main Oct 30, 2026
13 checks passed
@linghengqian
linghengqian deleted the refactor/reuse-libraries branch October 30, 2026 01:43
@linghengqian linghengqian added this to the 1.0.0 milestone Oct 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant