save
This commit is contained in:
parent
b1ca6cb4ba
commit
5a96e41469
BIN
.project.el.gpg
BIN
.project.el.gpg
Binary file not shown.
|
@ -2,6 +2,12 @@
|
|||
#+AUTHOR: Yann Esposito
|
||||
#+EMAIL: yann.esposito@gmail.com
|
||||
|
||||
* Articles
|
||||
:PROPERTIES:
|
||||
:ID: A238A3BD-18B5-42D8-8FB3-E522DB3795CC
|
||||
:PUBDATE: <2019-07-28 Sun 15:11>
|
||||
:END:
|
||||
#+begin_archive
|
||||
@@html:<li>@@ @@html:<span class="archive-item"><span class="archive-date">@@ 2019-07-28: @@html:</span>@@ [[file:posts/autoload-emacs-script-by-project.org][Autoload Script by project]] @@html:</span>@@ @@html:</li>@@
|
||||
@@html:<li>@@ @@html:<span class="archive-item"><span class="archive-date">@@ 2019-07-07: @@html:</span>@@ [[file:posts/2019-07-04-static-org-publish.org][Static blog with org-mode]] @@html:</span>@@ @@html:</li>@@
|
||||
#+end_archive
|
||||
|
|
|
@ -27,42 +27,14 @@ body {
|
|||
-moz-hyphens:auto;
|
||||
-ms-hyphens:auto;
|
||||
}
|
||||
#preamble {
|
||||
border-bottom: solid 1px;
|
||||
}
|
||||
#preamble h1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#postamble {
|
||||
border-top: solid 1px;
|
||||
}
|
||||
#preamble,#postamble {
|
||||
padding: 10px 0;
|
||||
}
|
||||
.menu {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.menu:hover, .menu:focus {
|
||||
opacity:1;
|
||||
}
|
||||
#content,.content {
|
||||
max-width: 50em;
|
||||
margin: 0 auto;
|
||||
padding: 10px;
|
||||
}
|
||||
#content *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, pre, code, blockquote, ol, ul, ol ol, ul ul, ul ol, ol
|
||||
ul, li, p, section, header, footer {
|
||||
float: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, pre, code, blockquote, p, ul, ol, section, header,
|
||||
.figure {
|
||||
figure {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
@ -152,7 +124,6 @@ blockquote:after {
|
|||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0.3;
|
||||
}
|
||||
li {
|
||||
position: relative;
|
||||
|
@ -176,8 +147,6 @@ ol > li:before {
|
|||
opacity: 0.5;
|
||||
}
|
||||
|
||||
/* colors */
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 800px;
|
||||
|
@ -188,13 +157,6 @@ p > img, li > img {
|
|||
margin: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
footer {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.article-date {
|
||||
opacity: 0.5;
|
||||
font-size: 0.8;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
margin: 20px 0;
|
||||
|
@ -219,15 +181,32 @@ navigation {
|
|||
navigation > a {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.footpara { display: inline; }
|
||||
.footdef > sup {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.footdef > sup::after {
|
||||
content: ": ";
|
||||
}
|
||||
|
||||
/* org mode statuses */
|
||||
/* org mode ids and classes */
|
||||
.figure {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#preamble {
|
||||
border-bottom: solid 1px;
|
||||
}
|
||||
#preamble h1, #preamble h2 {
|
||||
margin: 0;
|
||||
}
|
||||
#postamble {
|
||||
border-top: solid 1px;
|
||||
}
|
||||
#preamble,#postamble {
|
||||
padding: 10px 0;
|
||||
}
|
||||
#content,.content {
|
||||
max-width: 50em;
|
||||
margin: 0 auto;
|
||||
padding: 10px;
|
||||
}
|
||||
#content *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.timestamp-wrapper {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
@ -236,6 +215,21 @@ navigation > a {
|
|||
font-weight: bold;
|
||||
padding: 1px 1ex;
|
||||
}
|
||||
.article-date {
|
||||
font-size: 0.8;
|
||||
font-style: italic;
|
||||
float: right;
|
||||
}
|
||||
.footpara {
|
||||
display: inline;
|
||||
}
|
||||
.footdef > sup {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.footdef > sup::after {
|
||||
content: ": ";
|
||||
}
|
||||
|
||||
|
||||
/* colors theme */
|
||||
:root {
|
||||
|
@ -258,7 +252,8 @@ navigation > a {
|
|||
--green: #859900;
|
||||
--transparent: rgba(255,255,255,0);
|
||||
|
||||
--main-background: #00151b; /* 0.5 darker than #002b36; */
|
||||
/* --main-background: #00151b; /* 0.5 darker than #002b36; */
|
||||
--main-background: var(--base03); /* 0.5 darker than #002b36; */
|
||||
--main-foreground: var(--base1);
|
||||
--second-foreground: var(--base0);
|
||||
--soft-foreground: var(--base01);
|
||||
|
@ -279,7 +274,6 @@ navigation > a {
|
|||
--todo-txt: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--main-background);
|
||||
color: var(--main-foreground);
|
||||
|
@ -301,6 +295,12 @@ pre::after,pre::before,hr:after {
|
|||
a:hover, a:active, a:focus {
|
||||
color: var(--yellow);
|
||||
}
|
||||
navigation a, navigation a:visited {
|
||||
color: var(--soft-foreground);
|
||||
}
|
||||
navigation a:focus, navigation a:hover {
|
||||
color: var(--yellow);
|
||||
}
|
||||
thead {
|
||||
background-color: var(--reveal-background);
|
||||
color: var(--second-fg);
|
||||
|
@ -308,24 +308,20 @@ thead {
|
|||
tr:hover {
|
||||
background-color: var(--reveal-background);
|
||||
}
|
||||
#postamble:hover, #preamble:hover {
|
||||
background-color: var(--reveal-background);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
h1 {
|
||||
color: var(--violet);
|
||||
}
|
||||
h2 {
|
||||
color: var(--magenta);
|
||||
}
|
||||
h2 {
|
||||
color: var(--violet);
|
||||
}
|
||||
h3 {
|
||||
color: var(--red);
|
||||
color: var(--blue);
|
||||
}
|
||||
h4 {
|
||||
color: var(--orange);
|
||||
color: var(--cyan);
|
||||
}
|
||||
h5 {
|
||||
color: var(--yellow);
|
||||
color: var(--green);
|
||||
}
|
||||
h6 {
|
||||
color: var(--green);
|
||||
|
@ -336,9 +332,15 @@ table, td, th {
|
|||
code {
|
||||
background: var(--reveal-background);
|
||||
}
|
||||
blockquote:after {
|
||||
color: var(--soft-foreground);
|
||||
}
|
||||
#preamble,#postamble {
|
||||
border-color: var(--transparent);
|
||||
}
|
||||
.article-date {
|
||||
color: var(--soft-foreground);
|
||||
}
|
||||
/* -------- */
|
||||
/* org colors */
|
||||
.todo, .done {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
36
src/demo.org
36
src/demo.org
|
@ -36,6 +36,9 @@ There should be a forced newline.
|
|||
It lets you and others work together on projects from anywhere.
|
||||
|
||||
*** Level 3
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: level-3
|
||||
:END:
|
||||
|
||||
#+begin_quote
|
||||
This is a blockquote following a header.
|
||||
|
@ -45,6 +48,9 @@ There should be a forced newline.
|
|||
#+end_quote
|
||||
|
||||
**** Level 4
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: level-4
|
||||
:END:
|
||||
|
||||
#+begin_src javascript
|
||||
// Javascript code with syntax highlighting.
|
||||
|
@ -78,6 +84,9 @@ There should be a forced newline.
|
|||
#+end_src
|
||||
|
||||
***** Level 5
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: level-5
|
||||
:END:
|
||||
|
||||
- this is an unordered list following a header.
|
||||
- this is an unordered list following a header.
|
||||
|
@ -121,6 +130,9 @@ Bad too wide table...
|
|||
------
|
||||
|
||||
*** Here is an unordered list:
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: here-is-an-unordered-list-
|
||||
:END:
|
||||
|
||||
- level 1 item
|
||||
- level 2 item
|
||||
|
@ -140,27 +152,39 @@ Bad too wide table...
|
|||
- level 2 item
|
||||
|
||||
**** Image test
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: image-test
|
||||
:END:
|
||||
|
||||
an image:
|
||||
|
||||
#+CAPTION: Testing include an image
|
||||
#+NAME: fig:test-image
|
||||
#+ATTR_HTML: The Experiment
|
||||
[[../assets/img/a.png]]
|
||||
[[../img/a.png]]
|
||||
|
||||
|
||||
We could try inline image
|
||||
[[../assets/img/a.png]]
|
||||
[[../img/a.png]]
|
||||
just to check.
|
||||
|
||||
- [[../assets/img/a.png]] item with img
|
||||
- [[../assets/img/a.png]] item with img
|
||||
- [[../assets/img/a.png]] item with img
|
||||
- [[../assets/img/a.png]] item with img
|
||||
- [[../img/a.png]] item with img
|
||||
- [[../img/a.png]] item with img
|
||||
- [[../img/a.png]] item with img
|
||||
- [[../img/a.png]] item with img
|
||||
|
||||
***** TODO todo
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: todo
|
||||
:END:
|
||||
****** IN-PROGRESS in-progress
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: in-progress
|
||||
:END:
|
||||
******* IN-REVIEW in-review
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: in-review
|
||||
:END:
|
||||
****** HOLD on hold state
|
||||
- State "HOLD" from "IN-REVIEW" [2019-07-09 Tue 13:44] \\
|
||||
some reason
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
#+TITLE: Detoxify the Web
|
||||
#+SUBTITLE: Take back control of what we are exposed to on the web
|
||||
#+AUTHOR: Yann Esposito
|
||||
|
||||
We are currently in an age were where you take your eyeball is a valuable resource.
|
||||
As such there are professional, and a lot of smart ones, whose only job is to ensure
|
||||
you pass more time looking at what there is best in their interrest, not yours.
|
||||
|
||||
There are a lot of techniques to grab your attention and not letting it go somewhere else.
|
||||
|
||||
- animation
|
||||
- colors
|
||||
- gaming the relationship
|
||||
- entertaining
|
||||
|
||||
All of them are in fact not really problematic. But now, this is too much.
|
||||
So in order to feel calm again we need to have better control of what we see, are exposed to.
|
||||
|
||||
If you take a look into the gopher sphere, you discover a lot text-oriented world.
|
||||
Of course there are pictures, but not in the middle of the article.
|
||||
|
||||
So mostly text. No gif, no animation, no music, no video, just textual content.
|
||||
|
||||
We passed from a world where there was too much textual content, to a world filled
|
||||
with meaningless animations, colors, pictures, effects, popups, etc...
|
||||
|
||||
*Detoxify the web*
|
||||
|
||||
Here is my solution:
|
||||
|
||||
- remove all the styles of all the websites so a lot of website cannot be used anymore that way.
|
||||
- replace by a default focus oriented style.
|
||||
|
||||
My only solution is to start a proxy between me and the web, I take the content and do a lot of cleanup.
|
||||
I remove all the CSS (faster), I use my own (nicer and contolled)
|
||||
|
||||
Simple and efficient.
|
||||
|
||||
This transformation should be done by the client.
|
|
@ -0,0 +1,13 @@
|
|||
#+TITLE: The sad state of text browsing
|
||||
#+AUTHOR: Yann Esposito
|
||||
|
||||
1. sad state, show HN, Reddit, worse, news websites, etc...
|
||||
2. bad for disabled people
|
||||
3. why not change the concept and make the provider of content not also provide the Look & Feel
|
||||
User controlled display of content. Less money, more quality, is it possible
|
||||
- no js
|
||||
- text only
|
||||
- interaction? controlled one
|
||||
- user facing code, yes, only signature, all the rest looks like bullshit to me
|
||||
4. not web 3.0, more like, fuck it, we need a BRUTALIST, Hacker-only oriented
|
||||
web. A safe place to talk between civilised people. What are the possible solutions
|
|
@ -8,21 +8,8 @@
|
|||
#+OPTIONS: H:5
|
||||
#+STARTUP: showeverything
|
||||
|
||||
This is a new take on my personal blog.
|
||||
I tried to have some minimalism in mind but not too much.
|
||||
Welcome to my personal blog.
|
||||
This is the 4th time I change the underlying technology behind my blog.
|
||||
|
||||
True minimalism would have been to only serve text files.
|
||||
But I would prefer to have at least links working. Also
|
||||
syntax highlighting of source code is nice.
|
||||
|
||||
But I dislike the new trend of design space and white colors.
|
||||
It doesn't really fit the spirit of my website.
|
||||
So it looks a bit more like a text into a colored terminal.
|
||||
|
||||
The CSS support dark/light theme.
|
||||
If unknown, the default color scheme is dark in the hope to save a bit of energy
|
||||
from screen.
|
||||
You can have a basic demo about most supported content styles/type (paragraphs,
|
||||
lists, tables, etc...) [[file:./demo.org][here]].
|
||||
|
||||
I write all the content with [[http://orgmode.org][org-mode]].
|
||||
- [[./archive.html][posts]]
|
||||
- [[./about][about me]]
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
#+TITLE: Static blog with org-mode
|
||||
#+AUTHOR: Yann Esposito
|
||||
#+EMAIL: yann.esposito@gmail.com
|
||||
#+DATE: 2019-07-04
|
||||
#+DATE: 2019-07-27
|
||||
#+KEYWORDS: programming, blog, org-mode
|
||||
#+OPTIONS: auto-id:t
|
||||
|
||||
#+begin_quote
|
||||
/tl;dr/: [[#current-solution-0a92][Full code for the impatient↓]]. Read the full blog post to have all the details.
|
||||
/tl;dr/: [[#current-solution][Full code for the impatient↓]].
|
||||
Read the full blog post to have all the details.
|
||||
#+end_quote
|
||||
|
||||
|
||||
This is the first article using my new blog system.
|
||||
|
||||
Once in a while, I create a new personal website.
|
||||
|
@ -63,6 +63,11 @@ You can add a task quite easily while doing other work in emacs.
|
|||
:CUSTOM_ID: how--831e
|
||||
:END:
|
||||
|
||||
You can easily follow the org-mode doc to make your own website.
|
||||
But I have added a few "goodies".
|
||||
|
||||
- attempt to obfuscate contact email,
|
||||
|
||||
** Basic Blog
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: basic-blog-a1fc
|
||||
|
@ -70,8 +75,7 @@ You can add a task quite easily while doing other work in emacs.
|
|||
|
||||
I put the need minimal code in a =.project.el.gpg= file of my blog repository.
|
||||
Inspired by this [[https://francismurillo.github.io/2017-02-15-Project-Script-Loader/][blog post]] I enhanced this version to be both more user friendly
|
||||
and secure (see [[#digression]]).
|
||||
|
||||
and secure (see [[./autoload-emacs-script-by-project.org][Autoload Emacs Script by Project]]).
|
||||
|
||||
But even before that, I simply put the code in has an elisp code block of my
|
||||
=index.org= that I exectued with =C-c C-c=.
|
||||
|
@ -188,7 +192,7 @@ That's better. But for a blog you also generally want to have your own CSS.
|
|||
|
||||
* Current Solution
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: current-solution-0a92
|
||||
:CUSTOM_ID: current-solution
|
||||
:END:
|
||||
|
||||
#+begin_src elisp
|
||||
|
@ -201,7 +205,8 @@ That's better. But for a blog you also generally want to have your own CSS.
|
|||
(setq rss-dir base-dir)
|
||||
(setq publish-rss-dir publish-dir)
|
||||
(setq css-name "minimalist.css")
|
||||
|
||||
(setq author-name "Yann Esposito")
|
||||
(setq author-email "yann.esposito@gmail.com")
|
||||
|
||||
(require 'org)
|
||||
(require 'ox-publish)
|
||||
|
@ -211,8 +216,8 @@ That's better. But for a blog you also generally want to have your own CSS.
|
|||
|
||||
(defun org-blog-prepare (project-plist)
|
||||
"With help from `https://github.com/howardabrams/dot-files'.
|
||||
Touch `index.org' to rebuilt it.
|
||||
Argument `PROJECT-PLIST' contains information about the current project."
|
||||
Touch `index.org' to rebuilt it.
|
||||
Argument `PROJECT-PLIST' contains information about the current project."
|
||||
(let* ((base-directory (plist-get project-plist :base-directory))
|
||||
(buffer (find-file-noselect (expand-file-name "index.org" base-directory) t)))
|
||||
(with-current-buffer buffer
|
||||
|
@ -224,7 +229,7 @@ That's better. But for a blog you also generally want to have your own CSS.
|
|||
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/assets/css/" css-name "\"/>"
|
||||
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
|
||||
"<link rel=\"alternative\" type=\"application/rss+xml\" title=\"Subscribe to articles\" href=\"/archives.xml\" />"
|
||||
"<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/favicon.ico\">"))
|
||||
"<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/assets/img/favicon.ico\">"))
|
||||
|
||||
(defun menu (lst)
|
||||
"Blog menu"
|
||||
|
@ -320,8 +325,8 @@ That's better. But for a blog you also generally want to have your own CSS.
|
|||
(defun org-blog-sitemap-function (title list)
|
||||
"Return sitemap using TITLE and LIST returned by `org-blog-sitemap-format-entry'."
|
||||
(concat "#+TITLE: " title "\n"
|
||||
"#+AUTHOR: Yann Esposito\n"
|
||||
"#+EMAIL: yann.esposito@gmail.com\n"
|
||||
"#+AUTHOR: " author-name "\n"
|
||||
"#+EMAIL: " author-email "\n"
|
||||
"\n#+begin_archive\n"
|
||||
(mapconcat (lambda (li)
|
||||
(format "@@html:<li>@@ %s @@html:</li>@@" (car li)))
|
||||
|
@ -399,96 +404,3 @@ That's better. But for a blog you also generally want to have your own CSS.
|
|||
'my-org-export-add-target-blank-to-http-links)
|
||||
#+end_src
|
||||
|
||||
* Digression
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: digression
|
||||
:END:
|
||||
|
||||
Auto loading a =.el= file when entering in a project.
|
||||
It should be easy and safe.
|
||||
|
||||
The best solution I found.
|
||||
Save your =.el= file as a =.el.gpg= file.
|
||||
With =epa= emacs should encrypt the file for you using your own private key.
|
||||
Great! Now we simply verify the file exists and load it only if it is encrypted
|
||||
with one of you /trusted keys/.
|
||||
The list of key fingerprint you trust is a configuration.
|
||||
|
||||
When opening a new file via projectile or dired
|
||||
|
||||
1. check the =.project.el.gpg= file is not already loaded for the current project
|
||||
2. check the =.project.el.gpg= file exists
|
||||
3. check the =.project.el.gpg= file is encrypted with a trusted key
|
||||
4. decrypt and load =.project.el.gpg=
|
||||
|
||||
That's it.
|
||||
|
||||
You simply need to set the =y/trusted-gpg-key-fingerprints= variable with the
|
||||
list of your own fingerprint.
|
||||
You can get a list of them with =gpg --list-secret-keys=.
|
||||
|
||||
Now you can be happy, this is really safe, in the sense that if you clone a new
|
||||
project from the internet with a =.project.el.gpg= file in it.
|
||||
That file won't be run on your system.
|
||||
|
||||
Of course if you want to share that =.el= to other people, you need to adapt.
|
||||
But for my use case, this is perfect.
|
||||
|
||||
Another use case would be to check the signature and trust people.
|
||||
|
||||
#+begin_src elisp
|
||||
(defvar y/trusted-gpg-key-fingerprints
|
||||
'("448E9FEF4F5B86DE79C1669B7B19A4C650D59646")
|
||||
"The list of GPG fingerprint you trust when decrypting a gpg file.
|
||||
You can retrieve the fingerprints of your own private keys with:
|
||||
`gpg --list-secret-keys' (take care of removing the spaces when copy/pasting here)")
|
||||
|
||||
(defun y/get-encryption-key (file)
|
||||
"given a gpg encrypted file, returns the fingerprint of they
|
||||
key that encrypted it"
|
||||
(string-trim-right
|
||||
(shell-command-to-string
|
||||
(concat
|
||||
"gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null"
|
||||
"|grep DECRYPTION_KEY"
|
||||
"|awk '{print $4}'"))))
|
||||
|
||||
(defun y/trusted-gpg-origin-p (file)
|
||||
"Returns true if the file is encrypted with a trusted key"
|
||||
(member (y/get-encryption-key file) y/trusted-gpg-key-fingerprints))
|
||||
|
||||
(defun y/project-el-auto-load ()
|
||||
(with-eval-after-load 'projectile
|
||||
(defconst y/project-file ".project.el.gpg"
|
||||
"Filename looked after to be loaded and evaluated.")
|
||||
(defvar y/loaded-projects (list)
|
||||
"Projects that have been loaded by `y/load-project-file'.")
|
||||
(defun y/load-project-file ()
|
||||
"Loads the `y/project-file' for a project. This is run
|
||||
once after the project is loaded signifying project setup."
|
||||
(interactive)
|
||||
(when (projectile-project-p)
|
||||
(lexical-let* ((project-root (projectile-project-root))
|
||||
(project-init-file (expand-file-name y/project-file project-root)))
|
||||
(when (and (not (member project-root y/loaded-projects)) ;; project file not already loaded
|
||||
(file-exists-p project-init-file) ;; project file exists
|
||||
(y/trusted-gpg-origin-p project-init-file)) ;; project file is tursted
|
||||
(message "Loading project init file for %s" (projectile-project-name))
|
||||
(condition-case ex
|
||||
(progn (load project-init-file)
|
||||
(add-to-list 'y/loaded-projects project-root)
|
||||
(message "%s loaded successfully" project-init-file))
|
||||
('error
|
||||
(message
|
||||
"There was an error loading %s: %s"
|
||||
project-init-file
|
||||
(error-message-string ex))))))))
|
||||
;; for some obscure reason there is not really a working hook
|
||||
;; to be used with projectile to launch the `y/load-project-file'
|
||||
;; only when switching to a new project.
|
||||
;; Thus the `y/loaded-projects' state.
|
||||
(add-hook 'find-file-hook #'y/load-project-file t)
|
||||
(add-hook 'dired-mode-hook #'y/load-project-file t)))
|
||||
#+end_src
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
;;; auto-load-project.el --- Auto load elisp file on project open
|
||||
|
||||
;; Copyright © 2019 Yann Esposito <yann.esposito@gmail.com>
|
||||
|
||||
;;; Commentary:
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(provide :auto-load-project)
|
||||
|
||||
(require 'projectile)
|
||||
|
||||
(defvar y/trusted-gpg-key-fingerprints
|
||||
'("448E9FEF4F5B86DE79C1669B0000000000000000")
|
||||
"The list of GPG fingerprint you trust when decrypting a gpg file.
|
||||
You can retrieve the fingerprints of your own private keys
|
||||
with: `gpg --list-secret-keys' (take care of removing the
|
||||
spaces when copy/pasting here)")
|
||||
|
||||
(defun y/get-encryption-key (file)
|
||||
"given a gpg encrypted file, returns the fingerprint of
|
||||
they key that encrypted it"
|
||||
(string-trim-right
|
||||
(shell-command-to-string
|
||||
(concat
|
||||
"gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null"
|
||||
"|grep DECRYPTION_KEY"
|
||||
"|awk '{print $4}'"))))
|
||||
|
||||
(defun y/trusted-gpg-origin-p (file)
|
||||
"Returns true if the file is encrypted with a trusted key"
|
||||
(member (y/get-encryption-key file) y/trusted-gpg-key-fingerprints))
|
||||
|
||||
|
||||
(defconst y/project-file ".project.el.gpg"
|
||||
"Project configuration file name.")
|
||||
|
||||
(defun y/init-project-el-auto-load ()
|
||||
"Initialize the autoload of .project.el.gpg for projects."
|
||||
|
||||
(with-eval-after-load 'projectile
|
||||
|
||||
(defvar y/loaded-projects (list)
|
||||
"Projects that have been loaded by `y/load-project-file'.")
|
||||
|
||||
(defun y/load-project-file ()
|
||||
"Loads the `y/project-file' for a project. This is run once
|
||||
after the project is loaded signifying project setup."
|
||||
(interactive)
|
||||
(when (projectile-project-p)
|
||||
(lexical-let* ((current-project-root (projectile-project-root))
|
||||
(project-init-file (expand-file-name y/project-file current-project-root)))
|
||||
(when (and (not (member current-project-root y/loaded-projects))
|
||||
(file-exists-p project-init-file)
|
||||
(y/trusted-gpg-origin-p project-init-file))
|
||||
(message "Loading project init file for %s" (projectile-project-name))
|
||||
(condition-case ex
|
||||
(progn (load project-init-file)
|
||||
(add-to-list 'y/loaded-projects current-project-root)
|
||||
(message "%s loaded successfully" project-init-file))
|
||||
('error
|
||||
(message
|
||||
"There was an error loading %s: %s"
|
||||
project-init-file
|
||||
(error-message-string ex))))))))
|
||||
(add-hook 'find-file-hook #'y/load-project-file t)
|
||||
(add-hook 'dired-mode-hook #'y/load-project-file t)))
|
||||
|
||||
(provide 'auto-load-project)
|
||||
|
||||
;;; auto-load-project.el ends here
|
|
@ -0,0 +1,133 @@
|
|||
#+TITLE: Autoload Script by project
|
||||
#+SUBTITLE: fast, secure, easy autoload
|
||||
#+AUTHOR: Yann Esposito
|
||||
#+EMAIL: yann.esposito@gmail.com
|
||||
#+DATE: 2019-07-28
|
||||
#+KEYWORDS: programming, blog, org-mode
|
||||
#+OPTIONS: auto-id:t
|
||||
|
||||
#+begin_quote
|
||||
/tl;dr/: A script that use projectile and GPG to securely load
|
||||
an emacs lisp script when opening a new project.
|
||||
|
||||
Check the [[#solution]] section to get the code.
|
||||
#+end_quote
|
||||
|
||||
* Problem
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: problem
|
||||
:END:
|
||||
|
||||
When providing a repository containing only org files of my blog.
|
||||
I also wanted to provide everything necessary for users to be able to publish my
|
||||
website.
|
||||
Emacs, org-publish mostly assume you should put all those details in a
|
||||
centralised place in your =~/.emacs.d/.init.el= file.
|
||||
|
||||
The main principle is quite simple.
|
||||
|
||||
1. When finding a new file in a project, check for the presence of a
|
||||
=.project.el.gpg= file.
|
||||
2. Check the file was encrypted with by a trusted fingerprint
|
||||
3. load the file
|
||||
|
||||
I found a solution that asked you each time you enter in a project if you trust
|
||||
it.
|
||||
This was both quite annoying and insecure as it is kind of easy to type 'y'
|
||||
instead of 'n' and to load an untrusted 3rd party script.
|
||||
|
||||
With emacs epa, opening encrypted gpg files is seamless.
|
||||
But here we not only want to open the gpg file but also check we trust the
|
||||
person that did it.
|
||||
|
||||
Note that checking who's encrypted a gpg file is not straightforward:
|
||||
|
||||
#+begin_src elisp
|
||||
(defun y/get-encryption-key (file)
|
||||
"given a gpg encrypted file, returns the fingerprint of they
|
||||
key that encrypted it"
|
||||
(string-trim-right
|
||||
(shell-command-to-string
|
||||
(concat
|
||||
"gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null"
|
||||
"|grep DECRYPTION_KEY"
|
||||
"|awk '{print $4}'"))))
|
||||
#+end_src
|
||||
|
||||
- The `--status-fd` should provide more script friendly output.
|
||||
GPG provide localized output by default which are therefore hard to use in
|
||||
script (for grep for example).
|
||||
- we do not want to decrypt to a file so we redirect the output to =/dev/null=
|
||||
|
||||
We use =projectile= to detect the project-root and when we are in a new project.
|
||||
Unfortunately the =projectile-after-switch-project-hooks= doesn't work as I
|
||||
expected.
|
||||
So I use the hooks =find-file-hook= and =dired-mode-hook= to try to load the
|
||||
file.
|
||||
In order not to load the code each time, I need to keep a local state of project
|
||||
already loaded.
|
||||
|
||||
* Solution
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: solution
|
||||
:END:
|
||||
|
||||
#+begin_src elisp
|
||||
(defun y/init-project-el-auto-load ()
|
||||
"Initialize the autoload of .project.el.gpg for projects"
|
||||
|
||||
(with-eval-after-load 'projectile
|
||||
|
||||
(defvar y/trusted-gpg-key-fingerprints
|
||||
'("448E9FEF4F5B86DE79C1669B0000000000000000")
|
||||
"The list of GPG fingerprint you trust when decrypting a gpg file.
|
||||
You can retrieve the fingerprints of your own private keys
|
||||
with: `gpg --list-secret-keys' (take care of removing the
|
||||
spaces when copy/pasting here)")
|
||||
|
||||
(defun y/get-encryption-key (file)
|
||||
"given a gpg encrypted file, returns the fingerprint of
|
||||
they key that encrypted it"
|
||||
(string-trim-right
|
||||
(shell-command-to-string
|
||||
(concat
|
||||
"gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null"
|
||||
"|grep DECRYPTION_KEY"
|
||||
"|awk '{print $4}'"))))
|
||||
|
||||
(defun y/trusted-gpg-origin-p (file)
|
||||
"Returns true if the file is encrypted with a trusted key"
|
||||
(member (y/get-encryption-key file) y/trusted-gpg-key-fingerprints))
|
||||
|
||||
|
||||
(defconst y/project-file ".project.el.gpg"
|
||||
"Project configuration file name.")
|
||||
|
||||
(defvar y/loaded-projects (list)
|
||||
"Projects that have been loaded by `y/load-project-file'.")
|
||||
|
||||
(defun y/load-project-file ()
|
||||
"Loads the `y/project-file' for a project. This is run once
|
||||
after the project is loaded signifying project setup."
|
||||
(interactive)
|
||||
(when (projectile-project-p)
|
||||
(lexical-let* ((current-project-root (projectile-project-root))
|
||||
(project-init-file (expand-file-name y/project-file current-project-root)))
|
||||
(when (and (not (member current-project-root y/loaded-projects))
|
||||
(file-exists-p project-init-file)
|
||||
(y/trusted-gpg-origin-p project-init-file))
|
||||
(message "Loading project init file for %s" (projectile-project-name))
|
||||
(condition-case ex
|
||||
(progn (load project-init-file)
|
||||
(add-to-list 'y/loaded-projects current-project-root)
|
||||
(message "%s loaded successfully" project-init-file))
|
||||
('error
|
||||
(message
|
||||
"There was an error loading %s: %s"
|
||||
project-init-file
|
||||
(error-message-string ex))))))))
|
||||
(add-hook 'find-file-hook #'y/load-project-file t)
|
||||
(add-hook 'dired-mode-hook #'y/load-project-file t)))
|
||||
|
||||
(y/init-project-el-auto-load)
|
||||
#+end_src
|
Loading…
Reference in New Issue