When I switched to Neovim, I ran into an unexpected issue. My favorite theme, Solarized, did not display correctly. Strangely enough, other schemes worked just fine.
After some investigation, I realized this only happened within a Tmux session. I always assumed setting $TERM
to screen-256color
was sufficient for modern color rendering. But it turns out, there is one more step to enable proper colors in Tmux.
The Fix
To get Solarized (or any other TrueColor Neovim scheme) working, you need to enable the following option to your Tmux configuration:
set -as terminal-features ',xterm*:RGB'
Why This Works
This arcane invocation tells Tmux that your terminal supports 24-bit TrueColor. Without this setting, Tmux defaults to 256-color mode, which may not render certain themes correctly.
In the terminfo specification, the RGB capability indicates TrueColor support. This line explicitly enables that feature for terminals that match the pattern xterm*.
Verifying TrueColor Support
To ensure your terminal supports TrueColor, run the following command:
echo -e "\e[48;2;255;0;0m \e[48;2;0;255;0m \e[48;2;0;0;255m \e[0m"
If the colors render correctly, your terminal is ready for TrueColor.
Conclusion
With this tweak, the Solarized theme now works perfectly in Neovim within Tmux. If you’re using a modern terminal, make sure to enable TrueColor for the best visual experience. Have fun customizing your setup!
I have recently set up a Jellyfin media server on my TrueNAS Scale box. It is an excellent piece of software and I am very happy with it overall. However, its DLNA plugin does not seem to play nicely with my TV. So I turned to my favorite DLNA server, MiniDLNA.
Unfortunately, MiniDLNA is not available in TrueNAS Scale’s App catalog. That said, TrueNAS Scale uses k3s under the hood to run apps, so you can easily deploy your own containerized applications with ease.
Read more…
In one of my recent merge requests, someone suggested to use non-capturing group to simplify one of my regular expressions. I haven’t heard of those before, so I hit up the search bar.
Turns out, non-capturing groups use this syntax (?:foobar)
. And unlike normal groups, they donot store reference to the matched content. That makes them perfect when you just want to specify a quantifier for a group.
I am definitely going to start using them often now. It’s moments like these, that make me love code reviews. No matter how much you think know, there is always someone dropping knowledge bombs.
For more regular expression tips, check out my lookbehind assertions example and Mozilla’s Regular Expressions Cheet Sheet.
Leveraged by tools such Ansible & Kubernetes, YAML has become a ubiqitous configuration format over the last decade. It owes its high adoption rate can be attributed to its simple design. It is consise yet readable, thanks to its minimal use of syntactical marks. However, under the surface it is surprisingly complex. This post aims to introduce some of its features you might not be aware of.
Read more…
I love chaining comparisons in Python. Not only are expressions like a < b < c
terser, but I also find them more readable than the alternative (a < b and b < c
). So when I started coding in Rust, I wanted something similar.
Limitation in Rust
Unfortunately, Rust does not support chaining operators. The arguments are revolving around cases where the interpretation would not be obvious (a != b != c
), or situations where they they could potentially decrease readability when used incorrectly. Those are fair points. But luckily, there are alternative approaches to achieve the same goals in Rust.
Read more…
Vim needs no introduction. However, what you might not be aware of is that there was a period when its development was stagnant. To address this issue, Neovim was born with the goal of creating an easier-to-maintain codebase, encouraging more community involvement, and adopting new features more rapidly. It made quite a splash several years ago.
After reading about Neovim for years, I finally decided to give it a try. The promises were enticing - faster plugins thanks to Lua, improved responsiveness, better syntax highlighting, and more. Once I started using it, I immediately noticed a snappier performance, more sensible default, and effortless pasting without needing to enable :paste
. Impressed, I began adapting my vimrc
to Neovim and sought Lua alternatives for the plugins I had used.
Read more…
I rely on Ansible for configuring my workstation, so I can easily synchronize it across multiple computer. However, I recently ran into an intriguing issue when my playbook failed with the following error after adding a third-party role:
'ansible_user' is undefined
This puzzled me, because by default all ansible_*
variables should be automatically populated, unless gather_facts has been explicitly disabled. It turns out that this behavior does not apply for ansible_user
, if you run the playbook on localhost with a local connection instead of SSH.
Read more…
Tmux has been an invaluable tool for me over the years. If you haven’t come across it before, Tmux is akin to a window manager for your terminal. It allows you to create multiple tabs, split windows into panes, and, most notably, keeps your remote sessions alive even after you disconnect.
I am also a fan of Powerline, the fancy status line for Vim, shell prompt and other applications. So, I naturally wanted to use it for my Tmux status line. But to my great surprise, it did not work as expected.
Read more…
The other day, I needed to create a multi-SAN TLS certificate in Terraform. The DNS names ended in a number sequence, so you could easily create them in a loop. Now, Terraform has count & for_each loop to create multiple resources. But what if you want to create a variable in a loop? (I am sure some people are screaming Pulumi now.)
Luckily, Terraform has null_resource available, which is exactly what I needed. It creates a virtual resource with a map of strings. So, you can define it in a loop and then use the result to declare a variable.
Here’s example code:
locals {
dns_sans = null_resource.dns_sans[*].triggers.dns_name
}
resource "null_resource" "dns_sans" {
count = var.replicas
triggers = {
dns_name = "service-${count.index}.service-internal"
}
}
This made my life easier. Maybe it will make yours too.
If you are using Ansible, there is high change you are also using AWX (or Ansible Tower) to orchestrate your jobs. And you might want trigger AWX jobs externally in some cases, such as from your CI pipeline. Luckily, AWX has an API that allows you to do just that.
To run AWX jobs remotely, you will need to make 3 API calls. One to start the job itself, another one to monitor its progress and lastly a request to print the output. You can see sample code to do that in bash shell below. In order to keep things simple, it uses authentication token, but you could also use OAuth 2.
POLLING_SLEEP=30
function print_job_output() {
api_request GET "/jobs/${id}/stdout/?format=txt"
}
response=$(
curl -s -i -o - "${AWX_API}/job_templates/${TEMPLATE_ID}/launch/" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json" \
-XPOST
)
http_status=$(head -n 1 <<<"${response}" | awk '{print $2}')
body=$(grep '^{' <<<"${response}")
if [[ ${http_status} != 201 ]]; then
echo "AWX returned error."
exit 1
fi
id=$(jq <<<"${body}" '.id')
echo "Monitoring job ID: ${id}"
while true; do
echo "Sleeping for ${POLLING_SLEEP}s"
sleep $POLLING_SLEEP
response="$(api_request GET "/jobs/${id}/" -i)"
http_status=$(head -n 1 <<<"${response}" | awk '{print $2}')
body=$(grep '^{' <<<"${response}")
if [[ ${http_status} != 200 ]]; then
echo "AWX returned error."
exit 1
fi
status="$(jq -r '.status' <<<"${body}")"
if [[ "${status}" == "failed" ]]; then
echo "Job failed."
print_job_output
exit 1
elif [[ "${status}" == "successful" ]]; then
echo "Job has finished successfully."
print_job_output
exit 0
fi
done