]> git.eshelyaron.com Git - esy-publish.git/commitdiff
Added back /esy.html displaying my Emacs config + new blog post
authorEshel Yaron <me@eshelyaron.com>
Sat, 8 Apr 2023 18:30:11 +0000 (21:30 +0300)
committerEshel Yaron <me@eshelyaron.com>
Sat, 8 Apr 2023 18:30:49 +0000 (21:30 +0300)
.gitignore
Makefile
dotfiles
publish.el
src/esy.org [new file with mode: 0644]
src/posts/2023-04-08-making-shell-scripts-executable-just-in-time.org [new file with mode: 0644]
src/style.css

index 4bc6c346433b582343ddfba68ae451032d03d4b2..173084765d76b1f786d0dd2b0b442597bcece906 100644 (file)
@@ -8,3 +8,7 @@
 /org/sitemap.org
 /org/theindex.inc
 /org/theindex.org
+/src/theindex.org
+/src/sitemap.org
+/src/theindex.inc
+/draft/
index 85455b78059c56e6c188055d43030a09a09815ff..4634536a3df0f06af2a943f7bd5f472fed10efb4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,13 @@
-.PHONY: local remote sweep
+.PHONY: local remote sweep dotfiles
 
 remote: local
        emacs -Q --batch -l publish.el -f esy/publish-to-remote
 
-local: sweep
+local: sweep dotfiles
        emacs -Q --batch -l publish.el -f esy/publish
 
 sweep:
        git submodule update --remote -- sweep
+
+dotfiles:
+       git submodule update --remote -- dotfiles
index 58ef0679439d4a756235774c5357e97859469911..b69b414c0274b11708d3efc28538ec201be76159 160000 (submodule)
--- a/dotfiles
+++ b/dotfiles
@@ -1 +1 @@
-Subproject commit 58ef0679439d4a756235774c5357e97859469911
+Subproject commit b69b414c0274b11708d3efc28538ec201be76159
index 497827f520bbd6a83401dfb060d2bb6d87d96f56..994472ce94e4502d3553d0491c1b368ed6c257fd 100644 (file)
@@ -10,6 +10,7 @@
 
 (use-package htmlize            :ensure t :demand t)
 (use-package rainbow-delimiters :ensure t :demand t)
+(use-package org-transclusion   :ensure t :demand t)
 
 (add-hook 'prog-mode-hook #'rainbow-delimiters-mode)
 
       (insert "- [[file:" (file-relative-name dir) "][...older posts]]"))
     (delete-blank-lines)))
 
+
+(defun esy/publish-prepare (&rest _)
+  (esy/publish-transclude-config)
+  (esy/publish-posts-prepare-index))
+
+(function-put 'esy/init-step 'doc-string-elt 2)
+
+(defun esy/publish-transclude-config (&rest _)
+  (with-current-buffer (find-file-noselect (expand-file-name "esy.org" esy/publish-src-directory))
+    (org-transclusion-add-all)))
+
 (defun esy/publish-posts-prepare-index (&rest _)
   (dolist (dir (list esy/publish-org-posts-directory esy/publish-src-directory))
     (with-current-buffer (find-file-noselect (expand-file-name "index.org" dir))
                       :completion-function #'esy/publish-completion-function
                       :base-directory esy/publish-src-directory
                       :publishing-directory publishing-directory
-                      :preparation-function #'esy/publish-posts-prepare-index
+                      :preparation-function #'esy/publish-prepare
                       :completion-function #'ignore
                       :base-extension "org"
                       :recursive t
diff --git a/src/esy.org b/src/esy.org
new file mode 100644 (file)
index 0000000..91c715e
--- /dev/null
@@ -0,0 +1,9 @@
+#+TITLE: GNU Emacs Configuration
+#+AUTHOR: Eshel Yaron
+#+DESCRIPTION: Personal GNU Emacs configuration of Eshel Yaron
+#+KEYWORDS: eshel emacs configuration literate org mode elisp
+
+I've recently moved from a literate Emacs configuration based on Org
+mode to a simpler =init.el= file, reproduced below:
+
+#+transclude: [[file:~/checkouts/eshelyaron.com/dotfiles/.emacs.d/init.el]] :src emacs-lisp
diff --git a/src/posts/2023-04-08-making-shell-scripts-executable-just-in-time.org b/src/posts/2023-04-08-making-shell-scripts-executable-just-in-time.org
new file mode 100644 (file)
index 0000000..3f2308e
--- /dev/null
@@ -0,0 +1,143 @@
+#+TITLE:       Making Shell Scripts Executable Just-in-Time
+#+SUBTITLE:    A different take on adding exec permissions to shell script in Emacs
+#+DESCRIPTION: Blog post by Eshel Yaron about a different take on adding exec permissions to shell script in Emacs
+#+KEYWORDS:    emacs lisp
+#+DATE:        2023-04-08
+
+In my work I often need to write small programs or scripts that
+accomplish very specific tasks.  Many of these involve fetching and
+analyzing data from JSON-based APIs, and I tend to use shell scripts
+for that sorta thing.  I mostly rely on ~curl~, ~jq~, and ~parallel~,
+as well as the other usual suspects ~grep~, ~sed~, ~tr~, ~head~,
+~sort~, ~uniq~, and the occasional ~awk~.
+
+I usually begin writing these scripts as one-liners in an [[info:emacs#Interactive Shell][Emacs Shell
+buffer]] before moving to a =.sh= file.  There I can generalize that
+one-liner and format it nicely, and at some point also test it.  Of
+course, to test it I need to run it, and that requires giving the
+newly created shell script /executable permissions/.
+
+In Emacs, there are quite a few ways one can go about making their
+script executable.  You can, for instance, do ~M-! M-n chmod +x RET~,
+but if you ask me that's too much typing for such a common task!
+Worse, it's not very /Emacsy/ either.  Instead, the way I've been
+doing this for a long time was jumping to Dired with ~C-x C-j~, and
+immediately typing ~M +x RET~ to make the file executable.  That works
+well and doesn't require me to type ~chmod~, but it still takes some
+typing and--crucially--it forces me to switch to Dired, when I really
+just wanted was to test my shell script from its own buffer.
+
+Recently, after going through that flow one too many times, I figured
+/there must be a better way/.  Ideally I would have simply liked to
+hit ~C-c C-x~ (AKA ~executable-interpret~) in my shell script buffer
+without the need to explicitly make it executable beforehand.  I
+wasn't surprised to quickly discover that Emacs comes with a dedicated
+solution for this problem built-in.  Typing ~C-h f~ followed by ~exec
+file TAB~ lists among the completion candidates a function
+~executable-make-buffer-file-executable-if-script-p~ that, as its
+lengthy name suggest, makes the visited file executable if it happens
+to be a script.
+
+The standard piece of advice regarding this function, that you find
+written throughout the interwebs by Emacs users and developers alike,
+is that one should put this function in their after ~after-save-hook~.
+
+In 2003, Stefan Monnier [[https://lists.gnu.org/archive/html/emacs-devel/2003-05/msg00249.html][wrote on the emacs-devel mailing list]]:
+
+#+begin_quote
+we have make-buffer-file-executable-if-script-p and I recommend
+everybody add it to his after-save-hook.
+#+end_quote
+
+This solves the problem of manually changing a shell script's
+permissions prior to running it, because the script is made executable
+the second you save it--and saving the shell script buffer to a file
+is anyway a prerequisite for running it.
+
+In the 20 years that passed since Stefan brought up that nifty trick,
+this advice was echoed in many esteemed Emacs blogs:
+
+- Micky Petersen [[https://www.masteringemacs.org/article/script-files-executable-automatically][suggested it]] in his /Mastering Emacs/ book,
+- Marcin Borkowski mentioned it among other [[https://mbork.pl/2015-01-10_A_few_random_Emacs_tips][random Emacs tips]],
+- Oleh Krehel included it in his [[https://oremacs.com/2016/01/18/emacs-rhythmbox/][Emacs as system-wide Rhythmbox]] post, and
+- Bozhidar Batsov [[https://emacsredux.com/blog/2021/09/29/make-script-files-executable-automatically/][wrote about it on his blog]] as well.
+
+Yet, the function itself predates Stefan's comment.  It appears that
+originally Noah Friedman wrote it all the way back in the year 2000
+when it was added to Emacs by Dave Love--tracing the function's
+history by going to its definition in =executable.el= and hitting ~M-h
+C-x v h~ reveals the following commit:
+
+#+begin_src diff
+  commit 778e1d17edb36cc53fd7419436311f2e2bc622ff
+  Author: Dave Love <fx@gnu.org>
+  Date:   Fri Jun 9 09:38:58 2000 +0000
+
+      ...
+      (make-buffer-file-executable-if-script-p): New function from Noah
+      Friedman.
+
+  diff --git a/lisp/progmodes/executable.el b/lisp/progmodes/executable.el
+  --- a/lisp/progmodes/executable.el
+  +++ b/lisp/progmodes/executable.el
+  @@ -264,1 +270,16 @@
+  -
+  +(defun make-buffer-file-executable-if-script-p ()
+  +  "Make file executable according to umask if not already executable.
+  +If file already has any execute bits set at all, do not change existing
+  +file modes."
+  +  (and (save-excursion
+  +         (save-restriction
+  +           (widen)
+  +           (goto-char (point-min))
+  +           (save-match-data
+  +             (looking-at "^#!"))))
+  +       (let* ((current-mode (file-modes (buffer-file-name)))
+  +              (add-mode (logand ?\111 (default-file-modes))))
+  +         (or (/= (logand ?\111 current-mode) 0)
+  +             (zerop add-mode)
+  +             (set-file-modes (buffer-file-name)
+  +                             (logior current-mode add-mode))))))
+#+end_src
+
+As we see in the above patch, back in 2000 this function would simply
+look at the start of your buffer, and if begins with a /shebang/ it'd
+ensure that the file has executable permissions.  Other than the name
+of the function becoming yet a little longer (the ~executable-~ prefix
+was added, so to follow Elisp namespacing conventions), not much has
+changed in terms of its implementation since then.
+
+Although, as I described earlier, putting
+~executable-make-buffer-file-executable-if-script-p~ into one's
+~after-save-hook~ is a practice promoted by many esteemed members of
+the Emacs community (heck, it even made it to [[https://sachachua.com/dotemacs/index.html][Sacha Chua's config]]), I
+felt uneasy about this solution.  I save lots of files, and only a
+minuscule fraction of them are scripts that I wanna make executable.
+That means that the vast majority of my save operations will involve
+some futile busywork and incur a (tiny, but still) needless
+performance penalty.  I also think that the way this function decides
+whether or not to make a file executable is too coarse.  It doesn't
+examine the file's extension for example, nor does it take into
+account the buffer's major mode.  Is it always TRT to make every file
+that starts with a ~#~ and a ~!~ executable?  I'm not sure, really.
+It's probably fine, but it makes my security-spidey-sense tingle
+nonetheless.
+
+The solution I came up with is both more conservative (no chance of
+random files becoming executable against my wishes) and more efficient
+(no penalizing all file saves for a few odd scripts).  Instead of
+adding ~executable-make-buffer-file-executable-if-script-p~ to my
+after ~after-save-hook~, I settled on adding it as a /advice/ to the
+command ~executable-interpret~.  Here's the relevant excerpt from [[file:esy.org][my
+config]]:
+
+#+begin_src emacs-lisp
+  (with-eval-after-load 'executable
+     (define-advice executable-interpret (:before (&rest _) ensure-executable)
+       (unless (file-exists-p buffer-file-name)
+         (basic-save-buffer))
+       (executable-make-buffer-file-executable-if-script-p)))
+#+end_src
+
+This ~:before~ advice takes care of making the visited executable
+exactly when I'm trying to actually execute it--just in time.
index ab2cabd2d45059dde4a5b451fc98d19eb6862a5c..6ce3c2d7021aed621007a1534d30dd4e046531bf 100644 (file)
@@ -4,7 +4,7 @@ body {
     margin-left: auto;
     margin-right: auto;
     width: 90%;
-    max-width: 90ch;
+    max-width: 82ch;
     /* font-size: 1.1em; */
     font-family: "Helvetica Neue", sans-serif;
 }
@@ -220,6 +220,82 @@ Generated with `M-x org-html-htmlize-generate-css` after loading the
   /* font-lock-regexp-grouping-construct */
   color: #3fb83f;
 }
+.org-diff-added {
+  /* diff-added */
+  color: #a0e0a0;
+  background-color: #003b1f;
+}
+.org-diff-changed {
+  /* diff-changed */
+  color: #efef80;
+  background-color: #363300;
+}
+.org-diff-changed-unspecified {
+  /* diff-changed-unspecified */
+  color: #efef80;
+  background-color: #363300;
+}
+.org-diff-error {
+  /* diff-error */
+  color: #ef6560;
+  font-weight: bold;
+}
+.org-diff-file-header {
+  /* diff-file-header */
+  font-weight: bold;
+}
+.org-diff-function {
+  /* diff-function */
+  background-color: #303230;
+}
+.org-diff-hunk-header {
+  /* diff-hunk-header */
+  background-color: #303230;
+  font-weight: bold;
+}
+.org-diff-index {
+  /* diff-index */
+  font-style: italic;
+}
+.org-diff-indicator-added {
+  /* diff-indicator-added */
+  color: #a0e0a0;
+  background-color: #003b1f;
+}
+.org-diff-indicator-changed {
+  /* diff-indicator-changed */
+  color: #efef80;
+  background-color: #363300;
+}
+.org-diff-indicator-removed {
+  /* diff-indicator-removed */
+  color: #ffbfbf;
+  background-color: #4e1119;
+}
+.org-diff-nonexistent {
+  /* diff-nonexistent */
+  font-weight: bold;
+}
+.org-diff-refine-added {
+  /* diff-refine-added */
+  color: #a0e0a0;
+  background-color: #03512f;
+}
+.org-diff-refine-changed {
+  /* diff-refine-changed */
+  color: #efef80;
+  background-color: #4a4a00;
+}
+.org-diff-refine-removed {
+  /* diff-refine-removed */
+  color: #ffbfbf;
+  background-color: #751a1f;
+}
+.org-diff-removed {
+  /* diff-removed */
+  color: #ffbfbf;
+  background-color: #4e1119;
+}
 
 .timestamp {
     color: #5dc0aa;