bjorn.now

Things I've Learned (TIL)

Things I want to remember and might help others. Quick discoveries from debugging sessions, neat tricks, useful commands, and so on.

08 October, 2025

The regex | (or) operator splits the pattern left or right unless explicitly grouped. Group it using (pattern) if you need to use the match later, or as a non-capturing group (?:pattern) if you do it for clarity.

I used to think there was some magic rule about how | decided where to split, but it’s simply: characters and subpatterns concatenate into a single pattern first, then | splits the entire thing left and right unless you explicitly group it. That’s because | has the lowest precedence so all other operations happen first.

So while I used to think that prefix_cat|dog would match prefix_cat and prefix_dog, it actually matches prefix_cat and then dog.

And that exact problem in a real-world example, to find if a page is linking to another, both relative or full URL:

[… more]
til Updated 2 min read #how-to, #regex, #background-work

Execute the data node/step and pin it, instead of rerunning the workflow or the step that relies on that data, when modifying a step that needs fresh data.

n8n workflow showing a sequential workflow starting with RSS fetching and going through Telegram, Mastodon, and Bluesky

Example: When modifying Mastodon/Bluesky cross-posting nodes that depend on an RSS Feed Trigger, execute and pin the RSS node. Then iterate on the posting logic that only depends on the RSS data without triggering any posting.

[… more]
til 1 min read #n8n

Hugo’s resources.Get only finds files in assets/, not static/.

I kept my assets/resources in static/ and didn’t get why I always had to write /absolute/urls and use hope as a strategy, when I moved them into assets/ I could look up all my assets.

[… more]
til 1 min read #hugo

01 October, 2025

Use usermod -l newuser -d /home/newuser -m olduser when renaming a user account, because it’ll update all relevant system files and move the home folder in one go.

Then groupmod -n newgroup oldgroup if you need to rename the group.

[… more]
til 1 min read #linux

Use curl -k --resolve bjorn.now:443:127.0.0.1 https://bjorn.now when testing an HTTPS site locally using Caddy, instead of curl -k -H "Host: bjorn.now" https://localhost, because Caddy relies on SNI and refuses the connection if it doesn’t know which host you’re trying to connect to.

[… more]
til 2 min read #how-to, #linux

30 September, 2025

Use :focus-visible instead of :focus, because it will only show when helpful.

:focus-visible only shows focus styling when the browser determines keyboard navigation is being used, unlike :focus which triggers on all interactions including mouse clicks.

[… more]
til 1 min read #css

23 September, 2025

Recycling bins are for packaging, not the products themselves. Wooden ice cream stick? That’s “other,” it’s part of the product, at least in Karlstad. The wrapper? That goes in the appropriate recycling bin based on material.

This seems to be based on EU law, which makes sense, so I’ll assume the gist of this is true in other EU countries too.

[… more]
til 2 min read #background-work

17 September, 2025

to refresh a link preview in Telegram open a chat with Webpage Bot and send the URL, for those cases where you fix a typo or update the image.

20 August, 2025

15 August, 2025

If you start a non-interactive bash shell it will source the content of the file defined in BASH_ENV (and ENV for a POSIX shell).

[… more]
til 1 min read Singapore • #shell-scripting

The YAML anchors to name a reusable section are defined by &name and then to use them to replace the value of something you do *name, if you want to “unsplat”/merge a dictionary/object then use <<: *name and then it’ll insert it at that point.

[… more]
til 1 min read Singapore • #yaml

13 August, 2025

/usr/bin/env executes commands with flags/subcommands, not just bare executables. Which is great if you, for example, have a script/lint that’s a Python script, and it needs dependencies from a virtualenv that isn’t active when you call it.

Just put your shebang as /usr/bin/env uv run python3 and it always runs in the virtualenv, no wrapper script needed. This feels obvious in hindsight, it’s what you expect from these tools 😃

til Singapore • #shell-scripting

02 August, 2025

Drone CI has special handling for ${VAR} and will replace it itself instead of letting the shell do it. So if you have export PATH=${GOPATH}/bin:$PATH and have $GOPATH defined in your global environment key, then it’ll become a blank value. But if you do export PATH=$GOPATH/bin:$PATH it’ll work. You can escape by adding an extra dollar sign, i.e. $${VAR} will be evaluated by the shell.

This is different from normal shell where ${VAR} is the same as $VAR and it exists to make sure you can concatenate strings without issues, i.e.

VAR=m000
echo "'${VAR}est'"  # => 'm000est'
echo "'$VARest'"    # => ''
til Updated Singapore • #drone-ci

Drone CI will not smartly skip subsequent steps if you have a when on the very first step in a depends_on chain, so you have to repeat the when condition for each step because it doesn’t realize that the first dependency is gone.

til Singapore • #drone-ci

Drone CI’s when for deciding in which cases to run steps/pipelines targets the merge target branch for PRs and not the actual PR’s, also it pulls from refs/pull/<num>/head instead of refs/heads/<branch> so you can’t target the branch itself using the pull_request event (use push on the pipeline and then branch for the step instead).

til Singapore • #drone-ci

In shell scripts if you do "$@" it will actually expand “quoted sentences” correctly, and if you just do $@ it will always unwrap them into single words, I thought that if you did "$@" it would combine all arguments into a single argument, and what it does is do what I thought $@ alone did.

I.e., with "$@" the arguments "one two" three will be 2 arguments, the first being "one two", and without it will become three arguments, all separated by space.

til Singapore • #shell-scripting

The <link rel="canonical" href="…"> is a bit like a correlation ID between services and I need also to have it on my canonical page, because if someone sends it with ?utm_source=foo it could be a different page than the one without the query string.

til Singapore • #html

Python doesn’t require typing.Dict and typing.List for those types since Python 3.9, you can just do dict[] and list[].

til Singapore • #python

The scripts/commands inside a shell script inherit access to STDIN when you call them, so if you have a shells script that only has cat and you do ./script.sh < script.sh then it’ll output the content of itself

til Singapore • #shell-scripting