/ docker-compose.el
docker-compose.el
1 ;;; docker-compose.el --- Interface to docker-compose -*- lexical-binding: t -*- 2 3 ;; Author: Philippe Vaucher <philippe.vaucher@gmail.com> 4 5 ;; This file is NOT part of GNU Emacs. 6 7 ;; This program is free software; you can redistribute it and/or modify 8 ;; it under the terms of the GNU General Public License as published by 9 ;; the Free Software Foundation; either version 3, or (at your option) 10 ;; any later version. 11 ;; 12 ;; This program is distributed in the hope that it will be useful, 13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 ;; GNU General Public License for more details. 16 ;; 17 ;; You should have received a copy of the GNU General Public License 18 ;; along with GNU Emacs; see the file COPYING. If not, write to the 19 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 ;; Boston, MA 02110-1301, USA. 21 22 ;;; Commentary: 23 24 ;;; Code: 25 (eval-when-compile 26 (setq-local byte-compile-warnings '(not docstrings))) 27 28 (require 's) 29 (require 'aio) 30 (require 'dash) 31 (require 'transient) 32 33 (require 'docker-group) 34 (require 'docker-utils) 35 (require 'docker-process) 36 37 (defgroup docker-compose nil 38 "Docker compose customization group." 39 :group 'docker) 40 41 (defcustom docker-compose-command "docker compose" 42 "The `docker-compose' binary." 43 :group 'docker-compose 44 :type 'string) 45 46 (defun docker-compose-run-docker-compose-async (action &rest args) 47 "Execute \"`docker-compose-command' ACTION ARGS\" and return a promise with the results." 48 (apply #'docker-run-async docker-compose-command (docker-compose-arguments) action args)) 49 50 (defun docker-compose-run-docker-compose-async-with-buffer (action &rest args) 51 "Execute \"`docker-compose-command' ACTION ARGS\" and display output in a new buffer." 52 (apply #'docker-run-async-with-buffer-interactive docker-compose-command (docker-compose-arguments) action args)) 53 54 (aio-defun docker-compose-services () 55 "Return the list of services." 56 (s-split "\n" (aio-await (docker-compose-run-docker-compose-async "config" "--services" "2>/dev/null")) t)) 57 58 (aio-defun docker-compose-read-services-names () 59 "Read the services names." 60 (completing-read-multiple "Services: " (aio-await (docker-compose-services)))) 61 62 (aio-defun docker-compose-read-service-name () 63 "Read one service name." 64 (completing-read "Service: " (aio-await (docker-compose-services)))) 65 66 (defun docker-compose-read-project (prompt &rest _args) 67 "Read the `docker-compose' project forwarding PROMPT." 68 (completing-read 69 prompt 70 ;; in docker compose v2, we can obtain the list of 71 ;; projects with 'ls' argument 72 (if (string-match-p "\\bdocker\\s-+compose\\b" docker-compose-command) 73 (split-string 74 (shell-command-to-string 75 (concat docker-compose-command " ls" " --all" " -q")) 76 "\n" 77 t)))) 78 79 (defun docker-compose-read-log-level (prompt &rest _args) 80 "Read the `docker-compose' log level forwarding PROMPT." 81 (completing-read prompt '(DEBUG INFO WARNING ERROR CRITICAL))) 82 83 (defun docker-compose-read-directory (prompt &optional initial-input _history) 84 "Wrapper around `read-directory-name' forwarding PROMPT and INITIAL-INPUT." 85 (read-directory-name prompt nil nil t initial-input)) 86 87 (defun docker-compose-read-environment-file (prompt &optional initial-input _history) 88 "Wrapper around `read-file-name' forwarding PROMPT and INITIAL-INPUT." 89 (read-file-name prompt nil nil t initial-input)) 90 91 (defun docker-compose-read-compose-file (prompt &optional initial-input _history) 92 "Wrapper around `read-file-name' forwarding PROMPT and INITIAL-INPUT." 93 (read-file-name prompt nil nil t initial-input (apply-partially 'string-match ".*\\.yml\\|.*\\.yaml"))) 94 95 (aio-defun docker-compose-run-action-for-one-service (action args services) 96 "Run \"docker-compose ACTION ARGS SERVICES\"." 97 (interactive (list 98 (-last-item (s-split "-" (symbol-name transient-current-command))) 99 (transient-args transient-current-command) 100 nil)) 101 (setq services (aio-await (docker-compose-read-services-names))) 102 (docker-compose-run-docker-compose-async-with-buffer action args services)) 103 104 (defun docker-compose-run-action-for-all-services (action args) 105 "Run \"docker-compose ACTION ARGS\"." 106 (interactive (list 107 (-last-item (s-split "-" (symbol-name transient-current-command))) 108 (transient-args transient-current-command))) 109 (docker-compose-run-docker-compose-async-with-buffer action args)) 110 111 (aio-defun docker-compose-run-action-with-command (action args service command) 112 "Run \"docker-compose ACTION ARGS SERVICE COMMAND\"." 113 (interactive (list 114 (-last-item (s-split "-" (symbol-name transient-current-command))) 115 (transient-args transient-current-command) 116 nil 117 (read-string "Command: "))) 118 (setq service (aio-await (docker-compose-read-service-name))) 119 (docker-compose-run-docker-compose-async-with-buffer action args service command)) 120 121 (transient-define-prefix docker-compose-build () 122 "Transient for \"docker-compose build\"." 123 :man-page "docker-compose build" 124 ["Arguments" 125 ("b" "Build argument" "--build-arg " read-string) 126 ("c" "Compress build context" "--compress") 127 ("f" "Always remove intermediate containers" "--force-rm") 128 ("m" "Memory limit" "--memory " transient-read-number-N0) 129 ("n" "Do not use cache" "--no-cache") 130 ("p" "Attempt to pull a newer version of the image" "--pull") 131 ("r" "Build images in parallel" "--parallel")] 132 ["Actions" 133 ("B" "Build" docker-compose-run-action-for-one-service) 134 ("A" "All services" docker-compose-run-action-for-all-services)]) 135 136 (transient-define-prefix docker-compose-config () 137 "Transient for \"docker-compose config\"." 138 :man-page "docker-compose config" 139 ["Arguments" 140 141 ("r" "Pin image tags to digests" "--resolve-image-digests") 142 ("s" "Print the service names" "--services") 143 ("v" "Print the volume names" "--volumes")] 144 ["Actions" 145 ("V" "Config" docker-compose-run-action-for-all-services)]) 146 147 (transient-define-prefix docker-compose-create () 148 "Transient for \"docker-compose create\"." 149 :man-page "docker-compose create" 150 ["Arguments" 151 ("b" "Build" "--build") 152 ("f" "Force recreate" "--force-recreate") 153 ("n" "No recreate" "--no-recreate")] 154 ["Actions" 155 ("C" "Create" docker-compose-run-action-for-one-service) 156 ("A" "All services" docker-compose-run-action-for-all-services)]) 157 158 (transient-define-prefix docker-compose-down () 159 "Transient for \"docker-compose down\"." 160 :man-page "docker-compose down" 161 ["Arguments" 162 ("o" "Remove orphans" "--remove-orphans") 163 ("t" "Timeout" "--timeout " transient-read-number-N0) 164 ("v" "Remove volumes" "--volumes")] 165 ["Actions" 166 ("W" "Down" docker-compose-run-action-for-one-service) 167 ("A" "All services" docker-compose-run-action-for-all-services)]) 168 169 (transient-define-prefix docker-compose-exec () 170 "Transient for \"docker-compose exec\"." 171 :man-page "docker-compose exec" 172 ["Arguments" 173 ("P" "Privileged" "--privileged") 174 ("T" "Disable pseudo-tty" "-T") 175 ("d" "Detach" "-d") 176 ("e" "Env KEY=VAL" "-e " read-string) 177 ("u" "User " "--user " read-string) 178 ("w" "Workdir" "--workdir " read-string)] 179 ["Actions" 180 ("E" "Exec" docker-compose-run-action-with-command)]) 181 182 (transient-define-prefix docker-compose-logs () 183 "Transient for \"docker-compose logs\"." 184 :man-page "docker-compose logs" 185 ["Arguments" 186 ("T" "Tail" "--tail " read-string) 187 ("f" "Follow" "--follow") 188 ("n" "No color" "--no-color") 189 ("t" "Timestamps" "--timestamps")] 190 ["Actions" 191 ("L" "Logs" docker-compose-run-action-for-one-service) 192 ("A" "All services" docker-compose-run-action-for-all-services)]) 193 194 (transient-define-prefix docker-compose-pull () 195 "Transient for \"docker-compose pull\"." 196 :man-page "docker-compose pull" 197 ["Arguments" 198 ("d" "Include dependencies" "--include-deps") 199 ("i" "Ignore pull failures" "--ignore-pull-failures") 200 ("n" "No parallel" "--no-parallel")] 201 ["Actions" 202 ("F" "Pull" docker-compose-run-action-for-one-service) 203 ("A" "All services" docker-compose-run-action-for-all-services)]) 204 205 (transient-define-prefix docker-compose-push () 206 "Transient for \"docker-compose push\"." 207 :man-page "docker-compose push" 208 ["Arguments" 209 ("i" "Ignore push failures" "--ignore-push-failures")] 210 ["Actions" 211 ("P" "Push" docker-compose-run-action-for-one-service) 212 ("A" "All services" docker-compose-run-action-for-all-services)]) 213 214 (transient-define-prefix docker-compose-restart () 215 "Transient for \"docker-compose restart\"." 216 :man-page "docker-compose restart" 217 ["Arguments" 218 ("t" "Timeout" "--timeout " transient-read-number-N0)] 219 ["Actions" 220 ("T" "Restart" docker-compose-run-action-for-one-service) 221 ("A" "All services" docker-compose-run-action-for-all-services)]) 222 223 (transient-define-prefix docker-compose-rm () 224 "Transient for \"docker-compose rm\"." 225 :man-page "docker-compose rm" 226 ["Arguments" 227 ("f" "Force" "--force") 228 ("s" "Stop" "--stop") 229 ("v" "Remove anonymous volumes" "-v")] 230 ["Actions" 231 ("D" "Remove" docker-compose-run-action-for-one-service) 232 ("A" "All services" docker-compose-run-action-for-all-services)]) 233 234 (transient-define-prefix docker-compose-run () 235 "Transient for \"docker-compose run\"." 236 :man-page "docker-compose run" 237 :value '("--rm") 238 ["Arguments" 239 ("E" "Entrypoint" "--entrypoint " read-string) 240 ("N" "Name" "--name " read-string) 241 ("T" "Disable pseudo-tty" "-T") 242 ("d" "Detach" "-d") 243 ("e" "Env KEY=VAL" "-e " read-string) 244 ("l" "Label" "--label " read-string) 245 ("n" "No deps" "--no-deps") 246 ("r" "Remove container when it exits" "--rm") 247 ("s" "Enable services ports" "--service-ports") 248 ("u" "User " "--user " read-string) 249 ("w" "Workdir" "--workdir " read-string)] 250 ["Actions" 251 ("R" "Run" docker-compose-run-action-with-command)]) 252 253 (transient-define-prefix docker-compose-start () 254 "Transient for \"docker-compose start\"." 255 :man-page "docker-compose start" 256 ["Actions" 257 ("S" "Start" docker-compose-run-action-for-one-service) 258 ("A" "All services" docker-compose-run-action-for-all-services)]) 259 260 (transient-define-prefix docker-compose-stop () 261 "Transient for \"docker-compose stop\"." 262 :man-page "docker-compose stop" 263 ["Arguments" 264 ("t" "Timeout" "--timeout " transient-read-number-N0)] 265 ["Actions" 266 ("O" "Stop" docker-compose-run-action-for-one-service) 267 ("A" "All services" docker-compose-run-action-for-all-services)]) 268 269 (transient-define-prefix docker-compose-up () 270 "Transient for \"docker-compose up\"." 271 :man-page "docker-compose up" 272 ["Arguments" 273 ("b" "Build" "--build") 274 ("c" "Scale" "--scale " transient-read-number-N0) 275 ("d" "Detach" "-d") 276 ("f" "Force recreate" "--force-recreate") 277 ("n" "No deps" "--no-deps") 278 ("q" "Quiet pull" "--quiet-pull") 279 ("r" "Remove orphans" "--remove-orphans") 280 ("t" "Timeout" "--timeout " transient-read-number-N0)] 281 ["Actions" 282 ("U" "Up" docker-compose-run-action-for-one-service) 283 ("A" "All services" docker-compose-run-action-for-all-services)]) 284 285 (transient-define-prefix docker-compose-pause () 286 "Transient for \"docker-compose pause\"." 287 :man-page "docker-compose pause" 288 ["Actions" 289 ("Z" "Pause" docker-compose-run-action-for-one-service) 290 ("A" "All services" docker-compose-run-action-for-all-services)]) 291 292 (transient-define-prefix docker-compose-unpause () 293 "Transient for \"docker-compose unpause\"." 294 :man-page "docker-compose unpause" 295 ["Actions" 296 ("N" "Unpause" docker-compose-run-action-for-one-service) 297 ("A" "All services" docker-compose-run-action-for-all-services)]) 298 299 (docker-utils-define-transient-arguments docker-compose) 300 301 ;;;###autoload (autoload 'docker-compose "docker-compose" nil t) 302 (transient-define-prefix docker-compose () 303 "Transient for docker-compose." 304 :man-page "docker-compose" 305 ["Arguments" 306 ("a" "No ANSI" "--no-ansi") 307 ("c" "Compatibility" "--compatibility") 308 ("d" "Project directory" "--project-directory " docker-compose-read-directory) 309 ("e" "Environment file" "--env-file " docker-compose-read-environment-file) 310 ("f" "Compose file" "--file " docker-compose-read-compose-file) 311 ("h" "Host" "--host " read-string) 312 ("l" "Log level" "--log-level " docker-compose-read-log-level) 313 ("p" "Project name" "--project-name " docker-compose-read-project) 314 ("r" "Profile" "--profile " read-string) 315 ("v" "Verbose" "--verbose")] 316 [["Images" 317 ("B" "Build" docker-compose-build) 318 ("F" "Pull" docker-compose-pull) 319 ("P" "Push" docker-compose-push)] 320 ["Containers" 321 ("C" "Create" docker-compose-create) 322 ("D" "Remove" docker-compose-rm) 323 ("Z" "Pause" docker-compose-pause) 324 ("N" "Unpause" docker-compose-unpause) 325 ("U" "Up" docker-compose-up) 326 ("W" "Down" docker-compose-down)] 327 ["State" 328 ("O" "Stop" docker-compose-stop) 329 ("S" "Start" docker-compose-start) 330 ("T" "Restart" docker-compose-restart)] 331 ["Other" 332 ("E" "Exec" docker-compose-exec) 333 ("L" "Logs" docker-compose-logs) 334 ("R" "Run" docker-compose-run) 335 ("V" "Config" docker-compose-config)]]) 336 337 (provide 'docker-compose) 338 339 ;;; docker-compose.el ends here