# README

This is a GitBook frontend for my notes repository.

> I've picked up things here and there over the years. Some things I remember, some I forget. Some I write down, some I don't. Some I write down twice because I forgot about the first time. I'm becoming frustrated with the difficulties involved in retrieving solutions to problems I've solved in the past. I'll recognize the need for a specific command or pattern, only to scour three sets of notes and whichever one of the repositories I worked in the last few weeks for the right section of code to review or copy. I want these little post-its of bit sized knowledge to be more easily retrieved.

This web interface is useful for sharing notes with friends and colleagues, but it is not how I consume note contents. Built into the notes repository is a command line interface which allows for quick entry and retrieval. Read more about it on [GitHub](https://github.com/eliasnorrby/notes).


# Literal curly braces (raw)

Ansible makes heavy use of [Jinja](https://jinja.palletsprojects.com) for templating. Notably, variables can be referenced using double curly braces:

```yaml
name: "{{ app_name }}"
```

If we want to type literal double curly braces, e.g. for use in a `docker` or `podman` format string, we can use a `raw` section:

```yaml
- name: Check if container is running
  command: podman ps -a --filter "name={{ app_name }}" --format "{% raw %}{{.State}}{% endraw %}"
  register: container_state
  changed_when: False
```

`{{ app_name }}` will be replaced with a variable value, while `{{.State}}` won't be.


# Case statement

```bash
case $var in
  one) echo "hello" ;;
  two) echo "world" ;;
esac
```


# Change quote style mid-string

When building a string in bash, we're not limited to one type of quotes (i.e. single `'` or double `"` quotes) - you can switch mid string. This is especially useful if we require double quotes for part of the string to interpolate a variable, but using double quotes for the entire string would require many other characters to be escaped.

Say we have this `grep` command for finding `yaml` code blocks within a markdown file:

````bash
grep -E '^[[:space:]]*```ya?ml[[:space:]]*$' example.md
````

If we want to use a variable for the language part, we can use double quotes for just that part of the string, eliminating the need to escape the backticks:

````bash
language='ya?ml'
grep -E '^[[:space:]]*```'"$language"'[[:space:]]*$' test/assets/example.md
````


# Comparing versions

Here's a couple of functions for comparing versions.

```bash
version_less_than() {
  printf '%s\n%s' "$1" "$2" | sort --version-sort --check
}

version_greater_than() {
  ! version_less_than "$1" "$2"
}
```

* `--version-sort` (shorthand `-V`) sorts based on version numbers.
* `--check` (shorthand `-C`) make `sort` check for sorted input instead of sorting.

Example usage:

```bash
MY_VERSION=1.2.3
if version_less_than "$MY_VERSION" 4.3.0; then
  : # do something
fi
```

Source: `man sort`


# Hiding credentials on the command line

Some commands require sensitive information to be passed on the command line. If we don't want these to show up in the command history, we can use `read` with the `-s` flag to store secret values in variables:

```bash
read -p "PASSWORD: " -s mypass
cmd login --creds user:${mypass}
```


# Directory of script

It's often useful to know the path of the script being executed, e.g. for sourcing other files.

```bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
```

This solution works as long as the last component of the path used to find the script is not a symlink.

Source: [stackoverflow](https://stackoverflow.com/a/246128)


# Using find to run a command on multiple files

Find has an `-exec` action for running a command against found files.

```bash
# create backups of all markdown files
find . -type f -name '*.md' -exec cp {} {}.bak \;
```

The command will be executed for each found file. The construct '{}' will be replaced by the current file name. The command must be terminated with a semi-colon that must be escaped to protect it from interpretation by the shell.

## A word of caution

There are both security and performance implications which may be mitigated by using one of the variations `-exec command {} +`, `-execdir command` or `-execdir command {} +`.

Source: `man find`


# Getting the line number of the nth match

If we're aiming to find the line number of the nth match, we might think to use `grep` and parse it's output.

Take this markdown document for example, where we want to find the line number of the second occurrence of `---`:

*input.md*

```
---
title: some frontmatter
--- <-- We want the line number of this line

# Some content
```

```bash
grep '^---$' -m 2 -n input.md | tail -1 | cut -d ":" -f 1
```

This gets the job done, but it invokes a number of subshells, reducing performance. An alternative using `awk` may be less I/O intensive:

```bash
awk '/^---$/ && (++c == 2) { print NR; exit }' input.md
```

[Source](https://stackoverflow.com/questions/57044203/how-to-get-the-line-number-of-nth-match#comment100617725_57044437)


# Getopts

Here's a useful pattern for collecting command line options in a script:

```bash
ERROR="Bad usage, see ${0##*/} -h"

read -r -d "" USAGE <<EOF
Short description

Usage: ${0##*/} [-fh]
  -f ARG      Do something
  -h          Show usage

Example:
  ${0##*/} -f my-arg

EOF

if [ "$1" = "--help" ]; then
  echo "$USAGE" && exit 0
fi

while getopts f:h opt; do
  case $opt in
    f) VAR=$OPTARG                         ;;
    h) echo "$USAGE" && exit 0             ;;
    *) echo "$ERROR" && exit 1             ;;
  esac
done
```

I like to combine this with a check for superfluous positional arguemnts:

```bash
POS_ARG=${*:$OPTIND:1}

OTHER_ARGS=${*:$OPTIND+1}

if [ -n "$OTHER_ARGS" ]; then
  echo "ERROR: Unprocessed positional arguments: $OTHER_ARGS"
  exit 1
fi
```

If you only expect a specific number of positional arguments, this is a good safety measure, because it reduces the risk of missing important options. Because `getopts` will stop processing options as soon as it hits a positional argument, in a case like this:

```bash
$ ./my_script.sh -f f_arg pos_arg -h
```

the `-h` flag will be ignored. Imagine it being a dry-run toggle, for example - better to quit than to miss that.


# Parsing output with long lines using less

Output from commands that generate very long lines (e.g. tables produced by `docker` or `kubectl`) can be hard to parse when lines are wrapped. We can pipe their output to `less` using the `-S` or `--chop-long-lines` flag to have the lines be truncated instead of wrapped. The arrow keys can be used to view parts of lines that don't fit on the screen.

```bash
kubectl get pods -o wide | less -S
```

Source: `man less`


# Print line at number

To print a line at a given position (say, line 10) within a file, we can use this invocation of `sed`:

```bash
sed '10q;d' file
```

`d` means every line will be removed from the output, except for line 10, where the deletion is short-circuited by quitting `q`. The result is that only line 10 is printed, and the rest of the file isn't processed.

An alternative is:

```bash
sed -n '10p' file
```

It is somewhat more intuitive, using `-n` to avoid printing lines, except for line 10 which is explicitly printed. The drawback is that the entire file is processed. Keep this in mind if performance is a priority.

[Source](https://stackoverflow.com/a/6022431)


# Remove final newline

We can use `head` with the `-c` or `--bytes` flag to remove a number of bytes from the end.

> ```
> -c, --bytes=[-]NUM
>      print the first NUM bytes of each file; with the leading '-', print all but the last NUM bytes of each file
> ```

So, if we want to remove a single newline from the input (or any character for that matter):

```bash
$ echo "stuff" | head -c -1
```


# Reading content between markers

Say we want to read some file contents between a set of markers, like the frontmatter of a markdown document, for example:

*blog-post.md*

```
---
title: My first blog post
tags:
  - blog
  - first
  - thing
---

# Welcome to my blog

Lots of interesting content.
```

We can use `sed` to read the content contained within the `---` markers with:

```bash
sed -n '/^---$/,/^---$/p' blog-post.md
```

We're telling `sed` to print all lines in the range defined by matches of the pattern. If we want to exclude the markers themselves, we can delete them by extending the command:

```bash
sed -n '/^---$/,/^---$/ { /^---$/d; p; }' blog-post.md
```

This works well for piping the output to another tool, like `yq`:

```bash
sed -n '/^---$/,/^---$/ { /^---$/d; p; }' blog-post.md | yq eval '.title' -
```

However, we run into problems if the marker used occurs in other places within the document. In markdown, `---` can be used as a horizontal divider:

*blog-post.md*

```
---
title: My first blog post
tags:
  - blog
  - first
  - thing
---

# Welcome to my blog

Lots of interesting content.

---

A completely separate topic.
```

If we want to only read the frontmatter, `awk` is more suitable. To start off with, we can achieve something similar to the `sed` solution using a flag:

```bash
awk '/^---$/{flag =! flag; next}flag' blog-post.md
```

If we want to print only the frontmatter, we need something else:

```bash
awk '/^---$/{if (flag == 0) {flag = 1;next} else {exit}}flag' notes/bash-substring-contains.md
```

After reading the second occurence of the marker, `awk` will skip the rest of the file.

## Side note

If the starting pattern and ending pattern are different, we can use another solution:

```bash
awk '/start/{flag=1;next}/end/{flag=0}flag'
```


# Determine if a script was sourced or executed

Bash only allows `return` in a function or at the top level of a sourced script. We can call it in a subshell and use the exit code to determine if the script is being sourced or not.

```bash
(return 0 2>/dev/null) && sourced=1 || sourced=0
```

If all we want to do is to halt execution at some point if we're sourcing the script, we can use the simpler `return 0 2>/dev/null` as a short-circuit.

```bash
#!/usr/bin/env bash

_a_function() {
  echo "useful stuff"
}

return 0 2>/dev/null

echo "I'll only print when script is executed"
_a_function
```

Example usage:

```bash
source ./script.sh
# _a_function now available for use
_a_function
# useful stuff

. ./script
# I'll only print when script is executed
# useful stuff
```

Source: [stackoverflow](https://stackoverflow.com/a/28776166)


# Bash substring

Does `var` contain 'substring'?

```bash
if [ "${var#*substring}" != "${var}" ]; then
  # do stuff
fi
```

This works by using string substitution: if removing the substring from the variable results in a string different from the original, we know the latter contains the former - otherwise, it would remain unchanged.


# Run a function on interrupt or error

It is sometimes necessary for a script to clean up when exiting prematurely. Perhaps some temporary files are written to, and the execution is halted due to an error or a user interrupt (`ctrl-c`).

We can do so using `trap`:

```bash
cleanup() {
  rm some-temp-file-maybe
  # ... other actions
  exit 1
}

trap cleanup ERR SIGINT
```

We're tying the `cleanup` function to the signals `ERR` and `SIGINT`. This function will be called if the script exits with a non-zero return code or if it is interrupted using `ctrl-c`.

Source: `man trap`


# Reference variable by name

In bash, one can use the syntax `${!varname}` to expand the value of the variable with the name `$varname`.

```bash
PARAM_REV=master
varname=PARAM_REV
echo $varname
# PARAM_REV
echo ${!varname}
# master
```

Source:

* `LESS='-p Parameter Expansion' man bash`


# Bypassing Chrome's NET::ERR\_CERT\_INVALID page

If met with the `NET::ERR_CERT_INVALID` in Chrome, it can be bypassed by typing `thisisunsafe` anywhere on the page.

Source: [stackoverflow](https://stackoverflow.com/a/58957322)


# box-sizing: border-box

The default value for `box-sizing` is `content-box`, which means that sizes apply to an element's content. For example:

```markup
<style>
  section {
    width: 150px;
  }

  p {
    width: 100%;
    padding: 16px;
    border: 2px solid;
    /* Toggle this on and off in the devtools! */
    /* box-sizing: border-box; */
  }
</style>

<section>
  <p>Hello World</p>
</section>
```

With `box-sizing: content-box;`, the rectangle defined by the black border will be 186px wide: the content is set to be 150px, with the padding and border being added on top of that. With `box-sizing: border-box;`, the size calculations are done with regards to the border instead. To set this as the default for all elements, include this snippet in your global styles:

```css
*,
*::before,
*::after {
  box-sizing: border-box;
}
```


# The currentColor keyword

If we don't specify a color for a border, it will use the text's color by default. If we want to make this explicit, we can use the `currentColor` keyword:

```css
.box {
  color: royalblue;
  border: 2px solid currentColor;
}
```

This keyword can be used anywhere a color can be used, not just with borders.


# Wrapper taking up at least 100% height

Height is different to width in that, whereas the default width behaviour is to fill the available space, the default height behaviour is to be as small as possible.

A common source of frustration is to make an element take up *at least* 100% of the available height (say, to position a footer at the bottom of a page, regardless of the content's length).

```css
html, body {
  height: 100%;
}

.wrapper {
  min-height: 100%;
}
```

## Why not `vh`?

The `vh` unit, or viewport height, appears to be designed exactly for this purpose. However, it leads to some unfortunate effects, especially on mobile devices. Because the address bar and footer controls on mobile browsers will slide away as the user scrolls, the viewport height changes. These browsers will set `vh` equal to the *maximum viewport height*, after scrolling, which won't match the viewable area when the controls are visible.

## Footer at the bottom

A max height wrapper is a pre-requisite for positioning a footer at the bottom of the page. To do it, we can use flexbox:

```css
html, body {
  height: 100%;
}

.wrapper {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

.footer {
  margin-top: auto;
}
```

```markup
<div class="wrapper">
  <p>
    Content
  </p>
  <footer>At the bottom</footer>
</div>
```

[Source](https://courses.joshwcomeau.com/css-for-js/01-rendering-logic-1/11-height)


# Using margin: auto; for centering

In addition to specifying explicit values for `margin`, we can use the keyword `auto`.

```css
.centered {
  margin-left: auto;
  margin-right: auto;
}
```

The browser will seek to fill the maximum available space by applying the same margin on the left and right sides of the element. This has the side-effect of centering the element: neat!

There are two caveats:

* It only works for horizontal margin
* It only works on elements with an explicit width

With *grid* and *flexbox*, we have other ways of centering elements, but the `margin: auto` trick is especially useful because it can be selectively applied to a single element on a page.


# Margin-collapse

Margin-collapse is tricky.

## Only vertical margins collapse

Horizontal margins between columns do not collapse.

## Margins only collapse in Flow layout

If a container is using another layout, like `flex`, the margins of that container's children won't collapse.

## Only adjacent elements collapse

Any additional element (be it an invisible and empty `<br />` tag) will prevent margins from collapsing:

```markup
<p>First</p>
<br />
<p>Second</p>
```

## The bigger margin wins

If elements specify different margins, the distance between them will be at least as large as the biggest number.

## Nesting doesn't prevent collapsing

```markup
<style>
  p {
    margin-top: 20px;
    margin-bottom: 20px;
  }
</style>

<div>
  <p>First</p>
</div>
<p>Second</p>
```

> Margin is meant to increase the distance between siblings. It is *not* meant to increase the gap between a child and its parent's bounding box; that's what padding is for.

The margin of the inner paragraph is transferred to the parent element. The effect would have been the same if the margin had been applied to the `<div>` directly instead of the `<p>`.

... unless the `<div>` had some bottom padding. That would block margin-collapse, as would a border. *Margins have to be touching to collapse*.

## Margins can collapse in the same direction

**A 0px margin is still a collapsible margin**

```markup
<style>
  .blue {
    background-color: lightblue;
  }
  p {
    margin-top: 20px;
  }
</style>

<section class="blue">
  <p>Paragraph</p>
</section>
```

We might expect the blue background to extend above the text, but it does not. Again, the purpose of margin is *not* to increase the gap between a child and its parent's bounding box. The margin specified is transferred to the parent, overlapping with its `0 px` margin.

## More than two margins can collapse

With the largest one winning, as seen previously.

## Negative margins

Negative margins can pull elements, making neighbours overlap.


# Which unit to use?

* For **typography**, use `rem`
* For **box model properties** (`margin`/`padding`/`border`), use `px`
* For `width`/`height`, use `%` or `px`
* For `color`, use `hsl`


# Hiding elements

We can use `display: none;` to hide elements, but that can cause problems for people with screen readers. If we want to hide an element visually, but have them available for assistive technologies, we can use a rule like this one:

```css
.visually-hidden {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}
```

[Source](https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/)


# Conditional git config includes

Sometimes we want to use different git configurations for different sub-directories. For client work, for example, we might want to use another email address for our commits. While this could be accomplished by modifying the local git configuration in each repository, there is another way to solve the problem in one stroke: conditional includes.

Using the `include` keyword, we can include other configuration files. With `includeIf`, we can supply a condition that must be met. There are many options, but one of the most useful ones is `gitdir`. If all work related projects are contained in a directory called `work` (be that in `~/work` or `~/dev/work` or somewhere else entirely), we can use this statement to include work-specific configuration for those projects:

```
[includeIf "gitdir:work/"]
  path = config-work
```

The path to the config file to include is relative to the main configuration file, unless an absolute path is given. Within it, we can override settings from the main file, e.g.:

```
[user]
  email = me@work.com
```

Source: `man git-config`


# Viewing the evolution of a line or function

`git log`'s `-L` flag allows us to trace the evolution of a single line, a range of lines, or a function.

## Line range

To view the history of a range of lines, specify `<start>:<end>` following `-L`:

```bash
git log -L5,15:pom.xml
```

To view a single line, use the same line number for both `<start>` and `<end>`:

```bash
git log -L9,9:pom.xml
```

## Function

To view the history of a function, specify `:<funcname>` following `-L`, where `<funcname>` is a regular expression.

```bash
git log -L:getUserById:path/to/my/Class.java
```

From the `git-log` manpage:

> The function names are determined in the same way as git diff works out patch hunk headers

This is, by default:

> ... a line that begins with an alphabet, an underscore or a dollar sign

This won't be approriate in many cases, particularly not in Java where methods are members of classes, meaning they are indented. The default funcname regex does not match any lines with leading whitespace. To change how hunk headers are determined, we need to update `.gitattributes` within the project:

```
*.java  diff=java
```

We can define a custom regular expression that suits our purposes, but there are a few built-in patterns to make this easier, and `java` is one of them, so we're all set.

Source:

* `LESS='-p Defining a custom hunk-header' man gitattributes`
* `LESS='-p -L<start>' man git-log`


# Name of current branch

To get the name of the current branch, we can use `git rev-parse`:

```bash
git rev-parse --abbrev-ref HEAD
```

This will print the name of the current branch *as long as we have one checked out*. If we're in a detached HEAD state, it will print HEAD.

Since version 2.22, `git branch` has a `--show-current` option. It will give us the name of the current branch, but intead will print nothing if we're in a detached HEAD state.

```bash
git branch --show-current
```

Source: `man git-rev-parse`, `man git-branch`


# Get the path to the repository root

It's often handy in scripts to get the (absolute) path to the current git repository.

```bash
git rev-parse --show-toplevel
```

Source: `LESS='-p ^\s*--show-toplevel' man git-rev-parse`


# Replaying a set of changes on a specific branch

```bash
git rebase --onto somewhere base extension
```

> Take the `extension` branch, figure out where it diverged from the `base` branch, and replay these patches in the `extension branch` as if it was based off the `somewhere` branch instead.

With a history like this,

```
c1 - c2 - c3 (master)
      \
        c4 - c5 - c6 (server)
              \
                c7 - c8 (client)
```

running

```bash
git rebase --onto master server client
```

would result in:

```
          (master)
c1 - c2 - c3 - c7' - c8' (client)
      \
        c4 - c5 - c6 (server)
```

**NB**: if the rebase of `extension` on `base` would not result in any commit (`extension` and `base` pointing to the same commit), no commits will be added onto `somewhere`.

Say a fix has been developed on the branch `fix`, and that the changes have been incorporated in the `master` branch in some manner (merge, cherry-pick, etc). Now we want to backport the fix to a previous point in the history, `v1`.

```
(v1)           (master)            (v1)           (master)
c1 - c2 - c3 - c4'                 c1 - c2 - c3 - c5
      \                   OR             \      /
        c4 (fix)                           c4 (fix)
```

In this case, running

```bash
git rebase --onto v1 master fix
```

will only move `fix` to `v1`.

```
(v1, fix)           (master)
c1 - c2 - c3 - c4'
```

The changes in `c4` will not be replayed on top of `c1`, because replaying `fix` on `master` would not include `c4`, since it's already part of `master`.

Source:

* [Pro Git](https://git-scm.com/book/en/v2)
* [experience](https://github.com/tektoncd/pipeline/pull/3958#issuecomment-849188512)
* [learning the hard way](https://github.com/eliasnorrby/tektoncd-helpers/issues/1)


# Hide file from git diff output

`git diff` will show patches for text files, but not for binary files, like images, because the output is not likely to make any sense. Some checked in text files, like `package-lock.json`, can be considered binary too: while technically composed of text characters, it's opaque to a human reader. In addition to that, it's diff output long, and commonly fills the entire screen when running `git diff`. To avoid this, we can mark the file as binary by unsetting the diff attribute in `.gitattributes`:

```
package-lock.json -diff
```

Source: `LESS='-p Marking files as binary' man gitattributes`


# Listing untracked files with git status

When a new directory is added to a git repository, only the directory itself will be listed as an untracked file in the output from `git status`:

```bash
$ git status --porcelain
 M commit_notes.sh
?? notes/rust/
```

Use the `--untracked-files` (`-u`) flag to list individual untracked files within the directory:

```bash
$ git status --porcelain -u
 M commit_notes.sh
?? notes/rust/match-operator.md
?? notes/rust/unwrapping.md
```

Source: `man git-status`


# Setting up GitHub Actions

To set up GitHub Actions in a project, all you need to do is place a valid workflow file within the `.github/workflows` directory. Here's an example (`.github/workflows/example.yaml`), running `yamllint` when a pull request is opened.

```yaml
name: example job

on: pull_request

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: yamllint
        run: yamllint .
```


# Appending items to a list

There are multiple ways of adding items to a list in groovy. One of them is using the `<<` operator:

```groovy
def list = []
list << "item"
```

Another is using `+` or `+=`:

```groovy
list += "a" + ["b", "c", "d"]
```

`<<` is mutating while `+=` is not - it creates a new list.

[Source](http://docs.groovy-lang.org/next/html/documentation/working-with-collections.html#_adding_or_removing_elements)


# Waiting for a pod to be ready

If we need to wait for a pod to come up before proceeding with other actions, we can use `kubectl wait`:

```bash
kubectl wait --for=condition=ready pod --selector "app=my-app" --timeout=60s
```

Source: `man kubectl-wait`


# Passing arguments to make rules

A word of caution: this sort of goes against how `make` is intended to be used. But it can be convenient, and fun.

```
action:
  @echo command $(filter-out $@,$(MAKECMDGOALS))

%:
  @:
```

Example call:

```bash
make action arg1 arg2
```

* `$(MAKECMDGOALS)` is the list of targets passed to make (`action arg1 arg2`)
* `$@` is an automatic variable expanding to the name of the target rul (`action`)
* `filter-out` is a function that removes items from a list: `filter-out $@,$(MAKECMDGOALS) -> arg1 arg2`
* `%` is a wildcard. If no rule is matched (as for `arg1` and `arg2` in the example), this goal will be run
* `:` is a no-op. It means "do nothing", like the bash equivalent
* `@` in front of a recipe makes it silent

The result is that `command` will be invoked as: `command arg1 arg2`. `make` will still recognize `arg1` and `arg2` as targets, but will run the wildcard rule for them, silently doing nothing.

A side effect of the wildcard target is that `make` won't complain if we pass an invalid target - it will just silently do nothing:

```bash
make non-existing targets
```

And if one of the arguments we want to pass to `command` also is the name of another target, recipes in that rule will be executed as well - which probably is not somehting we want.

Source: [stackoverflow](https://stackoverflow.com/a/6273809)


# Running make in a set of subdirectories

```
.
├── Makefile
├── scripts
│   └── Makefile
└── tasks
    └── Makefile
```

All Makefiles have `all`, `test` and `lint` targets.

```
SUBDIRS = scripts tekton
TOPTARGETS = all lint test

$(TOPTARGETS): $(SUBDIRS)

$(SUBDIRS):
  $(MAKE) -C $@ $(MAKECMDGOALS)

.PHONY: $(SUBDIRS) $(TOPTARGETS)
```

`$@` is an automatic variable that contains the target name (one of `SUBDIRS` in this case).

Source: [stackoverflow](https://stackoverflow.com/a/17845120)


# Update a value in a project's .npmrc

`npm config` will prefer to use the user-local `.npmrc` (i.e. `~/.npmrc`). We can repurpose the `--userconfig` flag to write to an arbitrary file (e.g. an `.npmrc` stored in a project) instead:

```bash
npm config set key value --userconfig .npmrc
```

This can be useful in CI scenarios:

```yaml
- name: Configure NPM_TOKEN
  run: npm config set '//npm.pkg.github.com/:_authToken' "${NPM_TOKEN}" --userconfig .npmrc
  env:
    NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Install dependencies
  run: npm ci
- name: Remove NPM_TOKEN config
  run: npm config delete '//npm.pkg.github.com/:_authToken' --userconfig .npmrc
```

Source: [stackoverflow](https://stackoverflow.com/questions/27788398/how-to-npm-config-save-into-project-npmrc-file)


# Target last container

`podman` remembers the last container used in any command. To target it again without referencing a container name or id, use the `-l` flag:

```bash
$ podman inspect hello-world
$ podman exec -l ls /tmp
```


# The match operator

A powerful tool for handling control flow in Rust is the `match` operator. It compares a value against a set of patterns and executes code based on which pattern matches.

```rust
match result {
  Ok(content) => handle(content),
  Err(error) => panic!("Can't deal with: {}", error),
}
```

> "The power of match comes from the expressiveness of the patterns and the fact that the compiler confirms that all possible cases are handled."

[Source](https://doc.rust-lang.org/1.39.0/book/ch06-02-match.html)


# Unwrapping a Result

Many functions in Rust have their return values wrapped in a [`result`](https://doc.rust-lang.org/1.39.0/std/result/index.html). The `Result` type is an enum with two variants, `Ok` and `Err`.

```rust
let result = std::fs::read_to_string("test.txt");
```

It is common to have to handle these cases separately. One can use a `match` statement, like so:

```rust
let content = match result {
  Ok(content) => content,
  Err(error) => panic!("Can't deal with: {}", error),
};
```

but this is in fact so common that there is a shortcut method called `unwrap`:

```rust
let content = result.unwrap();
```

## Returning and the question mark

If we don't want to panic (and exit), we can return an error instead:

```rust
let content = match result {
  Ok(content) => content,
  Err(error) => return Err(error.into()),
};
```

Just like calling `.unwrap()` on a `Result` is a shortcut for `match` with `panic!` in the error arm, `?` is a shortcut for a `match` with a `return` in the error arm:

```rust
let content = result?;
```

### Sources

* [Unwrapping](https://rust-cli.github.io/book/tutorial/errors.html#unwrapping)
* [Question mark](https://rust-cli.github.io/book/tutorial/errors.html#question-mark)


# New window with prompt

For the longest time, I've hade the following binding in my `tmux.conf`:

```
bind c new-window -c "#{pane_current_path}" \; command-prompt -p " Window name:" -I "#{window_name}" "rename-window '%%'"
```

A new window is created, using the same path as my current pane, and I'm presented with a prompt to give the window a meaningful name, with the default being 'zsh'. This is great when I need another window for the project I'm working on, but falls a little short when I want to navigate to a new project.

I frequently use [`z`](https://github.com/rupa/z) with [`fzf`](https://github.com/junegunn/fzf) to navigate to project directories. When opening a window for a separate project, I end up going through these steps:

* Hit `prefix + c`
* Type the name of the project (to name the window)
* Type `z <project name>` to navigate to the project directory

I want to eliminate the need to type the project name twice and came up with this mapping:

```
bind z new-window \; command-prompt -p " Window name:" "rename-window '#{?#{!=:%1,''},%1,zsh}' \; send-keys z ' %1' C-m"
```

Here, we're reading the project name with `command-prompt`, using the input as the window name (unless it's blank, in which case we use a format conditional to substitute 'zsh' as a default name). The input is also used in a send-keys command to execute `z` when the shell loads, navigating to the project directory.


# Running ngrok in the background

By default, `ngrok` launches an interactive session. This is great for local development, but not if we're using it in an automated fashion (e.g. in CI). We can use this method to run `ngrok` in the background, and to acquire the randomly generated public url:

```bash
# run ngrok in the background
ngrok http 80 --log=stdout >/dev/null &
# get the public url
PUBLIC_URL=$(curl -sS http://localhost:4040/api/tunnels | jq -r '.tunnels[0].public_url')
```

Source: [ngrok/issues/57](https://github.com/inconshreveable/ngrok/issues/57)


# Using entr to react to file changes

[`entr`](https://github.com/eradman/entr) is "A utility for running arbitrary commands when files change". It's available on both Linux and MacOS.

```bash
# Run make build when source files change
find . -type f -name '*.c' | entr make build
```

`/_` is a shorthand that is replaced with the absolute path of the first file to trigger an event.

```bash
# Run my_script.sh on save
echo my_script.sh | entr /_
```


# Inferring the type of elements in an array

```typescript
type Unarray<T> = T extends Array<infer U> ? U : T
```


# The command-line window

Sometimes (just like on the shell command-line), you're typing a command and realize you have need for the full editing tools of a vim buffer. Enter: the command-line window. Also known as "the thing that pops up when I try to quit but mistype the keys `:q`".

Open the command-line window using:

* `ctrl-f` while on the command-line
* `q:` while in normal mode

This opens a window with your command history. If invoked from the command line, the last line will contain the command typed thus far.

```
: w
: %s/foo/bar/g
: the command I was currently typing|
[Command Line]
```

Here, you can edit the current or a previous command, and execute the command under the cursor with `CR`.

Source: `:help cmdline-window`


# Populate quickfix list with eslint errors

Set the `makeprg` to `eslint`:

```vim
set makeprg=npx\ eslint\ -f\ unix\ --quiet\ 'src/**/*.{js,ts,jsx,tsx}'
```

Then run `make` to populate the quickfix list.

Source: [til.hashrocket.com](https://til.hashrocket.com/posts/bkeplhlekr-fill-your-quickfix-window-with-lint)


# Visual increment

Vim's increment (`CTRL-A`) can be used on a visual selection for more control. One could, for example, only increment the first digit in a series of numbers.

To create an incrementing sequence, select multiple lines and use `g CTRL-A`. This can also be used with a count. Use `g CTRL-A` to turn:

```
0
0
0
0
0
```

into:

```
1
2
3
4
5
```

or `10g CTRL-A` to turn it into:

```
10
20
30
40
50
```

Source:

* `:h CTRL-A`
* `:h v_g_CTRL-A`


# Opening a list of files in split windows

To open a list of files in split windows, we can use the `-o` and `-O` flags:

```bash
# Opens files in horizontal splits
vim -o file1 file2 file3

# The same, but with vertical splits
vim -O file1 file2 file3
```

Likewise, the `-n` flag can be used to open files in a tab each.

See `:help -o` for more information.


# Insert line above matched line

When used in a substitution command, `&` is replaced by the text that matches the search pattern. If we match a pattern from the beginning of a line, we can insert something above it by replacing the match with the new content, a line break and the matched pattern.

```ts
export class MyClass {
  property: string;
}
```

```vim
:%s/^export class/\/\/ This is a class\r&/
```

```diff
+ // This is a class
  export class MyClass {
    property: string
  }
```

Source: `:h &`


# Spelling

Here are some useful commands and bindings for making use of vim's spell checking:

* `:set spell` enables spelling checking
* `]s` and `[s` navigates between misspelled words
* `z=` brings up a list of suggestions to replace a misspelled word

Calling `z=` with a count (i.e. `1z=`) will select that suggestion without prompting.

Source: `:h spell`


# The tabular plugin

The [tabular](https://github.com/godlygeek/tabular) vim plugin can be used for some cool text manipulation. Say we have some command output we want to make more readable:

```
~/forks/tektoncd/pipeline/docs
❯ grep -rHin 'weight:'
variables.md:4:weight: 15
taskruns.md:4:weight: 2
labels.md:4:weight: 10
metrics.md:4:weight: 14
pipelineruns.md:4:weight: 4
runs.md:4:weight: 2
resources.md:4:weight: 6
workspaces.md:4:weight: 5
install.md:4:weight: 1
migrating-v1alpha1-to-v1beta1.md:4:weight: 18
deprecations.md:4:weight: 19
auth.md:4:weight: 7
tekton-controller-performance-configuration.md:4:weight: 16
conditions.md:4:weight: 11
podtemplates.md:4:weight: 12
events.md:4:weight: 2
container-contract.md:4:weight: 8
tekton-bundle-contracts.md:4:weight: 8
tasks.md:4:weight: 1
migrating-from-knative-build.md:4:weight: 17
pipelines.md:4:weight: 3
logs.md:4:weight: 9
enabling-ha.md:4:weight: 13
```

If we put it in a vim buffer, it's as easy as calling:

```
:Tabularize /:
```

```
variables.md                                   : 4 : weight : 15
taskruns.md                                    : 4 : weight : 2
labels.md                                      : 4 : weight : 10
metrics.md                                     : 4 : weight : 14
pipelineruns.md                                : 4 : weight : 4
runs.md                                        : 4 : weight : 2
resources.md                                   : 4 : weight : 6
workspaces.md                                  : 4 : weight : 5
install.md                                     : 4 : weight : 1
migrating-v1alpha1-to-v1beta1.md               : 4 : weight : 18
deprecations.md                                : 4 : weight : 19
auth.md                                        : 4 : weight : 7
tekton-controller-performance-configuration.md : 4 : weight : 16
conditions.md                                  : 4 : weight : 11
podtemplates.md                                : 4 : weight : 12
events.md                                      : 4 : weight : 2
container-contract.md                          : 4 : weight : 8
tekton-bundle-contracts.md                     : 4 : weight : 8
tasks.md                                       : 4 : weight : 1
migrating-from-knative-build.md                : 4 : weight : 17
pipelines.md                                   : 4 : weight : 3
logs.md                                        : 4 : weight : 9
enabling-ha.md                                 : 4 : weight : 13
```

## Bonus: buffer filtering

After some block selection editing,

```
variables.md                                    weight: 15
taskruns.md                                     weight: 2
labels.md                                       weight: 10
metrics.md                                      weight: 14
pipelineruns.md                                 weight: 4
runs.md                                         weight: 2
resources.md                                    weight: 6
workspaces.md                                   weight: 5
install.md                                      weight: 1
migrating-v1alpha1-to-v1beta1.md                weight: 18
deprecations.md                                 weight: 19
auth.md                                         weight: 7
tekton-controller-performance-configuration.md  weight: 16
conditions.md                                   weight: 11
podtemplates.md                                 weight: 12
events.md                                       weight: 2
container-contract.md                           weight: 8
tekton-bundle-contracts.md                      weight: 8
tasks.md                                        weight: 1
migrating-from-knative-build.md                 weight: 17
pipelines.md                                    weight: 3
logs.md                                         weight: 9
enabling-ha.md                                  weight: 13
```

we can use the external `sort` program to sort the entries based on their respective weights:

```
%!sort -n -t ':' -k2
```

```
install.md                                      weight: 1
tasks.md                                        weight: 1
events.md                                       weight: 2
runs.md                                         weight: 2
taskruns.md                                     weight: 2
pipelines.md                                    weight: 3
pipelineruns.md                                 weight: 4
workspaces.md                                   weight: 5
resources.md                                    weight: 6
auth.md                                         weight: 7
container-contract.md                           weight: 8
tekton-bundle-contracts.md                      weight: 8
logs.md                                         weight: 9
labels.md                                       weight: 10
conditions.md                                   weight: 11
podtemplates.md                                 weight: 12
enabling-ha.md                                  weight: 13
metrics.md                                      weight: 14
variables.md                                    weight: 15
tekton-controller-performance-configuration.md  weight: 16
migrating-from-knative-build.md                 weight: 17
migrating-v1alpha1-to-v1beta1.md                weight: 18
deprecations.md                                 weight: 19
```

Source: [tabular](https://github.com/godlygeek/tabular)


# Populate quickfix list with tsc errors

Customize `errorformat` and set `makeprg` to `tsc`:

```vim
set errorformat=%f\\(%l\\,%c\\):\ error\ %m
set makeprg=npx\ tsc\ --noEmit\ --pretty\ false
```

Then run `make` to populate the quickfix list.


# Yaml multiline strings

First, decide whether you want to fold newlines or preserve them using the *Block Style Indicator*. There are two options:

* The literal style (`|`) will preserve newlines
* The folded style (`>`) will replace newlines with spaces

(To get a newline using the folded style, use a blank line (two newlines).)

```yaml
multiline: >
  this will
  be a single
  line

  but this will be a
  separate one
```

```yaml
multiline: |
  these newlines
  will be preserved
```

Then there are options for newlines at the end:

* `|-` will strip newlines
* `|+` will keep all trailing newlines
* `|` (specifying nothing) will leave a single newline

These options are available both in the folded and the literal style.

[Source](https://yaml-multiline.info/)


# Lazy loading command setup

This is a pattern one can use to post-pone expensive setup calls, shortening shell startup time:

```
if [ $commands[kubectl] ]; then
  kubectl() {
    unfunction "$0"
    source <(kubectl completion zsh)
    $0 "$@"
  }
fi
```

However, the `zsh` completion system seems sofisticated enough to handle this out of the box, as long as completion scripts are supplied in the correct way. Take a look in `/usr/share/zsh/site-functions`.

A similar pattern can be used to load tools like `nvm` on demand:

```
nvm() {

  unfunction "$0"

  [ -z "$NVM_DIR" ] && export NVM_DIR="$HOME/.nvm"
  source /usr/share/nvm/nvm.sh
  source /usr/share/nvm/install-nvm-exec

  $0 "$@"
}
```

[Source](https://frederic-hemberger.de/notes/shell/speed-up-initial-zsh-startup-with-lazy-loading/)


