1
0
mirror of https://github.com/jbranchaud/til synced 2026-01-03 15:18:01 +00:00

Compare commits

...

18 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
c58bfbb37f Reorder commands: commit before pull --rebase
Co-authored-by: jbranchaud <694063+jbranchaud@users.noreply.github.com>
2026-01-01 00:18:25 +00:00
copilot-swe-agent[bot]
9b5af6a535 Simplify pull command to use configured upstream
Co-authored-by: jbranchaud <694063+jbranchaud@users.noreply.github.com>
2026-01-01 00:17:23 +00:00
copilot-swe-agent[bot]
62d194f492 Add git pull --rebase to notes:push task
Co-authored-by: jbranchaud <694063+jbranchaud@users.noreply.github.com>
2026-01-01 00:16:23 +00:00
copilot-swe-agent[bot]
7a7a0faf94 Initial plan 2026-01-01 00:14:07 +00: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
jbranchaud
883b3e6ee6 Add Run Dev Processes With Overmind Instead Of Foreman as a Rails TIL 2025-12-28 10:21:35 -06:00
jbranchaud
57c4954d6f Add Regenerate Lock File With Newer Bundler as a Ruby TIL 2025-12-26 11:33:07 -06:00
jbranchaud
86a7815a9f Add Specify Default Team And App For Project as a Heroku TIL 2025-12-22 20:46:45 -06:00
jbranchaud
676038e992 Add Create A Module Of Utility Functions as a Ruby TIL 2025-12-17 16:54:43 -06:00
jbranchaud
01fd503a92 Add Run Rails Console With Remote Dokku App as a Rails TIL 2025-12-15 22:27:47 -06:00
jbranchaud
8b718aee4f Add Describe Current Changes And Create New Change as a jj TIL 2025-12-10 11:34:13 -06:00
jbranchaud
88f49de7f3 Add Reference The Full Match In The Replacement as a Sed TIL 2025-12-01 14:07:25 -06:00
jbranchaud
9f9fce7835 Add Make Structs Easier To Use With Keyword Initialization as a Ruby TIL 2025-12-01 06:35:43 -06:00
jbranchaud
65a4d0ef3d Add Rename A Bunch Of Files By Constructing mv Commands as a Unix TIL 2025-11-30 20:10:59 -06:00
jbranchaud
6c8a5eb36d Add new newsletter URL 2025-11-29 15:10:43 -06:00
jbranchaud
fed722d7fe Add Access Your GitHub Profile Photo as a GitHub TIL 2025-11-29 14:40:34 -06:00
15 changed files with 554 additions and 2 deletions

View File

@@ -8,9 +8,9 @@ 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.
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
_1702 TILs and counting..._
_1715 TILs and counting..._
See some of the other learning resources I work on:
@@ -448,6 +448,7 @@ If you've learned something here, support my efforts writing daily TILs by
### GitHub
- [Access Your GitHub Profile Photo](github/access-your-github-profile-photo.md)
- [Open A PR To An Unforked Repo](github/open-a-pr-to-an-unforked-repo.md)
- [Target Another Repo When Creating A PR](github/target-another-repo-when-creating-a-pr.md)
- [Tell gh What The Default Repo Is](github/tell-gh-what-the-default-repo-is.md)
@@ -505,6 +506,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Open Dashboard For Specific Add-On](heroku/open-dashboard-for-specific-add-on.md)
- [Run SQL Against Remote Postgres Database](heroku/run-sql-against-remote-postgres-database.md)
- [Set And Show Heroku Env Variables](heroku/set-and-show-heroku-env-variables.md)
- [Specify Default Team And App For Project](heroku/specify-default-team-and-app-for-project.md)
- [SSH Into Heroku Server Hosting App](heroku/ssh-into-heroku-server-hosting-app.md)
### HTML
@@ -663,6 +665,7 @@ If you've learned something here, support my efforts writing daily TILs by
### jj
- [Colocate jj And git Directories For Project](jj/colocate-jj-and-git-directories-for-project.md)
- [Describe Current Changes And Create New Change](jj/describe-current-changes-and-create-new-change.md)
- [Find System-wide Config File For User](jj/find-system-wide-config-file-for-user.md)
- [Squash Changes Into Parent Commit Interactively](jj/squash-changes-into-parent-commit-interactively.md)
@@ -1164,6 +1167,8 @@ If you've learned something here, support my efforts writing daily TILs by
- [Rounding Numbers With Precision](rails/rounding-numbers-with-precision.md)
- [Run A Rake Task Programmatically](rails/run-a-rake-task-programmatically.md)
- [Run Commands With Specific Rails Version](rails/run-commands-with-specific-rails-version.md)
- [Run Dev Processes With Overmind Instead Of Foreman](rails/run-dev-processes-with-overmind-instead-of-foreman.md)
- [Run Rails Console With Remote Dokku App](rails/run-rails-console-with-remote-dokku-app.md)
- [Run Some Code Whenever Rails Console Starts](rails/run-some-code-whenever-rails-console-starts.md)
- [Scaffold Auth Functionality With Rails 8 Generator](rails/scaffold-auth-functionality-with-rails-8-generator.md)
- [Schedule Sidekiq Jobs Out Into The Future](rails/schedule-sidekiq-jobs-out-into-the-future.md)
@@ -1359,6 +1364,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Create A Hash From An Array Of Arrays](ruby/create-a-hash-from-an-array-of-arrays.md)
- [Create Listing Of All Middleman Pages](ruby/create-listing-of-all-middleman-pages.md)
- [Create Mock Class That Can Be Overridden](ruby/create-mock-class-that-can-be-overridden.md)
- [Create A Module Of Utility Functions](ruby/create-a-module-of-utility-functions.md)
- [Create Named Structs With Struct.new](ruby/create-named-structs-with-struct-new.md)
- [Create Thumbnail Image For A PDF](ruby/create-thumbnail-image-for-a-pdf.md)
- [Decompose Unicode Character With Diacritic Mark](ruby/decompose-unicode-character-with-diacritic-mark.md)
@@ -1406,12 +1412,14 @@ 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)
- [List The Running Ruby Version](ruby/list-the-running-ruby-version.md)
- [Listing Local Variables](ruby/listing-local-variables.md)
- [Make An Executable Ruby Script](ruby/make-an-executable-ruby-script.md)
- [Make Structs Easier To Use With Keyword Initialization](ruby/make-structs-easier-to-use-with-keyword-initialization.md)
- [Map With Index Over An Array](ruby/map-with-index-over-an-array.md)
- [Mock Method Chain Calls With RSpec](ruby/mock-method-chain-calls-with-rspec.md)
- [Mocking Requests With Partial URIs Using Regex](ruby/mocking-requests-with-partial-uris-using-regex.md)
@@ -1439,6 +1447,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Read The First Line From A File](ruby/read-the-first-line-from-a-file.md)
- [Refer To Implicit Block Argument With It](ruby/refer-to-implicit-block-argument-with-it.md)
- [Reference Hash Key With Safe Navigation](ruby/reference-hash-key-with-safe-navigation.md)
- [Regenerate Lock File With Newer Bundler](ruby/regenerate-lock-file-with-newer-bundler.md)
- [Rendering ERB](ruby/rendering-erb.md)
- [Replace The Current Process With An External Command](ruby/replace-the-current-process-with-an-external-command.md)
- [Require Entire Gemfile In Pry Session](ruby/require-entire-gemfile-in-pry-session.md)
@@ -1498,6 +1507,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [OSX sed Does Regex A Bit Different](sed/osx-sed-does-regex-a-bit-different.md)
- [Output Only Lines Involved In A Substitution](sed/output-only-lines-involved-in-a-substitution.md)
- [Reference A Capture In The Regex](sed/reference-a-capture-in-the-regex.md)
- [Reference The Full Match In The Replacement](sed/reference-the-full-match-in-the-replacement.md)
- [Use An Alternative Delimiter In A Substitution](sed/use-an-alternative-delimiter-in-a-substitution.md)
### Shell
@@ -1524,6 +1534,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
@@ -1559,6 +1570,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)
@@ -1715,6 +1727,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Produce A Lowercase V4 UUID](unix/produce-a-lowercase-v4-uuid.md)
- [Provide A Fallback Value For Unset Parameter](unix/provide-a-fallback-value-for-unset-parameter.md)
- [Remove A Directory Called `-p`](unix/remove-a-directory-called-dash-p.md)
- [Rename A Bunch Of Files By Constructing mv Commands](unix/rename-a-bunch-of-files-by-constructing-mv-commands.md)
- [Repeat Yourself](unix/repeat-yourself.md)
- [Replace Pattern Across Many Files In A Project](unix/replace-pattern-across-many-files-in-a-project.md)
- [Run A Command Repeatedly Several Times](unix/run-a-command-repeatedly-several-times.md)

View File

@@ -49,6 +49,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 @@
# Access Your GitHub Profile Photo
Let's say I have my [GitHub profile](https://github.com/jbranchaud) pulled up in
the browser.
```
https://github.com/jbranchaud
```
If I then add `.png` to the end of that in the URL bar:
```
https://github.com/jbranchaud.png
```
I'll be redirected to the URL where the full image file lives. In my case:
```
https://avatars.githubusercontent.com/u/694063?v=4
```
You can pull up yours `https://github.com/<username>.png` to access your profile
image.
[source](https://dev.to/10xlearner/how-to-get-the-profile-picture-of-a-github-account-1d82)

View File

@@ -0,0 +1,34 @@
# Specify Default Team And App For Project
Typically when you run commands with the Heroku CLI you'll need to specify the
name of the app on Heroku you're targeting with the `--app` flag. However, to
first see the names of the apps you may want to run `heroku apps` (or `heroku
list`). That will list the apps for your default team.
If you need to see apps for a different team (i.e. organization), you'll need to
specify that team either with the `--team` flag or by setting that as an
environment variable.
Here I do the latter in an `.envrc` file:
```
# Heroku
export HEROKU_ORGANIZATION=visualmode
```
Once that is set and the environment reloaded, running `heroku apps` will show
the apps specific to that team on Heroku.
Similarly, if you want to set a default app for your project so that you don't
have to always specify the `--app` flag, you can update your `.envrc`
accordingly.
```
# Heroku
export HEROKU_ORGANIZATION=visualmode
export HEROKU_APP=my-app
```
I had a hard time finding official documentation for this which is why I'm
writing this up here. I've manually verified this works with my own team and
app.

View File

@@ -0,0 +1,28 @@
# Describe Current Changes And Create New Change
One of the first patterns I learned with `jj` was a pair of commands to
essentially "commit" the working copy and start a fresh, new change. So if I am
done making some changes, I can add a description to the `(no description)`
working copy and then start a new working copy _change_.
```bash
$ jj describe -m "Add status subcommand to show current status"
$ jj new
```
I learned from [Steve](https://steveklabnik.com/) in the [jj
discord](https://discord.gg/dkmfj3aGQN) that a shorthand for this pattern is to
use the `jj commit` command directly.
> When called without path arguments or `--interactive`, `jj commit` is
> equivalent to `jj describe` followed by `jj new`.
That means, instead of the above pair of commands, I could have done:
```bash
$ jj commit -m "Add status subcommand to show current status"
```
That would have had the same result in my case. However, notice the caveats
mentioned in the quote above and check out `man jj-commit` for more details on
that.

View File

@@ -0,0 +1,46 @@
# Run Dev Processes With Overmind Instead Of Foreman
Most Rails projects that I have worked on have used
[`foreman`](https://github.com/ddollar/foreman) as a development dependency for
running all the processes declared in your Procfile (`Procfile.dev`). As far as
having a single command to run everything (Rails server, asset building,
worker(s), etc.), it does the job.
`foreman` has some serious points of friction though. The one that really stands
out to me is that when I try to debug the development Rails server with
`binding.irb` or `binding.pry`, the other processes tend to interfere.
The alternative to `foreman` that I've been trying out recently is
[`overmind`](https://github.com/DarthSim/overmind). A specific selling point of
`overmind` is that it runs all the development processes in a `tmux` session.
That means you can individually connect to, inspect, and restart each process.
Once you've installed `overmind` (`brew install overmind`), then you can easily
swap it in for `foreman` like so:
```bash
$ overmind start -f Procfile.dev
```
You can connect to any of those processes directly:
```bash
$ overmind connect sidekiq
```
When you want to `binding.irb` the Rails server, you can specifically connect to
the `web` process to do that.
```bash
$ overmind connect web
```
If you need to stop all the process, you can run the `kill` subcommand.
```bash
$ overmind kill
```
Lastly, if you have a `bin/dev` script in your project, it is probably using
`foreman`. If you and your team prefer `overmind`, then update that script
accordingly and you can simply run `bin/dev` going forward.

View File

@@ -0,0 +1,49 @@
# Run Rails Console With Remote Dokku App
Whenever I want to `rails console` into the _staging_ server of an app I'm
working on, I first have to `ssh` into server and then I have to come up with
the [`dokku`](https://dokku.com/) command to run `rails console` against the app
on that server.
```bash
local> ssh app-staging # app-staging is an SSH alias
staging> dokku run my-app rails console
```
I figured out how to reduce the friction of this by collapsing it into a single
command that I can run locally. I can remotely run the `dokku` command with
`ssh` using an interactive session (`-t`).
```bash
local> ssh -t app-staging dokku run my-app rails console
```
That will open up a `rails console` session directly in the current shell
session via a remote SSH connection. The `-t` flag is important because that
makes the session interactive so that I can interact with the REPL.
I've even packaged this up into a bin script (`bin/staging-console`) with a
couple checks to enhance the DX. I won't put the whole thing here, but the gist
of it is:
```bash
#!/usr/bin/env bash
set -e
if [ -z "$DOKKU_STAGING_SSH_ALIAS" ]; then
echo "Error: DOKKU_STAGING_SSH_ALIAS environment variable is not set."
echo ""
# echo more help details here ...
exit 1
fi
# Check if SSH alias exists
# ...
# Check if we can reach the server
# ...
# Run the console
ssh -t "$DOKKU_STAGING_SSH_ALIAS" dokku run my-app rails console "$@"
```

View File

@@ -0,0 +1,59 @@
# Create A Module Of Utility Functions
In my [latest blog post](https://www.visualmode.dev/create-a-module-of-utility-functions-in-ruby),
I went into full detail about how the [`Module#module_function` method](https://ruby-doc.org/3.4.1/Module.html#method-i-module_function) works.
It creates both a module of utility functions that we can access directly on
that module like we would with `self` methods. It can also be included in a
class as a way of sharing copies of those utility functions with the class. A
key point to them being copies is that they can then be overridden by the
including class.
Here is the example I used in the blog post:
```ruby
module MarkdownHelpers
module_function
def heading(text, level = 1)
("#" * level) + " #{text}"
end
def link(text, href)
"[#{text}](#{href})"
end
def image(alt_text, href)
"!#{link(alt_text, href)}"
end
end
```
I won't cover everything that the blog post covers, but what I found really nice
about this pattern is that I can call those utility functions directly with the
module as the receiver:
```bash
$ ruby -r ./markdown_helpers.rb -e 'puts MarkdownHelpers.link("Click here", "https://example.com")'
[Click here](https://example.com)
```
The alternative to this generally looks like:
```ruby
module MarkdownHelpers
def self.heading(text, level = 1)
("#" * level) + " #{text}"
end
def self.link(text, href)
"[#{text}](#{href})"
end
def self.image(alt_text, href)
"!#{link(alt_text, href)}"
end
end
```
That would be fine, but we completely lose out on the ability to include it as a
mix-in with other classes.

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,44 @@
# Make Structs Easier To Use With Keyword Initialization
Typically a [`Struct`](https://ruby-doc.org/3.4.1/Struct.html#method-c-new) in
Ruby is defined and initialized like so:
```ruby
> Subscriber = Struct.new(:email, :first_name, :status, :tags)
=> Subscriber
> s1 = Subscriber.new('bob.burgers@example.com', 'Bob', :active, [:food, :family])
=> #<struct Subscriber email="bob.burgers@example.com", first_name="Bob", status=:active, tags=[:food, :family]>
> s1.email
=> "bob.burgers@example.com"
```
That's a nice way to structure light-weight objects.
A potential challenge with multi-argument `Struct` definitions like this,
especially when they aren't colocated with initialization, is that it can be
hard to remember or distinguish the argument order when initializing an instance
of one.
Ruby 2.5 added the `keyword_init` option to help with this exact issue. When
that option is set to `true` for a `Struct` definition, then we get to
initialize it with keyword arguments rather than positional arguments.
```ruby
> Subscriber = Struct.new(:email, :first_name, :status, :tags, keyword_init: true)
=> Subscriber(keyword_init: true)
* s1 = Subscriber.new(
* first_name: 'Bob',
* email: 'bob.burgers@example.com',
* tags: [:food, :family],
* status: :active
> )
=> #<struct Subscriber email="bob.burgers@example.com", first_name="Bob", status=:active, tags=[:food, :family]>
> s1.email
=> "bob.burgers@example.com"
```
Notice I have to use keyword arguments now and that because of that I can
organize them in whatever order makes sense. Coming back to view this line of
code later, it is easy to see attribute each value corresponds to.
[source](https://www.bigbinary.com/blog/ruby-2-5-allows-creating-structs-with-keyword-arguments)

View File

@@ -0,0 +1,34 @@
# Regenerate Lock File With Newer Bundler
While upgrading to the latest Ruby version (4.0.0), I also wanted to upgrade the
version of `bundler` that my project uses. This shows up at the bottom of the
`Gemfile.lock` file as the `BUNDLED WITH` line. Despite installing the latest
version of `bundler`, I get the following message when I try to install
dependencies.
```bash
$ bundle install
Bundler 4.0.3 is running, but your lockfile was generated with 2.6.2.
Installing Bundler 2.6.2 and restarting using that version.
...
```
Instead, what we need to tell `bundle` to update the locked version of `bundler`
in the `Gemfile.lock`.
```bash
$ bundle update --bundler
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Bundle updated!
```
The `--bundler` flag for `bundle-update` says the following:
> Update the locked version of bundler to the invoked bundler version.
So we could pass a specific `bundler` version to that flag, but in this case I
want to use the version I'm invoking it with which is the latest that I just
installed.

View File

@@ -0,0 +1,27 @@
# Reference The Full Match In The Replacement
The `&` can be used in the replacement part of a `sed` expression as reference
to the string match for this iteration of the expression. The occurrence of `&`
will be replaced with that entire match.
As the `sed` man page puts it:
> An ampersand (“&”) appearing in the replacement is replaced by the string
> matching the RE.
I made use of this recently with [a `sed` expression that was evaluating a list
of filenames that I wanted to construct into a sequence of `mv`
commands](unix/rename-a-bunch-of-files-by-constructing-mv-commands.md). I needed
the filename that I was matching on to appear as the first argument of the `mv`
command I was constructing.
Here is what that looks like:
```bash
$ ls *.pdf |
sed 's/\(..\)\(..\)\(..\) Statement\.pdf/mv "&" "20\3-\1-\2-statement.pdf"/'
```
Notice right after `mv` in literal quotes is the `&`. That will be replaced in
the resulting replacement with the full matching string of the regular
expression in the first part of the sed statement (`s/<RE>/`).

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,51 @@
# Rename A Bunch Of Files By Constructing mv Commands
I downloaded a bunch of bank statements as PDFs. On the upside they all were
consistently named. On the downside they used an unhelpful date format. With a
date format that puts year before month before day, the files easily sort
alphanumerically. However, these filenames used a date format that put month
before day before year.
Here is a subset of the files
```bash
$ ls *.pdf
'012524 Statement.pdf'
'012725 Statement.pdf'
'022624 Statement.pdf'
'022625 Statement.pdf'
'032524 Statement.pdf'
'032525 Statement.pdf'
```
Notice they are named with `MMDDYY Statement.pdf`. I would instead like for them
to be named as `YYYY-MM-DD-statement.pdf`.
I can generate a series of `mv` statements that then get piped to `sh` which
_evaluates_ them. But first, let's do a dry run of a `sed` statement that
rearranges the date parts.
```bash
$ ls *.pdf | sed 's/\(..\)\(..\)\(..\) Statement\.pdf/mv "&" "20\3-\1-\2-statement.pdf"/'
mv "012524 Statement.pdf" "2024-01-25-statement.pdf"
mv "012725 Statement.pdf" "2025-01-27-statement.pdf"
mv "022624 Statement.pdf" "2024-02-26-statement.pdf"
mv "022625 Statement.pdf" "2025-02-26-statement.pdf"
mv "032524 Statement.pdf" "2024-03-25-statement.pdf"
mv "032525 Statement.pdf" "2025-03-25-statement.pdf"
```
The way this works is that all the `pdf` files in the current directly get
listed out. That gets piped to a `sed` statement that matches on capture groups
against the first three pairs of characters (the date parts) in the filenames.
It matches on the rest of the filename (` Statement.pdf`). This is then replaced
by a `mv `, the full match of the original filename (`&`), and then the new
filename made up of the rearranged date parts.
I can then pipe it to `sh` to run those `mv` commands.
```bash
$ ls *.pdf |
sed 's/\(..\)\(..\)\(..\) Statement\.pdf/mv "&" "20\3-\1-\2-statement.pdf"/' |
sh
```