Skip to content

Instantly share code, notes, and snippets.

@Kyrremann
Last active February 5, 2026 00:02
Show Gist options
  • Select an option

  • Save Kyrremann/24420c7ceee5f25a82d14255b7990766 to your computer and use it in GitHub Desktop.

Select an option

Save Kyrremann/24420c7ceee5f25a82d14255b7990766 to your computer and use it in GitHub Desktop.
My dotfiles
echo ".bash_profile"
# Harden homebrew
# from: https://github.com/AtropineTears/TheMacHardeningScripts/blob/main/AutoHarden_MacOSX/scripts/brew/brew-harden.sh
export HOMEBREW_NO_INSECURE_REDIRECT=1
export HOMEBREW_FORCE_BREWED_CURL=1
export HOMEBREW_AUTO_UPDATE_SECS=60
export HOMEBREW_DISPLAY_INSTALL_TIMES=1
export HOMEBREW_NO_ANALYTICS=1
export HOMEBREW_AUTO_UPDATE_SECS=300
export HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS=3
export GOPATH=/Users/kyrremann/workspace/go
export TERM=xterm-256color
export TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE=/var/run/docker.sock
export DOCKER_HOST="unix://${HOME}/.colima/docker.sock"
export PATH=/opt/homebrew/opt/postgresql@17/bin:/Users/kyrremann/.local/bin:/Users/kyrremann/.local/share/mise/shims:/Applications/Emacs.app/Contents/MacOS:/Applications/Emacs.app/Contents/MacOS/bin:/Users/kyrremann/.krew/bin:$GOPATH/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
# The next line enables shell command completion for gcloud.
. '/usr/local/bin/google-cloud-sdk/completion.bash.inc'
. '/usr/local/bin/google-cloud-sdk/path.bash.inc'
eval "$(/opt/homebrew/bin/brew shellenv)"
[[ -r "/opt/homebrew/etc/profile.d/bash_completion.sh" ]] && . "/opt/homebrew/etc/profile.d/bash_completion.sh"
. "$HOME/.cargo/env"
# Scaleway CLI autocomplete initialization.
eval "$(scw autocomplete script shell=bash)"
if [ -s ./.bashrc ]
then
source ./.bashrc
else
echo "No .bashrc found"
fi
# Set up gpg-agent automatically for every shell
if [ -z "$(pgrep gpg-agent)" ]; then
eval "$(gpg-agent --daemon)"
fi
ssh-add --apple-use-keychain 2>/dev/null;
# The "FRX" options make "less" more compatible with "git", and the "i" option enables case insensitive search in less.
export LESS="FRXim"
export HISTCONTROL=ignoreboth
export EDITOR="emacsclient"
export LANG="en_US.UTF-8"
export PAGER=cat
export KUBECTX_CURRENT_FGCOLOR="$(tput bold)$(tput setaf 6)" # blue text
export KUBECTX_CURRENT_BGCOLOR=$(tput setab 7) # white background
### ###
# Aliases #
### ###
alias ls='TERM=screen ls -GF'
alias ll='ls -alFh'
alias grep='grep --color=always --binary-files=without-match'
alias k=kubectl
complete -F _complete_alias k
alias kns=kubens
complete -F _complete_alias kns
alias ktx=kubectx
complete -F _complete_alias ktx
alias krew='k krew'
alias python=python3
### ###
# Functions #
### ###
cnrm() {
readonly cluster=${1:?"Cluster is missing"}; shift
readonly namespace=${1:?"Namespace is missing"}; shift
kubectl logs --context "$cluster" --namespace cnrm-system --container manager --tail=100 --follow=true \
"$(kubectl get pod --context "$cluster" --namespace cnrm-system --selector cnrm.cloud.google.com/scoped-namespace="$namespace" --no-headers -o jsonpath='{.items[0].metadata.name}')" "$@"
}
kube-all() {
readonly namespace=${1:?"Namespace is missing"}; shift
kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found --namespace="$namespace"
}
parse_git_branch() {
git branch --no-color 2> /dev/null | sed -e '/^[^*]/d' -e "s/$(git rev-parse --abbrev-ref origin/HEAD 2> /dev/null | sed s/origin\\///)/~/" -e 's/* \(.*\)/[\1]/'
}
### ###
# PS1 #
### ###
# PROMPT_COMMAND='PS1_CMD1=$(git branch --show-current 2>/dev/null)'; PS1='[\t]\[\e[38;5;220m\][${PS1_CMD1}]\[\e[38;5;40m\]\W\[\e[0m\]\$ '
# https://bash-prompt-generator.org/
PROMPT_COMMAND='GIT_BRANCH=$(git branch --show-current 2>/dev/null)'
PS1='[\t]\[\e[1;33m\][${GIT_BRANCH}]\[\e[1;32m\]\W\[\e[0m\]\$ '
# export PS1='[\t]$(kube_ps1)\e[1;33m$(parse_git_branch)\e[0m\e[1;32m\W\e[0m\$ '
. "$HOME/.cargo/env"
eval "$(/opt/homebrew/bin/mise activate bash)" # added by https://mise.run/bash
;; -*- lexical-binding: t; -*-
(require 'json)
(require 'url)
(defvar gitmoji-json-url "https://raw.githubusercontent.com/carloscuesta/gitmoji/refs/heads/master/packages/gitmojis/src/gitmojis.json")
(defvar gitmoji-json-local "~/.emacs.d/gitmojis.json")
(defun gitmoji--download-emoji-json ()
"Download Gomoji JSON from Githun if not present."
(unless (file-exists-p gitmoji-json-local)
(url-copy-file gitmoji-json-url gitmoji-json-local t)))
(defun gitmoji--load-emojis ()
"Load Gomojis from local file."
(gitmoji--download-emoji-json)
(with-temp-buffer
(insert-file-contents gitmoji-json-local)
(let ((json-array-type 'list))
(alist-get 'gitmojis (json-read)))))
(defun gitmoji ()
"Prompt for an Gomoji and insert it at point."
(interactive)
(let* ((emojis (gitmoji--load-emojis))
(choices (mapcar (lambda (e)
(let ((emoji (alist-get 'emoji e))
(name (alist-get 'name e))
(desc (alist-get 'description e)))
(cons (format "%s %s — %s" emoji name desc) emoji)))
emojis))
(choice (completing-read "Pick emoji: " (mapcar #'car choices) nil t))
(emoji-str (cdr (assoc choice choices))))
(if (string= (buffer-name) "COMMIT_EDITMSG")
(insert (concat emoji-str ": "))
(insert emoji-str))))
;;; -*- lexical-binding: t -*-
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(aw-keys '(97 115 100 102 103 104 106 107 108))
'(backup-by-copying t)
'(backup-directory-alist '(("." . "~/.emacs.d/backups")))
'(clean-buffer-list-delay-general 1)
'(clean-buffer-list-kill-buffer-names
'("*Help*" "*Apropos*" "*Buffer List*" "*Compile-Log*" "*info*" "*vc*"
"*vc-diff*" "*diff*" "*kubel*"))
'(conf-mode-hook '(display-line-numbers-mode prettify-symbols-mode) t)
'(copilot-indent-offset-warning-disable t)
'(copilot-max-char -1)
'(custom-safe-themes
'("8fbf2d585f1138caaafa9e523fa3a20614c1d1dcc6002c9808c3e40028e21df4"
"488b82a8d9ace0aea8a6825db144e3c65c4f1ef3e090b618bf311d9cdb513322"
"3656585faa5bd9925c0ee134b5eecf6d6a45ee2a2e81e633fa7a9a0659fdc095"
"317f9fc9b30ce2d8064965f1a3cf8645ee3e8dc26dfcdc9f0e2ead337f37cac3"
"7e98dc1aa7f5db0557691da690c38d55e83ddd33c6d268205d66e430d57fb982"
"8dbbcb2b7ea7e7466ef575b60a92078359ac260c91fe908685b3983ab8e20e3f"
default))
'(default-frame-alist nil)
'(delete-old-versions t)
'(desktop-save-mode t)
'(dired-kill-when-opening-new-dired-buffer t)
'(display-buffer-base-action '(nil))
'(display-time-24hr-format t)
'(display-time-default-load-average nil)
'(display-time-mode t)
'(global-auto-revert-mode t)
'(global-undo-tree-mode t)
'(go-ts-mode-build-tags '("integration_test"))
'(go-ts-mode-indent-offset 2)
'(highlight-indent-guides-method 'fill)
'(inhibit-default-init t)
'(initial-buffer-choice t)
'(isearch-lazy-count t)
'(ivy-count-format "(%d/%d) ")
'(ivy-sort-matches-functions-alist
'((t) (ivy-completion-in-region . ivy--shorter-matches-first)
(ivy-switch-buffer . ivy-sort-function-buffer)))
'(ivy-use-virtual-buffers 'recentf)
'(js-indent-level 2)
'(kubectx-mode-line-string-format "[%C:%N]" t)
'(link-hint-action-fallback-commands
'(:open
(lambda nil
(condition-case _
(progn (browse-url (git-link-homepage "origin")) t)
(error nil)))))
'(magit-clone-default-directory 'my/magit-clone-default-directory)
'(magit-clone-name-alist
'(("\\`\\(?:github:\\|gh:\\)?\\([^:]+\\)\\'" "github.com"
"github.user")
("\\`\\(?:gitlab:\\|gl:\\)\\([^:]+\\)\\'" "gitlab.com"
"gitlab.user")
("\\`\\(?:sourcehut:\\|sh:\\)\\([^:]+\\)\\'" "git.sr.ht"
"sourcehut.user")
("^(nais|navikt)\\/([^:]+)$" "github.com" "github.user")))
'(midnight-hook
'(clean-buffer-list
(lambda nil
(shell-command
"gh gist edit 24420c7ceee5f25a82d14255b7990766 --add ~/.emacs.d/kyb.el"))
(lambda nil
(shell-command
"gh gist edit 24420c7ceee5f25a82d14255b7990766 --add ~/.emacs.d/init.el"))
(lambda nil
(shell-command
"gh gist edit 24420c7ceee5f25a82d14255b7990766 --add ~/.emacs.d/my.el"))
(lambda nil
(shell-command
"gh gist edit 24420c7ceee5f25a82d14255b7990766 --add ~/.emacs.d/gitmoji.el"))
(lambda nil
(shell-command
"gh gist edit 24420c7ceee5f25a82d14255b7990766 --add ~/.bash_profile"))
(lambda nil
(shell-command
"gh gist edit 24420c7ceee5f25a82d14255b7990766 --add ~/.bashrc"))))
'(midnight-mode t)
'(package-archives
'(("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "https://melpa.org/packages/")))
'(package-install-upgrade-built-in t)
'(package-selected-packages
'(ace-window auto-package-update breadcrumb breadcrumb-mode company
copilot copilot-chat diff-hl emojify expand-region
git-link go-dlv go-mode go-snippets gotest-ts gptel
graphql-mode highlight-indent-guides jsonian
kotlin-ts-mode kubectx-mode kubel link-hint magit
marginalia mistty modus-themes move-text orderless
rainbow-delimiters rainbow-mode rust-mode
sqlite-mode-extras svelte-mode terraform-mode tidal
undo-tree vertico yasnippet-snippets))
'(prog-mode-hook
'(display-line-numbers-mode prettify-symbols-mode subword-mode))
'(project-file-history-behavior 'relativize)
'(project-switch-commands
'((project-find-file "Find file" nil)
(project-find-regexp "Find regexp" nil)
(project-find-dir "Find directory" nil)
(mistty-in-project "Shell" 115)
(magit-project-status "Magit" 109)))
'(project-vc-ignores '(".git" "venv" "node_modules" "vendor"))
'(ring-bell-function 'ignore)
'(rust-always-locate-project-on-open t)
'(rust-format-on-save t)
'(rust-rustfmt-switches '("--edition=2024"))
'(savehist-mode t)
'(scroll-bar-mode nil)
'(set-mark-command-repeat-pop t)
'(shell-file-name "/opt/homebrew/bin/fish")
'(split-height-threshold 160)
'(switch-window-shortcut-style 'alphabet)
'(tab-width 2)
'(terraform-command "terraform")
'(terraform-format-on-save t)
'(text-mode-hook
'(display-line-numbers-mode flyspell-mode text-mode-hook-identify))
'(undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo-tree-history")))
'(undo-tree-visualizer-diff t)
'(use-package-always-ensure t)
'(use-short-answers t)
'(yaml-indent-offset 2))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(ansi-color-bright-yellow ((t (:background "gray100" :foreground "#884900"))))
'(ansi-color-yellow ((t (:background "gray100" :foreground "#6f5500"))))
'(term ((t (:background "#ffffff" :foreground "#000000"))))
'(term-color-yellow ((t (:background "white" :foreground "#6f5500")))))
(load "~/.emacs.d/kyb.el")
(load "~/.emacs.d/my.el")
(load "~/.emacs.d/gitmoji.el")
(load-theme 'modus-operandi)
(set-face-font 'default "Andale Mono-14")
(set-fontset-font t 'symbol "Apple Color Emoji")
;; Proper Mac keybindings
(setq mac-right-option-modifier nil)
;; Keybindings
(global-set-key (kbd "C-c C-c") 'compile)
(global-set-key (kbd "M-z") 'zap-up-to-char)
;; setq
(setq default-directory "~/workspace/")
;; TODO: Dette fra copilot funker ikke
(defun my/slick-cut-a (orig beg end &rest args)
""
(interactive
(if (use-region-p)
(apply orig beg end args)
(apply orig (line-beginning-position)
(line-beginning-position 2) args))))
(advice-add 'kill-region :around #'my/slick-cut-a)
(defadvice kill-region (before slick-cut activate compile)
"When called interactively with no active region, kill a single line instead."
(interactive
(if mark-active (list (region-beginning) (region-end))
(list (line-beginning-position)
(line-beginning-position 2)))))
;;; only use-package after this line
(use-package emacs
:init
;; Add prompt indicator to `completing-read-multiple'.
;; Prepending [CRM] to the prompt string to help users quickly recognize that the prompt supports multiple selections
(defun crm-indicator (args)
(cons (concat "[CRM] " (car args)) (cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
;; Do not allow the cursor in the minibuffer prompt
(setq minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt))
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
;; Vertico minibuffer configurations.
:custom
;; Enable context menu. `vertico-multiform-mode' adds a menu in the minibuffer
;; to switch display modes.
(context-menu-mode t)
;; Support opening new minibuffers from inside existing minibuffers.
(enable-recursive-minibuffers t)
;; Hide commands in M-x which do not work in the current mode. Vertico
;; commands are hidden in normal buffers. This setting is useful beyond
;; Vertico.
(read-extended-command-predicate #'command-completion-default-include-p)
;; Do not allow the cursor in the minibuffer prompt
(minibuffer-prompt-properties
'(read-only t cursor-intangible t face minibuffer-prompt)))
;; end Vertico
(use-package files
:ensure nil
:hook (after-save . executable-make-buffer-file-executable-if-script-p))
(use-package vc
:config
;; ignore whitespace changes (-b)
;; ignore changes whose lines are all blank (-B)
;; use unified diff format (-u).
(setq vc-diff-switches '("-b" "-B" "-u"))
(setq vc-git-diff-switches nil))
(use-package auto-package-update
:config
(auto-package-update-at-time "11:30"))
(use-package move-text
:config
(move-text-default-bindings))
(use-package rainbow-mode
:config
(rainbow-mode))
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
(use-package breadcrumb
:hook
(prog-mode . breadcrumb-mode)
(conf-mode . breadcrumb-mode))
(use-package which-key
:config
(which-key-mode))
(use-package expand-region
:config
(global-set-key (kbd "C-=") 'er/expand-region))
(use-package diff-hl
:config
(global-diff-hl-mode)
(diff-hl-margin-mode)
:hook
('magit-post-refresh . diff-hl-magit-post-refresh))
;; Enable Vertico
;; From: https://github.com/minad/vertico?tab=readme-ov-file#configuration
(use-package vertico
;; :custom
;; (vertico-scroll-margin 0) ;; Different scroll margin
;; (vertico-count 20) ;; Show more candidates
;; (vertico-resize t) ;; Grow and shrink the Vertico minibuffer
;; (vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
:init
(vertico-mode))
(use-package orderless
:custom
;; Configure a custom style dispatcher (see the Consult wiki)
;; (orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch))
;; (orderless-component-separator #'orderless-escapable-split-on-space)
(completion-styles '(orderless basic))
(completion-category-overrides '((file (styles partial-completion))))
(completion-category-defaults nil) ;; Disable defaults, use our settings
(completion-pcm-leading-wildcard t)) ;; Emacs 31: partial-completion behaves like substring
(use-package marginalia
;; Bind `marginalia-cycle' locally in the minibuffer. To make the binding
;; available in the *Completions* buffer, add it to the
;; `completion-list-mode-map'.
:bind (:map minibuffer-local-map
("M-A" . marginalia-cycle))
;; The :init section is always executed.
:init
;; Marginalia must be activated in the :init section of use-package such that
;; the mode gets enabled right away. Note that this forces loading the
;; package.
(marginalia-mode))
(use-package ace-window
:bind (("M-o" . ace-window)))
(use-package link-hint
:bind
("C-c l o" . link-hint-open-link)
("C-c l c" . link-hint-copy-link)
("C-c l p" . link-hint-open-link-at-point))
(use-package mistty-project
:ensure nil
:config
(mistty-project-init-kill-buffer)
(keymap-set project-prefix-map "s" 'mistty-in-project)) ; enable Mistty for C-x p s
(use-package company
:hook (after-init . global-company-mode)
:custom
(company-idle-delay 0.1)
(company-minimum-prefix-length 1)
:bind (:map company-active-map
("TAB" . company-complete-selection)
("<tab>" . company-complete-selection)
("C-n" . company-select-next)
("C-p" . company-select-previous)))
;; Enable Eglot
(use-package eglot
:config
(add-to-list 'eglot-server-programs '((graphql-mode) . ("graphql-lsp" "server" "-m" "stream")))
(add-to-list 'eglot-server-programs '((js-mode js-ts-mode) . ("typescript-language-server" "--stdio")))
(add-to-list 'eglot-server-programs '((kotlin-mode kotlin-ts-mode) . ("kotlin-language-server")))
(add-to-list 'eglot-server-programs '((lua-mode) . ("lua-language-server")))
(add-to-list 'eglot-server-programs '((sh-mode bash-ts-mode) . ("bash-language-server" "start")))
;; Eglot uses the built-in project package to identify the LSP workspace for a newly-opened buffer.
;; Link: https://go.dev/gopls/editor/emacs#configuring-project-for-go-modules-in-emacs
(defun my/project-find-go-module (dir)
(when-let* ((root (locate-dominating-file dir "go.mod")))
(cons 'go-module root)))
(cl-defmethod project-root ((project (head go-module)))
(cdr project))
(add-hook 'project-find-functions #'my/project-find-go-module)
;; end
:hook
(bash-ts-mode . eglot-ensure)
(go-ts-mode . eglot-ensure)
(graphql-mode . eglot-ensure)
(js-mode . eglot-ensure)
(js-ts-mode . eglot-ensure)
(kotlin-ts-mode . eglot-ensure)
(lua-mode . eglot-ensure)
(python-mode . eglot-ensure)
(python-ts-mode . eglot-ensure)
(ruby-mode . eglot-ensure)
(ruby-ts-mode . eglot-ensure)
(rust-mode . eglot-ensure)
(sh-mode . eglot-ensure)
(before-save . (lambda ()
(when (eglot-managed-p)
(eglot-format-buffer -10 t)
(eglot-code-action-organize-imports t))))
:bind (("C-c e a" . eglot-code-actions)
("C-c e r" . my/eglot-rename-with-symbol)
("C-c e o" . eglot-code-action-organize-imports)
("C-c e i" . eglot-find-implementation)
;; Flymake
("C-c e f d" . flymake-show-project-diagnostics)
("C-c e f p" . flymake-goto-prev-error)
("C-c e f n" . flymake-goto-next-error))
:custom-face
(eglot-highlight-symbol-face ((t (:underline t :inherit bold))))
:custom
(eglot-workspace-configuration
'((:gopls .
((staticcheck . t)
(buildFlags . ["-tags=integration_test"])
(matcher . "CaseSensitive")
(completeUnimported . t)
(usePlaceholders . t)
(analyses . ((unusedparams . t)
(shadow . t)))
(hints . ((assignVariableTypes . t)
(compositeLiteralFields . t)
(constantValues . t)
(functionTypeParameters . t)
(parameterNames . t)
(rangeVariableTypes . t))))))))
;; end Eglot
(use-package copilot
:vc (:url "https://github.com/copilot-emacs/copilot.el"
:rev :newest
:branch "main")
:bind (:map copilot-completion-map
("<tab>" . copilot-accept-completion)
("TAB" . copilot-accept-completion)
("C-<tab>" . copilot-accept-completion-by-word))
;; :hook
;; (prog-mode . copilot-mode)
)
(use-package copilot-chat
:bind (:map global-map
("C-c C-y" . copilot-chat-yank)
("C-c M-y" . copilot-chat-yank-pop)
("C-c C-M-y" . (lambda () (interactive) (copilot-chat-yank-pop -1)))))
(use-package gptel
:ensure t
:config
(setq gptel-model 'gpt-5.2)
(setq gptel-backend (gptel-make-gh-copilot "Copilot")))
(use-package ruby-mode
:config
(defun my/ruby-compile-command ()
(set (make-local-variable 'compile-command)
(concat "ruby " buffer-file-name)))
:hook (ruby-mode . my/ruby-compile-command))
;; Handles ANSI colors in the compilation buffer
(use-package ansi-color
:hook (compilation-filter . ansi-color-compilation-filter))
(use-package kubel
:bind (
(:map kubel-mode-map
("RET" . kybel-describe-popup)
("C" . kybel-set-context)
("N" . kubel-set-namespace)
("P" . kubel-port-forward-pod)
("R" . kubel-set-resource)
("M-s" . kubel-scale-replicas)
("S" . #'tabulated-list-sort)
("C-b" . #'tabulated-list-previous-column)
("C-f" . #'tabulated-list-next-column)
("n" . #'next-line)
("p" . #'previous-line))))
(use-package kubectx-mode
:hook (after-init . kubectx-mode))
(use-package emojify
:custom
(emojify-display-style 'unicode)
(emojify-emoji-styles '(unicode github)))
(use-package magit
:config
(require 'magit-patch)
(transient-insert-suffix
'magit-patch "r" '("b" "Save diff as patch to buffer" my/magit-write-patch-to-buffer))
(transient-insert-suffix
'magit-patch "r" '("w" "Copy diff as patch to kill-ring" my/magit-copy-patch-kill-ring))
:hook (magit-mode . emojify-mode))
(use-package yaml-mode
:config
(defun my/kubectx-get-context ()
(shell-command-to-string "kubectl config view --minify --output jsonpath='{.contexts[0].name}'"))
(defun my/kubectx-get-namespace ()
(shell-command-to-string "kubectl config view --minify --output jsonpath='{...contexts[0].context.namespace}'"))
(defun my/yaml-compile-command ()
(set (make-local-variable 'compile-command)
(concat "kubectl apply --context=" (my/kubectx-get-context) " --namespace=" (my/kubectx-get-namespace) " -f " buffer-file-name)))
:hook
(yaml-mode . my/yaml-compile-command)
(yaml-mode . highlight-indent-guides-mode))
(use-package sqlite-mode-extras
:hook ((sqlite-mode . sqlite-extras-minor-mode)))
(use-package avy
:bind (("M-g M-c" . avy-goto-char-timer)
("M-g M-g" . avy-goto-line)
("M-g M-w" . avy-goto-word-1)))
(use-package so-long
:ensure nil
:defer t)
(use-package jsonian
:after so-long
:custom
(jsonian-no-so-long-mode))
;; -*- lexical-binding: t; -*-
(require 'transient)
(defun kybel--exec (process-name args &optional readonly callback)
"Utility function to run commands in the proper context and namespace.
PROCESS-NAME is an identifier for the process. Default to \"kubel-command\".
ARGS is a ist of arguments.
CALLBACK is a function that will be executed when the command completes.
READONLY If true buffer will be in readonly mode(view-mode)."
(when (equal process-name "")
(setq process-name "kubel-command"))
(let ((buffer-name (format "*kubel-resource:%s:%s:%s*" kubel-context kubel-namespace (string-join args "_")))
(error-buffer (kubel--process-error-buffer process-name))
(cmd (append (list kubel-kubectl) args)))
(when (get-buffer buffer-name)
(kill-buffer buffer-name))
(when (get-buffer error-buffer)
(kill-buffer error-buffer))
(kubel--log-command process-name cmd)
(make-process :name process-name
:buffer buffer-name
:sentinel (kubel--sentinel callback)
:file-handler t
:stderr (get-buffer-create error-buffer)
:command cmd)
(pop-to-buffer buffer-name)
(if readonly
(with-current-buffer buffer-name
(view-mode)))))
(defun kybel-describe-resource ()
"Describe the resource under the cursor."
(interactive)
(let* ((resource (kubel--get-resource-under-cursor))
(process-name (format "kubel - %s - %s" kubel-resource resource)))
(kubel--exec process-name (list "describe" kubel-resource resource) t)
(yaml-mode)
(view-mode)
(goto-char (point-min))))
;; kubectl patch application <MYAPP> -p '[{"op": "remove", "path": "/status/synchronizationHash"}]' --type=json
(defun kybel--jab-nais-app ()
"Removes the synchronization hash from the resource"
(interactive)
(let* ((resource-name (kubel--get-resource-under-cursor))
(process-name (format "kubel - %s - %s" kubel-resource resource-name))
(cmd (list "patch" kubel-resource resource-name "-p '[{\"op\": \"remove\", \"path\": \"/status/synchronizationHash\"}]' --type=json")))
(message "Command: %s" cmd)
(kubel--exec process-name cmd t)))
(defun kybel-view-secret ()
"View the decryptet secret under the cursor."
(interactive)
(let* ((resource (kubel--get-resource-under-cursor))
(process-name (format "kubel - %s - %s" kubel-resource resource)))
(kybel--exec process-name (list "view-secret" resource "--all") t)
(yaml-mode)
(view-mode)
(goto-char (point-min))))
(transient-define-prefix kybel-describe-popup ()
"Kubel Describe Menu"
["Actions"
("RET" "Describe" kybel-describe-resource)
("e" "Edit" kubel-get-resource-details)
("j" "Jab" kybel--jab-nais-app :if (lambda () (member kubel-resource '("applications.nais.io"))))
("w" "View" kybel-view-secret :if (lambda () (member kubel-resource '("secrets"))))])
;; Let you list all resources of one kind cross namespaces
(defvar-local kubel-all-namespaces nil
"If we should list all resources of one kind.")
(defvar-local kubel-watch-resource nil
"If we should watch the resource.")
;; We have added checks for postfixes, so that we know later if we want to watch or see all resources in the cluster
(defun kubel-set-resource (&optional refresh)
"Set the resource.
If called with a prefix argument REFRESH, refreshes
the context caches, including the cached resource list."
(interactive "P")
(when refresh (kubel--invalidate-context-caches))
(when (member "--all-namespaces" (transient-args 'kybel-set-resource-popup)) (setq kubel-all-namespaces t))
(when (member "--watch" (transient-args 'kybel-set-resource-popup)) (setq kubel-watch-resource t))
(let* ((current-buffer-name (kubel--buffer-name))
(resource-list (kubel--kubernetes-resources-list))
(kubel--buffer (get-buffer current-buffer-name))
(last-default-directory (when kubel--buffer (with-current-buffer kubel--buffer default-directory))))
(with-current-buffer (clone-buffer)
(setq kubel-resource
(completing-read "Select resource: " resource-list))
(switch-to-buffer (current-buffer))
(kubel-refresh last-default-directory))))
(defun kybel--get-command-postfix ()
"Return the command postfix."
(concat
(if kubel-all-namespaces
(progn
(setq kubel-all-namespaces nil)
" --all-namespaces"))
(if kubel-watch-resource
(progn
(setq kubel-watch-resource nil)
" --watch"))))
;; We need to add postfix to the command, as -A/-w is not a prefix option
(defun kubel--populate-list ()
"Return a list with a tabulated list format and \"tabulated-list-entries\"."
(let* ((body (kubel--exec-to-string (concat (kubel--get-command-prefix) " get " kubel-resource (kybel--get-command-postfix))))
(entrylist (kubel--parse-body body)))
(when (string-prefix-p "No resources found" body)
(message "No resources found")) ;; TODO exception here
(list (kubel--get-list-format entrylist) (kubel--get-list-entries entrylist))))
(transient-define-prefix kybel-set-resource-popup ()
"Kubel Set Resource Menu"
["Arguments"
("-A" "All namespaces" "--all-namespaces")]
;; ("-w" "Watch resource" "--watch")] ; Not workin as watch is holding the process
["Actions"
("RET" "Continue" kubel-set-resource)])
(defun kubel--populate-list ()
"Return a list with a tabulated list format and \"tabulated-list-entries\"."
(let* ((body (kubel--exec-to-string (concat (kubel--get-command-prefix) " get " kubel-resource)))
(entrylist (kubel--parse-body body)))
(when (string-prefix-p "No resources found" body)
(message "No resources found")) ;; TODO exception here
(list (kubel--get-list-format entrylist) (kubel--get-list-entries entrylist))))
(defun kubel--exec-to-string (cmd)
"Replace \"shell-command-to-string\" to log to process buffer.
CMD is the command string to run."
(kubel--log-command "kubectl-command" cmd)
(with-output-to-string
(with-current-buffer standard-output
(shell-command cmd t "*kubel stderr*"))))
(defun kybel-set-context ()
"Set the context."
(interactive)
(let* ((kubel--buffer (get-buffer (kubel--buffer-name)))
(last-default-directory (when kubel--buffer (with-current-buffer kubel--buffer default-directory))))
(with-current-buffer (clone-buffer)
(setq kubel-context
(completing-read
"Select context: "
(split-string (kubel--exec-to-string (format "%s config view -o jsonpath='{.contexts[*].name}'" kubel-kubectl)) " ")))
(kubel--invalidate-context-caches)
(setq kubel-namespace (shell-command-to-string "kubectl config view --minify --output 'jsonpath={..namespace}'"))
(switch-to-buffer (current-buffer))
(kubel-refresh last-default-directory))))
;; -*- lexical-binding: t; -*-
;; Copied from http://emacsredux.com/blog/2013/05/22/smarter-navigation-to-the-beginning-of-a-line/
(defun my/smarter-move-beginning-of-line (arg)
"Move point back to indentation of beginning of line.
Move point to the first non-whitespace character on this line.
If point is already there, move to the beginning of the line.
Effectively toggle between the first non-whitespace character and
the beginning of the line.
If ARG is not nil or 1, move forward ARG - 1 lines first. If
point reaches the beginning or end of the buffer, stop there."
(interactive "^p")
(setq arg (or arg 1))
;; Move lines first
(when (/= arg 1)
(let ((line-move-visual nil))
(forward-line (1- arg))))
(let ((orig-point (point)))
(back-to-indentation)
(when (= orig-point (point))
(move-beginning-of-line 1))))
;; remap C-a to `smarter-move-beginning-of-line'
(global-set-key [remap move-beginning-of-line]
'my/smarter-move-beginning-of-line)
;; kubectl patch <RESOURCE> <MYAPP> -p '[{"op": "remove", "path": "/status/synchronizationHash"}]' --type=json
(defun my/jab--get-cluster-resources (resource-type)
"Fetch resources from the cluster based on RESOURCE-TYPE."
(split-string (shell-command-to-string (format "kubectl get %s --ignore-not-found --no-headers --output=name" resource-type))))
(defun my/jab-nais-app ()
"Removes the synchronization hash from the resource"
(interactive)
(let* ((resource-type (completing-read "Resource type: " '("app" "naisjob" "aivenapp")))
(resources (my/jab--get-cluster-resources resource-type))
(chosen-resource (completing-read (format "Choose %s: " resource-type) resources))
(cmd (format "kubectl patch %s -p '[{\"op\": \"remove\", \"path\": \"/status/synchronizationHash\"}]' --type=json --subresource=status"
chosen-resource)))
(message "Command: %s" cmd)
(shell-command cmd)))
;; Reverse with C-u
(defun my/sort-lines-by-length (reverse beg end)
"Sort lines by length."
(interactive "P\nr")
(save-excursion
(save-restriction
(narrow-to-region beg end)
(goto-char (point-min))
(let ;; To make `end-of-line' and etc. to ignore fields.
((inhibit-field-text-motion t))
(sort-subr reverse 'forward-line 'end-of-line nil nil
(lambda (l1 l2)
(apply #'< (mapcar (lambda (range) (- (cdr range) (car range)))
(list l1 l2)))))))))
(defun my/toggle-final-newline ()
"Controls whether Emacs automatically adds a newline at the end of files when saving."
(interactive)
(setq require-final-newline (not require-final-newline)))
(defun my/add-string-to-end-of-lines-in-region (start end string)
"Prompt for string, add it to end of lines in the region"
(interactive
(list (region-beginning)
(region-end)
(read-string (format-prompt
"String rectangle"
(or (car string-rectangle-history) ""))
nil 'string-rectangle-history
(car string-rectangle-history)
'inherit-input-method)))
(goto-char start)
(while (<= (point) end)
(end-of-line)
(insert string)
(forward-line 1)))
(global-set-key (kbd "C-x r e") 'add-string-to-end-of-lines-in-region)
;; From https://blog.lambda.cx/posts/emacs-align-columns/
(defun my/align-non-space (BEG END)
"Align non-space columns in region BEG END."
(interactive "r")
(align-regexp BEG END "\\(\\s-*\\)\\S-+" 1 1 t))
;; *** Custom Patch Command Definitions For Magit
(defun my/magit-write-patch (write-function &optional arg)
"Write patch of current diff to a temp buffer, then run WRITE-FUNCTION.
This is useful as a backend abstraction for
`my/magit-write-patch-to-buffer' and `my/magit-copy-patch-kill-ring'.
Cribs lots of code from `magit-patch-save'. Inspired by
https://github.com/magit/magit/issues/4462#issuecomment-892358920."
(interactive)
(require 'magit-mode)
(require 'magit-diff)
(unless (derived-mode-p 'magit-diff-mode)
(user-error "Only diff buffers can be saved as patches"))
(let ((rev magit-buffer-range)
(typearg magit-buffer-typearg)
(args magit-buffer-diff-args)
(files magit-buffer-diff-files))
(cond ((eq magit-patch-save-arguments 'buffer)
(when arg
(setq args nil)))
((eq (car-safe magit-patch-save-arguments) 'exclude)
(unless arg
(setq args (-difference args (cdr magit-patch-save-arguments)))))
((not arg)
(setq args magit-patch-save-arguments)))
(with-temp-buffer
(magit-git-insert "diff" rev "-p" typearg args "--" files)
(funcall write-function)))
(magit-refresh))
(defun my/magit-write-patch-to-buffer (buffer)
"Write patch format of current diff into chosen buffer.
Finishes with chosen buffer displayed. Uses `magit-patch-save'
internally, so inherits its settings."
(interactive (list (ivy-read "Dump/append patch to buffer: " #'internal-complete-buffer
:def "*patch*")))
(my/magit-write-patch '(lambda () (append-to-buffer buffer (point-min) (point-max))
(pop-to-buffer buffer))))
(defun my/magit-copy-patch-kill-ring ()
"Copy patch format of current diff into kill ring.
Uses `magit-patch-save' internally, so inherit it's settings."
(interactive)
(my/magit-write-patch '(lambda () (kill-region (point-min) (point-max)))))
(defun my/kill-kubel-buffers ()
"Kill all kubel buffers."
(interactive)
(kill-matching-buffers "\*kubel.*" nil 'NO-ASK))
(defun my/kill-other-buffers ()
"Kill all other buffers than the one you are in now."
(interactive)
(mapc 'kill-buffer (delq (current-buffer) (buffer-list))))
(defun my/eglot-rename-with-symbol ()
"Call `eglot-rename' with the symbol at point as default."
(interactive)
(let* ((sym (thing-at-point 'symbol t))
(new (read-from-minibuffer "New name: " sym)))
(eglot-rename new)))
(defun my/magit-clone-default-directory (url)
"Specify directory based on repo-path"
(cond
;; navikt repos go to ~/workspace/nav/REPO
((string-match "navikt/\\([^/]+\\)" url)
(expand-file-name "~/workspace/nav/"))
;; nais repos also go to ~/workspace/nav/REPO
((string-match "nais/\\([^/]+\\)" url)
(expand-file-name "~/workspace/nav/"))
;; all others go to ~/workspace/REPO
((string-match "\\([^/:]+\\)[:/]\\([^/]+\\)\\([^/]+\\)\\.git$" url)
(expand-file-name "~/workspace/"))
(t
(expand-file-name "~/workspace/"))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment