hamacs/ha-programming-ansible.org
2024-07-02 10:11:23 -07:00

8.2 KiB
Raw Blame History

Programming Ansible

Configuring Ansible and YAML

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 Tree Sitter for the YAML mode or not.

YAML

Doing a lot of 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 yaml-pro that is now based on Tree Sitter. Lets make sure the Tree-Sitter version works:

  (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"))))))
major-mode-hydras/yaml-ts-mode/body

Allow this mode in Org blocks:

  (add-to-list 'org-babel-load-languages '(yaml-ts . t))

And we hook

  (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)))

Since I can never remember too many keybindings for particular nodes, we create a Hydra just for it.

  (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")))))

Note that these packages need the following to run properly:

  pip install yamllint

Jinja2

A lot of projects (like Ansible and Zuul) uses Jinja2 with YAML, so we first install the jinja2-mode:

  (use-package jinja2-mode
    :mode (rx ".j2" string-end))

Jinja is a template system that integrates inside formats like JSON, HTML or YAML. The polymode project glues modes like jinja2-mode to yaml-mode.

I adapted this code from the poly-ansible project:

  (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")))))

We need to make sure the mixed-pitch-mode doesnt screw things up.

  (add-hook 'poly-yaml-jinja2-mode-hook (lambda () (mixed-pitch-mode -1)))

  ;; (add-hook 'yaml-ts-mode-hook 'poly-yaml-jinja2-mode)
  ---
  # 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

Ansible

Do I consider all YAML files an Ansible file needing ansible-mode? Maybe we just have a toggle for when we want the Ansible feature.

  (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))

The ansible-vault-password-file variable needs to change per project, so lets use the .dir-locals.el file, for instance:

  ((nil . ((ansible-vault-password-file . "playbooks/.vault-password"))))

The YAML files get access Ansibles documentation using the ansible-doc project (that accesses the ansible-doc interface):

  (use-package ansible-doc
    :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")))))

Can we integrate Ansible with LSP using ansible-language-server project (see this documentation)?

Using npm to install the program:

  npm install -g @ansible/ansible-language-server

But … will I get some use out of this? Ill come back to it later.