├── LICENSE ├── README.md ├── TODO.org └── chef-mode.el /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Maciej Pasternacki 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the copyright holder nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL MACIEJ PASTERNACKI BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Chef-mode is an Emacs minor mode to make editing 2 | [Opscode Chef](http://www.opscode.com/chef/) 3 | [repositories](http://wiki.opscode.com/display/chef/Chef+Repository) 4 | easier. 5 | 6 | It defines a minor mode, chef-mode, and a corresponding 7 | global-chef-mode, with two keybindings: 8 | 9 | * C-c C-c (M-x chef-knife-dwim) - when editing part of chef repository 10 | (cookbook, data bag item, node/role/environment definition), uploads 11 | that part to the Chef Server by calling appropriate knife command 12 | * C-c C-k (M-x knife) - runs a user-specified knife command 13 | 14 | The library detects bundler and, if Gemfile is present on top-level of 15 | the Chef repository, runs 'bundle exec knife' instead of plain 16 | 'knife'. 17 | 18 | If `chef-use-rvm` is non-nil, it talks with 19 | [rvm.el](https://github.com/senny/rvm.el) to use proper Ruby and 20 | gemset. 21 | -------------------------------------------------------------------------------- /TODO.org: -------------------------------------------------------------------------------- 1 | 2 | * Road map 3 | 4 | ** DONE Detect Chef repository root 5 | CLOSED: [2011-08-29 Mon 00:14] 6 | *** DONE Detect Bundler Gemfile in Chef repository root 7 | CLOSED: [2011-08-29 Mon 00:14] 8 | 9 | ** DONE C-c C-c -- knife-dwim -- DWIM send-to-server 10 | CLOSED: [2011-08-29 Mon 00:15] 11 | - [X] inside a cookbook - knife cookbook upload 12 | - [X] inside a role - knife role from file 13 | - [X] inside a data bag file - data bag from file 14 | - [X] flexible enough to allow data bag from yaml 15 | 16 | ** DONE Make C-c C-c not conflict with other modes when not in chef-repo 17 | CLOSED: [2011-08-29 Mon 00:38] 18 | ** TODO C-c C-f -- find files inside project 19 | 20 | *** C-c C-f c -- ask for cookbook, then: 21 | **** r - recipe 22 | **** f - file 23 | **** t - template 24 | **** m - metadata 25 | **** d - definition file 26 | **** a - attributes file 27 | **** l - library file 28 | **** s - resource 29 | **** p - provider 30 | **** e - readme 31 | *** C-c C-f r -- role 32 | *** C-c C-f d -- data bag item 33 | *** C-c C-f f - config file 34 | *** C-c C-f e - environment 35 | 36 | ** DONE M-x knife (C-c C-k) to run arbitrary knife command 37 | CLOSED: [2011-08-29 Mon 00:15] 38 | ** TODO ? M-x rake (C-c C-r) 39 | -------------------------------------------------------------------------------- /chef-mode.el: -------------------------------------------------------------------------------- 1 | ;;; chef-mode.el --- minor mode for editing an opscode chef repository 2 | 3 | ;; Copyright (C) 2011 Maciej Pasternacki 4 | 5 | ;; Author: Maciej Pasternacki 6 | ;; Created: 28 Aug 2011 7 | ;; Version: 0.1 8 | ;; Keywords: chef knife 9 | 10 | ;; This file is NOT part of GNU Emacs. 11 | 12 | ;;; License: 13 | 14 | ;; Copyright (c) 2011, Maciej Pasternacki 15 | ;; All rights reserved. 16 | 17 | ;; Redistribution and use in source and binary forms, with or without 18 | ;; modification, are permitted provided that the following conditions are met: 19 | ;; * Redistributions of source code must retain the above copyright 20 | ;; notice, this list of conditions and the following disclaimer. 21 | ;; * Redistributions in binary form must reproduce the above copyright 22 | ;; notice, this list of conditions and the following disclaimer in the 23 | ;; documentation and/or other materials provided with the distribution. 24 | ;; * Neither the name of the copyright holder nor the 25 | ;; names of its contributors may be used to endorse or promote products 26 | ;; derived from this software without specific prior written permission. 27 | 28 | ;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 29 | ;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 30 | ;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 31 | ;; DISCLAIMED. IN NO EVENT SHALL MACIEJ PASTERNACKI BE LIABLE FOR ANY 32 | ;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 33 | ;; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 34 | ;; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 35 | ;; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 36 | ;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 37 | ;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | 39 | ;;; Commentary: 40 | 41 | ;; This library defines a minor mode to work with Opscode Chef 42 | ;; (http://www.opscode.com/chef/) repository. It creates two 43 | ;; keybindings: 44 | 45 | ;; - C-c C-c (M-x chef-knife-dwim) - when editing part of chef 46 | ;; repository (cookbook, data bag item, node/role/environment 47 | ;; definition), uploads that part to the Chef Server by calling 48 | ;; appropriate knife command 49 | ;; - C-c C-k (M-x knife) - runs a user-specified knife command 50 | 51 | ;; The library detects bundler and, if Gemfile is present on top-level 52 | ;; of the Chef repository, runs 'bundle exec knife' instead of plain 53 | ;; 'knife'. 54 | 55 | ;; If chef-use-rvm is non-nil, it talks with rvm.el 56 | ;; (https://github.com/senny/rvm.el) to use proper Ruby and gemset. 57 | 58 | ;;; Code: 59 | 60 | 61 | (defvar chef-knife-command "knife" 62 | "Knife command to run") 63 | 64 | (defvar chef-use-bundler t 65 | "Use `bundle exec knife' if Gemfile exists") 66 | 67 | (defvar chef-use-rvm t 68 | "If non-nil, require rvm.el and call rvm-activate-corresponding-ruby on chef repo root before calling knife") 69 | 70 | (defvar chef-mode-map (make-sparse-keymap) 71 | "Key map for chef-mode") 72 | 73 | (define-key chef-mode-map (kbd "\C-c \C-k") 'knife) 74 | (define-key chef-mode-map (kbd "\C-c \C-c") 'chef-knife-dwim) 75 | 76 | (define-minor-mode chef-mode 77 | "Mode for interacting with Opscode Chef" 78 | nil chef-mode-map) 79 | 80 | (defun turn-on-chef-mode () 81 | "Enable chef-mode." 82 | (chef-mode 1)) 83 | 84 | (define-globalized-minor-mode global-chef-mode 85 | chef-mode turn-on-chef-mode) 86 | 87 | (defun find-chef-root (&optional path) 88 | (when (null path) 89 | (setq path (or buffer-file-name 90 | default-directory))) 91 | (cond 92 | ((not (file-directory-p path)) 93 | (find-chef-root (concat (file-name-as-directory path) ".."))) 94 | ((equal (expand-file-name path) (expand-file-name "~")) nil) 95 | ((equal (expand-file-name path) "/") nil) 96 | ((let ((ff (directory-files path))) 97 | (or (member ".chef" ff) 98 | (and (member "cookbooks" ff) 99 | (member "roles" ff) 100 | (member "config" ff)))) 101 | (file-name-as-directory (expand-file-name path))) 102 | (t (find-chef-root (concat (file-name-as-directory path) ".."))))) 103 | 104 | (defun chef/fallback (trigger) 105 | (let* ((chef-mode nil) 106 | (command (key-binding trigger))) 107 | (when (commandp command) 108 | (call-interactively command)))) 109 | 110 | (defmacro chef/with-root-or-fallback (trigger &rest body) 111 | `(let ((chef-root (find-chef-root))) 112 | (if (null chef-root) 113 | (chef/fallback ,trigger) 114 | ,@body))) 115 | 116 | (defun chef-run-knife (command &rest args) 117 | (when chef-use-rvm 118 | (rvm-activate-corresponding-ruby)) 119 | 120 | (let ((knife-buffer (get-buffer-create "*knife*")) 121 | (use-bundler (and chef-use-bundler (file-exists-p "Gemfile")))) 122 | (with-current-buffer knife-buffer 123 | (setq default-directory chef-root) 124 | (setq list-buffers-directory default-directory) 125 | (special-mode) 126 | (toggle-read-only 0) 127 | (erase-buffer) 128 | (insert (concat "# " default-directory "\n" 129 | (when use-bundler 130 | "bundle exec ") 131 | chef-knife-command " " command " " 132 | (mapconcat 'identity args " ") 133 | "\n\n"))) 134 | (if use-bundler 135 | (apply 'call-process 136 | "bundle" nil knife-buffer 137 | "bundle" "exec" chef-knife-command (cons command args)) 138 | (apply 'call-process 139 | chef-knife-command nil knife-buffer 140 | chef-knife-command (cons command args))) 141 | (with-current-buffer knife-buffer 142 | (toggle-read-only 1)) 143 | (switch-to-buffer-other-window knife-buffer t) 144 | (fit-window-to-buffer))) 145 | 146 | (defun knife (command) 147 | "Run knife" 148 | (interactive "Command: knife ") 149 | (chef/with-root-or-fallback 150 | (kbd "\C-c \C-k") 151 | (apply 'chef-run-knife (split-string-and-unquote command)))) 152 | 153 | (defun chef-knife-dwim () 154 | "Upload currently edited thing to the Chef server. 155 | 156 | Guesses whether you have " 157 | (interactive) 158 | (chef/with-root-or-fallback 159 | (kbd "\C-c \C-c") 160 | (let ((b (current-buffer))) 161 | (save-some-buffers nil (lambda () 162 | (eq b (current-buffer))))) 163 | (if buffer-file-name 164 | (let ((default-directory chef-root) 165 | (rpath (file-relative-name buffer-file-name chef-root))) 166 | (cond 167 | ((string-match "^\\(?:site-\\)?cookbooks/\\([^/]+\\)/" rpath) 168 | (print (match-string 1 rpath)) 169 | (chef-run-knife "cookbook" "upload" (match-string 1 rpath))) 170 | ((string-match "^\\(role\\|node\\|environment\\)s/\\(.*\\)" rpath) 171 | (chef-run-knife (match-string 1 rpath) "from" "file" (match-string 2 rpath))) 172 | ((string-match "^data.bags/\\([^/]+\\)/\\(.*\\.yaml\\)" rpath) 173 | (chef-run-knife "data" "bag" "from" "yaml" (match-string 1 rpath) (match-string 2 rpath))) 174 | ((string-match "^data.bags/\\([^/]+\\)/\\(.*\\)" rpath) 175 | (chef-run-knife "data" "bag" "from" "file" (match-string 1 rpath) (match-string 2 rpath))) 176 | (t (chef/fallback (kbd "\C-c \C-c"))))) 177 | (chef/fallback (kbd "\C-c \C-c"))))) 178 | 179 | 180 | 181 | (defun chef-resource-lookup () 182 | "Open the documentation in a browser for the chef resource at point" 183 | (interactive) 184 | (let* ((base "http://wiki.opscode.com/display/chef/Resources") 185 | (anchor "#Resources-") 186 | (tbl '( 187 | ("cookbook_file" . "CookbookFile") 188 | ("cron" . "Cron") 189 | ("deploy" . "Deploy") 190 | ("directory" . "Directory") 191 | ("env" . "Env") 192 | ("erl_call" . "ErlangCall") 193 | ("execute" . "Execute") 194 | ("file" . "File") 195 | ("git" . "Git") 196 | ("group" . "Group") 197 | ("http_request" . "HTTPRequest") 198 | ("ifconfig" . "Ifconfig") 199 | ("link" . "Link") 200 | ("log" . "Log") 201 | ("mdadm" . "Mdadm") 202 | ("mount" . "Mount") 203 | ("ohai" . "Ohai") 204 | ("package" . "Package") 205 | ("apt_package" . "Package") 206 | ("dpkg_package" . "Package") 207 | ("easy_install_package" . "Package") 208 | ("freebsd_package" . "Package") 209 | ("macports_package" . "Package") 210 | ("portage_package" . "Package") 211 | ("rpm_package" . "Package") 212 | ("gem_package" . "Package") 213 | ("yum_package" . "Package") 214 | ("zypper_package" . "Package") 215 | ("powershell" . "PowerShellScript") 216 | ("remote_directory" . "RemoteDirectory") 217 | ("remote_file" . "RemoteFile") 218 | ("route" . "Route") 219 | ("ruby_block" . "RubyBlock") 220 | ("scm" . "SCM") 221 | ("script" . "Script") 222 | ("bash" . "Script") 223 | ("csh" . "Script") 224 | ("perl" . "Script") 225 | ("python" . "Script") 226 | ("ruby" . "Script") 227 | ("service" . "Service") 228 | ("subversion" . "Subversion") 229 | ("template" . "Template") 230 | ("user" . "User") 231 | )) 232 | (target (assoc-string (symbol-at-point) tbl))) 233 | 234 | (if target 235 | (browse-url (concat base anchor (cdr target))) 236 | (browse-url base)))) 237 | 238 | 239 | (provide 'chef-mode) 240 | ;;; chef-mode.el ends here 241 | --------------------------------------------------------------------------------