1
0
mirror of https://github.com/jbranchaud/til synced 2026-01-08 01:28:02 +00:00

Compare commits

17 Commits

Author SHA1 Message Date
jbranchaud
087766a792 Add Apply Successive Filters To Lines In Less as a Unix TIL 2026-01-07 19:14:52 -06:00
jbranchaud
4801e730f9 Add Allow Edits From The Start as a Claude Code TIL 2026-01-07 10:51:45 -06:00
jbranchaud
bd021f7eab Add Check Ruby Version For Production App as a Heroku TIL 2026-01-04 20:30:59 -06:00
jbranchaud
8d8cfd56ce Add Determine Absolute Path Of Top-Level Project Directory as a Git TIL 2026-01-03 16:36:37 -06:00
jbranchaud
f4faa06258 Add another useful link to recent TIL 2026-01-03 12:53:33 -06:00
jbranchaud
8ccbd82320 Add Display Line Numbers While Using Less as a Unix TIL 2026-01-02 18:40:28 -07:00
jbranchaud
5ea4165893 Add Look In Ruby Version Dotfile as a Mise TIL 2026-01-01 16:00:46 -07:00
jbranchaud
73476a8d16 Add Install From Nonstandard Brewfile as a Brew TIL 2026-01-01 12:16:51 -07:00
jbranchaud
c5ce81f918 Update README to reflect current work 2026-01-01 12:06:16 -07:00
jbranchaud
32be787998 Update copyright year to 2026 2026-01-01 11:51:51 -07:00
jbranchaud
1d835d3553 Simplify notes:sync to pull directly into local main
Was fetching remote then checking out stale local branch
2026-01-01 11:51:23 -07:00
copilot-swe-agent[bot]
0d4959046d Reorder commands: commit before pull --rebase
Co-authored-by: jbranchaud <694063+jbranchaud@users.noreply.github.com>
2025-12-31 17:28:33 -07:00
copilot-swe-agent[bot]
b1198d2488 Simplify pull command to use configured upstream
Co-authored-by: jbranchaud <694063+jbranchaud@users.noreply.github.com>
2025-12-31 17:28:33 -07:00
copilot-swe-agent[bot]
6c3805e7cd Add git pull --rebase to notes:push task
Co-authored-by: jbranchaud <694063+jbranchaud@users.noreply.github.com>
2025-12-31 17:28:33 -07:00
jbranchaud
d980514bff Add Create Interactive Picker For Set Of Subtasks as a Taskfile TIL 2025-12-31 12:33:50 -07:00
jbranchaud
db26fc97c6 Add Set Up Forwarding Prefix For Nested Session as a tmux TIL 2025-12-31 12:10:52 -07:00
jbranchaud
8094448877 Add Join URI Path Parts as a Ruby TIL 2025-12-30 10:39:03 -07:00
12 changed files with 374 additions and 5 deletions

View File

@@ -6,11 +6,11 @@ A collection of concise write-ups on small things I learn day to day across a
variety of languages and technologies. These are things that don't really
warrant a full blog post. These are things I've picked up by [Learning In
Public™](https://dev.to/jbranchaud/how-i-built-a-learning-machine-45k9) and
pairing with smart people at Hashrocket.
working across different projects via [VisualMode](https://www.visualmode.dev/).
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
_1712 TILs and counting..._
_1722 TILs and counting..._
See some of the other learning resources I work on:
@@ -131,6 +131,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Clean Up Your Brew Installations](brew/clean-up-your-brew-installations.md)
- [Configure Brew Environment Variables](brew/configure-brew-environment-variables.md)
- [Export List Of Everything Installed By Brew](brew/export-list-of-everything-installed-by-brew.md)
- [Install From Nonstandard Brewfile](brew/install-from-nonstandard-brewfile.md)
- [Install Go Packages In Brewfile](brew/install-go-packages-in-brewfile.md)
- [List All Services Managed By Brew](brew/list-all-services-managed-by-brew.md)
@@ -157,6 +158,7 @@ If you've learned something here, support my efforts writing daily TILs by
### Claude Code
- [Allow Edits From The Start](claude-code/allow-edits-from-the-start.md)
- [Monitor Usage Limits From CLI](claude-code/monitor-usage-limits-from-cli.md)
- [Open Current Prompt In Default Editor](claude-code/open-current-prompt-in-default-editor.md)
@@ -348,6 +350,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Count Number Of Commits On A Branch](git/count-number-of-commits-on-a-branch.md)
- [Create A New Branch With Git Switch](git/create-a-new-branch-with-git-switch.md)
- [Delete All Untracked Files](git/delete-all-untracked-files.md)
- [Determine Absolute Path Of Top-Level Project Directory](git/determine-absolute-path-of-top-level-project-directory.md)
- [Determine The Hash Id For A Blob](git/determine-the-hash-id-for-a-blob.md)
- [Diffing With Patience](git/diffing-with-patience.md)
- [Dropping Commits With Git Rebase](git/dropping-commits-with-git-rebase.md)
@@ -500,6 +503,7 @@ If you've learned something here, support my efforts writing daily TILs by
### Heroku
- [Check Ruby Version For Production App](heroku/check-ruby-version-for-production-app.md)
- [Connect To A Database By Color](heroku/connect-to-a-database-by-color.md)
- [Deploy A Review App To A Different Stack](heroku/deploy-a-review-app-to-a-different-stack.md)
- [Diagnose Problems In A Heroku Postgres Database](heroku/diagnose-problems-in-a-heroku-postgres-database.md)
@@ -746,6 +750,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Create Umbrella Task For All Test Tasks](mise/create-umbrella-task-for-all-test-tasks.md)
- [List The Files Being Loaded By Mise](mise/list-the-files-being-loaded-by-mise.md)
- [Look In Ruby Version Dotfile](mise/look-in-ruby-version-dotfile.md)
- [Preserve Color Output For Task Command](mise/preserve-color-output-for-task-command.md)
- [Read Existing Dot Env File Into Env Vars](mise/read-existing-dot-env-file-into-env-vars.md)
- [Run A Command With Specific Tool Version](mise/run-a-command-with-specific-tool-version.md)
@@ -1412,6 +1417,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Install Latest Version Of Ruby With asdf](ruby/install-latest-version-of-ruby-with-asdf.md)
- [Invoking Rake Tasks Multiple Times](ruby/invoking-rake-tasks-multiple-times.md)
- [IRB Has Built-In Benchmarking With Ruby 3](ruby/irb-has-built-in-benchmarking-with-ruby-3.md)
- [Join URI Path Parts](ruby/join-uri-path-parts.md)
- [Jump Out Of A Nested Context With Throw/Catch](ruby/jump-out-of-a-nested-context-with-throw-catch.md)
- [Last Raised Exception In The Call Stack](ruby/last-raised-exception-in-the-call-stack.md)
- [Limit Split](ruby/limit-split.md)
@@ -1533,6 +1539,7 @@ If you've learned something here, support my efforts writing daily TILs by
### Taskfile
- [Create Interactive Picker For Set Of Subtasks](taskfile/create-interactive-picker-for-set-of-subtasks.md)
- [Run A Task If It Meets Criteria](taskfile/run-a-task-if-it-meets-criteria.md)
### tmux
@@ -1568,6 +1575,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Reset An Option Back To Its Default Value](tmux/reset-an-option-back-to-its-default-value.md)
- [Set Environment Variables When Creating Session](tmux/set-environment-variables-when-creating-session.md)
- [Set Session Specific Environment Variables](tmux/set-session-specific-environment-variables.md)
- [Set Up Forwarding Prefix For Nested Session](tmux/set-up-forwarding-prefix-for-nested-session.md)
- [Show The Current Value For An Option](tmux/show-the-current-value-for-an-option.md)
- [Swap Split Panes](tmux/swap-split-panes.md)
- [Switch To A Specific Session And Window](tmux/switch-to-a-specific-session-and-window.md)
@@ -1601,6 +1609,7 @@ If you've learned something here, support my efforts writing daily TILs by
### Unix
- [All The Environment Variables](unix/all-the-environment-variables.md)
- [Apply Successive Filters To Lines In Less](unix/apply-successive-filters-to-lines-in-less.md)
- [Authorize A cURL Request](unix/authorize-a-curl-request.md)
- [Cat A File With Line Numbers](unix/cat-a-file-with-line-numbers.md)
- [Cat Files With Color Using Bat](unix/cat-files-with-color-using-bat.md)
@@ -1634,6 +1643,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Different Ways To Generate A v4 UUID](unix/different-ways-to-generate-a-v4-uuid.md)
- [Display All The Terminal Colors](unix/display-all-the-terminal-colors.md)
- [Display Free Disk Space](unix/display-free-disk-space.md)
- [Display Line Numbers While Using Less](unix/display-line-numbers-while-using-less.md)
- [Display The Contents Of A Directory As A Tree](unix/display-the-contents-of-a-directory-as-a-tree.md)
- [Do A Dry Run Of An rsync](unix/do-a-dry-run-of-an-rsync.md)
- [Do Not Overwrite Existing Files](unix/do-not-overwrite-existing-files.md)
@@ -2053,7 +2063,7 @@ I shamelessly stole this idea from
## License
&copy; 2015-2025 Josh Branchaud
&copy; 2015-2026 Josh Branchaud
This repository is licensed under the MIT license. See `LICENSE` for
details.

View File

@@ -32,8 +32,7 @@ tasks:
notes:sync:
desc: Sync latest changes from the notes submodule
cmds:
- git submodule update --remote {{.NOTES_DIR}}
- cd {{.NOTES_DIR}} && git checkout main
- cd {{.NOTES_DIR}} && git checkout main && git pull
silent: false
notes:open:
@@ -49,6 +48,7 @@ tasks:
cmds:
- git add NOTES.md
- git commit -m "Update notes - $(date '+%Y-%m-%d %H:%M')"
- git pull --rebase
- git push
status:
- git diff --exit-code NOTES.md

View File

@@ -0,0 +1,25 @@
# Install From Nonstandard Brewfile
When you want to install the packages listed in the `Brewfile` for your current
project (or dotfiles), you can run:
```bash
$ brew bundle
```
And `brew` knows to look for and use the `Brewfile` in the current directory.
If, however, you are trying to run `brew bundle` for a `Brewfile` located
somewhere besides the current directory *OR* you want to target a file with a
non-standard name (like
[`Brewfile.personal`](https://github.com/jbranchaud/dotfiles/blob/main/Brewfile.personal)),
then you can use the `--file` flag.
```bash
$ brew bundle --file Brewfile.personal
```
This is what I do [here in my `dotfiles`
repo](https://github.com/jbranchaud/dotfiles/blob/b053f6251cae7ed52f698fc2a2c40ba82c5881b0/installer/mac-setup.sh#L42-L48).
See `man brew` and find the section on `brew bundle` for more details.

View File

@@ -0,0 +1,35 @@
# Allow Edits From The Start
A common pattern for me when using Claude Code is that I start it up in a
project, I prompt it with a question or feature spec, it either comes up with a
plan or just starts working, and as soon as it is ready to make its first edits
to a file, it prompts me something like:
```
Do you want to make this edit to Taskfile.yml?
1. Yes
2. Yes, allow all edits during this session (shift+tab)
3. Type here to tell Claude what to do differently
```
That's a nice default so that I don't get surprised by Claude Code editing a
bunch of files.
However, if I'm in a git-backed project and I'm going into a session intending
to make edits, then I can skip the formalities. I can tell Claude Code when
starting up the session that edits are allowed.
```sh
$ claude --permission-mode acceptEdits
```
When I do this, I'll see the following indicator below the prompt input field:
```
⏵⏵ accept edits on (shift+tab to cycle)
```
If I've already started `claude` but I forgot to specify that permission mode, I
can also toggle right into _accept edits_ by hitting `Shift+Tab`.
[source](https://www.youtube.com/watch?v=_IK18goX4X8)

View File

@@ -0,0 +1,39 @@
# Determine Absolute Path Of Top-Level Project Directory
The `git rev-parse` command is a git plumbing command for parsing different
kinds of things in git into a canonical form that can be used in a deterministic
way by scripts. I would typically think of using it to work with branch names,
tags, and other kinds of refs.
There is a handy, sorta off-label use for it in determining the absolute path of
the root directory for the current git repository. Use the `--show-toplevel`
flag with no other arguments.
```bash
git rev-parse --show-toplevel
/Users/lastword/dev/jbranchaud/til
```
Here, I am in the local copy of [my TIL repo](https://github.com/jbranchaud/til). This command gives me the absolute
path of the top-level directory where that `.git` directory resides.
This is useful for scripts that need to orient themselves to the current
project's top-level directory regardless of what directory they are being
executed from. This is useful for things like a git hook script or monorepos
with scripts located in a specific sub-project directory.
Also worth mentioning is the `--show-superproject-working-tree` flag. In my TIL
repo, I have a private repository included as a submodule. Within that directory
`--show-toplevel` will produce the absolute path to the submodule. If I instead
want the absolute path of the _super project_ (in this case TIL), then I can use
this other flag.
```bash
git rev-parse --show-toplevel
/Users/lastword/dev/jbranchaud/til/notes
git rev-parse --show-superproject-working-tree
/Users/lastword/dev/jbranchaud/til
```
See `man git-rev-parse` for more details.

View File

@@ -0,0 +1,32 @@
# Check Ruby Version For Production App
While deploying a fresh Rails app to Heroku recently, I ran into an issue. The
`it` block argument wasn't working despite being on Ruby 4.0. Or so I thought.
Running the following command reported the Ruby version of that Heroku server
instance:
```bash
heroku run -- ruby --version
Running ruby --version on ⬢ my-app... up, run.3090
ruby 3.3.9 (2025-07-24 revision f5c772fc7c) [x86_64-linux]
```
I was on `3.3.9` which must have been the fallback default at the time.
Though I had set the Ruby version in my `.ruby-version` file, I had neglected to
specify it in the `Gemfile` as well. Once I added it to the `Gemfile` and
redeployed, my Heroku server instance was running the expected version of Ruby.
```bash
heroku run -- ruby --version
Running ruby --version on ⬢ my-app... up, run.5353
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux]
```
Note: because [I have set `HEROKU_ORGANIZATION` and
`HEROKU_APP`](set-default-team-and-app-for-project.md) in my environment
(`.envrc`) for the local copy of the app, I don't need to specify those when
running the `heroku run` command above.
See `heroku run --help` for more details.

View File

@@ -0,0 +1,27 @@
# Look In Ruby Version Dotfile
Newer versions of [`mise`](https://mise.jdx.dev/dev-tools/) specifically only
look for tool versions in `mise.toml` as well as the asdf `.tool-versions` file.
A lot of Ruby projects use the `.ruby-version` file to indicate the Ruby version
of a project. To continue to use the `.ruby-version` file instead of migrating
to `mise.toml`, you need to tell `mise` that you prefer to use the idiomatic
version file.
I added the following line to my
[`~/.config/mise/config.toml`](https://github.com/jbranchaud/dotfiles/commit/8edeb7a9c53500e89e88b4079cbd1859ebebcbda)
file:
```toml
idiomatic_version_file_enable_tools = ["ruby"]
```
Now, whenever `mise` is looking for the specified Ruby version of a project, it
will also look for `.ruby-version`.
Here is a [full list of idomatic version files supported by
`mise`](https://mise.jdx.dev/configuration.html#idiomatic-version-files).
See
[`idiomatic_version_file_enable_tools`](https://mise.jdx.dev/configuration/settings.html#idiomatic_version_file_enable_tools)
as well as the [Ruby-specific documentation](https://mise.jdx.dev/lang/ruby.html#ruby-version-and-gemfile-support)
for more details.

View File

@@ -0,0 +1,43 @@
# Join URI Path Parts
The
[`URI.join`](https://ruby-doc.org/stdlib-2.5.1/libdoc/uri/rdoc/URI.html#method-c-join)
method seems like a handy way to combine a base URL with some subpath. However,
there are some subtle gotchas depending on where forward slashes appear in the
two arguments.
Let's first look at the, in my opinion, desired behavior:
```ruby
> URI.join("https://example.com/api/v1/", "users")
=> #<URI::HTTPS https://example.com/api/v1/users>
```
The base URL has a trailing slash and the path that I want to join to it has no
leading slash. The result is a path where `users` is joined to the end of the
base URL. That's what I'm looking for.
Now, let's see some variations on the above approach that give results that I
wasn't expecting and don't want.
```ruby
> URI.join("https://example.com/api/v1", "/users") # 1
=> #<URI::HTTPS https://example.com/users>
> URI.join("https://example.com/api/v1", "users") # 2
=> #<URI::HTTPS https://example.com/api/users>
> URI.join("https://example.com/api/v1/", "/users") # 3
=> #<URI::HTTPS https://example.com/users>
```
1. No trailing slash on the base URL. Leading slash on the path to join. The
path portion of the base URL is wiped out and `/users` is joined in.
2. No trailing slash on the base URL. No leading slash on the path to join. The
`users` path replaces the last part of the path in the base URL.
3. Both a trailing slash in the base URL and a leading slash in the path to
join. Same behavior as 1.
I have two takeaways from this:
- Use with caution. If I'm going to use `URI.join` for this purpose, I need to
be careful to only use the form in the first code block.
- The `URI.join` method is probably meant to be primarily used to join a domain
(e.g. `http://example.com`) that has no path with some path segment.

View File

@@ -0,0 +1,74 @@
# Create Interactive Picker For Set Of Subtasks
For [my TIL repo](https://github.com/jbranchaud/til), I have a `Taskfile.yml`
that defines a set of `notes:*` tasks for interacting with a `NOTES.md` file
that lives in a private Git submodule.
I wanted to make it easier on myself to not have to remember all the different
`notes` subtasks, so I created a helper task to make it easy to see the options
and run one.
A summary of the Taskfile is shown below including the entirety of the `notes`
task. That task will parse a listing of the available tasks (via `task --list`
and some `sed` commands) and pass those to `fzf` to provide an interactive
picker of the available subtasks.
```yaml
tasks:
notes:
desc: Interactive picker for notes tasks
cmds:
- |
TASK=$(task --list | grep "^\* notes:" | sed 's/^\* notes://' | sed 's/\s\+/ - /' | fzf --prompt="Select notes task: " --height=40% --reverse) || true
if [ -n "$TASK" ]; then
TASK_NAME=$(echo "$TASK" | awk '{print $1}' | sed 's/:$//')
task notes:$TASK_NAME
fi
interactive: true
silent: true
notes:edit:
...
notes:sync:
...
notes:open:
...
notes:push:
...
notes:status:
...
notes:pull:
...
notes:diff:
...
notes:log:
...
```
Now I can run the `notes` task to get a summary and interactive picker that
looks like the following:
```sh
task notes
Select notes task:
9/9
> │ Interactive picker for notes tasks
diff: Show uncommitted changes in notes
edit: All-in-one edit, commit, and push notes
log: Show recent commit history for notes
open: Opens NOTES.md (syncs latest changes first) in default editor
pull: Pull latest changes (alias for sync)
push: Commit and push changes to notes submodule
status: Check status of notes submodule
sync: Sync latest changes from the notes submodule
```
It pulls in the subtask name and description. I can then use `fzf`'s navigation
and filtering to narrow down and select the task I want to run.

View File

@@ -0,0 +1,24 @@
# Set Up Forwarding Prefix For Nested Session
I use
[`ctrl-z`](https://github.com/jbranchaud/dotfiles/blob/main/config/tmux/tmux.conf#L57)
(instead of `ctrl-b`) for my tmux prefix key. I also have [`ctrl-z
ctrl-z`](https://github.com/jbranchaud/dotfiles/blob/main/config/tmux/tmux.conf#L138-L139)
configured to toggle back and forth between the previously visited window.
With that in mind, I needed to set up a specific keybinding to send the prefix
key to an inner (nested) tmux session. That's because sometimes, like with a
tool such as `overmind`, you can end up connected to a tmux session while
already within a tmux session.
So, I have `Ctrl-z a` send the prefix key to the inner tmux session. This is
what I added to my
[`tmux.conf`](https://github.com/jbranchaud/dotfiles/blob/main/config/tmux/tmux.conf#L167-L168):
```
# send prefix to inner tmux session (C-z a)
bind-key a send-prefix
```
Simply doing `Ctrl-z d` will detach me from the outer tmux session. If I want to
detach from an inner tmux session, I can use my new keybinding -- `Ctrl-z a d`.

View File

@@ -0,0 +1,33 @@
# Apply Successive Filters To Lines In Less
Let's say I've opened a large Rails log file with `less`:
```bash
$ less logs/development.log
```
I have an idea of what I'm looking for, but there is way more noise than signal.
I can start to filter out some of the noise. The `&` command starts a filter
prompt. If I start to filter by something like `INSERT INTO`, then a ton of
lines disappear leaving just those matching that pattern.
Scrolling through the current set of lines, I start to have a better idea of
what I'm looking for, but there is still too much noise. I can apply an
additional successive filter on the remaining lines by hitting `&` again and
entering in another pattern -- e.g. `GoodJob`.
Now I only see lines that contain both `INSERT INTO` and `GoodJob` somewhere in
them.
As `less` puts it:
> Multiple & commands may be entered, in which case only lines which match all
> of the patterns will be displayed.
If I want to undo all the filtering, I just need to enter an empty `&` filter
prompt and it will reset things back to displaying all lines.
> If pattern is empty (if you type & immediately followed by ENTER), any
> filtering is turned off, and all lines are displayed.
See `man less` for more details.

View File

@@ -0,0 +1,27 @@
# Display Line Numbers While Using Less
Including line numbers while viewing files with `less` can provide useful
context for understanding where you are within the file. This is especially true
if you've used `&` to filter down to lines that match a pattern.
You can start `less` with line numbers with the `-N` flag (or `--LINE-NUMBERS`
if you really want to spell it out).
```bash
$ less -N log/development.log
```
If you've already started up `less` and wish you had included line numbers,
there is no reason to restart it with the flag. Instead, toggle the line numbers
option on within the `less` process. To do this, type `-N`. It will prompt you
with `Constantly display line numbers (press RETURN)`. Hit enter and line
numbers will appear to the left of each line in the file.
Similarly, to toggle line numbers back off within `less`, hit `-n` (lower-case
`n`), accept the prompt, and back off they go.
Both of these (`-N`/`-n`) are options being set (toggled) via the `-` command.
There are many other options like these that can be configured within a `less`
session in the same way.
See `man less` and find the `-` command and the available `OPTIONS`.