atomicules

Mostly walking the dogs

Switched to Android

I recently switched to Android. I got a Pixel 7a on contract for a steal - I couldn’t get any new iPhone anywhere near the price. I say switched, but I’m not a die-hard iPhone user. I was coming from an original iPhone SE (after switching back to that from a iPhone 8).

Things I miss from iOS:

  • No easy scroll to top by touching the status bar.
  • No unread count badge indicator on apps.
  • No shortcuts app. But I was mostly using this for running some scripts via ssh and changing my watch faces based on sunset and sunrise (more on that later). Maybe I could use Tasker for the ssh stuff?
  • There is no keyboard shortcut popup for adding “.co.uk”, when entering URLs, just “.com”, etc.
  • Audio transcription doesn’t seem to be as accurate. Has not got my daughter’s name right yet when setting reminders.
  • Notification and vibration settings felt somewhat hidden on Android - took me ages to figure out why Tasks reminders weren’t vibrating. It wasn’t clear from the settings page that I could click/touch on an entry for further settings (I thought it was just a title for a toggle), but that could just be learning Android vs iOS.
  • Not found a good SFTP client yet. But also Panic discontinued Transmit so that was unlikely to remain working forever on iOS. I’m using Ghost Commander which works, is free (and open-source), but isn’t pretty. Definitely had some issues getting ssh keys working.

Things I like:

  • Whoop, I get to use K-9 mail which is great.
  • The Google migration from iPhone is really good.
  • Firefox is better on Android.
  • The Pixel hardware design (camera bump) is my favourite of all phones since the original iPhone SE. All other phones look shite in my professional opinion.
  • 1Password integration seems much better, especially with apps, which is bizarre for an app that started on macOS and iOS.
  • Airmessage is really good. I’m able to use our family desktop iMac for that and I have continued messaging family (all on iOS) via that; I thought I’d have to “meet half way” and use WhatsApp, but nope.
  • I read lots of bad things about the under screen Touch ID, but for me it’s brilliant and way more reliable than the iPhone 8 or iPhoneSE. I am not bothering with the Face ID, maybe I would if I had a Pixel 8.
  • Predictive text seems better (it automatically predicts Gaelic when I’m doing Scottish Gaelic in Duolingo, etc).

Things I am indifferent about:

  • Navigating the UI is mostly the same (despite coming from a ye-olde button iPhone, I’m aware of iOS’s gesture navigation because of A’s iPhone).
  • Most apps are the same. The only unique to iOS apps I used were Panic’s Prompt and Transmit (which as mentioned they discontinued).
  • Siri / Reminders. I now just use Google Assistant and Google Tasks for ad-hoc reminders. This is literally the only thing I ever used Siri for.
  • Connectbot is “good enough”. Had some issues creating ssh keys when I wanted to share those with Ghost Commander. Basically ended up creating a key elsewhere and copying to the phone.

I think basically, nowadays, everything is gravy. Android and iOS are both great. Pixel and iPhone are both great. You are basically being a dick if you hate one over the other.

Apple Watch

Having an Apple Watch did make me think twice about switching, but the main two things I’ve used it for are:

  1. Pagerduty alerts and being able to ack in the shower.
  2. WorkOutDoors (still superb).

Thankfully 1 is no longer a requirement: I have saner on-call rotations now. And for 2 I can carry on using it as I always have, which is mostly using it as a sports watch instead of a smart watch.

I still have my iPhone SE plugged in as a “desktop” phone (with headphones taking advantage of that headphone socket) so whilst at home (which is most of the time) my watch still works exactly as it did before, giving me double the iMessage/Airmessage notifications.

And actually, when out and about, connecting my watch to the Pixel’s hotspot works pretty well and means my watch mostly just works as it did before. The only issue is that sometimes it randomly disconnects and then the hotspot turns off.

My thoughts were that my watch is four years old now. It’s stuck on WatchOS 8 and so abandoned by Apple. The battery is still pretty good though and I can see it lasting another year, but I’d be surprised if it lasts much longer than that. And this Pixel I can use until 2028. So when my watch dies I plan on getting a cheap running watch (Garmin Forerunner 55, Coros Pace 2, Polar Pacer, etc), especially as I don’t do that much running anymore anyway. As long as I undercut the Apple Watch SE price I’m winning (for my needs); I still think the Apple Watch SE is superb value especially when coupled with WorkOutDoors (also superb value); The watch is Apple’s best product in my opinion. But I can do Pixel plus Garmin, etc cheaper.

And the Pixel 7a is really a superb bit of kit for the price.

Running Taskwarrior via launchd

As (somewhat*) recently mentioned I switched back to Taskwarrior. On my server I just re-enabled my cronjob to rollover incomplete tasks each day. On my work macOS machine I was doing this manually to start with until I found time to figure out launchd (it had been a few years). I ended up with this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Label</key>
	<string>org.taskwarrior.rollover</string>
	<key>Program</key>
	<string>/Users/simon/bin/task_rollover</string>
	<key>StandardOutPath</key>
	<string>/tmp/task.out</string>
	<key>StandardErrorPath</key>
	<string>/tmp/task.err</string>
	<key>WorkingDirectory</key>
	<string>/Users/simon</string>
	<key>UserName</key>
	<string>simon</string>
	<key>RunAtLoad</key>
	<false />
	<key>StartCalendarInterval</key>
	<dict>
		<key>Hour</key>
		<integer>0</integer>
		<key>Minute</key>
		<integer>1</integer>
	</dict>
</dict>
</plist>

And not that I needed to for this case, but if you want to run at multiple times then you need to use array:

	<key>StartCalendarInterval</key>
	<array>
		<dict>
			<key>Hour</key>
			<integer>09</integer>
			<key>Minute</key>
			<integer>0</integer>
		</dict>
		<dict>
			<key>Hour</key>
			<integer>22</integer>
			<key>Minute</key>
			<integer>0</integer>
		</dict>
	</array>

(I needed this kind of setup for something else, but might as well include it in these notes/post).

Anyway, the script I was calling was just:

#!/bin/sh

/opt/pkg/bin/task $(/opt/pkg/bin/task rc:/Users/simon/.taskrc status:pending due.before:today ids) rc:/Users/simon/.taskrc rc.confirmation:no rc.bulk:100 modify due:today

I couldn’t figure out getting it working without a wrapper script.

Also, to get it to actually load and run properly I couldn’t use the (deprecated?) load/unload commands:

launchctl load ~/Library/LaunchAgents/org.taskwarrior.rollover.plist
launchctl unload ~/Library/LaunchAgents/org.taskwarrior.rollover.plist

I had to do:

launchctl enable user/501/~/Library/LaunchAgents/org.taskwarrior.rollover.plist
launchctl bootstrap gui/501 ~/Library/LaunchAgents/org.taskwarrior.rollover.plist

The 501 you can get from running id. Then you should see the launchagent listed via launchctl list.

To run it once:

launchctl kickstart gui/501/org.taskwarrior.rollover

To disable:

launchctl disable user/501/~/Library/LaunchAgents/org.taskwarrior.rollover.plist
launchctl bootout gui/501 ~/Library/LaunchAgents/org.taskwarrior.rollover.plist

You need to disable and re-enable if you make changes to the plist file. I like how Apple streamlined that from two unload and load commands to needing four commands. Thanks to stumbling across this Reddit post for those.

Also, the StandardOutPath and StandardErrorPath didn’t actually help at all with debugging why it wasn’t running so those can be removed.

Give me the simplicity of NetBSD’s cron any day.

* - I wrote this up ages ago and forgot to post it

Rust Notes

I finally got around to writing a teeny Rust program. As ever for me, I needed a itch that I could scratch before I could write something. I’ve dabbled in Rust before, but mostly as build fixes for NetBSD. Unfortunately/stupidly I wrote this at work so I can’t share it in full here, but I can write up my notes much like I did my Golang ones.

  • As a rough guide for my brain I like to think of Rust as an imperative version of Haskell, specifically Haskell via Stack
  • …because when creating new things you should use cargo to get started. I.e. cargo new my-project instead of just creating a new file with a *.rs extension and trying to use rustc.
  • The error messages when trying to build/compile are very Haskelly - yes, they probably do tell you exactly what is wrong, but in general they aren’t any help (and you will just end up searching) unless you are an expert and then you probably aren’t making those mistakes anyway; rustc --explain is cool though.
  • Need to use the keyword mut if you want a mutable variable.
  • The amount of dependencies scare me a bit. I used reqwest. Just adding that one dependency to my project resulted in about 90 crates being compiled. Maybe there is something lighter-weight than reqwest? Perhaps ureq. Nothing built-in? Also, I had to explicitly enable a blocking/non-async client by setting reqwest = { version = "0.11", features = ["json", "blocking"] } in the [dependencies] section of my Cargo.toml because I didn’t need the complexity of async; Although it seems difficult to actually escape.
  • A lot of Rust examples are out of date. This can be true for any language, but I guess Rust has evolved rapidly. E.g. I started looking at this example reqwest code, but found out error_chain was no more.
  • Box<dyn Error> is super useful, specifically using fn main() -> Result<(), Box<dyn Error>> otherwise you can’t get out of complier errors as you can’t appease all the expected error types.
  • The ? operator and error propagation stuff makes me think of Haskell’s Data.Maybe.
  • Related: the use of unwrap() for quick/lazy getting at of values is like Haskell’s fromJust. In both cases you probably shouldn’t really use it, but it’s fine for quick/little programs.
  • serde_json works really similar to Haskell’s Aeson. I like this a lot.
  • Especially using the #[derive(Serialize, Deserialize, Debug)] stuff:

      use serde::{Deserialize, Serialize};
    
      #[derive(Serialize, Deserialize, Debug)]
      struct NotificationRuleList {
          total: i32,
          notification_rules: Vec<NotificationRule>,
      }
        
      #[derive(Serialize, Deserialize, Debug)]
      struct NotificationRule {
          id: String,
          #[serde(rename = "type")]
          rule_type: String, // Needed to rename this field as `type` is a reserved keyword
          start_delay_in_minutes: i32,
          urgency: String,
          contact_method: ContactMethod,
      }
        
      #[derive(Serialize, Deserialize, Debug)]
      struct ContactMethod {
          id: String,
          #[serde(rename = "type")]
          method_type: String, // Needed to rename this field as `type` is a reserved keyword
      }
    
      [...]
    
      let response = client
      .get(format!(
          "https://api.pagerduty.com/users/{pd_userid}/notification_rules?urgency=low"
      ))
      .header(AUTHORIZATION, format!("Token token={pd_token}"))
      .header(ACCEPT, "application/json")
      .send()?
      .text()?;
    
      let notification_rule_list: NotificationRuleList = serde_json::from_str(&response)?;
      # Maybe there is probably a way to use `.json()` instead of `.text()` and go straight to `serde_json`?
    
      for mut notification_rule in notification_rule_list.notification_rules {
    
      [...]
    

    These examples are for parsing the PagerDuty notification rules response.

  • Using Debug is handy for printing out the structure: println!("deserialized = {:?}", notification_rule_list);.
  • I still don’t understand String vs &str. I just know you can’t pattern match on String so have to do match method_type.as_str().
  • The wiki page is a really good introduction and I should have read that first.
  • The book is also very good (already linked to bits of it here).
  • Cross compiling doesn’t seem to be as easy as Golang, although this could be because I was trying to go from macOS arm64 to NetBSD amd64:
    1. Need(?) rustup
    2. rustup target add x86_64-unknown-netbsd
    3. cargo build --target=x86_64-unknown-netbsd (rustc --print target-list is handy as wasn’t sure what it was called, had tried with amd64-unknown-netbsd)
    4. But then I hit errors with openssl
    5. So then I tried rustls-tls, but then hit:

       error occurred: Failed to find tool. Is `x86_64--netbsd-gcc` installed?`
      
    6. I gave up

That’s it for now, hope I manage to find reasons/excuses to do some more.


[EDIT: 2024-02-15] Added some further notes on cross-compilation.

Helix Editor

Some brief (and somewhat belated) notes about Helix.

At about just the time I managed an extension for Nova I started using Helix daily; You know how it goes, always with the shiny.

Coming from quite a few years of using Vim there are some things I struggle with or miss:

But some things I really like:

  • The idea of selection —> action over Vim’s action —> selection is great.
  • More consistent and logical keyboard commands if sometimes a little longer: Adding gs, gl and ge to Vim’s gg for instance.
  • The built-in space+f file-name search.
  • The built-in space+/ file contents search.
  • The overlay for shell commands.
  • The default purple theme
  • Sane defaults, a simple configuration file, and most of what you need is built-in

I’m going to keep playing with Helix. Financially it’s going to make more sense than paying for Nova every year; Although really I owe a few years of Vim sponsorship; And I supposed I should sponsor Helix if I do start using it all the time; One day.

Related: If I had a computer I would be doing more to get Pkgsrc’s package updated.

Making do, mending, getting by (redux)

Of sorts, although things aren’t that bad.

I somewhat recently dropped my (now old) new old phone and cracked the screen. It was still usable, but I’d been toying with getting the battery replaced via a third party since it was at 45% battery health, but with the screen broken I’d have to replace that first and at that price might I might as well get a second hand SE 2020 instead. If I had that much money. Which I don’t. So I switched back to my original iPhone SE (85% battery health). I was amazed at how much better and brighter the screen is. I can use Apple Pay without it freezing the screen. It’s so light and small I don’t even notice it in my pocket. The A9 processor is still fast enough. This was the peak of phone technology.

In hindsight I would have not bothered with the iPhone 8, I got that because I anticipated the iPhone SE going out of support, but it actually took a year longer than I thought it would until the latest iOS wouldn’t run on the SE, plus Apple pretty much do support the previous iOS for another year anyway (iOS 15 just got an update so really it’s only going to be right about now as iOS 17 is released that it is unsupported). I’ve decided it can last a little bit longer still and then maybe I’ll have to get something on contract - I’m paying £10 a month anyway for data/minutes/sms, if I got a contract at around £20 a month then that seems really quite reasonable.


My plan for one pair of running shoes a year is not going to work out. I’m not even at six months and I’ve had to re-glue the sole on one shoe and stitch a tear in the upper in the other (to be fair, the tear is exactly where I’ve removed the internal reinforcement cage, because it gave me a blister). I had not factored in all the dog walking, and what’s more the shoes getting constantly soaked from dewy grass which I’m sure has weakened the uppers. I hope they can keep going a bit longer (more glue, more sewing) and then I think I’m going to try some cheapish Decathlon trail shoes (£40 for the Evadict Easytrail to £60 for the Evadict TR2) which can do the combo of road, trail and dog walking; I slipped over at the weekend on a muddy trail run in my “road” shoes so it’s time for something with more grip.


And this was going to be a separate post, but actually there isn’t that much to say so it makes sense to be part three here. I’ve sadly given up using Teuxdeux (again) and have switched back to Taskwarrior. It’s going to save me a bit of cash and I wasn’t thrilled with the direction the preview was going in: Function over fashion, whereas I really appreciated the fashion of Teuxdeux, if I want pure functionality… well, there’s Taskwarrior. Since I last used Taskwarrior the bugs that impacted my reports have gone so I don’t need any wrapper scripts, etc and I can have simple reports like so:

#Custom report - Teuxdeux
report.teuxdeux.description=TeuxDeux style
report.teuxdeux.sort=due+/,tags-,description+
report.teuxdeux.filter=(status:completed or status:pending) and due.after:sow-1min and due.before:eow and -BLOCKED
report.teuxdeux.columns=id,uuid.short,tags,due,description.truncated_count
report.teuxdeux.labels=ID,UUID,Context,Due,Description

#Custom report - Teuxdeux
report.roll.description=TeuxDeux rolling
report.roll.sort=due+/,tags-,description+
report.roll.filter=(status:completed or status:pending) and due.after:-2days and due.before:5days and -BLOCKED
report.roll.columns=id,uuid.short,tags,due,description.truncated_count
report.roll.labels=ID,UUID,Context,Due,Description

I am trying to use it mostly like Teuxdeux, but with:

  • annotations for links, etc instead of markdown in Teuxdeux
  • sometimes using tags instead of short prefixes like I would in Teuxdeux

I.e. just keeping it as simple as possible. I also resurrected my crontab entry for rolling over incomplete todos.

I never finished off updating Haskerdeux for the new Teuxdeux API, but using Taskwarrior means I could easily update my Apple Watch shortcuts so I can once again see and complete todos from my watch. I thought about setting up a sync server, but I might as well just have my home todos on my server and my work todos on my work laptop and separate things that way. Simple.


[EDIT: 2023-10-30] Turns out the daylight saving bug is still there in Taskwarrior. Almost nine years since I provided a patch. For now I’m not going to update my patch and rebuild from source because I’m using on my work macOS machine and my NetBSD server so it’s easier just to use the binary. I’ll just modify tasks after daylight savings occur using task $(task +TEMPLATE uuids) modify due:'due + 1 hours', etc; At least I just use recurrence.limit=1 now; I did think about just setting my recurring tasks to midday, but this other old bug screws up my report grouping.

LINK: Rip Bram Moolenaar

A few days late mentioning and linking to this, but what a legacy to leave.

When I first started using Vim it took me so long to learn h,j,k,l over the cursor keys that I thought I would never get it, but now it’s ingrained: I use Vim mode in Nova, Vi mode on the command line, etc. And It’s jarring when I can’t*. And I know that’s technically more Vi than Vim, but I would have never have learnt Vi if it were not for Vim.

Anyway, I emailed Bram once and he replied far more quickly than I ever manage to and was amazingly gracious in just a few lines. I did feel suitably awed and humbled.

* - Related, I’ve been playing about with Helix which I actually really like, but it’s so hard unlearning x for d, etc.

LINK: Groovy Extension For Nova

Has taken me three months to do this (obviously not full-time) and all I’ve really done is cobble together other people’s hard work. It’s just so hard to find the time to do stuff, even when some of this I could almost semi-legitimately count as “work”.

Not much to say about it because it’s not wonderful, but:

  • Not over-joyed at a JavaScript based extension system. But Panic do have great docs and tools and the experience of developing and submitting within Nova is pretty cool.
  • I’m really struggling to wrap my head around tree-sitter. I think I need to work through a really basic example so it begins to make sense to me, but… finding time.
  • And similarly, the highlights.scm. I don’t quite get that yet either, but in writing this post I’ve just discovered the Syntax Inspector which should help me a lot (I guess in general I just need more time to read docs).
  • Groovy itself I don’t mind just because it’s something different from Go and Python.
  • Jenkins I hated (say compared to Tekton) until I learnt about JobDSL and that is pretty cool.

Github and not Fossil because Nova has built-in Git support so it just makes life easier… hmmm… maybe I should build a Fossil extension?!

Still Computerless

It’s my one year anniversary of being Computerless. When we moved house we were downsizing a lot. Based on where we thought things we going in the new spot there was no room for my desktop computer as the family one would have to go on that desk instead; For unknown reasons the built-in office was not to be my office and instead I’d be in the utility room with the dogs; Of course the family computer did eventually end up in the “office” so in hindsight I would have had room for my desktop on my desk; Ach.

Anyway, since I didn’t actually have a functioning desktop at that point, just a monitor and keyboard, and since there wasn’t going to be room for them anyway it meant there was no point bringing them - we were also moving most of the stuff ourselves so anything that reduced the number of journeys back and forth had to go. That meant farewell my stereo I had for over twenty years, farewell to a lot of cameras, even farewell to the middle length headphone cable I never used.

So I completely understand having to go without a computer for a bit (moving also cost us a fortune so we had no money spare at the time), but I don’t really get how it’s now a year later and I’m still without one. I really want to be able to do some personal programming projects again. Work is converging to “boring”: Python and Golang. And yet there are so many other programming languages I’d like to dabble in.

This is going to be rectified at some point this year. After all, I’m not after anything fancy, a refurb Thinkpad X250 or X260, which will work well with NetBSD, is easily do-able for between £100 and £200.

Patience.

Of course, yes, I have shared access to the family computer. I mostly just get to use that for family finances.


[EDIT: 2023-12-26] Of course this didn’t actually happen. I was so adamant it was happening because I knew I’d been a “good boy” all year: I’d done exactly what I say to others and sacrificed other things (Teuxdeux, and shampoo for my scalp psoriasis; TMI, but such are the joys of old age) because getting a computer was my priority - It worked and I’d saved enough to get a ye-olde Thinkpad. But then I had to donate that to my missus and kids because they’d failed to save anything for a trip they had almost four years of notice to save for. Such is life. Will try again next year.

Compose

I feel like I have to write something. Compose was officially End of Life on 1st March, but only at the end of April did it actually stop being my full time job. And I was still making production code changes (believe it or not!) at the start of May. And this weekend finally marked the end of me being on-call for it (something like an 8184 hour shift - not too shabby).

I joined Compose just under a year after the acquisition (sad I have to link to the way back pages) and perhaps this was more significant to me than others because it was when I officially crossed over from mechanical engineering to software engineering (despite unofficially doing bits and pieces for years). I suspect not though. I think there really was something special about Compose.

I don’t really know how much I am allowed to say so will try to keep it brief:

  • Best job I ever had (I suppose technically still is).
  • I absolutely cannot claim to have built the place or come up with the idea. I can, however, say, without any doubt, that I kept the place running until the very end.
  • Some incredibly talented people worked there and I learned a lot from them and feel emotional attachments to some folk that are of a strength that would probably scare them. I miss folk that have moved on a LOT.
  • Compose never put anyone in a box and I loved this. Sure you had your “team” but nothing was “not your job”.
  • There was some kind of plan there behind the acquisition and with a transition that lasted eight years IBM definitely did not “Our Incredible Journey” Compose. Ok, it probably didn’t work out how anyone thought it would, but they didn’t just buy it and shut it down.
  • In fact, the transition was so gradual that from an insiders point of view I would liken it to boiling a frog. I am that frog. But somehow still alive.

I would love to write more about the pros and cons of the technologically behind Compose versus that behind the replacement offering, IBM Cloud Databases (because it would be really interesting), but pretty sure that crosses the line of stuff I’m not allowed to talk about though.

Given the timing of the End of Life I’m going to count myself as very, very lucky that I didn’t end up resource actioned. Instead I get to carry on fixing broken things and playing about with databases.

LINK: Pkgsrc Wip Package For Libvips Updated To 8.14.1

About all I can manage at the moment as far as open-source “contributions” go (packaging up someone else’s hard work).

Libvips switched to the meson build system with 8.14. I can’t say I’m a huge fan, I prefer the simplicity of autotools, but fortunately switching over was much easier than I thought (even doing all the changes via my phone) thanks to a bunch of other hard work by others.

It irks me that my markup is wrong in the commit message and I can’t ever change it.

These are the ten most recent posts, for older posts see the Archive.