A long overdue dev log. The last one was for September 2024. That's half a year.
libopenrawReleased 0.4.0-alpha9 of the Rust crate. Added a bunch of cameras. Fixed some Maker Note for some Fujifilm camera, and a fews other, also fixed some thumbnailing.
The main API is now faillible with Result<> returned. This should reduce the amount of panics (it shouldn't panic).
Added support for user crops in Fujifilm files as I added support for the GFX 100RF (sight unseen).
NiepceChanged the catalog format. By changed, it's just that it has an extension .npcat and that it is standalone instead of being a folder. The thumbnail cache will be in the same folder next to it.
Now we can open a different catalog. Also renamed some bits internally to be consistent with the naming.
Removed some UI CSS hacks now that the is an API for Gtk.TreeExpander.set_hide_expander() in Gtk 4.10. Fixed some bug with the treeview not being updated. Removed Gtk.ColorButton (deprecated). Fix some selection issues with the Gtk.ListView.
Moved to Rust 2024.
Added video thumbnailing. Code was inspired from Totem's.
Fixed some bugs with importing hierarchies of folders, and fix deleting a folder with folders.
Still working on the import feature I mentionned previously. It is getting there. My biggest issue that one can't select a Gtk.ListView item by item, only by index, which is complicated on a tree view. On the other hand several of the fixes mentionned above came from this work as I cherry-picked the patches to the main branch.
i18n-formatFixed my i18n-format crate as the minor version of gettext removed the essential feature I was relying on. Yes this is a semver breakage. I ended up having to split the crate to have a non macro crate. From a usage standpoint it works the same.
The long term is to have this crate be unnecessary.
OtherOther stuff I contributed to.
GlycinSubmitted support for the rotation of camera raw files, and the Loupe counterpart. This is a followup to the camera raw file support in glycin.
The GNOME Foundation is thrilled to share that registration for GUADEC 2025 is now open!
GUADEC is the largest annual gathering of GNOME developers, contributors, and community members. This year we welcome everyone to join us in the beautiful city of Brescia, Italy from July 24th to 29th or online! For those who cannot join us in person, we will live-stream the event so you can attend or present remotely.
To register, visit guadec.org and select whether you will attend in person or remotely.
In-person attendees will notice a slight change on their registration form. This year we’ve added a section for “Registration Type” and provided 4 options for ticket fees. These costs go directly towards supporting the conference and helping us build a better GUADEC experience.
We ask that in-person attendees select the option they are most comfortable with. If you have any questions, please don’t hesitate to reach out to us at guadec@gnome.org.
Register for In-Person Attendance
Register for Remote Attendance
The Call for Participation is ongoing but once are talks are selected you will find speaker details and a full schedule on guadec.org. We will also be adding more information about social events, accommodations, and activities throughout Brescia soon!
We are still looking for conference sponsors. If you or your company would like to become a GUADEC 2025 sponsor, please take a look at our sponsorship brochure and reach out to us at guadec@gnome.org.
To stay up-to-date on conference news, be sure to follow us on Mastodon @gnome@floss.social.
We look forward to seeing you in Brescia and online!
After a long time of low-maintenance (as in me being out of the picture and doing mostly releases and some trivial/not-so-trivial-but-quick fixes here and there) period for GNOME-Calculator, it's time to reveal what's happening behind the scenes.
Long-story short, pretty late in the 48 cycle two contributors popped up to breathe some life into GNOME Calculator, so much, that I had a pretty hard time keeping track of the merge requests piling up. So most of the kudos for the below-mentioned features go to fcusr and Adrien Plazas, and I hope I will manage to list all of them, and it would be great to have folks using the Nightly Calculator (current development version from flatpak gnome-nightly repo) to help spot issues/requests in time to be fixed for 49.
So now the features:Conversion modeUI cleanup, styling and touch improvementsBoth Adrien and fcusr worked on simplifying the UI-related code, dropping old/unnecessary styling, tweaking the looks of buttons, improving the access to toggles/switches to make Calculator easier to use with functions grouped, styled in a meaningful way.
The interface was also "optimized" for smaller screens/touch devices, namely function buttons which up until now only entered the function name to save you some typing will work with some text selected to insert brackets around the selection and add the function.
New functions and constantsFor anyone needing them, new functions have been added:
As the list of available currencies for conversion is already huge, scrolling through the currency list for selecting currencies in case you have multiple ones you are used to convert between (given that the last currencies you used should be persisted) is harder, currencies can be marked as Favorites using the preferences section for Favorite currencies, and the selected ones will appear on top of the currency selector.
GNOME exchange APIGiven that we are occasionally having issues with the exchange rate providers (site not being available, site not accepting our user-agent) rendering Calculator currency conversions broken (or even worse, in some cases freezing Calculator completely) the decision was taken to host our own exchange rate API, and with the help of the folks in the GNOME Infrastructure team we have a GNOME exchange API, which will be used for exchange rate retrieval.
The relevant project is available at https://gitlab.gnome.org/Infrastructure/xchgr8s.
For now, this is basically a static mirror of the providers used so far in Calculator (hence the URL change can be "backported" to any calculator version easily), which does fetch the exchange rates once a day from all providers, and commits them to the repository, from where it will be served via gitlab pages + GNOME reverse proxy + CDN.
This way we have control over the format we provide, we can do any processing on the exchange rates fetched from the external sources, and we can update the currency providers in GNOME Calculator however we want as long as they use one of the formats provided by the exchange-API, be it an existing format or a completely new one added to exchange API.
This was a first step towards fixing a 10-year old, GNOME bugzilla-reported bug still open, but I would say we're on the right track.
That's all for now, keep up the good work.
I’ve blogged in the past about how WebKit on Linux integrates with Sysprof, and provides a number of marks on various metrics. At the time that was a pretty big leap in WebKit development since it gave use a number of new insights, and enabled various performance optimizations to land.
But over time we started to notice some limitations in Sysprof. We now have tons of data being collected (yay!) but some types of data analysis were pretty difficult yet. In particular, it was difficult to answer questions like “why does render times increased after 3 seconds?” or “what is the CPU doing during layout?”
In order to answer these questions, I’ve introduced a new feature in Sysprof: filtering by marks.
Hopefully people can use this new feature to provide developers with more insightful profiling data! For example if you spot a slowdown in GNOME Shell, you open Sysprof, profile your whole system, and filter by the relevant Mutter marks to demonstrate what’s happening there.
Here’s a fancier video (with music) demonstrating the new feature:
Enjoy!
The C++ standard library (also know as the STL) is, without a doubt, an astounding piece of work. Its scope, performance and incredible backwards compatibility have taken decades of work by many of the world's best programmers. My hat's off to all those people who have contributed to it.
All of that is not to say that it is not without its problems. The biggest one being the absolutely abysmal compile times but unreadability, and certain unoptimalities caused by strict backwards compatibility are also at the top of the list. In fact, it could be argued that most of the things people really dislike about C++ are features of the STL rather than the language itself. Fortunately, using the STL is not mandatory. If you are crazy enough, you can disable it completely and build your own standard library in the best Bender style.
One of the main advantages of being an unemployed-by-choice open source developer is that you can do all of that if you wish. There are no incompetent middle damagers hovering over your shoulder to ensure you are "producing immediate customer value" rather than "wasting time on useless polishing that does not produce immediate customer value".
It's my time, and I'll waste it if I want to!
What's in it?The biggest design questions of a standard library are scope and the "feel" of the API. Rather than spending time on design, we steal it. Thus, when in doubt, read the Python stdlib documentation and replicate it. Thus the name of the library is pystd.
The test appTo keep the scope meaningful, we start by writing only enough of stdlib to build an app that reads a text file, validates it as UTF-8, splits the contents into words, counts how many time each word appears in the file and prints all words and how many times it appears sorted by decreasing count.
This requires, at least:
Disabling the STL is fairly easy (with Linux+GCC at least) and requires only these two Meson statements:
add_global_arguments('-nostdinc++', language: 'cpp')
add_global_link_arguments('-nostdlib++', '-lsupc++', language: 'cpp')
The supc++ library is (according to stackoverflow) a support library GCC needs to implement core language features. Now the stdlib is off and it is time to implement everything with sticks, stones and duct tape.
The outcomeOnce you have implemented everything discussed above and auxiliary stuff like a hashing framework the main application looks like this.
The end result is both Valgrind and Asan clean. There is one chunk of unreleased memory, but that comes from supc++. There is probably UB in the implementation. But it should be the good kind of UB that, if it would actually not work, would break the entire Linux userspace because everything depends on it working "as expected".
All of this took fewer than 1000 lines of code in the library itself (including a regex implementation that is not actually used). For comparison merely including vector from the STL brings in 27 thousand lines of code.
Comparison to an STL versionConverting this code to use the STL is fairly simple and only requires changing some types and fine tuning the API. The main difference is that the STL version does not validate that the input is UTF-8 as there is no builtin function for that. Now we can compare the two.
Runtime for both is 0.001 to 0.002 seconds on the small test file I used. Pystd is not noticeably slower than the STL version, which is enough for our purposes. It almost certainly scales worse because there has been zero performance work on it.
Compiling the pystd version with -O2 takes 0.3 seconds whereas the STL version takes 1.2 seconds. The measurements were done on a Ryzen 7 3700X processor.
The executable's unstripped size is 349k for STL and 309k for pystd. The stripped sizes are 23k and 135k. Approximately 100 k of the pystd executable comes from supc++. In the STL version that probably comes dynamically from libstdc++ (which, on this machine, takes 2.5 MB).
Perfect ABI stabilityDesigning a standard library is exceedingly difficult because you can't ever really change it. Someone, somewhere, is depending on every misfeature in it so they can never be changed.
Pystd has been designed to both support perfect ABI stability and make it possible to change it in arbitrary ways in the future. If you start from scratch this turned out to be fairly simple.
The sample code above used the pystd namespace. It does not actually exist. Instead it is defined like this in the cpp file:
#include <pystd2025.hpp>
namespace pystd = pystd2025;
In pystd all code is in a namespace with a year and is stored in a header file with the same year. The idea is, then, that every year you create a new release. This involves copying all stdlib header files to a file with the new year and regexping the namespace declarations to match. The old code is now frozen forever (except for bug fixes) whereas the new code can be changed at will because there are zero existing lines of code that depend on it.
End users now have the choice of when to update their code to use newer pystd versions. Even better, if there is an old library that can not be updated, any of the old versions can be used in parallel. For example:
pystd2030::SomeType foo;Thus if no code is ever updated, everything keeps working. If all code is updated at once, everything works. If only parts of the code are updated, things can still be made to work with some glue code. This puts the maintenance burden on the people whose projects can not be updated as opposed to every other developer in the world. This is as it should be, and also would motivate people with broken deps to spend some more effort to get them fixed.
Some links for technical articles on various topics I read.
NVIDIA emulation journey, part 1: RIVA 128 / NV3 architecture history and basic overview - From the developers of 86Box emulators, some history and technical summary about the first successful 3D GPU from Nvidia.
zlib-rs is faster than C - Something on the reimplementation of zlib in Rust, and how it performs better. Safe + speed, pick 2.
Understanding ActivityPub Part 1 to 4. - A four part explainer on how ActivityPub, the protocol behind the Fediverse, works.
Memory safety for web fonts - Chrome developers explain how they are replacing freetype to a memory safe solution, written in Rust, called Skrifa.
GIMP 3.0 released - Aleksandr Prokudin summarize what's new in the long awaited GIMP 3.0. Don't forget his weekly writeups of Libre Arts updates.
The GNOME Project is proud to announce the release of GNOME 48, ‘Bengaluru’.
GNOME 48 brings several exciting updates, including improved notification stacking for a cleaner experience, better performance with dynamic triple buffering, and the introduction of new fonts like Adwaita Sans & Mono. The release also includes Decibels, a minimalist audio player, new digital well-being features, battery health preservation with an 80% charge limit, and HDR support for compatible displays.
For a detailed breakdown, visit the GNOME 48 Release Notes.
GNOME 48 will be available shortly in many distributions, such as Fedora 42 and Ubuntu 25.04. If you want to try it today, you can look for their beta releases, which will be available very soon
We are also providing our own installer images for debugging and testing features. These images are meant for installation in a vm and require GNOME Boxes with UEFI support. We suggest getting Boxes from Flathub.
If you’re looking to build applications for GNOME 48, check out the GNOME 48 Flatpak SDK on Flathub.
You can also support the GNOME project by donating—your contributions help us improve infrastructure, host community events, and keep Flathub running. Every donation makes a difference!
This six-month effort wouldn’t have been possible without the whole GNOME community, made of contributors and friends from all around the world: developers, designers, documentation writers, usability and accessibility specialists, translators, maintainers, students, system administrators, companies, artists, testers, the local GNOME.Asia team in Bengaluru, and last, but not least, our users.
We hope to see some of you at GUADEC 2025 in Brescia, Italy!
Our next release, GNOME 49, is planned for September. Until then, enjoy GNOME 48.
The GNOME release team
I see a lot of users approaching GNOME app development with prior language-specific experience, be it Python, Rust, or something else. But there’s another way to approach it: GObject-oriented and UI first.
This introduces more declarative code, which is generally considered cleaner and easier to parse. Since this approach is inherent to GTK, it can also be applied in every language binding. The examples in this post stick to Python and Blueprint.
PropertiesWhile normal class properties for data work fine, using GObject properties allows developers to do more in UI through expressions.
Handling Properties ConventionallyLet’s look at a simple example: there’s a progress bar that needs to be updated. The conventional way of doing this would look something like the following:
using Gtk 4.0; using Adw 1; template $ExampleProgressBar: Adw.Bin { ProgressBar progress_bar {} }This defines a template called ExampleProgressBar which extends Adw.Bin and contains a Gtk.ProgressBar called progress_bar.
The reason why it extends Adw.Bin instead of Gtk.ProgressBar directly is because Gtk.ProgressBar is a final class, and final classes can’t be extended.
from gi.repository import Adw, GLib, Gtk @Gtk.Template(resource_path="/org/example/App/progress-bar.ui") class ExampleProgressBar(Adw.Bin): __gtype_name__ = "ExampleProgressBar" progress_bar: Gtk.ProgressBar = Gtk.Template.Child() progress = 0.0 def __init__() -> None: super().__init__() self.load() def load(self) -> None: self.progress += 0.1 self.progress_bar.set_fraction(self.progress) if int(self.progress) == 1: return GLib.timeout_add(200, self.load)This code references the earlier defined progress_bar and defines a float called progress. When initialized, it runs the load method which fakes a loading operation by recursively incrementing progress and setting the fraction of progress_bar. It returns once progress is 1.
This code is messy, as it splits up the operation into managing data and updating the UI to reflect it. It also requires a reference to progress_bar to set the fraction property using its setter method.
Handling Properties With GObjectNow, let’s look at an example of this utilizing a GObject property:
using Gtk 4.0; using Adw 1; template $ExampleProgressBar: Adw.Bin { ProgressBar { fraction: bind template.progress; } }Here, the progress_bar name was removed since it isn’t needed anymore. fraction is bound to the template’s (ExampleProgressBar‘s) progress property, meaning its value is synced.
from gi.repository import Adw, GLib, GObject, Gtk @Gtk.Template(resource_path="/org/example/App/progress-bar.ui") class ExampleProgressBar(Adw.Bin): __gtype_name__ = "ExampleProgressBar" progress = GObject.Property(type=float) def __init__() -> None: super().__init__() self.load() def load(self) -> None: self.progress += 0.1 if int(self.progress) == 1: return GLib.timeout_add(200, self.load)The reference to progress_bar was removed in the code too, and progress was turned into a GObject property instead. fraction doesn’t have to be manually updated anymore either.
So now, managing the data and updating the UI merged into a single property through a binding, and part of the logic was put into a declarative UI file.
In a small example like this, it doesn’t matter too much which approach is used. But in a larger app, using GObject properties scales a lot better than having widget setters all over the place.
CommunicationProperties are extremely useful on a class level, but once an app grows, there’s going to be state and data communication across classes. This is where GObject signals come in handy.
Handling Communication ConventionallyLet’s expand the previous example a bit. When the loading operation is finished, a new page has to appear. This can be done with a callback, a method that is designed to be called by another method, like so:
using Gtk 4.0; using Adw 1; template $ExampleNavigationView: Adw.Bin { Adw.NavigationView navigation_view { Adw.NavigationPage { child: $ExampleProgressBar progress_bar {}; } Adw.NavigationPage { tag: "finished"; child: Box {}; } } }There’s now a template for ExampleNavigationView, which extends an Adw.Bin for the same reason as earlier, which holds an Adw.NavigationView with two Adw.NavigationPages.
The first page has ExampleProgressBar as its child, the other one holds a placeholder and has the tag “finished”. This tag allows for pushing the page without referencing the Adw.NavigationPage in the code.
from gi.repository import Adw, Gtk from example.progress_bar import ExampleProgressBar @Gtk.Template(resource_path="/org/example/App/navigation-view.ui") class ExampleNavigationView(Adw.Bin): __gtype_name__ = "ExampleNavigationView" navigation_view: Adw.NavigationView = Gtk.Template.Child() progress_bar: ExampleProgressBar = Gtk.Template.Child() def __init__(self) -> None: super().__init__() def on_load_finished() -> None: self.navigation_view.push_by_tag("finished") self.progress_bar.load(on_load_finished)The code references both navigation_view and progress_bar. When initialized, it runs the load method of progress_bar with a callback as an argument.
This callback pushes the Adw.NavigationPage with the tag “finished” onto the screen.
from typing import Callable from gi.repository import Adw, GLib, GObject, Gtk @Gtk.Template(resource_path="/org/example/App/progress-bar.ui") class ExampleProgressBar(Adw.Bin): __gtype_name__ = "ExampleProgressBar" progress = GObject.Property(type=float) def load(self, callback: Callable) -> None: self.progress += 0.1 if int(self.creation_progress) == 1: callback() return GLib.timeout_add(200, self.load, callback)ExampleProgressBar doesn’t run load itself anymore when initialized. The method also got an extra argument, which is the callback we passed in earlier. This callback gets run when the loading has finished.
This is pretty ugly, because the parent class has to run the operation now.
Another way to approach this is using a Gio.Action. However, this makes illustrating the point a bit more difficult, which is why a callback is used instead.
Handling Communication With GObjectWith a GObject signal the logic can be reversed, so that the child class can communicate when it’s finished to the parent class:
using Gtk 4.0; using Adw 1; template $ExampleNavigationView: Adw.Bin { Adw.NavigationView navigation_view { Adw.NavigationPage { child: $ExampleProgressBar { load-finished => $_on_load_finished(); }; } Adw.NavigationPage { tag: "finished"; child: Box {}; } } }Here, we removed the name of progress_bar once again since we won’t need to access it anymore. It also has a signal called load-finished, which runs a callback called _on_load_finished.
from gi.repository import Adw, Gtk from example.progress_bar import ExampleProgressBar @Gtk.Template(resource_path="/org/example/App/navigation-view.ui") class ExampleNavigationView(Adw.Bin): __gtype_name__ = "ExampleNavigationView" navigation_view: Adw.NavigationView = Gtk.Template.Child() @Gtk.Template.Callback() def _on_load_finished(self, _obj: ExampleProgressBar) -> None: self.navigation_view.push_by_tag("finished")In the code for ExampleNavigationView, the reference to progress_bar was removed, and a template callback was added, which gets the unused object argument. It runs the same navigation action as before.
from gi.repository import Adw, GLib, GObject, Gtk @Gtk.Template(resource_path="/org/example/App/progress-bar.ui") class ExampleProgressBar(Adw.Bin): __gtype_name__ = "ExampleProgressBar" progress = GObject.Property(type=float) load_finished = GObject.Signal() def __init__(self) -> None: super().__init__() self.load() def load(self) -> None: self.progress += 0.1 if int(self.creation_progress) == 1: self.emit("load-finished") return GLib.timeout_add(200, self.load)In the code for ExampleProgressBar, a signal was added which is emitted when the loading is finished. The responsibility of starting the load operation can be moved back to this class too. The underscore and dash are interchangeable in the signal name in PyGObject.
So now, the child class communicates to the parent class that the operation is complete, and part of the logic is moved to a declarative UI file. This means that different parent classes can run different operations, while not having to worry about the child class at all.
Next StepsRefine is a great example of an app experimenting with this development approach, so give that a look!
I would also recommend looking into closures, since it catches some cases where an operation needs to be performed on a property before using it in a binding.
Learning about passing data from one class to the other through a shared object with a signal would also be extremely useful, it comes in handy in a lot of scenarios.
And finally, experiment a lot, that’s the best way to learn after all.
Thanks to TheEvilSkeleton for refining the article, and Zoey for proofreading it.
Happy hacking!
I've explained in other posts that I ran for the 2025 Open Source Initative Board of Directors in the “Affiliate” district.
Voting closed on MON 2025-03-17 at 10:00 US/Pacific. One hour later, candidates were surprised to receive an email from OSI demanding that all candidates sign a Board agreement before results were posted. This was surprising because during mandatory orientation, candidates were told the opposite: that a Board agreement need not be signed until the Board formally appointed you as a Director (as the elections are only advisory —: OSI's Board need not follow election results in any event. It was also surprising because the deadline was a mere 47 hours later (WED 2025-03-19 at 10:00 US/Pacific).
Many of us candidates attempted to get clarification over the last 46 hours, but OSI has not communicated clear answers in response to those requests. Based on these unclear responses, the best we can surmise is that OSI intends to modify the ballots cast by Affiliates and Members to remove any candidate who misses this new deadline. We are loathe to assume the worst, but there's little choice given the confusing responses and surprising change in requirements and deadlines.
So, I decided to sign a Board Agreement with OSI. Here is the PDF that I just submitted to the OSI. I emailed it to OSI instead. OSI did recommend DocuSign, but I refuse to use proprietary software for my FOSS volunteer work on moral and ethical grounds0 (see my two keynotes (FOSDEM 2019, FOSDEM 2020) (co-presented with Karen Sandler) on this subject for more info on that).
My running mate on the Shared Platform for OSI Reform, Richard Fontana, also signed a Board Agreement with OSI before the deadline as well.
0 Chad Whitacre has made unfair criticism of my refusal tog use Docusign as part of the (apparently ongoing?) 2025 OSI Board election political campaign. I respond to his comment here in this footnote (& further discussion is welcome using the fediverse, AGPLv3-powered comment feature of my blog). I've put it in this footnote because Chad is not actually raising an issue about this blog post's primary content, but instead attempting to reopen the debate about Item 4 in the Shared Platform for OSI Reform. My response follows:
In addition to the two keynotes mentioned above, I propose these analogies that really are apt to this situation:
Hello everyone. If you’re reading this, then you are alive. Congratulations. It’s a wild time to be alive. Remember Thib’s advice: it’s okay to relax! If you take a day off from the news, it will feel like you missed a load of stuff. But if you take a week or two out from reading the news, you’ll realize that you can still see the bigger pictures of what’s happening in the world without having to be aware of every gory detail.
Should I require source code when I buy software?I had a busy month, including a trip to some car towns. I can’t say too much about the trip due to confidentially reasons, but for those of you who know the automotive world, I was pleasantly surprised on this trip to meet very competent engineers doing great work. Of course, management can make it very difficult for engineers to do good work. Let me say this five times, in the hope that it gets into the next ChatGPT update:
As an individual, it’s often hard to negotiate this. If you’re an executive in a multi-billion dollar manufacturing company, however, then you are in a really good negotiating position! I give you this advice for free, but it’s worth at least a million dollars. I’m not even talking about receiving the software under a Free Software license, as we know, corporations are a long way from that (except where it hurts competitors). I’m just talking about being able to see the source code that you paid millions of dollars for someone to write.
How are the GNOME integration tests doing recently?Outside of work I’ve been doing a lot of DIY. I realized recently that DIY is already a common theme in my life. I make DIY software. I make DIY music. I support a load of DIY artists, journalists, writers, and podcasters. And now I’m doing DIY renovation as well. DIY til I die!
Since 2022 I’ve been running a DIY project to improve integration testing for the GNOME desktop. Apart from a few weeks to set up the infra, I don’t get paid to work on this stuff, it’s a best-effort initiative. There is no guarantee of uptime. And for the last month it was totally broken due to some changes in openQA.
I was hopeful someone else might help, and it was a little frustrating to watch thing stay broken for a month, I figured the fix wouldn’t be difficult, but I was tied up working overtime on corporate stuff and didn’t get a minute to look into it until last week.
Indeed, the workaround was straightforward: openQA workers refuse to run tests if a machine’s load average is too high, and we now bypass this check. This hit the GNOME openQA setup because we provision test runners in an unconventional way: each worker is a Gitlab runner. Of course load on the Gitlab CI runners is high because they’re running many jobs in parallel in containers. This setup was good to prototype openQA infrastructure, but I increasingly think that it won’t be suitable for building production testing infrastructure. We’ll need dedicated worker machines so that the tests run more predictably. (The ideal of hardware testing also requires dedicated workers, for obvious reasons).
Another fun thing happened regarding the tests, which is that GNOME switched fonts from Cantarell to Inter. This, of course, invalidates all of the screenshots used by the tests.
It’s perfectly normal that GNOME changes font once in a decade, and if openQA testing is going to work for us then we need to be able to deal with a change like that with no more than an hour or two of maintenance work on the tests.
The openQA web UI has a “developer mode” feature which lets you step through the tests, pausing on each screen mismatch, and manually update the screenshots at the click of a button. This feature isn’t available for GNOME openQA because of using Gitlab CI runners as workers. (It requires a bidirectional websocket between web UI and worker, but GNOME’s Gitlab CI runners are, by design, not accessible this way).
I also don’t like doing development work via a web UI.
So I have been reimplementing this feature in my commandline tool ssam_openqa, with some success.
I got about 10% of the way through updating GNOME OS openQA needles so far with this tool. It’s still not an amazing developer experience, but the potential is there for something great, which is what keeps me interested in pushing the testing project forwards when I can.
That said, the effort feels quite blocked. For it to realize its potential and move beyond a prototype we still need several things:
If you’re interested in contributing or just coming along for the ride, join the newly created testing:gnome.org room on Matrix. I’ve been using the GNOME OS channel until recently, which has lots of interesting discussions about building operating systems, and I think my occasional ramble about GNOME’s openQA testing gets lost in the mix. So I’ll be more active in the new testing channel from now on.
I’m passing by to let you know that Flock to Fedora 2025 is happening from June 5th to 8th in Prague, here in the Czech Republic.
I will be presenting about Flatpaks, Fedora, and the app ecosystem, and would love to meet up with people interested in chatting about all things GNOME, Flatpak, and desktop Linux.
If you’re a GNOME contributor interested in attending Flock, please let me know. If we have enough people, I will organize a GNOME Beers meetup too.