config

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

dockerfile-mode.el (11099B)


      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-build-extra-options nil
     76   "Extra command-line options to send to docker build.
     77 
     78 Use this variable to add custom command-line switches not covered by
     79 existing dockerfile-build-* variables.
     80 
     81 Example:
     82 (setq-default dockerfile-build-extra-options \"--network host\")"
     83   :group 'dockerfile
     84   :type 'string)
     85 
     86 (defcustom dockerfile-use-buildkit nil
     87   "Use Docker buildkit for building images?
     88 
     89 This is the new buildsystem for docker, and in time it will replace the old one
     90 but for now it has to be explicitly enabled to work.
     91 It is supported from docker 18.09"
     92   :type 'boolean)
     93 
     94 (defcustom dockerfile-enable-auto-indent t
     95   "Toggles the auto indentation functionality."
     96   :type 'boolean)
     97 
     98 (defcustom dockerfile-indent-offset (or standard-indent 2)
     99   "Dockerfile number of columns for margin-changing functions to indent."
    100   :type 'integer
    101   :safe #'integerp
    102   :group 'dockerfile)
    103 
    104 (defface dockerfile-image-name
    105   '((t (:inherit (font-lock-type-face bold))))
    106   "Face to highlight the base image name after FROM instruction.")
    107 
    108 (defface dockerfile-image-alias
    109   '((t (:inherit (font-lock-constant-face bold))))
    110   "Face to highlight the base image alias inf FROM ... AS <alias> construct.")
    111 
    112 (defconst dockerfile--from-regex
    113   (rx "from " (group (+? nonl)) (or " " eol) (? "as " (group (1+ nonl)))))
    114 
    115 (defvar dockerfile-font-lock-keywords
    116   `(,(cons (rx (or line-start "onbuild ")
    117                (group (or "from" "maintainer" "run" "cmd" "expose" "env" "arg"
    118                           "add" "copy" "entrypoint" "volume" "user" "workdir" "onbuild"
    119                           "label" "stopsignal" "shell" "healthcheck"))
    120                word-boundary)
    121            font-lock-keyword-face)
    122     (,dockerfile--from-regex
    123      (1 'dockerfile-image-name)
    124      (2 'dockerfile-image-alias nil t))
    125     ,@(sh-font-lock-keywords)
    126     ,@(sh-font-lock-keywords-2)
    127     ,@(sh-font-lock-keywords-1))
    128   "Default `font-lock-keywords' for `dockerfile mode'.")
    129 
    130 (defvar dockerfile-mode-map
    131   (let ((map (make-sparse-keymap))
    132         (menu-map (make-sparse-keymap)))
    133     (define-key map "\C-c\C-b" #'dockerfile-build-buffer)
    134     (define-key map "\C-c\M-b" #'dockerfile-build-no-cache-buffer)
    135     (define-key map "\C-c\C-c" #'comment-region)
    136     (define-key map [menu-bar dockerfile-mode] (cons "Dockerfile" menu-map))
    137     (define-key menu-map [dfc]
    138       '(menu-item "Comment Region" comment-region
    139                   :help "Comment Region"))
    140     (define-key menu-map [dfb]
    141       '(menu-item "Build" dockerfile-build-buffer
    142                   :help "Send the Dockerfile to docker build"))
    143     (define-key menu-map [dfb]
    144       '(menu-item "Build without cache" dockerfile-build-no-cache-buffer
    145                   :help "Send the Dockerfile to docker build without cache"))
    146     map))
    147 
    148 (defvar dockerfile-mode-syntax-table
    149   (let ((table (make-syntax-table)))
    150     (modify-syntax-entry ?# "<" table)
    151     (modify-syntax-entry ?\n ">" table)
    152     (modify-syntax-entry ?' "\"" table)
    153     (modify-syntax-entry ?= "." table)
    154     table)
    155   "Syntax table for `dockerfile-mode'.")
    156 
    157 (define-abbrev-table 'dockerfile-mode-abbrev-table nil
    158   "Abbrev table used while in `dockerfile-mode'.")
    159 
    160 (unless dockerfile-mode-abbrev-table
    161   (define-abbrev-table 'dockerfile-mode-abbrev-table ()))
    162 
    163 (defun dockerfile-indent-line-function ()
    164   "Indent lines in a Dockerfile.
    165 
    166 Lines beginning with a keyword are ignored, and any others are
    167 indented by one `dockerfile-indent-offset'. Functionality toggled
    168 by `dockerfile-enable-auto-indent'."
    169   (when dockerfile-enable-auto-indent
    170     (unless (member (get-text-property (line-beginning-position) 'face)
    171              '(font-lock-comment-delimiter-face font-lock-keyword-face))
    172      (save-excursion
    173        (beginning-of-line)
    174        (unless (looking-at-p "\\s-*$") ; Ignore empty lines.
    175          (indent-line-to dockerfile-indent-offset))))))
    176 
    177 (defun dockerfile-build-arg-string ()
    178   "Create a --build-arg string for each element in `dockerfile-build-args'."
    179   (mapconcat (lambda (arg) (concat "--build-arg="  (replace-regexp-in-string "\\\\=" "=" (shell-quote-argument arg))))
    180              dockerfile-build-args " "))
    181 
    182 (defun dockerfile-standard-filename (file)
    183   "Convert the FILE name to OS standard.
    184 If in Cygwin environment, uses Cygwin specific function to convert the
    185 file name.  Otherwise, uses Emacs' standard conversion function."
    186   (if (fboundp 'cygwin-convert-file-name-to-windows)
    187       (replace-regexp-in-string
    188        (rx "\\") "\\\\" (cygwin-convert-file-name-to-windows file) t t)
    189     (convert-standard-filename file)))
    190 
    191 (defun dockerfile-tag-string (image-name)
    192   "Return a --tag shell-quoted IMAGE-NAME string.
    193 
    194 Returns an empty string if IMAGE-NAME is blank."
    195     (if (string= image-name "") "" (format "--tag %s " (shell-quote-argument image-name))))
    196 
    197 (define-obsolete-variable-alias 'docker-image-name 'dockerfile-image-name "2017-10-22")
    198 
    199 (defvar dockerfile-image-name nil
    200   "Name of the dockerfile currently being used.
    201 This can be set in file or directory-local variables.")
    202 
    203 (defvar dockerfile-image-name-history nil
    204   "History of image names read by `dockerfile-read-image-name'.")
    205 
    206 (defun dockerfile-read-image-name ()
    207   "Read a docker image name."
    208   (read-string "Image name: " dockerfile-image-name 'dockerfile-image-name-history))
    209 
    210 
    211 ;;;###autoload
    212 (defun dockerfile-build-buffer (image-name &optional no-cache)
    213   "Build an image called IMAGE-NAME based upon the buffer.
    214 
    215 If the prefix arg NO-CACHE is set, don't cache the image.
    216 
    217 The shell command used to build the image is:
    218 
    219     sudo docker build    \\
    220       --no-cache         \\
    221       --force-rm         \\
    222       --pull             \\
    223       --tag IMAGE-NAME   \\
    224       --build-args args  \\
    225       --progress type    \\
    226       -f filename        \\
    227       directory"
    228 
    229   (interactive (list (dockerfile-read-image-name) prefix-arg))
    230   (save-buffer)
    231     (compilation-start
    232         (format
    233             "%s%s%s build %s %s %s %s %s --progress %s %s -f %s %s"
    234             (if dockerfile-use-buildkit "DOCKER_BUILDKIT=1 " "")
    235             (if dockerfile-use-sudo "sudo " "")
    236             dockerfile-mode-command
    237             (if no-cache "--no-cache" "")
    238             (if dockerfile-build-force-rm "--force-rm " "")
    239             (if dockerfile-build-pull "--pull " "")
    240             (dockerfile-tag-string image-name)
    241             (dockerfile-build-arg-string)
    242             dockerfile-build-progress
    243             (or dockerfile-build-extra-options "")
    244             (shell-quote-argument (dockerfile-standard-filename
    245 				   (or (file-remote-p (buffer-file-name) 'localname)
    246 				       (buffer-file-name))))
    247             (shell-quote-argument (dockerfile-standard-filename
    248 				   (or (file-remote-p default-directory 'localname)
    249 				       default-directory))))
    250     nil
    251     (lambda (_) (format "*docker-build-output: %s *" image-name))))
    252 
    253 ;;;###autoload
    254 (defun dockerfile-build-no-cache-buffer (image-name)
    255   "Build an image called IMAGE-NAME based upon the buffer without cache."
    256   (interactive (list (dockerfile-read-image-name)))
    257   (dockerfile-build-buffer image-name t))
    258 
    259 (defun dockerfile--imenu-function ()
    260   "Find the previous headline from point.
    261 
    262 Search for a FROM instruction.  If an alias is used this is
    263 returned, otherwise the base image name is used."
    264   (when (re-search-backward dockerfile--from-regex nil t)
    265     (let ((data (match-data)))
    266       (when (match-string 2)
    267         ;; we drop the first match group because
    268         ;; imenu-generic-expression can only use one offset, so we
    269         ;; normalize to `1'.
    270         (set-match-data (list (nth 0 data) (nth 1 data) (nth 4 data) (nth 5 data))))
    271       t)))
    272 
    273 ;;;###autoload
    274 (define-derived-mode dockerfile-mode prog-mode "Dockerfile"
    275   "A major mode to edit Dockerfiles.
    276 \\{dockerfile-mode-map}"
    277   (set-syntax-table dockerfile-mode-syntax-table)
    278   (set (make-local-variable 'imenu-generic-expression)
    279        `(("Stage" dockerfile--imenu-function 1)))
    280   (set (make-local-variable 'require-final-newline) mode-require-final-newline)
    281   (set (make-local-variable 'comment-start) "#")
    282   (set (make-local-variable 'comment-end) "")
    283   (set (make-local-variable 'comment-start-skip) "#+ *")
    284   (set (make-local-variable 'parse-sexp-ignore-comments) t)
    285   (set (make-local-variable 'font-lock-defaults)
    286        '(dockerfile-font-lock-keywords nil t))
    287   (setq local-abbrev-table dockerfile-mode-abbrev-table)
    288   (set (make-local-variable 'indent-line-function) #'dockerfile-indent-line-function))
    289 
    290 ;;;###autoload
    291 (add-to-list 'auto-mode-alist
    292              (cons (concat "[/\\]"
    293                            "\\(?:Containerfile\\|Dockerfile\\)"
    294                            "\\(?:\\.[^/\\]*\\)?\\'")
    295                    'dockerfile-mode))
    296 
    297 ;;;###autoload
    298 (add-to-list 'auto-mode-alist '("\\.dockerfile\\'" . dockerfile-mode))
    299 
    300 (provide 'dockerfile-mode)
    301 
    302 ;;; dockerfile-mode.el ends here