Say hello to Oliver, the stork dropped him off on the 13 December 2024. It was bit of a hard landing, but as first time parents we didn’t expect it to be easy.
So far he is extremely cute, especially when he sleeps ๐
Jonasfj.dk/Blog A blog by Jonas Finnemann Jensen January 1, 2025 Hello OliverSay hello to Oliver, the stork dropped him off on the 13 December 2024. It was bit of a hard landing, but as first time parents we didn’t expect it to be easy. So far he is extremely cute, especially when he sleeps ๐ June 23, 2024 3D printing a worthy taskmaster trophyFor New Years we had a couple of friends over for a small celebration. Just a causal dinner party, a tiny and amount of fireworks and some champagne. To pass the time waiting for midnight we broke out a board game. My friend served as TaskMaster, and through ingenuity (if I may say so myself) and plausibly a bit of luck: I somehow won! In the TV show there is a trophy, which is a golden sculpture of the TaskMasters head. In the board game there is also a trophy, however it’s just a tiny cardboard cutout of the TV show trophy. The rules of the board game states that the winner should keep the trophy until next time the game is played. However, as they live a few hours away, and they might be playing the board game other people next time, this seemed a bit impractical. So they brought the game, including the paper trophy, home. Naturally, I felt that I was owed a worthy trophy! So I found a picture of my friend, and paid an artist on fiverr.com to create a 3D rendering of his head. With a bit of scaling and a few tweaks I was able to slice the head and print it on my 3D printer. Given all the work I had for through getting the design for this trophy ready, I ended up printing 7 trophies all together. I gave myself one, which I deem to have won fair and square! The rest I gifted to my friend, such that he can give out a decent trophy next time he serves as TaskMaster. After all, who wouldn’t want to have a golden bust of my friend’s head! November 24, 2023 Traveling New ZealandHaving traveled 4270 km through most of New Zealand by campervan — we are getting ready for a long flight home. We landed in Auckland about 4 weeks ago. It took a little while to adjust to gravity being upside down and driving on the left side of the road. But with earth being above us the whole gravity issue wasn’t much of an issue. Driving on the wrong side of the road took some getting used to, but we’ve only passed an oncoming car on the right twice — and one of those oncoming cars were definitely turists driving on the right (well, “wrong”) side of the road! On the North Island we dug for hot springs on hot water beach, river rafted the Tongariro river, snug into the secret hot tub spot (for a hot tub in the forest), and hiked the Tongariro alpine crossing. We did initially have to reschedule the Tongariro alpine crossing due to weather, which gave us time to do a few other hikes and a visit to waitomo caves to spot glow worms. In the end we had fantastic weather on the Tongariro alpine crossing, partly because we got up very early — which wasn’t much of a problem as there is little to do in a campervan after sun down (which kept our jetlag going for the first 2 weeks). On the South Island we finally got a proper dose of sunshine in Abel Tasman. Here we could open the campervan doors towards the ocean and enjoy the golden beaches — pretty much without getting out of bed — this also made getting out of bed rather difficult! When we finally did make it out of bed the sunshine had turned to light rain, and we had the pleasure of hiking around for a day, spending the following day kayaking off the coast passing a few seals and spotting a few lively dolphins from the water taxi! Heading on we passed pancake rocks before hitting Franz Josef glacier — unfortunately our helihike got cancelled due to weather, so we had to settle for a short scenic helicopter flight without landing or exploring the glacier. On our way from Franz Josef we made an unscheduled stop at Puzzle World and walked 2.5 km in a maze we thought would only two “smart” adults like us 10 minutes to complete. Arriving in Queenstown we started to take things a bit “slow”, by jetboating or just relaxing a bit with a movie in the campervan. Used the better part of a day driving to Milford Sound, where we spent two hours top side on a cruise enjoying the fantastic waterfalls. It was two hours spent in partial rain and damp weather, so when we got back to the campervan we snuggled up under blankets in the back and had the best nap ever, before heading back to Queenstown. After a bit of civilization in Queenstown we headed to Mount Cook where we did a few hikes, enjoying the view after going up a 2200 step stairway to Searly Tarns. A view that was greatly enhanced by some homemade tuna salad ๐ Finally, we’re now sitting in Christchurch Airport waiting to head home — having enjoyed a few well deserved “slow” days in Christchurch: just relaxing in the Adrenaline Forest or taking a private surfing lesson (along the only part of the beach covered in clouds). All in all we’ve greatly enjoyed our journey through New Zealand! May 14, 2023 Stacking boxes for Google I/OThis year I had the pleasure of doing a Google I/O talk with John Ryan. Under the title “Building a package in Dart”, the aim was to showcase some of aspects involved when publishing packages on pub.dev. If you’re interested you can watch the video on youtube, or embedded below: The talk was pre-recorded, as I wasn’t near a studio I recorded my part of the talk in my garage. And when it is clean my garage is just white walls, which doesn’t make for the coolest backdrop. So I stacked a bunch of cheap cardboard boxes for use as a background. I obviously, did this so that I could introduce by myself by saying: “Hi, I’m Jonas, I work on package delivery, not this kind of packages”, while pointing on the boxes stacked up behind me. I will confess that this is an extraordinarily lame joke! In any case, I hope you enjoyed watching the joke as much as I enjoyed making it. I haven’t really made this kind of video before. A it was a lot of fun, and hard work. At the end of the recording I had surprisingly little voice left, how people do longer recordings I don’t know. But this experience certainly gave me a lot of respect for youtubers doing hours of scripted video. April 5, 2023 Just MarriedWe got married in Tranbjerg Church. Originally built towards the end of 12th century the Church is fairly small, which made it easy to fill the church to the brim. The ceremony was quite short, but that didn’t prevent it from feeling like an eternity. We had a cozy wedding reception and dinner, mostly close friends and family (which easily grew the guest list past 70 entries). For music we had kortlang.dk play, and I’d strongly recommend them, they had a wonderful vibe. In some sense, doing a classic wedding party makes you feel old, but tradition is not without merit. Instead of doing a boring speech, at the dinner, my siblings opted to do a fun bid. Supposedly, they had reached out the HR department at work, who kindly informed them that plans were in motion to replace all employees with chat bots. Naturally, the HR department had shared a beta version of chat bot meant to replaced me. So my siblings fired up a presentation and proceeded to pretend to test the chat bot. Checking to see if it would give answers similar to what they’d expect from me. Apparently, when prompted about bread slice size and topping size mismatches, their “chat bot” proceeded to propose that one 3D prints a mold to cut bread in such sizes that it fits the topping perfectly. To which my siblings had to agree, that this was in line with what I would do. They had a bunch of other fun prompts for their fake “chat bot”. All in all it was a very fun (and contemporary) story vehicle. I’ll freely admit that when topping doesn’t neatly fit a slice of bread it is a problem. I have yet to bake round ryebread that would fit a salami slice. But I wouldn’t put it past me to do something like that. Ofcourse, it would be much easier to just 3D print a mold matching the salami slice and use to stamp out a matching slice of bread. In any case, my siblings found it necessary to gift me a 3D printer. So I’ll guess I’ll have to 3D print a mold too. Maybe the cutoff can be used for รธllebrรธd ๐ May 14, 2022 Vendoring Dart DependenciesDisclaimer: The opinions stated here are my own, not necessarily those of my employer. When resolving dependencies for a Dart or Flutter application it is only possible to have one version of a given package. This ensures that the dependency graph is fairly lean, since each package can only be in the graph ones. So unlike npm, you won’t end up with 3 different versions of some utility package. On the flip side, you can run into dependency conflicts. If two packages have dependency on There are some workarounds for these scenarios:
However, these workarounds are not always possible. The author of At some point, you may reach the conclusion that it would better to have multiple versions of Vendoring DependenciesTo vendor dependencies with
The # vendor.yaml import_rewrites: # Map of rewrites that should be applied to lib/, bin/ and test/ # Example: # import "package:<package>/..." # is rewritten to: # import "package:myapp/src/third_party/<name>/lib/..." # where 'myapp' is the root project name. <package>: <name> vendored_dependencies: # Specification of which packages to be vendor into: # lib/src/third_party/<name>/ <name>: package: <package> version: <version> import_rewrites: # Rewrites to be applied inside: lib/src/third_party/<name>/ <package>: <name> include: # Glob patterns for which files to include from the package. # For syntax documentation see: https://pub.dev/packages/glob # # If not specified `include` will default to the following list: - pubspec.yaml # always renamed vendored-pubspec.yaml - README.md - LICENSE - CHANGELOG.md - lib/** - analysis_options.yaml In effect, the When you’ve written a Once you have vendored dependencies it is advicable that you commit your Hence, you can also use All in all, I think In general, we should all hope we’ll never have to vendor dependencies. October 31, 2021 Bought a HomeThis summer Dorrit and I bought our first house. The house is 165mยฒ plus a 50mยฒ built-in garage suitable for all sorts of hobbies, workout equipment, parties, etc. I don’t imagine we’ll use the garage for parking, the garage is much to nice for that — or rather I care too little for cars! We got the keys October 1st and moved into the garage, while we got the house ready. There weren’t much to get ready, mostly just hanging up lights, giving the walls a fresh coat of paint, cleaning the insides of cabinets, finding old snacks hidden behind drawers and such. I’m sure there’ll also be old biscuits behind one of the drawers when we move out, it’s not exactly a place you look often ๐ We owe huge credits to friends and family for helping out with moving and painting the interior. Turns out that when 10 people show up, a lot can get done in a days work. So far the life as homeowner is pretty sweet. Living two adults in a one bedroom apartment during covid-19 lockdowns did get a little cramped — especially when both of us were working from home. Having a more space certainly isn’t overrated. September 13, 2020 Reviving a php4/mysql4 LAMP Application with dockerBack in high school (HTX 2005-2008) I operated a custom MediaWiki application for collaborative note taking, tracking home work and occasional sharing of homework 🙈. In case you don’t know MediaWiki is the software behind wikipedia.org. At the time I was using a Danish hosting provider, and couldn’t get LaTeX integration working properly, so I ended up hacking MediaWiki to use mimetex. Similarly, I added a few extensions for calendar integration, raw HTML, etc. These hacks and extensions made upgrading MediaWiki challenging, hence, I never upgraded past MediaWiki 1.5.5 released in 2006. It should not surprise anyone that the wiki was full of spam a few years later. Even though, write access was only granted to trusted users. Some bot must have been scanning the internet for MediaWiki installations with known vulnerabilities, and automatically exploited those vulnerabilities to post spam. Naturally, I ended up taking the wiki offline, being too busy to fix it. Then earlier this year I decided that it was the time to revive my old wiki. But how do you revive an ancient php4 / mysql4 application? It’s probably possible to tweak it such that it works on newer versions of PHP and MySQL. But my database dumps from mysql4 didn’t import on mysql5 without hacks, and some of my extensions didn’t work with php5. So I decided to go looking for a way to install and run php4 and mysql4. Initially, I went looking for a docker image or virtual machine with a php4 and mysql4 LAMP stack pre-configured. But I had no such luck, there was a few php4 docker images, but they were running mysql5. Then I found the End-Of-Life Debian images on hub.docker.com/r/debian/eol/. Using FROM debian/eol:sarge ENV DEBIAN_FRONTEND noninteractive # Install php4, mysql4, apache2, imagemagick, build-essential and phpmyadmin (for good measure) RUN apt-get update -y \ && apt-get install -y \ mysql-server \ mysql-client \ php4 \ apache2 \ libapache2-mod-php4 \ php4-mysql \ imagemagick \ build-essential \ phpmyadmin # Enable mod_rewrite RUN a2enmod rewrite \ && sed -i 's/AllowOverride None/AllowOverride all/' /etc/apache2/sites-available/default # Launch apache2 and mysql when starting the container ENTRYPOINT /bin/bash -c 'apache2 > /dev/null && mysqld > /dev/null & exec bash --login' The # Setup mysql with empty root password mysqladmin -u root password '' # Create $DATABASE_USERNAME with $DATABASE_PASSWORD echo "GRANT ALL PRIVILEGES ON *.* TO '$DATABASE_USERNAME'@'localhost' IDENTIFIED BY '$DATABASE_PASSWORD';" | mysql; # Create $DATABASE_NAME and load contents from SQL dump. echo "CREATE DATABASE $DATABASE_NAME; USE $DATABASE_NAME;" | cat - /src/database-dump.sql | mysql # Copy php files and resources to /var/www cp -r /src /var/www From php4 your application can connect to wget \ --no-clobber \ --recursive \ --user-agent='Mozilla/5.0 (X11; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0' \ --page-requisites \ --adjust-extension \ --span-hosts \ --convert-links \ --restrict-file-names=windows,ascii \ --trust-server-names \ --domains "$DOCKER_CONTAINER_IP" \ -e robots=off \ --tries 1 \ "http://$DOCKER_CONTAINER_IP/" If your old LAMP application contains absolute links, one can temporarily tweak I had actually dreaded this project a bit, fearing that I would have to follow a lengthy install guide to setup a server on a slow virtual machine. But thanks to the amazing Debian EOL images for docker reviving and old php4 / mysql4 LAMP app was almost a breeze — who knew restarting apache could make you feel all nostalgic ๐ December 30, 2019 Running acyclic steps in DartWhen writing servers I often find myself having an acyclic graph of minor setup tasks. These tasks often include steps such as:
Depending on the service many of these steps have to be done before the server starts accepting traffic. As many of the steps depend upon the output of previous steps, this often becomes a large sequential method that does one step at time. It usually happens that some steps are completely unrelated and could easily run concurrently. For example, there is no reason my server application can’t load templates from disk while also initiating a connection to database. However, a method that concurrently executes unrelated steps, while ensuring that steps with inter-dependencies are executed sequentially, can quickly become complex and hard to maintain. To solve this problem I’ve published package:acyclic_steps. I’m sure there are other ways of solving this problem, feel free to show how you do this in the comment section. Disclaimer: package:acyclic_steps is not an officially supported Google product. See the public release process documentation for details. Essentially, I enjoy publishing neat re-usable bits and patterns through the dart-neats project, which I started in order to publish neat things ๐ At the high-level package:acyclic_steps facilities the definition of steps. Where a step may produce a value, and may have dependencies upon other steps. /// A Step that loads configuration. // NOTE: Typing Step<Config> is necessary for inference final Step<Config> configStep = Step .define('load-config') .build(() async { // Load configuration somehow return Config.fromJson(json.decode( await File('config.json').readAsString(), )); }); /// A step that connects to a database. final Step<DBConnection> databaseStep = Step .define('connect-database') .dep(configStep) // Add dependency upon the configStep .build(( // Result from the configStep is injected as cfg. // Typing not needed, as Config will be inferred from configStep. cfg, ) async { return await DBConnection.connect(cfg.database); }); On its own a Instead a Future<void> main() async { // Create Runner object final runner = Runner(); // Run the databaseStep, which will run the configStep first. final dbconn = await runner.run(databaseStep); // NOTE: The type of the result is inferred from databaseStep. assert(dbconn is DBConnection); } Having to define a graph of steps from scratch whenever the initial input changes is not very convenient. To facilitate injection of some initial input for a graph of steps, the When a step is overridden a value is injected into the /// A virtual step for injecting initial configuration. final Step<Config> configStep = Step .define('config') .build(() async { throw UnsupportedError('configStep must be overridden'); }); Future<void> main() async { // Create Runner object final runner = Runner(); runner.override(configStep, Config(/* ... */)); // Run the databaseStep, which will use the result of the overridden configStep final dbconn = await runner.run(databaseStep); } The ability to override a step can also be used for dependency injection. When writing tests it might be desirable to override the step that provides a shared cache (like redis or memcached) with a fake in-memory implementation suitable for local testing. Beyond overriding steps the import 'package:retry/retry.dart'; Future<void> main() async { Future<T> wrapStepWithRetry<T>( Step<T> step, Future<T> Function() runStep, ) async { return await retry(() async => await runStep()); } // Create Runner object final runner = Runner(wrapRunStep: wrapStepWithRetry); // Run the databaseStep, with a runner that will retry step execution. final dbconn = await runner.run(databaseStep); } December 30, 2018 From Mozilla, San Francisco to Google, Aarhus DenmarkStanding at the end of 2018 and having written no blog posts all year, I figure now would be a good time to give a brief personal note on what changed in my life in 2018. Having realized that I didn’t want to make my life in America permanent, I relocated from San Francisco to Aarhus (Denmark) in April. I had a lot of great experiences in the Bay Area, both personally and professionsally. But I also missed friends and family, so moving back wasn’t a hard decision. In some ways it’s surprising it took this long. I like to joke that Trump made me want to move back to Denmark, but truth is that a run in with one of the many homeless people in San Francisco is enough to make me long for the welfare state I grew up in. Leaving Mozilla, however, was not an easy decision — I wish to stress that I left Mozilla after relocating (not because of my decision to relocate). After 5 years at Mozilla I needed new challenges. I spent almost all my time at Mozilla building TaskCluster, the task-graph execution framework powering continuous integration and release processes at Mozilla. Having been part of this project from the very start and allowed to take major design decisions, it was hard to leave this project behind. Yet, I have absolute confidence that my co-workers will be able to take this project into the future. In August I joined the Dart team at Google in Aarhus, to work on Dart related matters. Not being able to talk about everything I do is new to me, but as things become public I might try to write a technical note from time to time. Most of my work will still be open source, and if you stalk me on GitHub you’ll already have seen me involved in the package hosting service dart. So far working at Google have been a joy — the food is pretty good! This was also the year where I finally got an apartment with more than one room! And I bought real non-IKEA furniture like a grown-up would do, hehe. I also bought my first car (Blue, Toyota Yaris H2), and slowly getting good at not driving into bus-only lanes! Between my employment at Mozilla and Google, I spent a week of my summer attending YMCA-YWCA children summer camp again — lots of fun, pirate boat sailing and water fights. Overall I’m greatly enjoying the changes I’ve made, and looking forward to 2019. Older Posts » |