home

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

2020-03-22-org-mode-website.org (25387B)


      1 #+title: Migrating to an org-mode website
      2 #+date: <2020-03-22 Sun>
      3 #+filetags: orgmode website emacs
      4 #+setupfile: ../templates/post.org
      5 
      6 * Introduction
      7 :PROPERTIES:
      8 :ID:       24a765bd-a0ed-42cf-b96c-db667f7d37e2
      9 :END:
     10 
     11 This is a story… a story of me changing the way I code and publish my website. In the
     12 past, I've switch from [[https://vincent.demeester.fr/posts/2012-05-07-reinit-and-jekyll/][Jekyll]] to [[https://vincent.demeester.fr/posts/2015-05-01-orgmode-et-jekyll/]["=orgmode= and Jekyll"]] to [[https://vincent.demeester.fr/posts/2015-05-09-migration-to-hugo/][Hugo]] (sorry those are written
     13 in french). The past year, I've written and documented myself a little bit about
     14 [[https://www.theminimalists.com/minimalism/][minimalism]] and [[https://www.goodreads.com/book/show/40672036-digital-minimalism][digital minimalism]]. Although I don't see myself as a minimalist, it helped
     15 me realize some issues I had.
     16 
     17 I also realized if I want to write more, I need to lower the barrier between writing and
     18 publishing my content ; /if I want it to be published, of course/. This /post/ is about
     19 what I'm putting in place for this, with a premise : I spend my life in [[https://www.gnu.org/software/emacs/][Emacs]] and thus in
     20 [[https://orgmode.org/][=orgmode=]]. And [[https://orgmode.org/][=orgmode=]] is feature-full and has this badass feature : =org-publish=.
     21 
     22 To build and publish this website, we will /try/ to rely on a reproducible setup, meaning
     23 [[https://www.gnu.org/software/emacs/][Emacs]] and [[https://orgmode.org/][=orgmode=]] of course, [[https://www.gnu.org/software/make/][GNU Make]] of course *but* most importantly, [[https://nixos.org/nix/][Nix]] (in the near
     24 future 👼).
     25 
     26 :update:
     27 There is now an article about it, that uses literate programming: [[../articles/meta_publishing_this_website.org][publishing this
     28 website]]. The content of the post might no be up-to-date at some point.
     29 :end:
     30 
     31 
     32 * Requirements
     33 :PROPERTIES:
     34 :ID:       09991f8e-4257-443c-a3a3-40f449df4597
     35 :END:
     36 
     37 Let's list the requirements I feel I have for my website:
     38 
     39 - Full control over the URL of the published posts. ::
     40   This is a golden rule of the web: should I change the publishing system, I want to be
     41   able to stick to the same URLs or else all external references would be broken. This is
     42   a big no-no and in my opinion it makes most blogging systems unacceptable.
     43 - Top-notch Org support. ::
     44   I believe generators like Jekyll and Hugo only have partial Org support. You end up
     45   requiring some conversion tooling to get the job done.
     46 - Simple publishing pipeline. ::
     47   I want the generation process to be as simple as possible. This is important for
     48   maintenance. Should I someday switch host, I want to be sure that I can set up the same
     49   pipeline.
     50 - Full control over the publishing system. ::
     51   I want maximum control over the generation process. I don’t want to be restricted by a
     52   non-Turing-complete configuration file or a dumb programming language.
     53 - Ease of use. ::
     54   The process as a whole must be as immediate and friction-less as possible, or else I
     55   take the risk of feeling too lazy to publish new posts and update the content.
     56 - Hackability. ::
     57   Last but not least, and this probably supersedes all other requirements: The system must
     58   be hackable. Lisp-based systems are prime contenders in that area.
     59 
     60 * DONE Organizing
     61 CLOSED: [2020-03-21 Sat 17:57]
     62 :PROPERTIES:
     63 :ID:       78251967-c48a-476d-bc39-c748fc412927
     64 :END:
     65 :LOGBOOK:
     66 - State "DONE"       from "TODO"       [2020-03-21 Sat 17:57]
     67 :END:
     68 
     69 Let's describe what I do want to publish here:
     70 
     71 - Posts :: this is what we call /blog/ these days : short to medium article that are valid
     72   at a point of time, as may contain /deprecated/ content, or content that do not reflect
     73   my views at a later point in time.
     74 - Articles :: medium to long article about a topic. Those should be up-to-date or
     75   explicitly mark as deprecated or invalid. /In a ideal world this is my *ready for the
     76   public* knowledge database, a bit like [[https://braindump.jethro.dev/][Jethro's Braindump]]/.
     77 - Configurations :: medium to long article about my configurations. Those are base on my
     78   ~home~ /configuration/ mono-repository, and usually follow literate programming
     79   principles.
     80 - About :: an about page about the author of the website (aka [[https://vincent.demeester.fr][me]]), linking external
     81   contributions (GitHub/Gitlab/… profiles, Talks, …).
     82 
     83 * DONE Publishing
     84 CLOSED: [2020-03-25 Wed 15:54]
     85 :PROPERTIES:
     86 :ID:       da542763-78b9-46c0-bcf7-d8d2a2b18677
     87 :END:
     88 :LOGBOOK:
     89 - State "DONE"       from "TODO"       [2020-03-25 Wed 15:54]
     90 :END:
     91 
     92 As said above, the goal is to publish everything using only [[https://www.gnu.org/software/emacs/][Emacs]] and [[https://orgmode.org/][=orgmode=]] (with the
     93 help of some standard GNU tools).
     94 
     95 The [[file:~/src/sbr/publish.el][~publish.el~]] file is where all the magic happens:
     96 
     97 - I want to generate something that is ~html5~ (almost?). The /preamble/, /content/ and
     98   /postamble/ ~div~ can be customized using ~org-html-div~. Same goes for the /container/
     99   element that is used for "wrapping top level sections", it is customized using
    100   ~org-html-container-helement~ (we want ~<section>~). There is a few additional variable
    101   that I might document one day 😛.
    102 
    103   #+begin_src emacs-lisp
    104   (setq org-export-use-babel nil)
    105   (setq org-link-abbrev-alist '(("att" . org-attach-expand-link)))
    106 
    107   ;; setting to nil, avoids "Author: x" at the bottom
    108   (setq org-export-with-section-numbers nil
    109         org-export-with-smart-quotes t
    110         org-export-with-toc nil)
    111 
    112   (defvar sbr-date-format "%b %d, %Y")
    113 
    114   (setq org-html-divs '((preamble "header" "top")
    115                         (content "main" "content")
    116                         (postamble "footer" "postamble"))
    117         org-html-container-element "section"
    118         org-html-metadata-timestamp-format sbr-date-format
    119         org-html-checkbox-type 'unicode
    120         org-html-html5-fancy t
    121         org-html-doctype "html5"
    122         org-html-htmlize-output-type 'css
    123         org-html-htmlize-font-prefix "org-"
    124         org-src-fontify-natively t
    125         org-html-coding-system 'utf-8-unix)
    126   #+end_src
    127 
    128 - Part of the ~<head>~, preamble and postamble are customized for the website.
    129   + ~head~ ::
    130     #+begin_src emacs-lisp
    131     (defvar sbr-website-html-head
    132       "<link rel='icon' type='image/x-icon' href='/images/favicon.ico'/>
    133     <meta name='viewport' content='width=device-width, initial-scale=1'>
    134     <link rel='stylesheet' href='/css/new.css' type='text/css'/>
    135     <link rel='stylesheet' href='/css/syntax.css' type='text/css'/>
    136     <link href='/index.xml' rel='alternate' type='application/rss+xml' title='Vincent Demeester' />")
    137     #+end_src
    138   + premable ::
    139     #+begin_src emacs-lisp
    140     (defun sbr-website-html-preamble (plist)
    141       "PLIST: An entry."
    142       ;; Skip adding subtitle to the post if :KEYWORDS don't have 'post' has a
    143       ;; keyword
    144       (when (string-match-p "post" (format "%s" (plist-get plist :keywords)))
    145         (plist-put plist
    146                    :subtitle (format "Published on %s by %s."
    147                                      (org-export-get-date plist sbr-date-format)
    148                                      (car (plist-get plist :author)))))
    149 
    150       ;; Below content will be added anyways
    151       "<nav>
    152     <img src=\"/images/favicon.ico\" id=\"sitelogo\"/> <a href='/'>home</a> /
    153     <a href='/posts/'>posts</a> (<a href='/index.xml'>rss</a>) /
    154     <a href='/articles/'>articles</a> /
    155     <a href='/configurations/'>configurations</a> /
    156     <a href='https://dl.sbr.pm/'>files</a> /
    157     <a href='/about/'>about</a></li>
    158     </nav>")
    159     #+end_src
    160   + postamble ::
    161     #+begin_src emacs-lisp
    162     (defvar sbr-website-html-postamble
    163       "<footer>
    164          <span class='questions'>Questions, comments ? Please use my <a href=\"https://lists.sr.ht/~vdemeester/public-inbox\">public inbox</a> by sending a plain-text email to <a href=\"mailto:~vdemeester/public-inbox@lists.sr.ht\">~vdemeester/public-inbox@lists.sr.ht</a>.</span>
    165          <span class='opinions'>Opinions stated here are my own and do not express the views of my employer, spouse, children, pets, neighbors, secret crushes, favorite authors, or anyone else who is not me. And maybe not even me, depending on how old this is.</span>
    166          <span class='copyright'>
    167           Content and design by Vincent Demeester
    168           (<a rel='licence' href='http://creativecommons.org/licenses/by-nc-sa/3.0/'>Some rights reserved</a>)
    169         </span><br />
    170         <span class='engine'>
    171           Powered by <a href='https://www.gnu.org/software/emacs/'>Gnu Emacs</a> and <a href='https://orgmode.org'>orgmode</a>
    172         </span>
    173     </footer>")
    174     #+end_src
    175 - =orgmode= is able to generate a site-map. This is what we are going to use to generate
    176   the index files for ~posts/~ and ~articles~ mainly.
    177   #+begin_src emacs-lisp
    178   (defun sbr/org-sitemap-format-entry (entry style project)
    179     "Format posts with author and published data in the index page.
    180 
    181   ENTRY: file-name
    182   STYLE:
    183   PROJECT: `posts in this case."
    184     (cond ((not (directory-name-p entry))
    185            (format "%s — [[file:%s][%s]]
    186                    :PROPERTIES:
    187                    :PUBDATE: [%s]
    188                    :END:"
    189                    (format-time-string "%Y-%m-%d"
    190                                        (org-publish-find-date entry project))
    191                    entry
    192                    (org-publish-find-title entry project)
    193                    (format-time-string "%Y-%m-%d"
    194                                        (org-publish-find-date entry project))))
    195           ((eq style 'tree) (file-name-nondirectory (directory-file-name entry)))
    196           (t entry)))
    197 
    198   (defun sbr/org-publish-sitemap (title list)
    199     ""
    200     (concat "#+TITLE: " title "\n\n"
    201             (org-list-to-subtree list)))
    202   #+end_src
    203 - =orgmode= is able to generate a rss, with =ox-rss=. This is what we are going to use to
    204   generate the rss files for ~posts~ and ~articles~. This is _heavily_ customized.
    205   #+begin_src emacs-lisp
    206   (defun sbr/org-get-first-paragraph (file)
    207     "Get string content of first paragraph of file."
    208     (ignore-errors
    209       (with-temp-buffer
    210       (insert-file-contents file)
    211       (goto-char (point-min))
    212       (show-all)
    213       (let ((first-begin (progn
    214                            (org-forward-heading-same-level 1)
    215                            (next-line)
    216                            (point)))
    217             (first-end (progn
    218                          (org-next-visible-heading 1)
    219                          (point))))
    220         (buffer-substring first-begin first-end)))))
    221 
    222   (defun sbr/org-rss-publish-to-rss (plist filename pub-dir)
    223     "Prepare rss.org file before exporting."
    224     (let* ((postsdir (plist-get plist :base-directory)))
    225       (with-current-buffer (find-file filename)
    226         (erase-buffer)
    227         (insert "#+TITLE: Posts\n")
    228         (insert "#+AUTHOR: Vincent Demeester\n")
    229         (insert "#+OPTIONS: toc:nil\n")
    230         (let* ((files-all
    231                 (reverse (directory-files "." nil
    232                                           "[0-9-]+.*\\.org$")))
    233                (files (subseq files-all 0 (min (length files-all) 30))))
    234           (message (format "foo: %s" filename))
    235           (dolist (post files)
    236             (let* ((post-file post)
    237                    (post-title (org-publish-find-title post-file plist))
    238                    (preview-str (sbr/org-get-first-paragraph post-file))
    239                    (date (replace-regexp-in-string
    240                           "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)-.*"
    241                           "\\1" post)))
    242               (insert (concat "* [[file:" postsdir "/" post "][" post-title "]]\n\n"))
    243               (org-set-property "ID" post)
    244               (org-set-property "RSS_TITLE" post-title)
    245               ;; ox-rss prepends html-link-home to permalink
    246               (org-set-property "RSS_PERMALINK"
    247                                 (concat postsdir "/"
    248                                         (file-name-sans-extension post)
    249                                         ".html"))
    250               (org-set-property
    251                "PUBDATE"
    252                (format-time-string
    253                 "<%Y-%m-%d %a %H:%M>"
    254                 (org-time-string-to-time
    255                  (replace-regexp-in-string
    256                   "\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)-.*"
    257                   "\\1" post))))
    258               (insert preview-str)
    259               (newline 1)
    260               (insert (concat "[[file:" postsdir "/" post "][(Read more)]]\n\n"))))
    261           (save-buffer))))
    262     (let ((user-mail-address "t")
    263           (org-export-with-broken-links t)
    264           (org-rss-use-entry-url-as-guid nil))
    265       (org-rss-publish-to-rss plist filename pub-dir)))
    266   #+end_src
    267 - Finally let's set the ~org-publish-project-alist~ to publish our projects
    268   #+begin_src emacs-lisp
    269   (setq org-publish-project-alist
    270         `(("posts"
    271            :base-directory "posts"
    272            :base-extension "org"
    273            :recursive t
    274            :publishing-function org-html-publish-to-html
    275            :publishing-directory "./public/posts"
    276            :exclude ,(regexp-opt '("README.org" "draft"))
    277            :auto-sitemap t
    278            :with-footnotes t
    279            :with-toc nil
    280            :with-drawers t
    281            :sitemap-filename "index.org"
    282            :sitemap-title "Posts"
    283            :sitemap-format-entry sbr/org-sitemap-format-entry
    284            :sitemap-style list
    285            :sitemap-sort-files anti-chronologically
    286            :sitemap-function sbr/org-publish-sitemap
    287            :html-head-include-scripts nil
    288            :html-head-include-default-style nil
    289            :html-head ,sbr-website-html-head
    290            :html-preamble sbr-website-html-preamble
    291            :html-postamble ,sbr-website-html-postamble)
    292           ("posts-rss"
    293            :base-directory "posts"
    294            :base-extension "org"
    295            :recursive t
    296            :html-link-home "https://vincent.demeester.fr/"
    297            :rss-link-home "https://vincent.demeester.fr/posts/"
    298            :html-link-use-abs-url t
    299            :rss-extension "xml"
    300            :publishing-directory "./public"
    301            :publishing-function (sbr/org-rss-publish-to-rss)
    302            :section-number nil
    303            :exclude ".*"
    304            :include ("index.org"))
    305           ("articles"
    306            :base-directory "articles"
    307            :base-extension "org"
    308            :recursive t
    309            :publishing-function org-html-publish-to-html
    310            :publishing-directory "./public/articles"
    311            :exclude ,(regexp-opt '("README.org" "draft"))
    312            :auto-sitemap t
    313            :with-footnotes t
    314            :with-toc nil
    315            :with-drawers t
    316            :sitemap-filename "sitemap.org"
    317            :sitemap-title "Articles"
    318            :sitemap-style tree
    319            :sitemap-sort-files anti-chronologically
    320            ;;:sitemap-format-entry sbr/org-sitemap-format-entry
    321            ;;:sitemap-function sbr/org-publish-sitemap
    322            :html-head-include-scripts nil
    323            :html-head-include-default-style nil
    324            :html-head ,sbr-website-html-head
    325            :html-preamble sbr-website-html-preamble
    326            :html-postamble ,sbr-website-html-postamble)
    327           ("articles-assets"
    328            :exclude ,(regexp-opt '("*.org"))
    329            :base-directory "articles"
    330            :base-extension ,site-attachments
    331            :publishing-directory "./public/articles"
    332            :publishing-function org-publish-attachment
    333            :recursive t)
    334           ("about"
    335            :base-directory "about"
    336            :base-extension "org"
    337            :exclude ,(regexp-opt '("README.org" "draft"))
    338            :index-filename "index.org"
    339            :recursive nil
    340            :with-footnotes t
    341            :with-toc nil
    342            :with-drawers t
    343            :publishing-function org-html-publish-to-html
    344            :publishing-directory "./public/about"
    345            :html-head-include-scripts nil
    346            :html-head-include-default-style nil
    347            :html-head ,sbr-website-html-head
    348            :html-preamble sbr-website-html-preamble
    349            :html-postamble ,sbr-website-html-postamble)
    350           ("index"
    351            :base-directory ""
    352            :base-extension "org"
    353            :exclude ,(regexp-opt '("README.org" "draft"))
    354            :index-filename "index.org"
    355            :recursive nil
    356            :with-footnotes t
    357            :with-toc nil
    358            :with-drawers t
    359            :with-title nil
    360            :publishing-function org-html-publish-to-html
    361            :publishing-directory "./public"
    362            :html-head-include-scripts nil
    363            :html-head-include-default-style nil
    364            :html-head ,sbr-website-html-head
    365            :html-preamble sbr-website-html-preamble
    366            :html-postamble ,sbr-website-html-postamble)
    367           ("css"
    368            :base-directory "./css"
    369            :base-extension ,site-attachments
    370            :recursive t
    371            :publishing-directory "./public/css"
    372            :publishing-function org-publish-attachment
    373            :recursive t)
    374           ("images"
    375            :base-directory "./images"
    376            :base-extension ,site-attachments
    377            :publishing-directory "./public/images"
    378            :publishing-function org-publish-attachment
    379            :recursive t)
    380           ("assets"
    381            :base-directory "./assets"
    382            :base-extension ,site-attachments
    383            :publishing-directory "./public/assets"
    384            :publishing-function org-publish-attachment
    385            :recursive t)
    386           ("legacy"
    387            :base-directory "./legacy"
    388            :base-extension ,site-attachments
    389            :publishing-directory "./public/"
    390            :publishing-function org-publish-attachment
    391            :recursive t)
    392           ("all" :components ("posts" "about" "index" "articles" "articles-assets" "css" "images" "assets" "legacy" "posts-rss"))))
    393   #+end_src
    394 
    395 Here are some /inspiration/ I took for this publishing code:
    396 
    397 - [[https://thibaultmarin.github.io/blog/posts/2016-11-13-Personal_website_in_org.html][Personal website in org]]
    398 - [[https://vicarie.in/posts/blogging-with-org.html][Blogging with Org publishing]]
    399 - [[https://ambrevar.xyz/blog-architecture/][A blog in pure Org/Lisp]]
    400 - [[https://zngguvnf.org/2017-07-13--blogging-with-org-static-blog.html][Blogging with org-static-blog]]
    401 - [[https://bastibe.de/2013-11-13-blogging-with-emacs.html][Blogging with Emacs]]
    402 - [[https://gjhenrique.com/meta.html][Blogging with org-mode and Gitlab Pages]]
    403 
    404 
    405 * DONE Styling
    406 CLOSED: [2020-03-23 Mon 19:02]
    407 :PROPERTIES:
    408 :ID:       052e297f-01b2-41d3-9689-cd2946b56cfb
    409 :END:
    410 :LOGBOOK:
    411 - State "DONE"       from "STARTED"    [2020-03-23 Mon 19:02]
    412 :END:
    413 
    414 The style of the website has be as simple as possible, and also really light. This means:
    415 - use default system font as much as possible
    416 - have a small stylesheet, rely on the default as much as we can
    417 
    418 In addition, I want support for:
    419 - side notes
    420 - code syntax highlight
    421 - table of content
    422 
    423 The inspiration for this website, in term of style are the following:
    424 - [[https://vincent.demeester.fr/posts/2018-08-16-gotest-tools-assertions/][Vincent Demeester]]
    425 - [[https://braindump.jethro.dev/zettels/zettelkasten/][Jethro's Braindump]]
    426 - [[https://hamberg.no/gtd/][GTD in 15 minutes – A Pragmatic Guide to Getting Things Done]]
    427 - [[https://www.inkandswitch.com/local-first.html][Local-first software: You own your data, in spite of the cloud]]
    428 - [[https://archive.casouri.cat/note/2018/blog-in-org-mode-revisited/index.html][Blog in Org Mode, Revisited]]
    429 - [[https://kind.sigs.k8s.io/][kind]]
    430 - [[http://willcrichton.net/notes/idioms-of-dynamic-languages/][Idioms of Dynamic Languages | Will Crichton]]
    431 - [[https://peter.bourgon.org/blog/2019/09/11/programming-with-errors.html][Peter Bourgon · Programming with errors]]
    432 - [[https://johv.dk/blog/bare-metal-assembly-tutorial.html][Getting started with bare-metal assembly — Jonas Hvid]]
    433 
    434 To be able to define the style a bit, let's try some things below. From this point on,
    435 this is random content just to try my style out. 👼
    436 
    437 There is more in [[../articles/sandbox.org][the sandbox]].
    438 
    439 ------
    440 
    441 #+begin_abstract
    442 Let's dig into how I setup my development environment when working on ~tektoncd/pipeline~
    443 #+end_abstract
    444 
    445 #+TOC: headlines 2
    446 
    447 ** sub-heading 1
    448 
    449 Checking for errors is *very common* in Go, having =Comparison= function for it was a requirement.
    450 
    451 #+BEGIN_aside
    452 This is a side note. If collection is a string, item must also be a string, and is
    453 compared using =strings.Contains()=. If collection is a Map, contains will succeed if item
    454 is a key in the map.
    455 #+END_aside
    456 
    457 - =Error= fails if the error is =nil= *or* the error message is not the expected one.
    458 - =ErrorContains= fails if the error is =nil= *or* the error message does not contain the expected substring.
    459 - =ErrorType= fails if the error is =nil= *or* the error type is not the expected type.
    460 
    461 Let's first look at the most used : =Error= and =ErrorContains=.
    462 When you're working on ~pipeline~, usually you want :
    463 
    464 1. make sure it compiles : ~go build -v ./..~
    465 2. Running unit tests : ~go test ./...~ (bonus use [[https://github.com/vdemeester/ram][~ram~]] for continuous testing)
    466 3. End-to-end tests : ~go test -tags e2e ./...~ (or simply using `./test/` package)
    467 
    468    *Make sure you re-deploy before running the e2e tests* using ~ko apply -f ./config~,
    469    otherwise you're testing the wrong code.
    470 
    471 #+BEGIN_SRC go
    472   var err error
    473   // will fail with : expected an error, got nil
    474   assert.Check(t, cmp.Error(err, "message in a bottle"))
    475   err = errors.Wrap(errors.New("other"), "wrapped")
    476   // will fail with : expected error "other", got "wrapped: other"
    477   assert.Check(t, cmp.Error(err, "other"))
    478   // will succeed
    479   assert.Check(t, cmp.ErrorContains(err, "other"))
    480 #+END_SRC
    481 
    482 #+CAPTION: This is the caption for the next figure link (or table)
    483 #+NAME:   fig:SED-HR4049
    484 #+ATTR_ORG: :width 400/600
    485 #+ATTR_HTML: :width 100%
    486 [[../images/emacs/2020-02-29-13-46-08.png]]
    487 
    488 If this is the case, then what makes dynamic languages feel easy? Can we take what we learn in answering this question and improve the ergonomics of our static languages? For example, in 2018, I don’t think there’s as strong an argument for “you don’t have to write out the types,” since modern type inference eliminates most of the keystrokes required (even though many major languages still lack such facilities). Plus, saving a few keystrokes does not seem like a critical bottleneck in the programming process.
    489 
    490 #+NAME: fig:tektoncd-logo
    491 [[../images/emacs/2020-02-29-14-41-59.png]]
    492 
    493 
    494 ** sub-heading 2
    495 
    496 Some content from my other org-mode files.
    497 
    498 #+begin_description
    499 I already wrote 2 previous posts about golang and testing. It's something I care deeply about and I wanted to continue
    500 writing about it. It took me a bit more time than I thought, but getting back to it. Since the [[http://vincent.demeester.fr/posts/2017-04-22-golang-testing-golden-file/][last post]], Daniel
    501 Nephin
    502 and I worked (but mainly Daniel 🤗) on bootstrapping a testing helper library.
    503 #+end_description
    504 
    505 #+BEGIN_QUOTE
    506 Package assert provides assertions for comparing expected values to actual values. When
    507 assertion fails a helpful error message is printed.
    508 #+END_QUOTE
    509 
    510 There is already some good =testing= helpers in the Go ecosystem : [[https://github.com/stretchr/testify][=testify=]], [[http://labix.org/gocheck][=gocheck=]],
    511 [[https://github.com/onsi/ginkgo][=ginkgo=]] and a lot more — so why create a new one ? There is multiple reason for it, most
    512 of them can be seen in the following [[https://github.com/gotestyourself/gotest.tools/issues/49#issuecomment-362436026][GitHub issue]].
    513 
    514 [[https://github.com/dnephin/][Daniel]] also wrote a very useful [fn:1]converter if your code base is currently using =testify= :
    515 =gty-migrate-from-testify=.
    516 
    517 #+BEGIN_SRC sh
    518 $ go get -u gotest.tools/assert/cmd/gty-migrate-from-testify
    519 # […]
    520 $ go list \
    521      -f '{{.ImportPath}} {{if .XTestGoFiles}}{{"\n"}}{{.ImportPath}}_test{{end}}' \
    522      ./... | xargs gty-migrate-from-testify
    523 #+END_SRC
    524 
    525 We'll Use =Assert= for the rest of the section but any example here would work with
    526 =Check= too. When we said =Comparison= above, it's mainly the [[https://godoc.org/gotest.tools/assert#BoolOrComparison][BoolOrComparison]] interface —
    527 it can either be a boolean expression, or a [[https://godoc.org/gotest.tools/assert/cmp#Comparison][cmp.Comparison]] type. =Assert= and =Check= code
    528 will be /smart/ enough to detect which one it is.
    529 
    530 #+BEGIN_SRC go
    531   assert.Assert(t, ok)
    532   assert.Assert(t, err != nil)
    533   assert.Assert(t, foo.IsBar())
    534 #+END_SRC
    535 
    536 
    537 * What's next ?
    538 :PROPERTIES:
    539 :ID:       678bf6cb-68e7-4fc9-af50-23d283293ab1
    540 :END:
    541 
    542 One thing is to import old blog posts from [[https://vincent.demeester.fr][vincent.demeester.fr]]. This is easily done with
    543 [[https://pandoc.org/][Pandoc]] and a small bash loop — and some manual adjusting later on 😛.
    544 
    545 #+begin_src bash
    546 for post in ~/src/github.com/vdemeester/blog/content/posts/*.md; do
    547     pandoc -f markdown -t org -o posts/$(basename -s .md ${post}).org ${post}
    548 done
    549 #+end_src
    550 
    551 
    552 What is still /to do/ after this initial take.
    553 
    554 - [ ] List =FILETAGS= for taximony
    555 - [ ] Maybe use [[https://css-tricks.com/snippets/css/complete-guide-grid/][css grid]] for the UI
    556 
    557 * Footnotes
    558 :PROPERTIES:
    559 :ID:       220a0cce-39c3-4b5f-aaa0-66e3a2450f04
    560 :END:
    561 
    562 [fn:1] foo is bar, bar is baz