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