hamacs/ha-programming-ansible.org
Howard Abrams 6d92980311 Migration from ~/other to ~/src
Why was it any other way?
2024-10-19 13:34:01 -07:00

293 lines
11 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+title: Programming Ansible
#+author: Howard X. Abrams
#+date: 2024-06-07
#+tags: emacs
Configuring Ansible and YAML
#+begin_src emacs-lisp :exports none
;;; ha-programming-ansible.el --- Configuring Ansible and YAML -*- lexical-binding: t; -*-
;;
;; © 2024 Howard X. Abrams
;; Licensed under a Creative Commons Attribution 4.0 International License.
;; See http://creativecommons.org/licenses/by/4.0/
;;
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
;; Maintainer: Howard X. Abrams <howard.abrams@gmail.com>
;; Created: June 7, 2024
;;
;; While obvious, GNU Emacs does not include this file
;;
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
;; ~/src/hamacs/ha-programming-ansible.org
;; And tangle the file to recreate this one.
;;
;;; Code:
#+end_src
My day job now involves a lot of Ansible, and Ive been struggling to get the right balance here.
Much of the conflict stems from whether to use [[file:ha-programming.org::*Tree Sitter][Tree Sitter]] for the YAML mode or not.
* Visual Indentation
Moving by lines is our default navigation mode, but for Yaml and Python, that doesnt have s-expressions, we need something to move around by blocks. The [[https://codeberg.org/ideasman42/emacs-spatial-navigate][spatial navigation]] project attempts to address this. The problem is how to bind the functions.
The obvious keybindings are ~M-h/j/k/l~ … but that is used … well, somewhat. In org files, this is a way to move the outline of subtrees around. Useful, and =spatial-navigate= wouldnt be helpful in org files anyway.
#+begin_src emacs-lisp :tangle no
(use-package spatial-navigate
:straight (:repo "https://codeberg.org/ideasman42/emacs-spatial-navigate")
:config
(pretty-hydra-define spatial-navigate (:color amaranth :quit-key "q")
("Box"
(("k" spatial-navigate-backward-vertical-box "up")
("j" spatial-navigate-forward-vertical-box "Down")
("h" spatial-navigate-backward-horizontal-box "Down")
("l" spatial-navigate-forward-horizontal-box "Down"))
"Bar"
(("K" spatial-navigate-backward-vertical-bar "up")
("J" spatial-navigate-forward-vertical-bar "Down")
("H" spatial-navigate-backward-horizontal-bar "Down")
("L" spatial-navigate-forward-horizontal-bar "Down")))))
#+end_src
And we can attach this menu to the “g” section:
#+begin_src emacs-lisp :tangle no
(use-package spatial-navigate
:general
(:states '(normal visual motion operator)
"g t" '("spatial nav" . spatial-navigate/body)))
#+end_src
The [[https://github.com/antonj/Highlight-Indentation-for-Emacs][Highlight-Indentation]] project highlights each of the indent levels, which could be helpful for these modes:
#+begin_src emacs-lisp
(use-package highlight-indentation
:straight (:host github :repo "antonj/Highlight-Indentation-for-Emacs")
:hook ((yaml-mode . highlight-indentation-mode)
(yaml-ts-mode . highlight-indentation-mode)
(python-mode . highlight-indentation-mode)))
#+end_src
This project has another display feature, which just lines up the current level. So Ive created a toggle for this:
#+begin_src emacs-lisp
(use-package highlight-indentation
:config
(setq highlight-indentation-blank-lines t)
(set-face-background 'highlight-indentation-face "#332c26")
(set-face-background 'highlight-indentation-current-column-face "#66615c")
(defun ha-toggle-highlight-indentation ()
"Toggles through the indentation modes."
(interactive)
(cond
(highlight-indentation-mode
(progn
(highlight-indentation-mode -1)
(highlight-indentation-current-column-mode 1)))
(highlight-indentation-current-column-mode
(progn
(highlight-indentation-mode -1)
(highlight-indentation-current-column-mode -1)))
(t
(progn
(highlight-indentation-mode 1)
(highlight-indentation-current-column-mode -1)))))
(ha-leader "t i" '("show indents" . ha-toggle-highlight-indentation)))
#+end_src
* YAML
Doing a lot of [[https://github.com/yoshiki/yaml-mode][YAML work]], but the =yaml-mode= project needs a new maintainer, so I might as well switch over to the T version.
, so Ive switch to [[https://github.com/zkry/yaml-pro][yaml-pro]] that is now based on Tree Sitter. Lets make sure the Tree-Sitter version works:
#+begin_src emacs-lisp
(when (treesit-available-p)
(use-package yaml-ts-mode
:straight (:type built-in)
:mode ((rx ".yamllint")
(rx ".y" (optional "a") "ml" string-end))
:hook (yaml-ts-mode . (lambda () (mixed-pitch-mode -1)))
:mode-hydra
((:foreign-keys run)
("Simple"
(("l" ha-yaml-next-section "Next section")
("h" ha-yaml-prev-section "Previous"))))))
#+end_src
Allow this mode in Org blocks:
#+begin_src emacs-lisp :results silent
(add-to-list 'org-babel-load-languages '(yaml-ts . t))
#+end_src
And we hook
#+begin_src emacs-lisp
(use-package yaml-pro
:straight (:host github :repo "zkry/yaml-pro")
:after yaml-ts-mode
:hook ((yaml-ts-mode . yaml-pro-ts-mode)
(yaml-mode . yaml-pro-mode)))
#+end_src
Since I can never remember too many keybindings for particular nodes, we create a Hydra just for it.
#+begin_src emacs-lisp
(use-package major-mode-hydra
:after yaml-pro
:config
(major-mode-hydra-define yaml-ts-mode (:foreign-keys run)
("Navigation"
(("u" yaml-pro-ts-up-level "Up level" :color pink) ; C-c C-u
("J" yaml-pro-ts-next-subtree "Next subtree" :color pink) ; C-c C-n
("K" yaml-pro-ts-prev-subtree "Previous" :color pink)) ; C-c C-p
"Editing"
(("m" yaml-pro-ts-mark-subtree "Mark subtree") ; C-c C-@
("x" yaml-pro-ts-kill-subtree "Kill subtree") ; C-c C-x C-w
("p" yaml-pro-ts-paste-subtree "Paste subtree")) ; C-c C-x C-y
"Insert"
(("e" yaml-pro-edit-ts-scalar "Edit item") ; C-c '
("o" yaml-pro-ts-meta-return "New list item"))
"Refactor"
(("r" yaml-pro-ts-move-subtree-up "Raise subtree")
("t" yaml-pro-ts-move-subtree-down "Lower subtree")
("," combobulate-hydra/body ">>>"))
"Documentation"
(("d" hydra-devdocs/body "Devdocs")))))
#+end_src
Note that these packages need the following to run properly:
#+begin_src sh
pip install yamllint
#+end_src
* Jinja2
A lot of projects (like Ansible and Zuul) uses [[https://jinja.palletsprojects.com][Jinja2]] with YAML, so we first install the [[https://github.com/paradoxxxzero/jinja2-mode][jinja2-mode]]:
#+begin_src emacs-lisp
(use-package jinja2-mode
:mode (rx ".j2" string-end))
#+end_src
Jinja is a /template/ system that integrates /inside/ formats like JSON, HTML or YAML.
The [[https://polymode.github.io/][polymode]] project /glues/ modes like [[https://github.com/paradoxxxzero/jinja2-mode][jinja2-mode]] to [[https://github.com/yoshiki/yaml-mode][yaml-mode]].
I adapted this code from the [[https://github.com/emacsmirror/poly-ansible][poly-ansible]] project:
#+begin_src emacs-lisp
(use-package polymode
:config
(define-hostmode poly-yaml-hostmode :mode 'yaml-ts-mode)
(defcustom pm-inner/jinja2
(pm-inner-chunkmode :mode #'jinja2-mode
:head-matcher (rx "{"
(or "%" "{" "#")
(optional (or "+" "-")))
:tail-matcher (rx (optional (or "+" "-"))
(or "%" "}" "#")
"}")
:head-mode 'body
:tail-mode 'body
:head-adjust-face t)
"Jinja2 chunk."
:group 'innermodes
:type 'object)
(define-polymode poly-yaml-jinja2-mode
:hostmode 'poly-yaml-hostmode
:innermodes '(pm-inner/jinja2))
(major-mode-hydra-define+ yaml-ts-mode nil
("Extensions" (("j" poly-yaml-jinja2-mode "Jinja2")))))
#+end_src
We need to make sure the =mixed-pitch-mode= doesnt screw things up.
#+begin_src emacs-lisp
(add-hook 'poly-yaml-jinja2-mode-hook (lambda () (mixed-pitch-mode -1)))
#+end_src
We /can/ hook this up to Org, via:
#+begin_src emacs-lisp
(add-to-list 'org-babel-load-languages '(poly-yaml-jinja2 . t))
#+end_src
Now we can use either =yaml-ts= or =poly-yaml-jinja2= (which perhaps we should make an alias?):
#+begin_src poly-yaml-jinja2 :tangle no
---
# Let's see how this works
- name: Busta move
debug:
msg: >-
This {{ adjective }} {{ noun }} {{ verb }} the ball."
{% for x in does %}
What is this about?
{% endfor %}
vars:
adjective: small
noun: squirrel
verb: ate
#+end_src
* Ansible
Do I consider all YAML files an Ansible file needing [[https://github.com/k1LoW/emacs-ansible][ansible-mode]]? Maybe we just have a toggle for when we want the Ansible feature.
#+begin_src emacs-lisp
(use-package ansible
:straight (:host github :repo "k1LoW/emacs-ansible")
;; :mode ((rx (or "playbooks" "roles") (one-or-more any) ".y" (optional "a") "ml") . ansible-mode)
:config
(setq ansible-vault-password-file "~/.ansible-vault-passfile")
(major-mode-hydra-define+ yaml-ts-mode nil
("Extensions" (("a" ansible "Ansible"))))
(ha-leader "t y" 'ansible))
#+end_src
The [[help:ansible-vault-password-file][ansible-vault-password-file]] variable needs to change /per project/, so lets use the =.dir-locals.el= file, for instance:
#+begin_src emacs-lisp :tangle no
((nil . ((ansible-vault-password-file . "playbooks/.vault-password"))))
#+end_src
The YAML files get access Ansibles documentation using the [[https://github.com/emacsorphanage/ansible-doc][ansible-doc]] project (that accesses the [[https://docs.ansible.com/ansible/latest/cli/ansible-doc.html][ansible-doc interface]]):
#+begin_src emacs-lisp
(use-package ansible-doc
:after yaml-ts-mode
:hook (yaml-ts-mode . ansible-doc-mode)
:config
;; (add-to-list 'exec-path (expand-file-name "~/.local/share/mise/installs/python/3.10/bin/ansible-doc"))
(major-mode-hydra-define+ yaml-ts-mode nil
("Documentation"
(("D" ansible-doc "Ansible")))))
#+end_src
Can we integrate Ansible with LSP using [[https://github.com/ansible/ansible-language-server][ansible-language-server]] project (see [[https://emacs-lsp.github.io/lsp-mode/page/lsp-ansible/][this documentation]])?
Using =npm= to install the program:
#+begin_src sh
npm install -g @ansible/ansible-language-server
#+end_src
But … will I get some use out of this? Ill come back to it later.
* Technical Artifacts :noexport:
Let's provide a name so that the file can be required:
#+begin_src emacs-lisp :exports none
(provide 'ha-programming-ansible)
;;; ha-programming-ansible.el ends here
#+end_src
#+DESCRIPTION: Configuring Ansible and YAML
#+PROPERTY: header-args:sh :tangle no
#+PROPERTY: header-args:emacs-lisp :tangle yes
#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes
#+OPTIONS: num:nil toc:t todo:nil tasks:nil tags:nil date:nil
#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
# Local Variables:
# eval: (add-hook 'after-save-hook #'org-babel-tangle t t)
# End: