OK, inspired by the really cool Commitlint project which ensures you don’t just type “fix” when you do a git commit, I learned about a standard Conventional Commit format to make the lines useful. Basically, you can load into commitlint a standard set of rules for a pattern that looks like this:
# The general form
type[(scope,...)][!]: subject
# Some examples from minor to huge
fix(ui): Spacing in dialog box
feat(client, server): Time kept when rescheduling
feat!: Microserver API to v2
doc(slam): Documents API changes
refactor(profile): Uses new array
This seems silly, but the Angular convention is pretty useful because it makes searching short commit titles easy. The trick is to have some conventions for this, which they do:
- feat! With an exclamation point, it breaks or is incompatible so a MAJOR version v1 to v2
- feat. This is a new feature so its a MINOR update or going from v1.9 to v1.10
- fix. Adds a new feature, this is a PATCH update in Semantic Versioning so going from v1.1.1 to v1.1.2
- build. Change in the building
- chore. The usual housekeeping
- ci. A continuous integration fix.
- docs. Adding documentation
- perf. Making things go faster
- refactor. Not changing the output but changing the code (hopefully for the better)
- revert. When you make a mistake and have to go backward
- style. Just linting or making cosmetic changes
- test. Make sure you do some of this
The real benefit of this is that it makes it easy to find things just by searching titles
Applying this to Blogs: See the title of this post
I read this, and I immediately applied it to my commit messages thanks to pre-commit, so that when I commit something I run all kinds of linting and static checks. But, I just realized that this is actually really useful for this blog as well, so you will see this little bit of noisy text at the front of posts.
This works way better than remembering to hashtag a post or create categories. I’m just too lazy to do all that organization and this is sort of like that in a title. So thanks! And it’s another great example and I’ll start with just creating random types on the fly, like this roundup, so to parse the above:
Roundup(dev, podcast):...
This means it’s a “Roundup” or just a random bunch of topics thrown together that I do every week or so when the items do not really merit a separate post. I’ll probably have things like Review for reviews of products, History for ancient stuff, and Lessons for things learned over the years. You get the picture.
More Mac Keyboard Shortcuts with Globe
There is a mysterious keyboard key on the very bottom left of all Apple keyboards labeled fn or 🌐. I’ve always just used it to change the keyboard input language, but apparently, it is now being used in addition to other keys like Control, Option and Command. Unfortunately, Cheatsheet, one of my favorite Mac tools does not show what’s available. It is just so handy to hold down the Command ⌘ key and get a list of all active shortcuts, but MacMost has a rough list:
- Globe-Q brings up Apple Notes (and yes, I don’t know why the Q, maybe for Quick), you can also now drag from the lower right upwards and get a new note.
- Globe-F switches you to full-screen mode and back to windowed. This is really convenient on small keyboards, although I use Rectangle which has a way to do this, but this is true full-screen mode.
- Globe-D. Dictate text
- Globe-H. Hide the current window
- Globe-N. Really useful, gets rid of the notification pane on the right
- Globe-W. Go to the top of the current window.
Anyway really useful, its a bit sad I can’t find any way to type the 🌐 key on non-Mac keyboards, this doesn’t seem to be supported by the Keyboard application on the Mac.
The history and usage of .profile and .rc files on Unix derivatives
OK, I have to write this down somewhere as I spent three days working on my host of crazy configuration files. It turns out that understanding how files get run before you type is really complicated. The original idea was to have a .profile which would get you the command path and set up convenient things for you at startup. But after 50 years of Unix, (like the 30 years of Windows), it’s got really complicated. And it’s kind of sad that a Stackoverflow article is the best source for what to do and there is lots of misinformation. But the short story is that:
- The world started with Unix and the first shell written by Stephen Bourne was called amazingly enough the Bourne Shell which is typically called /bin/sh. It was released in 1979 as part of the Unix V7 release. That product had a magic script it would run before the user typed called .profile and this was the start of .profile hell.
- The shell that is used by default in most versions of Linux is Bash which stands for the Bourne Again Shell (get it?) written by Brian Fox as part of the GNU Project in 1989. This introduced the current world of hurt because it can use .bash_profile and .bashrc and also .profile, so what do you use when?
- Well, now you get into the various definition of user input on Unix. The first is that the original Unix didn’t have a graphical interface, it would just print login: and it was completely character based. It is somewhat humbling to realize that the target machine for Unix was the PDP-11/70 which is a 16-bit processor with 64Kb (not Mb, but Kb) of memory addressing with 56Kb usable for actual memory, so both Unix and /bin/sh had to be super tiny. This is called an interactive login shell.
- As an aside, you can still get this mode in modern Linux by hitting CTRL-ALT-F5 gets you the virtual device /dev/tty5 and you will get the same prompt that Dennis Ritchie, Ken Thompson, Doug McIlroy, Joe Ossanna, and you get back the familiar “text-mode interactive login shell” and the .profile which is read. For compatibility, this gets executed by /bin/sh automatically so it should use the /bin/sh scripting language, not the more advanced bash commands. And as an aside, in Ubuntu, if you press CTRL-ALT-F3, F4, F5, and F6, you get to /dev/tty3, 4, 5, and 6 respectively. And you can switch between these terminals with ALT-3, ALT-4, ALT-5, and ALT-6 respectively. Then /dev/tty1 is hard linked to the initial login prompt and /dev/tty2 is the logged-in user shell and you get there with ALT-1 and ALT-2. On some laptops, you actually need CTRL-ALT-FN-F3 because the F keys are overloaded on top of the number keys in 67-key keyboards.
- The other place where .profile is read is before the startup of the graphical desktop manager. In Linux, the GDM is just another program that is executed by the starting process on /dev/tty1. So, the starting system finds .profile and runs it. One implication is that you should not put interactive commands here. If you write with .profile, then this just gets swallowed up by the system console log. And if you ask for input, well, that’s very bad.
- The net rule is that .profile should be used for setting up things that are persistent and need to be set up once. A good example is exporting the PATH variable so commands can be found. Or, setting up things like completion scripts.
- However, there are things that can’t be exported from a parent shell to another one. For instance, aliases, histories, and functions cannot be exported. So to solve this problem with bash, there is a .bashrc file (and yes, I’ve no idea what rc stands for but learned it stands for run commands for the original CTSS program RUNCOM which would run commands before you started)
TL;dr: .bash_profile sources .profile, .profile sources .bashrc
The net of this entire mess is that the most portable thing to do is:
- Only put /bin/sh compatible syntax into
.profile
. This is because /bin/sh may call it and not honor the shebang convention. That is where you put #!/usr/bin/env bash on top and it calls bash. - .bash_profile is only called in what is technically called an interactive login shell. This is basically if you just ssh into a machine or if you are one of the /dev/tty accounts above. It should source .profile. Note that some not really well-behaved systems, looking at you Conda, incorrectly put their configuration code into .bash_profile in MacOS. It should really just write /bin/sh compatible code into .profile as this will not get executed properly if you are sharing configuration files.
- .profile should be just /bin/sh syntax and it should source .bashrc if you are in bash you can tell this by looking for the $BASH variable and it should source .zshrc if it detects $ZSHELL. This is because it gets started mainly just before Ubuntu boots the graphical desktop manager. It should have paths and command completions mainly. Things that export to subshells.
- .bashrc should have just things that are non-exportable, so this is stuff like functions, alias, and shell options. Also, it should detect if it is running interactively and suppress all output otherwise
- The hard case is that some things like gcloud have these include files that say they are for completions, but they really have lots of functions in them, so they need to go into .bashrc instead of .profile. Whereas pure things like terraform just call completion and these can go into
.profile
Why does this work?
Well, you have to go through the cases, so for MacOS they are:
- Initial boot. MacOS doesn’t run any shell stuff so no worries here.
- When you start Terminal (or iTerm2), the shell is considered an interactive login shell, so .bash_profile is called. This calls .profile and then .bashrc so all is good.
- When you ssh into the machine, this also calls .bash_profile, which calls .profile and then .bashrc so again all is good.
- If you run ssh <macos host> “hello world” then only .bashrc is executed, this is why things in .bashrc can’t depend on having it, so you should guard input
[[ $- ~= i ]]
which basically looks at the invocation string for bash with a huge number of single letters in it. If the letter “i” is present then it is interactive
As an aside, this is why many people just stuff everything into .bash_profile
if you are Mac only, it’s really all you need. And most applications miss the interactive guarding in .bashrc so check that.
On Ubuntu what happens is:
- When the system starts, .profile is read and so you get the paths you need in the GDM and when you have an interactive terminal /dev/tty3 to 6. As an aside what happens at the low level is a process getty that starts at the terminal and handles the login and then execs the appropriate shell.
- In Gnome, when you start a new Terminal, you get .bashrc, and because .profile has already been run you are in good shape.
- Then when you ssh in, you get .bash_profile sources so this gets you .profile and then .bashrc and all is good.
- If you run an ssh command like
ssh localhost time
then .bashrc is run and this should work fine, you won’t get all the path information from .profile or .bash_profile so you are a little bit stuck here if you need that. Hopefully you don’t need all that configuration for a quick ssh.