Featured image of post Удобный и красочный ZSH с p10k

Удобный и красочный ZSH с p10k

Мой опыт с Powerlevel10k для ZSH

Введение

Ранее, я уже описывал мой опыт с ZSH здесь. Сейчас всё немного иначе, я полностью перешёл на Powerlevel10k.

Это очень красивый и удобный обвес, который просто ставится и настраивается на ходу.

Настройка p10k

Настроить p10k можно очень просто, прямо на ходу, хоть по 10 раз на дню.

1
p10k configure

Установку самого p10k я зашил прямо в .zshrc, по этому, можно даже не следовать официальным инструкциям на сайте. Ну почти. Есть ещё рекомендованные шрифты, чтобы всё точно было красиво, описано вот здесь. Ниже, я записал и показал как выглядит настройка самого p10k. Вот только, в записи asciinema всё красивости квадратиками, не поддерживаются эти символы в записи, к сожалению. По этому, вот скриншот терминального окна.

(Нажми на изображение для увеличения)

В то время, как в записи, получилось не очень. Ну да ладно, хоть сами меню покажу.

Объяснение моего .zshrc

Всё написанное далее это куски самого .zshrc, которые можно скачать в конце статьи. Для вашего удобства, продублирую ссылку ещё и здесь.

Установка p10k

Выполнено это простым условием и прямым стягиванием из GitHub. Если у нас есть Git и ещё не стоит p10k - можем склонировать.

1
2
3
4
if command -v git >/dev/null && [ ! -d ~/powerlevel10k ]; then
  echo "info: installing p10k"
  git clone --depth 1 --single-branch https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k
fi

Плагины

Плагины ставлю просто, тоже клонированием из GitHub. Добавил комментарии прямо в код для пояснения происходящего.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# if we have git installed
if command -v git >/dev/null; then
  # and folder with plugins is absent
  if [ ! -d /usr/share/zsh/plugins ]; then
    # let's create it with sudo
    sudo mkdir -p /usr/share/zsh/plugins
  fi
  # and jump in
  cd /usr/share/zsh/plugins
  # now, let's read every line between <<EOF and EOF lines below
  while IFS= read -r REPO; do
    # and for every line, which will be available in variable named REPO
    # let's cut only last part of the url (ex: zsh-autosuggestions.git) and save to BASENAME var
    BASENAME="$(basename "$REPO")"
    # now, we will remove .git from the end of BASENAME and check either such folder exists
    if [ ! -d "${BASENAME%.git}" ]; then
      # if not, let's print an info message
      echo "${REPO}: Cloning to $(pwd)/${BASENAME%.git}"
      # and finally clone that plugin
      sudo git clone --depth 1 --single-branch "$REPO"
    fi
  done <<EOF
https://github.com/zsh-users/zsh-history-substring-search.git
https://github.com/zsh-users/zsh-syntax-highlighting.git
https://github.com/zsh-users/zsh-autosuggestions.git
EOF
  # after all plugins cloned, we cd back to the location we were
  cd - >/dev/null
fi
# okay, now we assume that all plugins are cloned and we have to include them
# once again, for every hardcoded path to .zsh file
while IFS= read -r SOURCE; do
  # we skip the plugin if it starts with '#' (commented out)
  if echo "$SOURCE" | grep -qE '^#'; then
    continue 
  elif [ -s "$SOURCE" ]; then
    # if it is enabled, we just include it in current shell session with source command
    source "$(realpath "$SOURCE")"
  fi
done <<EOF
/usr/share/zsh/plugins/zsh-history-substring-search/zsh-history-substring-search.zsh
/usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
/usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
EOF

Добавление путей в PATH

Часто бывает, что дополнительные бинарники мы ставим в нестандартные папки. У меня, к примеру, ~/.local/bin используется для всяких kubectl, helm, k9s и другого. Вот тут мы для каждой папочки, если она конечно есть, выполняем проверку и включаем её в переменную PATH.

1
2
3
4
5
6
7
8
9
while IFS= read -r P; do
  if [ -d "$P" ]; then
    export PATH="${P}:${PATH}"
  fi
done <<EOF
${HOME}/.local/share/gem/ruby/3.0.0/bin
${HOME}/projects/stm32/gcc-arm-none-eabi-10.3-2021.10/bin
${HOME}/.local/bin
EOF

Алиасы

Ну тут всё понятно, много всяких алиасов.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
alias ..="cd .. && ls -lhA --color=auto"
alias d='sudo -E docker'
alias dc="docker-compose" 
alias e="exit"
alias j="sudo journalctl"
alias l="ls -lh --color=always"
alias la="ls -lhA --color=always"
alias p="ping 8.8.8.8"
alias please='sudo'
alias reborn="sudo shutdown -r now"
alias s='sudo'
alias sctl="sudo systemctl"
alias svi="sudo vim"
alias svim="sudo vim"
alias t='terraform'
alias k="kubectl"
alias kd="kubectl describe"
alias kg="kubectl get"
alias ky="kubectl get -o yaml"
alias ke='kubectl exec -it'
alias vi="vim"
alias g="git"
alias b="sudo btrfs"
alias c="code ."
alias ansible-time="time ansible-playbook"

Git config

К слову, у меня ж ещё есть и Git config. Ссылка тут или то же самое в конце страницы. Из интересного тут две команды.

  1. git ps - пушит ветку, а если её ещё нет, ловит сообщение из лога ошибки git-а и пушит с –set-upstream
  2. git dpush (dummy push) - добавляет в коммит все файлы и папки, пишет стандартное сообщение к комиту и пушит
  3. git cc - просто делает коммит со стандартным сообщением
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[alias]
co = checkout
a = add .
ci = commit
cc = commit -m 'Nothing special. Just regular commit.'
ps = ! sh -c 'git push >/tmp/git-ps 2>&1 || grep -- \"git push --set-upstream\" /tmp/git-ps | sh'
pt = push --tags
pts = ! sh -c 'git push && git push --tags'
pl = pull
st = status
br = branch
hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
type = cat-file -t
dump = cat-file -p
dpush = ! sh -c 'git add . && git commit -m \"Nothing special. Just regular commit.\"' && git push

KUBECONFIG

Тут ищется стандартный kubeconfig для k3s или rke2 и прописывается в KUBECONFIG.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# if there is no file at the path that is already being set in KUBECONFIG var
if [ -z "$KUBECONFIG" ]; then
  # for every filepath from the list below
  while IFS= read -r K; do
    # check if file exists and it is not empty
    if [ -s "$K" ]; then
      # change owner and group to current user
      if ! stat -c '%u:%g' "$K" | grep -qF "$(id -u):$(id -g)"; then
        sudo chown "${USER}:${USER}" "$K"
        echo "info: chown $K"
      fi
      echo "info: selecting $K as kubeconfig"
      # and select as default KUBECONFIG
      export KUBECONFIG="$K"
    fi
  done << EOF
/etc/rancher/rke2/rke2.yaml
/etc/rancher/k3s/k3s.yaml
EOF
# last listed path wins
fi

Автоматическая загрузка утилит

Тут, значится, происходит следующее: .zshrc сам следит за тем, чтобы у нас были установлены бинарники утилит, которые мы используем. Если утилиты нет вообще, он её качает, если нет - ничего не делает, но я вручную могу обновить k9s выполнив _update_k9s. Здесь ставятся такие вещи.

  1. k9s - подробнее в моей статье
  2. kubepug - утилита для проверки устаревших API в кластере перед обновлением версии самого Kubernetes
  3. kubecolor - обёртка над kubectl для разноцветного вывода
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
_update_kubepug() {
  mkdir -p ~/.local/bin
  LOCATION="$(curl --connect-timeout 1 -sS https://github.com/rikatz/kubepug/releases/latest -D- | awk '/^location:/{print $2}')"
  LATEST_VERSION="$(echo "$LOCATION" | awk -F/ '{print $NF}' | grep -oE '[0-9a-zA-Z_.-]+')"
  if ! k9s version --short | grep -qF "$LATEST_VERSION"; then
    printf "Updating kubepug to %s\n" "$LATEST_VERSION"
    printf 'https://github.com/rikatz/kubepug/releases/download/%s/kubepug_linux_%s.tar.gz' \
      "$LATEST_VERSION" "$(uname -p | sed 's/unknown/amd64/g' | tr -d '[[:space:]]')" | \
    xargs curl -sSL | \
    tar -xpzf - kubepug
    install kubepug ~/.local/bin/kubepug -m 0755
    rm -f kubepug
  fi
}
if ! command -v kubepug &>/dev/null; then
  _update_kubepug
fi

_update_k9s() {
  mkdir -p ~/.local/bin
  LOCATION="$(curl --connect-timeout 1 -sS https://github.com/derailed/k9s/releases/latest -D- | awk '/^location:/{print $2}')"
  LATEST_VERSION="$(echo "$LOCATION" | awk -F/ '{print $NF}' | grep -oE '[0-9a-zA-Z_.-]+')"
  if ! k9s version --short | grep -qF "$LATEST_VERSION"; then
    printf "Updating k9s to %s\n" "$LATEST_VERSION"
    printf 'https://github.com/derailed/k9s/releases/download/%s/k9s_Linux_%s.tar.gz' \
      "$LATEST_VERSION" "$(uname -p | sed 's/unknown/x86_64/g' | tr -d '[[:space:]]')" | \
    xargs curl -sSL | \
    tar -xpzf - k9s
    install k9s ~/.local/bin/k9s -m 0755
    rm -f k9s
  fi
}
if ! command -v k9s &>/dev/null; then
  _update_k9s
fi

_update_kubecolor() {
  mkdir -p ~/.local/bin
  LOCATION="$(curl --connect-timeout 1 -sS https://github.com/hidetatz/kubecolor/releases/latest -D- | awk '/^location:/{print $2}')"
  LATEST_VERSION="$(echo "$LOCATION" | awk -F/ '{print $NF}' | grep -oE '[0-9a-zA-Z_.-]+' | sed 's/^v//g')"
  if ! kubecolor --kubecolor-version | grep -qF "$LATEST_VERSION"; then
    printf "Updating kubecolor to %s\n" "$LATEST_VERSION"
    printf 'https://github.com/hidetatz/kubecolor/releases/download/%s/kubecolor_%s_Linux_%s.tar.gz' \
      "v$LATEST_VERSION" "$LATEST_VERSION" "$(uname -p | sed 's/unknown/x86_64/g' | tr -d '[[:space:]]')" | \
    xargs curl -sSL | \
    tar -xpzf - kubecolor
    install kubecolor ~/.local/bin/kubecolor -m 0755
    rm -f kubecolor
  fi
}
if ! command -v kubecolor &>/dev/null; then
  _update_kubecolor
  alias kubectl="kubecolor"
  typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND="${POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND}|kubecolor"
fi

Так же, для ряда команд, если они такие есть, выполняется автоматическая подгрузка дополнений подсказки, чтобы zsh знал что подсказывать.

1
2
3
4
5
6
## Completions for 3-d party binaries
for COMMAND in kubectl helm k3d flux kubepug; do
  if command -v "$COMMAND" >/dev/null; then
    source <("$COMMAND" completion zsh)
  fi
done

Раздельные окружения

Вот тут самая хитрая и в то же время простая штука из моего арсенала. Самая ценная вещь в работе. Дело в том, что когда работаешь на разных проектах\окружениях, то самая страшная вещь, которую ты можешь сделать это что-то сломать. Так вот нет способа проще это сделать, чем забыть переключится на нужное окружение и применить что-то не туда куда думал.

Вот от этого как раз и спасает этот кусок кода.

1
2
3
if [ -f env.sh ]; then
  source ./env.sh
fi

Да всё так просто. А что собственно происходит? Дело в том, что zsh разово, при запуске, проверяет ту папку, в которой он запустился и если там есть файл с точным именем env.sh, то включает его в контекст. Объясняю.

Я использую Ranger для навигации по папкам из терминала. Допустим я работаю на проекте и у меня ещё блог есть. Создал вот такие папочки для проектов.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
.
├── MyBlog
│   ├── env.sh
│   └── kubeconfig.yaml
└── SuperWorkProject
    ├── development
    │   ├── env.sh
    │   └── kubeconfig.yaml
    └── production
        ├── env.sh
        └── kubeconfig.yaml

4 directories, 6 files

На боевом проекте у меня есть два окружения, везде Kubernetes, везде свои раздельные kubeconfig.yaml. Допустим мне нужно поработать на production. В его env.sh я запишу следующее.

1
2
3
4
5
6
7
8
#!/usr/bin/env sh

export KUBECONFIG='~/projects/SuperWorkProject/production/kubeconfig.yaml'
export AWS_PROFILE='production'

pf() {
  nohup sh -c 'kubectl get po -n monitoring -l app.kubernetes.io/name=grafana -o name | xargs -I{} kubectl port-forward {} 3000' 1>/dev/null &
}

Теперь, подключу открыв zsh в этой папке или просто через source ./env.sh. Всё, теперь я точно уверен, что работаю с production. Кроме того, объявил себе функцию для port-forward.

Licensed under Apache License, Version 2.0
Обновлено Dec 01, 2023 14:46 +0200
All rights reserved
Создано при помощи Hugo
Тема Stack, дизайн Jimmy