config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

dockerfile-mode.el (10720B)


      1 ;;; dockerfile-mode.el --- Major mode for editing Docker's Dockerfiles -*- lexical-binding: t -*-
      2 
      3 ;; Copyright (c) 2013 Spotify AB
      4 ;; Package-Requires: ((emacs "24"))
      5 ;; Homepage: https://github.com/spotify/dockerfile-mode
      6 ;; URL: https://github.com/spotify/dockerfile-mode
      7 ;; Version: 1.7
      8 ;; Keywords: docker languages processes tools
      9 ;;
     10 ;; Licensed under the Apache License, Version 2.0 (the "License"); you may not
     11 ;; use this file except in compliance with the License. You may obtain a copy of
     12 ;; the License at
     13 ;;
     14 ;; http://www.apache.org/licenses/LICENSE-2.0
     15 ;;
     16 ;; Unless required by applicable law or agreed to in writing, software
     17 ;; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     18 ;; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     19 ;; License for the specific language governing permissions and limitations under
     20 ;; the License.
     21 
     22 ;;; Commentary:
     23 
     24 ;; Provides a major mode `dockerfile-mode' for use with the standard
     25 ;; `Dockerfile' file format.  Additional convenience functions allow
     26 ;; images to be built easily.
     27 
     28 ;;; Code:
     29 
     30 (require 'sh-script)
     31 (require 'rx)
     32 
     33 
     34 (declare-function cygwin-convert-file-name-to-windows "cygw32.c" (file &optional absolute-p))
     35 
     36 (defgroup dockerfile nil
     37   "Dockerfile editing commands for Emacs."
     38   :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
     39   :prefix "dockerfile-"
     40   :group 'languages)
     41 
     42 (defcustom dockerfile-mode-command "docker"
     43   "Which binary to use to build images."
     44   :group 'dockerfile
     45   :type 'string)
     46 
     47 (defcustom dockerfile-use-sudo nil
     48   "Runs docker builder command with sudo."
     49   :type 'boolean
     50   :group 'dockerfile)
     51 
     52 (defcustom dockerfile-build-force-rm nil
     53   "Runs docker builder command with --force-rm switch."
     54   :type 'boolean
     55   :group 'dockerfile)
     56 
     57 (defcustom dockerfile-build-pull nil
     58   "Runs docker builder command with --pull switch."
     59   :type 'boolean
     60   :group 'dockerfile)
     61 
     62 (defcustom dockerfile-build-args nil
     63   "List of --build-arg to pass to docker build.
     64 
     65 Each element of the list will be passed as a separate
     66  --build-arg to the docker build command."
     67   :type '(repeat string)
     68   :group 'dockerfile)
     69 
     70 (defcustom dockerfile-build-progress "auto"
     71   "Type of --progress output (auto, plain, tty) of docker build."
     72   :group 'dockerfile
     73   :type 'string)
     74 
     75 (defcustom dockerfile-use-buildkit nil
     76   "Use Docker buildkit for building images?
     77 
     78 This is the new buildsystem for docker, and in time it will replace the old one
     79 but for now it has to be explicitly enabled to work.
     80 It is supported from docker 18.09"
     81   :type 'boolean)
     82 
     83 (defcustom dockerfile-enable-auto-indent t
     84   "Toggles the auto indentation functionality."
     85   :type 'boolean)
     86 
     87 (defcustom dockerfile-indent-offset (or standard-indent 2)
     88   "Dockerfile number of columns for margin-changing functions to indent."
     89   :type 'integer
     90   :safe #'integerp
     91   :group 'dockerfile)
     92 
     93 (defface dockerfile-image-name
     94   '((t (:inherit (font-lock-type-face bold))))
     95   "Face to highlight the base image name after FROM instruction.")
     96 
     97 (defface dockerfile-image-alias
     98   '((t (:inherit (font-lock-constant-face bold))))
     99   "Face to highlight the base image alias inf FROM ... AS <alias> construct.")
    100 
    101 (defconst dockerfile--from-regex
    102   (rx "from " (group (+? nonl)) (or " " eol) (? "as " (group (1+ nonl)))))
    103 
    104 (defvar dockerfile-font-lock-keywords
    105   `(,(cons (rx (or line-start "onbuild ")
    106                (group (or "from" "maintainer" "run" "cmd" "expose" "env" "arg"
    107                           "add" "copy" "entrypoint" "volume" "user" "workdir" "onbuild"
    108                           "label" "stopsignal" "shell" "healthcheck"))
    109                word-boundary)
    110            font-lock-keyword-face)
    111     (,dockerfile--from-regex
    112      (1 'dockerfile-image-name)
    113      (2 'dockerfile-image-alias nil t))
    114     ,@(sh-font-lock-keywords)
    115     ,@(sh-font-lock-keywords-2)
    116     ,@(sh-font-lock-keywords-1))
    117   "Default `font-lock-keywords' for `dockerfile mode'.")
    118 
    119 (defvar dockerfile-mode-map
    120   (let ((map (make-sparse-keymap))
    121         (menu-map (make-sparse-keymap)))
    122     (define-key map "\C-c\C-b" #'dockerfile-build-buffer)
    123     (define-key map "\C-c\M-b" #'dockerfile-build-no-cache-buffer)
    124     (define-key map "\C-c\C-c" #'comment-region)
    125     (define-key map [menu-bar dockerfile-mode] (cons "Dockerfile" menu-map))
    126     (define-key menu-map [dfc]
    127       '(menu-item "Comment Region" comment-region
    128                   :help "Comment Region"))
    129     (define-key menu-map [dfb]
    130       '(menu-item "Build" dockerfile-build-buffer
    131                   :help "Send the Dockerfile to docker build"))
    132     (define-key menu-map [dfb]
    133       '(menu-item "Build without cache" dockerfile-build-no-cache-buffer
    134                   :help "Send the Dockerfile to docker build without cache"))
    135     map))
    136 
    137 (defvar dockerfile-mode-syntax-table
    138   (let ((table (make-syntax-table)))
    139     (modify-syntax-entry ?# "<" table)
    140     (modify-syntax-entry ?\n ">" table)
    141     (modify-syntax-entry ?' "\"" table)
    142     (modify-syntax-entry ?= "." table)
    143     table)
    144   "Syntax table for `dockerfile-mode'.")
    145 
    146 (define-abbrev-table 'dockerfile-mode-abbrev-table nil
    147   "Abbrev table used while in `dockerfile-mode'.")
    148 
    149 (unless dockerfile-mode-abbrev-table
    150   (define-abbrev-table 'dockerfile-mode-abbrev-table ()))
    151 
    152 (defun dockerfile-indent-line-function ()
    153   "Indent lines in a Dockerfile.
    154 
    155 Lines beginning with a keyword are ignored, and any others are
    156 indented by one `dockerfile-indent-offset'. Functionality toggled
    157 by `dockerfile-enable-auto-indent'."
    158   (when dockerfile-enable-auto-indent
    159     (unless (member (get-text-property (line-beginning-position) 'face)
    160              '(font-lock-comment-delimiter-face font-lock-keyword-face))
    161      (save-excursion
    162        (beginning-of-line)
    163        (unless (looking-at-p "\\s-*$") ; Ignore empty lines.
    164          (indent-line-to dockerfile-indent-offset))))))
    165 
    166 (defun dockerfile-build-arg-string ()
    167   "Create a --build-arg string for each element in `dockerfile-build-args'."
    168   (mapconcat (lambda (arg) (concat "--build-arg="  (replace-regexp-in-string "\\\\=" "=" (shell-quote-argument arg))))
    169              dockerfile-build-args " "))
    170 
    171 (defun dockerfile-standard-filename (file)
    172   "Convert the FILE name to OS standard.
    173 If in Cygwin environment, uses Cygwin specific function to convert the
    174 file name.  Otherwise, uses Emacs' standard conversion function."
    175   (if (fboundp 'cygwin-convert-file-name-to-windows)
    176       (replace-regexp-in-string
    177        (rx "\\") "\\\\" (cygwin-convert-file-name-to-windows file) t t)
    178     (convert-standard-filename file)))
    179 
    180 (defun dockerfile-tag-string (image-name)
    181   "Return a --tag shell-quoted IMAGE-NAME string.
    182 
    183 Returns an empty string if IMAGE-NAME is blank."
    184     (if (string= image-name "") "" (format "--tag %s " (shell-quote-argument image-name))))
    185 
    186 (define-obsolete-variable-alias 'docker-image-name 'dockerfile-image-name "2017-10-22")
    187 
    188 (defvar dockerfile-image-name nil
    189   "Name of the dockerfile currently being used.
    190 This can be set in file or directory-local variables.")
    191 
    192 (defvar dockerfile-image-name-history nil
    193   "History of image names read by `dockerfile-read-image-name'.")
    194 
    195 (defun dockerfile-read-image-name ()
    196   "Read a docker image name."
    197   (read-string "Image name: " dockerfile-image-name 'dockerfile-image-name-history))
    198 
    199 
    200 ;;;###autoload
    201 (defun dockerfile-build-buffer (image-name &optional no-cache)
    202   "Build an image called IMAGE-NAME based upon the buffer.
    203 
    204 If the prefix arg NO-CACHE is set, don't cache the image.
    205 
    206 The shell command used to build the image is:
    207 
    208     sudo docker build    \\
    209       --no-cache         \\
    210       --force-rm         \\
    211       --pull             \\
    212       --tag IMAGE-NAME   \\
    213       --build-args args  \\
    214       --progress type    \\
    215       -f filename        \\
    216       directory"
    217 
    218   (interactive (list (dockerfile-read-image-name) prefix-arg))
    219   (save-buffer)
    220     (compilation-start
    221         (format
    222             "%s%s%s build %s %s %s %s %s --progress %s -f %s %s"
    223             (if dockerfile-use-buildkit "DOCKER_BUILDKIT=1 " "")
    224             (if dockerfile-use-sudo "sudo " "")
    225             dockerfile-mode-command
    226             (if no-cache "--no-cache" "")
    227             (if dockerfile-build-force-rm "--force-rm " "")
    228             (if dockerfile-build-pull "--pull " "")
    229             (dockerfile-tag-string image-name)
    230             (dockerfile-build-arg-string)
    231             dockerfile-build-progress
    232             (shell-quote-argument (dockerfile-standard-filename
    233 				   (or (file-remote-p (buffer-file-name) 'localname)
    234 				       (buffer-file-name))))
    235             (shell-quote-argument (dockerfile-standard-filename
    236 				   (or (file-remote-p default-directory 'localname)
    237 				       default-directory))))
    238     nil
    239     (lambda (_) (format "*docker-build-output: %s *" image-name))))
    240 
    241 ;;;###autoload
    242 (defun dockerfile-build-no-cache-buffer (image-name)
    243   "Build an image called IMAGE-NAME based upon the buffer without cache."
    244   (interactive (list (dockerfile-read-image-name)))
    245   (dockerfile-build-buffer image-name t))
    246 
    247 (defun dockerfile--imenu-function ()
    248   "Find the previous headline from point.
    249 
    250 Search for a FROM instruction.  If an alias is used this is
    251 returned, otherwise the base image name is used."
    252   (when (re-search-backward dockerfile--from-regex nil t)
    253     (let ((data (match-data)))
    254       (when (match-string 2)
    255         ;; we drop the first match group because
    256         ;; imenu-generic-expression can only use one offset, so we
    257         ;; normalize to `1'.
    258         (set-match-data (list (nth 0 data) (nth 1 data) (nth 4 data) (nth 5 data))))
    259       t)))
    260 
    261 ;;;###autoload
    262 (define-derived-mode dockerfile-mode prog-mode "Dockerfile"
    263   "A major mode to edit Dockerfiles.
    264 \\{dockerfile-mode-map}"
    265   (set-syntax-table dockerfile-mode-syntax-table)
    266   (set (make-local-variable 'imenu-generic-expression)
    267        `(("Stage" dockerfile--imenu-function 1)))
    268   (set (make-local-variable 'require-final-newline) mode-require-final-newline)
    269   (set (make-local-variable 'comment-start) "#")
    270   (set (make-local-variable 'comment-end) "")
    271   (set (make-local-variable 'comment-start-skip) "#+ *")
    272   (set (make-local-variable 'parse-sexp-ignore-comments) t)
    273   (set (make-local-variable 'font-lock-defaults)
    274        '(dockerfile-font-lock-keywords nil t))
    275   (setq local-abbrev-table dockerfile-mode-abbrev-table)
    276   (set (make-local-variable 'indent-line-function) #'dockerfile-indent-line-function))
    277 
    278 ;;;###autoload
    279 (add-to-list 'auto-mode-alist
    280              (cons (concat "[/\\]"
    281                            "\\(?:Containerfile\\|Dockerfile\\)"
    282                            "\\(?:\\.[^/\\]*\\)?\\'")
    283                    'dockerfile-mode))
    284 
    285 ;;;###autoload
    286 (add-to-list 'auto-mode-alist '("\\.dockerfile\\'" . dockerfile-mode))
    287 
    288 (provide 'dockerfile-mode)
    289 
    290 ;;; dockerfile-mode.el ends here