diff --git a/README.md b/README.md index b71856c..592580b 100644 --- a/README.md +++ b/README.md @@ -136,18 +136,21 @@ In general, it makes the whole experience of using org mode notebooks for infrsa This package works with "targets" defined at the top of the file, which also appear in heading tags: ```org -#+TARGET: /ssh:example1.com: (server1) -#+TARGET: /ssh:example2.com: (server2) +#+TARGET: server1 /ssh:example1.com: +#+TARGET: server2 /ssh:example2.com: * Do something on server1 :server1: * Do something on server2 :server2: ``` +Each `#+TARGET` names a tag and then its target, like org's `#+LINK`. The older +form, `#+TARGET: /ssh:example1.com: (server1)`, still works. + Source code blocks under a heading tag that matches a target (a "target tag") execute in on the server, instead of locally: ```org -#+TARGET: /ssh:example1.com: (server1) -#+TARGET: /ssh:example2.com: (server2) +#+TARGET: server1 /ssh:example1.com: +#+TARGET: server2 /ssh:example2.com: * Do something on server1 :server1: @@ -214,7 +217,7 @@ processing the output of the code block. For example, let's say we have a `TARGET` that points to a Podman container in a server: ```org -#+TARGET: /ssh:server.com|podman:my-container: (container) +#+TARGET: container /ssh:server.com|podman:my-container: ``` We can chain together a remote command and a local processing step as follows: @@ -242,7 +245,7 @@ This will work even if the `jq` command is not installed in the container :) A `#+TARGET` value can be a noweb-style reference: ```org -#+TARGET: <> (server) +#+TARGET: server <> #+name: input-instance : app1 @@ -302,7 +305,7 @@ When `devops-enable-session-async` is enabled, blocks are executed as if you had the `session` and `async` headers yourself. For example, ```org -#+TARGET: /ssh:example.com: (example) +#+TARGET: example /ssh:example.com: * Update packages :example: diff --git a/devops-test.el b/devops-test.el index c13032b..0ff2832 100644 --- a/devops-test.el +++ b/devops-test.el @@ -118,6 +118,31 @@ The directory is removed afterwards." "<> (server)") '("server" . "<>")))) +(ert-deftest devops--parse-target-keyword-link-style-test () + "A #+TARGET value may name its tag first, like #+LINK." + (should (equal (devops--parse-target-keyword "server1 /ssh:host1:") + '("server1" . "/ssh:host1:"))) + (should (equal (devops--parse-target-keyword "local /srv/app/") + '("local" . "/srv/app/"))) + (should (equal (devops--parse-target-keyword "server <>") + '("server" . "<>"))) + (should (equal (devops--parse-target-keyword + "server <>") + '("server" . "<>"))) + ;; A relative directory in the old form is not read as a tag. + (should (equal (devops--parse-target-keyword "app (local)") + '("local" . "app"))) + (should (null (devops--parse-target-keyword "server")))) + +(ert-deftest devops-target-tag-alist-mixed-test () + "Both #+TARGET forms may appear in one buffer." + (devops-test--with-org + (concat "#+TARGET: /srv/one/ (t1)\n" + "#+TARGET: t2 /srv/two/\n\n" + "* Heading\n") + (should (equal (devops-target-tag-alist) + '(("t1" . "/srv/one/") ("t2" . "/srv/two/")))))) + (ert-deftest devops-target-ref-test () "`devops-target-ref' tells a reference from a literal target." (should (equal (devops-target-ref "<>") "server-target()")) diff --git a/devops.el b/devops.el index 25a56b7..bbb3016 100644 --- a/devops.el +++ b/devops.el @@ -27,8 +27,8 @@ ;; ;; `devops.el' adds TARGET syntax to org-babel: ;; -;; #+TARGET: /ssh:web1.example.com: (web) -;; #+TARGET: /ssh:db1.example.com: (db) +;; #+TARGET: web /ssh:web1.example.com: +;; #+TARGET: db /ssh:db1.example.com: ;; ;; * Check the web server :web: ;; #+begin_src sh @@ -138,15 +138,17 @@ reference, a dynamic target, tangling, a drift check. Meaningful in `org-babel-after-execute-hook'." (not devops--inhibit-async)) -;; Maybe it would make sense to follow the #+LINK syntax? -;; #+TARGET: server /ssh:example1.com: - (defun devops--parse-target-keyword (value) - "Parse a #+TARGET VALUE like \"target1 (source)\" into (TAG . TARGET). + "Parse a #+TARGET VALUE into (TAG . TARGET). +VALUE is \"TAG TARGET\", like org's #+LINK, or the older \"TARGET (TAG)\". TARGET is a directory, a TRAMP prefix, or a reference to a named block written in noweb's brackets, `<>' or `<>'." - (when (string-match "\\`\\(<<.*>>\\|[^ ]+\\) +(\\([^)]+\\))\\'" value) - (cons (match-string 2 value) (match-string 1 value)))) + (let ((target "\\(<<.*>>\\|[^ ]+\\)")) + (cond + ((string-match (concat "\\`" target " +(\\([^)]+\\))\\'") value) + (cons (match-string 2 value) (match-string 1 value))) + ((string-match (concat "\\`\\([[:alnum:]_@#%]+\\) +" target "\\'") value) + (cons (match-string 1 value) (match-string 2 value)))))) (defun devops--org-keywords (key) "Return all values for keyword KEY as a list." diff --git a/examples/1_commands.org b/examples/1_commands.org index 5464fed..bc219a3 100644 --- a/examples/1_commands.org +++ b/examples/1_commands.org @@ -1,13 +1,13 @@ #+PROPERTY: header-args:sh :results output -#+TARGET: /ssh:example1.com: (server1) -#+TARGET: /ssh:example2.com: (server2) +#+TARGET: server1 /ssh:example1.com: +#+TARGET: server2 /ssh:example2.com: #+TITLE: Running server commands =devops.el= helps you run commands on one or more named targets by declaring them at the the top of the file. #+begin_example -#+TARGET: /ssh:example1.com: (server1) -#+TARGET: /ssh:example2.com: (server2) +#+TARGET: server1 /ssh:example1.com: +#+TARGET: server2 /ssh:example2.com: #+end_example Normally, org-babel runs commands on servers whenever the =:dir= header argument diff --git a/examples/2_tangle.org b/examples/2_tangle.org index ee599c5..deecdce 100644 --- a/examples/2_tangle.org +++ b/examples/2_tangle.org @@ -1,5 +1,5 @@ -#+TARGET: /ssh:example1.com: (server1) -#+TARGET: /ssh:example2.com: (server2) +#+TARGET: server1 /ssh:example1.com: +#+TARGET: server2 /ssh:example2.com: #+TITLE: Example 2: Tangle files =devops.el= helps you create files in remote servers.